Repository: bata24/gef Branch: dev Commit: bbd9dad04df6 Files: 42 Total size: 7.2 MB Directory structure: gitextract_mzckyijz/ ├── .gitignore ├── LICENSE ├── README.md ├── asmdb/ │ └── x86data.js ├── dev/ │ ├── README.md │ ├── angr/ │ │ └── test.c │ ├── bpf/ │ │ ├── bpf_insn.h │ │ └── test.c │ ├── dma-heap/ │ │ └── test.c │ ├── dtor/ │ │ ├── test.c │ │ └── test2.c │ ├── glibc-heap/ │ │ └── test.c │ ├── golang/ │ │ └── gc.go │ ├── iouring/ │ │ ├── test.c │ │ └── test2.c │ ├── ipcs/ │ │ ├── test_posix.c │ │ └── test_sysv.c │ ├── js/ │ │ └── test.js │ ├── partition-alloc-dump/ │ │ ├── downloader.py │ │ └── www/ │ │ └── inf-loop.html │ ├── seccomp/ │ │ ├── test1.c │ │ └── test2.c │ ├── simple-heap/ │ │ └── test.c │ ├── sock/ │ │ └── test.c │ ├── split/ │ │ ├── de-split.py │ │ └── split.py │ ├── tls/ │ │ └── test.c │ ├── tmux/ │ │ └── tmux_setup.py │ ├── update-kmalloc-tracer/ │ │ └── check.py │ ├── update-kops/ │ │ └── check.py │ ├── update-syscalls/ │ │ └── update-syscalls.py │ └── zellij/ │ ├── zellij-init.py │ └── zellij-wrapper.py ├── docs/ │ ├── BUILDING-QEMU-SYSTEM-ENV.md │ ├── FAQ.md │ ├── HOW-TO-DEBUG-AARCH64-MTE.md │ ├── QEMU-USER-SUPPORTED-ARCH.md │ └── SUPPORTED-MODE.md ├── gef.py ├── install-minimal.sh ├── install-no-uv.sh └── install-uv.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ a.out gc chrome_beta chrome_dev chrome_stable linux-* ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2021-2026 bata24 (@bata_24) This is a fork of GEF (https://github.com/hugsy/gef). This software is released under the MIT license. See https://opensource.org/licenses/MIT Copyright (c) 2013-2026 crazy rabbidz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ![](images/logo.png) ## Image ![](images/context.png) ## Table of Contents - [What Is This?](#what-is-this) - [Setup](#setup) - [Supported Environment](#supported-environment) - [Install](#install) - [Upgrade](#upgrade) - [Uninstall](#uninstall) - [Dependencies](#dependencies) - [Added / Improved Features](#added--improved-features) - [Supported Modes](#supported-modes) - [Qemu-system Cooperation](#qemu-system-cooperation) - [Qemu-user Cooperation](#qemu-user-cooperation) - [Heap Dump Features](#heap-dump-features) - [Improved Features](#improved-features) - [Added Features](#added-features) - [Other](#other) - [FAQ](#faq) - [Links](#links) ## What Is This? This is a fork of [GEF](https://github.com/hugsy/gef) that includes three major improvements: 1. Adds heuristic commands for kernel debugging __without requiring a symbolized `vmlinux`__ (for `qemu-system`, supports Linux kernel 3.x-6.19.x). 2. Expands support to [many architectures](docs/QEMU-USER-SUPPORTED-ARCH.md) (for `qemu-user`). 3. Provides heap dump commands for multiple memory allocators. Numerous other commands have been added and enhanced. Enjoy! ## Setup ### Supported Environment - Verified on Ubuntu 24.04-25.10. - Expected to work on Ubuntu 22.04-23.10. - Might work on Ubuntu 20.04-21.10, though not recommended. ### Install - Run the following command: ```bash wget -q https://raw.githubusercontent.com/bata24/gef/dev/install-uv.sh -O- | sudo sh ``` - Notes - To simplify installation, `gef.py` is always installed to `/root/.gef/gef.py` - The required Python packages are in `/root/.gef/.venv-gef`. - GEF's directory (`/root/.gef`) is also registered in `/root/.gdbinit`. - For more installation options (for non-`root` user, etc), see [docs/FAQ.md](docs/FAQ.md). - Or, quick trial (no installation): ```bash wget https://raw.githubusercontent.com/bata24/gef/dev/gef.py echo "source $(pwd)/gef.py" >> ~/.gdbinit ``` - Notes - Most features work fine even without external tools or `root` privileges. - For limitations, see [docs/FAQ.md](docs/FAQ.md). ### Upgrade ```bash python3 /root/.gef/gef.py --upgrade ``` - Note - If you get errors after upgrading, it may be due to old config. Try renaming `/root/.gef.rc`. ### Uninstall ```bash rm -rf /root/.gef rm -f /root/.gef.rc rm -rf /tmp/gef sed -i -e '/from gef import/d' /root/.gdbinit ``` ### Dependencies Please refer to [install-uv.sh](install-uv.sh) for installation requirements. ## Added / Improved Features ### Supported Modes - Standard debugging - Attaching to a running process - Attaching to a process in an isolated namespace (e.g., attaching from outside a **Docker** container) - Connecting to **Gdbserver** - Connecting to the GDB stub of **Qemu-system** - Connecting to the GDB stub of **Qemu-user** - Connecting to the GDB stub of **Intel Pin** - Connecting to the GDB stub of **Intel SDE** - Connecting to the GDB stub of **Qiling framework** - Connecting to the GDB stub of **KGDB** (requires GDB version 12 or later) - Connecting to the GDB stub of **VMWare** - Connecting to the GDB stub of **Wine** - Debugging with **Record and replay** (`rr replay`) For a comprehensive list and additional details, see [docs/SUPPORTED-MODE.md](docs/SUPPORTED-MODE.md). ### Qemu-system Cooperation - `pagewalk`: dumps page tables. - x64 (Supported: 4-Level/5-Level Paging) - ![](images/pagewalk-x64.png) - x86 (Supported: PAE/Non-PAE) - ![](images/pagewalk-x86.png) - ARM64 (Supported: Cortex-A only, EL0-EL3, Stage1-2) - ARM v8.7 base. 32bit mode is NOT supported. - ![](images/pagewalk-arm64.png) - Here is a sample of each level pagewalk from HITCON CTF 2018 `super_hexagon`. - ![](images/pagewalk-arm64-el123.png) - Secure memory scanning is also supported, but you have to break in the secure world. - ![](images/pagewalk-arm64-secure.png) - Pseudo memory map without detailed flags and permissions can be output even in the normal world (when OP-TEE). - ![](images/pagewalk-arm64-secure-pseudo.png) - ARM (Supported: Cortex-A only, LPAE/Non-LPAE, PL0/PL1) - ARM v7 base. PL2 is NOT supported. - ![](images/pagewalk-arm.png) - Secure memory scanning is also supported, and you don't have to break in the secure world (unlike ARM64). - ![](images/pagewalk-arm-secure.png) - `v2p`/`p2v`: displays the transformation between virtual addresses and physical addresses. - ![](images/v2p-p2v.png) - `xp`: is a shortcut for physical memory dump. - ![](images/xp.png) - `qreg`: displays the register values from qemu-monitor (allows getting values like `$cs` even under qemu 2.x). - It is a shortcut for `monitor info registers`. - It also prints the details of each bit of the system register when x64/x86. - ![](images/qreg.png) - `sysreg`: pretty prints system registers. - It shows `info registers` results, excluding general registers. - ![](images/sysreg.png) - `msr`: reads/writes MSR (Model Specific Registers) value by embedding/executing dynamic assembly. - Supported on x64 and x86. - ![](images/msr.png) - `cet`: displays Intel CET settings. - `vbar`: displays ARM/ARM64 vector table. - ![](images/vbar.png) - `kbase`: displays the kernel base address. - `kversion`: displays the kernel version. - `kcmdline`: displays the kernel cmdline used at boot time. - `kcurrent`: displays current task address. - ![](images/kbase-kversion-kcmdline-kcurrent.png) - `kvmmap`: prints kernel memory map. - ![](images/kvmmap.png) - `ksymaddr-remote`: displays kallsyms information from scanning kernel memory. - Supported kernel versions: 3.x to 6.19.x. - ![](images/ksymaddr-remote.png) - `ksymaddr-remote-apply`/`vmlinux-to-elf-apply`: applies kallsyms information obtained by `ksymaddr-remote` or `vmlinux-to-elf` to gdb. - ![](images/ksymaddr-remote-apply.png) - ![](images/vmlinux-to-elf-apply.png) - Once you get a symboled pseudo ELF file, you can reuse and apply it automatically even after rebooting qemu-system. - `vmlinux-to-elf-apply` and `ksymaddr-remote-apply` provide almost the same functionality. - `vmlinux-to-elf-apply`: Requires installation of external tools. Create `vmlinux` with symbols. - `ksymaddr-remote-apply`: Requires no external tools. Create a blank ELF with embedded symbols only. - `ktypes`: displays kernel type information from scanning kernel memory. - ![](images/ktypes.png) - `ktypes-load`: loads kernel type information from scanning kernel memory. - ![](images/ktypes-load.png) - `slub-dump`: dumps slub free-list. - Supported on x64/x86/ARM64/ARM + `SLUB` + no-symbol + kASLR. - Supported regardless of whether `CONFIG_SLAB_FREELIST_HARDENED` is `y` or `n`. - Supported regardless of whether `CONFIG_SLAB_VIRTUAL` is `y` or `n` (x64 only). - It supports `sheaf/barn` mechanism for linux 6.18~. - It supports dumping partial pages (`-v`) and NUMA node pages (`-vv`). - Since `page_to_virt` is difficult to implement, it will heuristically determine the virtual address from the free-list. - ![](images/slub-dump.png) - `slab-dump`: dumps slab free-list. - Supported on x64/x86/ARM64/ARM + `SLAB` + no-symbol + kASLR. - ![](images/slab-dump.png) - `slob-dump`: dumps slob free-list. - Supported on x64/x86/ARM64/ARM + `SLOB` + no-symbol + kASLR. - ![](images/slob-dump.png) - `slub-tiny-dump`: dumps slub-tiny free-list. - Supported on x64/x86/ARM64/ARM + `SLUB-TINY` + no-symbol + kASLR. - ![](images/slub-tiny-dump.png) - `slab-contains`: resolves the slab cache (`kmem_cache`) that a certain address (object) belongs to (for `SLUB`/`SLUB-TINY`/`SLAB`). - ![](images/slab-contains.png) - For `SLUB`/`SLUB-TINY`, if all chunks belonging to a certain `page` are in use, they will not be displayed by `slub-dump`/`slub-tiny-dump` command. - Even with such an address (object), this command may be able to resolve `kmem_cache`. - `kmem-cache-alias`: dumps `kmem_cache` alias name. - ![](images/kmem-cache-alias.png) - `buddy-dump`: dumps the zone of the page allocator (buddy allocator) free-list. - ![](images/buddy-dump.png) - `vmalloc-dump`: dumps `vmalloc` used-list and freed-list. - ![](images/vmalloc-dump.png) - `page`: displays the transformation between a `struct page` and its virtual/physical address. - ![](images/page.png) - There are shortcuts: `virt2page`, `page2virt`, `phys2page` and `page2phys`. - `slab-virtual`: displays the transformation between slab-meta and its slab-data/`struct page` address (for `CONFIG_SLAB_VIRTUAL=y`). - ![](images/slab-virtual.png) - `pageinfo`: dumps `struct page->{flags,page_type}`. - `highmem-dump`: dumps `HighMem` mappings. - ![](images/highmem-dump.png) - `kchecksec`: checks kernel security. - ![](images/kchecksec.png) - `kmagic`: displays useful addresses in the kernel. - ![](images/kmagic.png) - `kconfig`: dumps the kernel config if available. - ![](images/kconfig.png) - `syscall-table-view`: displays the system call table. - ![](images/syscall-table-view.png) - It also dumps the ia32/x32 syscall table under x64. - It also dumps the compat syscall table under ARM64. - `ksysctl`: dumps the sysctl parameters. - ![](images/ksysctl.png) - `ktask`: displays each task's address. - ![](images/ktask.png) - It also displays the memory map of the userland process. - ![](images/ktask-maps.png) - It also displays the register values saved on the kstack of the userland process. - ![](images/ktask-regs.png) - It also displays the file descriptors of the userland process. - ![](images/ktask-fd.png) - It also displays the signal handlers of the userland process. - ![](images/ktask-sighands.png) - It also displays the namespaces of the userland process. - ![](images/ktask-namespaces.png) - It also displays the seccomp-filter. - ![](images/ktask-seccomp.png) - `kmod`: displays each module's address. - ![](images/kmod.png) - It also displays the symbols of each module. - ![](images/kmod-syms.png) - `kload`: loads `vmlinux` without a load address. - It is useful if you have a `vmlinux` with `debuginfo` at hand. - `kmod-load`: loads the kernel module without a load address. - It is useful if you have a kernel module with `debuginfo` at hand. - `kops`: displays each operation's member. - ![](images/kops.png) - `kcdev`: displays information for each character device. - ![](images/kcdev.png) - `kbdev`: displays information for each block device. - If there are too many block devices, detection will not be successful. - This is because block devices are not managed in one place, so I use the list of `bdev_cache` obtained from the slub-dump results. - ![](images/kbdev.png) - `kfilesystems`: dumps supported file systems. - ![](images/kfilesystems.png) - `kclock-source`: dumps the clocksource list. - ![](images/kclock-source.png) - `kdmesg`: dumps the ring buffer of the dmesg area. - ![](images/kdmesg.png) - `kpipe`: displays information for each pipe. - ![](images/kpipe.png) - `kbpf`: dumps the BPF information. - ![](images/kbpf.png) - `ktimer`: dumps the timer. - ![](images/ktimer.png) - `kpcidev`: dumps the PCI devices. - ![](images/kpcidev.png) - `kipcs`: dumps IPCs information (System V semaphore, message queue and shared memory). - ![](images/kipcs.png) - `kdevio`: dumps I/O-port and I/O-memory information. - ![](images/kdevio.png) - `kdmabuf`: dumps DMA-BUF information. - ![](images/kdmabuf.png) - `kirq`: dumps irq information. - ![](images/kirq.png) - `knetdev`: displays net devices. - ![](images/knetdev.png) - `ksearch-code-ptr`: searches for the code pointer in kernel data area. - ![](images/ksearch-code-ptr.png) - `thunk-tracer`: collects and displays the thunk function addresses that are called automatically (x64/x86 only). - If this address comes from RW area, this is useful for getting RIP. - ![](images/thunk-tracer.png) - `usermodehelper-tracer`: collects and displays the information that is executed by `call_usermodehelper_setup`. - ![](images/usermodehelper-tracer.png) - `kmalloc-tracer`: collects and displays information when `kmalloc`/`kfree`. - ![](images/kmalloc-tracer.png) - `kmalloc-allocated-by`: calls a predefined set of system calls and prints structures allocated by `kmalloc` or freed by `kfree`. - ![](images/kmalloc-allocated-by.png) - `ktrace`: traces kernel functions and arguments. - ![](images/ktrace.png) - `xsm`: dumps secure memory when gdb is in normal world. - Supported on ARM64 and ARM. - ![](images/xsm.png) - `wsm`: writes the value to secure memory when gdb is in normal world. - Supported on ARM64 and ARM. - ![](images/wsm.png) - `bsm`: sets the breakpoint to secure memory when gdb is in normal world. - Supported on ARM64 and ARM. - ![](images/bsm.png) - `optee-break-ta`: sets the breakpoint to the offset of OPTEE-Trusted-App when gdb is in normal world. - Supported on ARM64 and ARM. - ![](images/optee-break-ta.png) - `optee-smc-service-dump`: dumps OPTEE SMC services. - Supported on ARM64. - ![](images/optee-smc-service-dump.png) - `optee-ta-dump`: dumps the information of OPTEE-Trusted-Apps from the memory or specified host directory. - Supported on ARM64 and ARM. - ![](images/optee-ta-dump.png) - `optee-shm-list`: shows the information of dynamic shared-memory buffers. - Supported on ARM64 and ARM. - ![](images/optee-shm-list.png) - `pac-keys`: pretty prints ARM64 PAC keys. - Supported on ARM64. - ![](images/pac-keys.png) - `uefi-ovmf-info`: dumps addresses of some important structures in each boot phase of UEFI when OVMF is used. - Supported on x64. - ![](images/uefi-ovmf-info.png) - `qemu-device-info`: dumps device information for qemu-escape. ### Qemu-user Cooperation - `si`/`ni`: are wrappers for native `si`/`ni` if OpenRISC 1000 or CRIS. - On OpenRISC 1000 architecture, branch operations don't work well, so GEF uses breakpoints to simulate. - On CRIS architecture, `stepi`/`nexti` commands don't work well, so GEF uses breakpoints to simulate. - If you want to use native `si`/`ni`, use the full form `stepi`/`nexti`. - `c`: is the wrapper for native `c` if gdb is connected to `qemu-user` or `Intel Pin`. - When connecting to gdb stub of `qemu-user` or `Intel Pin`, gdb does not trap `SIGINT` during `continue`. - If you want to trap, you need to issue `SIGTRAP` on the `qemu-user` or `pin` side, but switching screens is annoying. - This command realizes a pseudo `SIGTRAP` trap by trapping `SIGINT` on the Python side and throwing `SIGTRAP` back to `qemu-user` or `Intel Pin`. - It works only for local `qemu-user` or `Intel Pin`. - If you want to use native `c`, use the full form `continue`. ### Heap Dump Features - Glibc heap commands has been improved. Supports up to glibc 2.43. - It changes the color and prints a symbol if it exists. - ![](images/heap-bins.png) - They print bins information if the chunk is in free-list. - ![](images/heap-if-in-freelist.png) - Thread arena is supported for all `heap` commands. - Use `-a` option. - It supports some new sub-commands. - `heap arenas` - ![](images/heap-arenas.png) - `heap top` - ![](images/heap-top.png) - `heap bins-simple`: displays the heap like pwndbg style. - ![](images/heap-bins-simple.png) - `heap parse`: displays the heap like as Pwngdb/angelheap style. - ![](images/heap-parse.png) - `heap try-malloc` - ![](images/heap-try-malloc.png) - `heap try-free` - ![](images/heap-try-free.png) - You can also execute any command on the memory status after emulation is completed. - ![](images/heap-try-free-bs.png) - `heap try-realloc` - `heap try-calloc` - `heap tcache-index-helper` - ![](images/heap-index-helper.png) - `heap find-fake-fast`: searches for a memory with a size-like value that can be linked to the fastbin free-list. - ![](images/heap-find-fake-fast.png) - `heap extract-heap-addr`: analyzes tcache-protected-fd introduced from glibc-2.32. - ![](images/heap-extract-heap-addr.png) - `heap calc-protected-fd`: calculates a valid value as protected fd. - ![](images/heap-calc-protected-fd.png) - `heap visual-heap`: is colorized heap viewer. - ![](images/heap-visual-heap.png) - `heap tracer`: has been integrated from `heap-analysis-helper` and rewritten. - ![](images/heap-tracer.png) - `heap dump-image`: visualizes chunks on a heap as composition image. - ![](images/heap-dump-image.png) - `heap snapshot`: takes a snapshot of the heap. - `heap snapshot-compare`: compares current heap with a previously saved heap-snapshot. - ![](images/heap-snapshot-compare.png) - uClibc heap commands are added. - `uclibc-ng-heap-dump`: dumps uClibc-ng heap chunks. - Supported on x64 and x86, based on uClibc-ng malloc-standard. - ![](images/uclibc-ng-heap-dump.png) - How to test (x64): - Download and extract `x86-64--uclibc--stable-2025.08-1.tar.bz2` from https://toolchains.bootlin.com/ - Add `/PATH/TO/x86_64-buildroot-linux-uclibc/bin` to `$PATH`, then build as `x86_64-linux-gcc test.c`. - Fix interpreter by `patchelf --set-interpreter /PATH/TO/x86_64-buildroot-linux-uclibc/sysroot/lib/ld64-uClibc.so.0 a.out`. - `uclibc-ng-visual-heap`: is colorized heap viewer for uClibc-ng. - ![](images/uclibc-ng-visual-heap.png) - `partition-alloc-dump`: dumps Partition-Alloc free-list for chromium. - ![](images/partition-alloc-dump.png) - This command is reserved for the implementation of the latest version of Chromium. - Currently tested: v146.x / [1579809](https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/1579809/) / e1b69f3442bc215bd78dc42426c6b3232b5cd8be - Supported on x64 and ARM64 (maybe it works on x86/ARM too, but not tested). - It will try heuristic search if the binary has no symbol. - How to test: - See [dev/partition-alloc-dump/downloader.py](dev/partition-alloc-dump/downloader.py). - `tcmalloc-dump`: dumps TCMalloc (`gperftools-2.16-1` or named `libgoogle-perftools{4,-dev}`) free-list (x64 only). - ![](images/tcmalloc-dump.png) - How to test: - Execute as `LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc.so ./a.out`. - `musl-heap-dump`: dumps Musl-Libc v1.2.5 heap chunks (x64/x86 only). - ![](images/musl-heap-dump.png) - How to test: - Get and extract the latest source from https://musl.libc.org/ - Build with `./configure && make install`. - Build as `/usr/local/musl/bin/musl-gcc test.c`. - `go-heap-dump`: dumps Go Language v1.24.4 mheap (x64 only). - ![](images/go-heap-dump.png) - `tlsf-heap-dump`: dumps TLSF (Two-Level Segregated Fit) v2.4.6 free-list (x64 only). - ![](images/tlsf-heap-dump.png) - How to test (x64): - Get and extract the latest source from http://www.gii.upv.es/tlsf/ - Build with `cd TLSF-2.4.6/src && make && cd ../examples && make` then use `test1` etc. - `hoard-heap-dump`: dumps Hoard v3.2 (2025/12/31) free-list (x64 only). - ![](images/hoard-heap-dump.png) - How to test (x64): - Get and extract the latest source from https://github.com/emeryberger/Hoard - Build with `cd Hoard/src && make`. - Execute as `LD_PRELOAD=/PATH/TO/libhoard.so ./a.out`. - `mimalloc-heap-dump`: dumps mimalloc free-list (x64 only). - ![](images/mimalloc-heap-dump.png) - How to test (x64): - Get and extract the latest source from https://github.com/microsoft/mimalloc - Build with `mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make`. - Execute as `LD_PRELOAD=/PATH/TO/libmimalloc.so ./a.out`. - `scalloc-heap-dump`: dumps scalloc free-list (x64 only). - ![](images/scalloc-heap-dump.png) - How to test (x64): - Get and extract the latest source from https://github.com/cksystemsgroup/scalloc - Fix the bug with `sed -i -e 's/\(strncat(.*\), 1);/\1, 2);/' src/log.h`. - Build with `gyp --depth . scalloc.gyp && make`. - Enable overcommit with `echo 1 > /proc/sys/vm/overcommit_memory`. - Execute as `LD_PRELOAD=/PATH/TO/libscalloc.so ./a.out`. - `snmalloc-heap-dump`: dumps snmalloc free-list (x64 only). * ![](https://raw.githubusercontent.com/bata24/gef/dev/images/snmalloc-heap-dump.png) * How to test (x64): - Get and extract the latest source from https://github.com/microsoft/snmalloc - Build with `mkdir build && cd build && cmake -G Ninja .. -DCMAKE_BUILD_TYPE=Debug && ninja`. - Execute as `LD_PRELOAD=/PATH/TO/libsnmallocshim.so ./a.out`. - `optee-bget-dump`: dumps bget allocator of OPTEE-Trusted-App. - ![](images/optee-bget-dump.png) - `v8`: displays v8 (Chromium and `d8`) tagged object. - ![](images/v8.png) - It also loads more commands from latest gdbinit for v8. - ![](images/v8-load.png) - `cage`: displays v8 (Chromium and `d8`) ubercage. - ![](images/cage.png) - `v8-list-maps`: lists v8 (Chromium and `d8`) built-in maps. - ![](images/v8-list-maps.png) - `v8-dump-space`: dumps v8 (Chromium and `d8`) heap objects in each space. - ![](images/v8-dump-space.png) ### Improved Features - `vmmap` - It displays the memory map information even when connecting to gdb stub of `qemu-user`. - ![](images/vmmap-qemu-user.png) - It also supports `Intel Pin`. - ![](images/vmmap-pin.png) - It also supports `Intel SDE`. - ![](images/vmmap-sde.png) - It is redirected to `kvmmap` when connecting to gdb stub of `qemu-system`. - It supports detection and coloring of `Writable`, `ReadOnly`, `None` and `RWX` regions. - It shows the area each register points to. - `registers` - It also shows raw values of the flag register, the current ring, the exception level, the secure state, etc. - ![](images/registers-x64.png) - ![](images/registers-arm64.png) - ![](images/registers-arm.png) - `context` - It supports automatic display of system call arguments when calling a system call. - ![](images/context-syscall-args.png) - It supports new commands: - `context on` - `context off` - It supports automatic display of address and value when accessing memory. - ![](images/context-memory-access.png) - It supports smart symbol printing for C++ function. - ex: `std::map>` will be replaced by `std::map<...>`. - ![](images/smart-cpp-function-name.png) - command: `gef config context.smart_cpp_function_name true` or `smart-cpp-function-name` (later is used to toggle). - `telescope` - It displays ordinal numbers as well as offsets. - It displays if there are canaries and return addresses in the target area. - ![](images/telescope.png) - It supports blacklist address features (to avoid dying when touching the address mapped to the serial device). - It also shows the symbol if available. - It supports some new options: - `--is-addr` - `--is-not-addr` - `--is-zero` - `--is-not-zero` - `--tag` - `--uniq` - `--interval` - `--depth` - `--phys` - `--list-head` - `--slab-contains` - `--slab-contains-unaligned` - `proc-info` - It displays some additional information. - ![](images/proc-info.png) - `elf-info` - It displays Program Header and Section Header. - It supports parsing from memory. - It supports parsing remote binary (if download feature is available). - ![](images/elf-info.png) - `xinfo` - It shows more information. - ![](images/xinfo.png) - It also supports kernel debugging. - `checksec` - It shows additional information. - Static or Dynamic or Static-PIE - Stripped or not - Debuginfo or not - Intel CET IBT/SHSTK - ARMv8 PAC, ARMv8 MTE - RPATH, RUNPATH - Clang CFI/SafeStack - System-ASLR, GDB ASLR setting - It supports parsing remote binary (if download feature is available). - ![](images/checksec.png) - `got` - It displays not only GOT address but also PLT address. - ![](images/got.png) - It scans `.plt.sec` section if Intel CET is enabled. - It can also display the GOT of the library. - ![](images/got-libc.png) - It can also display `type`, `offset`, `reloc_arg`, `section` and `permission`. - ![](images/got-v.png) - `canary` - It displays all canary positions in memory. - ![](images/canary.png) - `edit-flags` - It displays the meaning of each bit if `-v` option is provided. - ![](images/edit-flags-x64.png) - ![](images/edit-flags-arm.png) - ![](images/edit-flags-arm64.png) - `unicorn-emulate` - It reads and writes correctly to the address pointed to by `$fs`/`$gs`. - It supports a new mode to stop after executing N instructions (`-g`). - It shows changed memories. - ![](images/unicorn-emulate.png) - It supports replacing the GOT of string functions using avx2 or NEON, which unicorn does not support, with slower original functions. - `ropper` - It does not reset autocomplete settings after calling imported `ropper`. - `hexdump` - It supports physical memory if under qemu-system. - It will retry with adjusting read size if reading memory fails. - By default, the same line is omitted. - ![](images/hexdump.png) - `patch` - It supports physical memory if under qemu-system. - Added some new modes: - `patch hex` - `patch pattern` - `patch nop` - `patch inf` - `patch trap` - `patch ret` - `patch syscall` - `patch history` - `patch revert` - `patch range-replace` - `nop` command has been integrated into `patch` as sub-command. - ![](images/patch.png) - `search-pattern` - It is supported under qemu-system (in short, it works without `/proc/self/maps`) - It supports some new options: - `--hex` - `--hex-regex` - `--aligned` - `--perm` - `--interval` - `--limit` - `--max-region-size` - `--phys` - It also searches for UTF-16 string if target string is ASCII. - ![](images/search-pattern.png) - `mprotect` - Rewritten to use `call-syscall`. - `hijack-fd` - It supports more architectures. - ![](images/hijack-fd.png) - `format-string-helper` - It supports more `printf`-like functions. - `theme` - Supports many colors. - ![](images/theme.png) - ![](images/theme-colors-sample.png) - `reset-cache` - It has been integrated into `gef` as sub-command. - The cache structure within GEF has changed significantly. This command corresponds to them. - `tmux-setup` - It has been integrated into `gef` as sub-command. - `screen` is no longer supported. - `tmux` settings are predefined and cannot be customized. - If you want to customize it, edit [dev/tmux/tmux_setup.py](dev/tmux/tmux_setup.py) and run `source /path/to/tmux_setup.py`. - If you're a `zellij` user, you can use [dev/zellij/zellij-wrapper.py](dev/zellij/zellij-wrapper.py). Run this script before starting `gdb` or `zellij`. ### Added Features - `pid`/`tid`: prints pid and tid. - `filename`: prints filename. - `fds`: shows opened file descriptors. - `auxv`: pretty prints ELF auxiliary vector. - Supported also under `qemu-user`. - ![](images/auxv.png) - `argv`/`envp`: pretty prints argv and envp. - ![](images/argv-envp.png) - `dumpargs`: dumps arguments of current function. - ![](images/dumpargs.png) - `vdso`: disassembles the text area of vdso smartly. - ![](images/vdso.png) - `vvar`: dumps the area of vvar. - This area is mapped to userland, but cannot be accessed from gdb. - Therefore, it executes the assembly code and retrieves the contents. - ![](images/vvar.png) - `gdtinfo`: pretty prints GDT entries. If userland, show sample entries. - ![](images/gdtinfo.png) - `idtinfo`: pretty prints IDT entries. If userland, show sample entries. - ![](images/idtinfo.png) - `tls`: pretty prints TLS area. Requires glibc. - ![](images/tls.png) - `fsbase`/`gsbase`: pretty prints `$fs_base`, `$gs_base`. - ![](images/fsbase_gsbase.png) - `libc`/`ld`/`heapbase`/`codebase`: displays each of the base address. - ![](images/base.png) - `got-all`: shows got entries for all libraries. - `break-rva`: sets a breakpoint at relative offset from codebase. - ![](images/break-rva.png) - `command-break`: sets a breakpoint which executes user defined command if hit. - ![](images/command-break.png) - `main-break`: sets a breakpoint at `main` with or without symbols, then continue. - This is useful when you just want to run to `main` using `qemu-user` or `pin`, or debugging no-symbol ELF. - `load-break`: breaks if something is loaded. - `regdump-break`: sets a breakpoint which dumps specified registers if hit. - `multi-break`: sets multiple breakpoints easily. - `break-if-taken`/`break-if-not-taken`: sets a breakpoint which breaks if branch is taken (or not taken). - `distance`: calculates the offset from its base address. - ![](images/distance.png) - `fpu`/`mmx`/`sse`/`avx`/`avx512`: pretty prints FPU/MMX/SSE/AVX/AVX512 registers. - ![](images/fpu-mmx-sse-avx.png) - `xmmset`: sets the value to xmm/ymm/zmm register simply. - ![](images/xmmset.png) - `mmxset`: sets the value to mm register simply. - ![](images/mmxset.png) - `exec-until`: executes until specified operation. - Supports the following patterns: - call - jmp - syscall - ret - indirect-branch (x64/x86 only) - all-branch (call || jmp || ret) - memory-access (detect just `[...]`) - specified-keyword-regex - specified-condition (expressions using register or memory values) - user-code - libc-code - secure-world - region-change - ![](images/exec-until.png) - `call-trace`: traces call, ret, and syscall instructions. - ![](images/call-trace.png) - `xuntil`: executes until specified address. - It is slightly easier to use than the original until command. - `add-symbol-temporary`: adds symbol information from command-line. - ![](images/add-symbol-temporary.png) - `errno`: displays errno list or specified errno. - ![](images/errno.png) - `u2d`: shows cast/convert u64 <-> double/float. - ![](images/u2d.png) - `unsigned`: shows unsigned value. - ![](images/unsigned.png) - `convert`: shows various conversion. - ![](images/convert.png) - `addressify`: converts reverse-order hex values to address. - ![](images/addressify.png) - `walk-link-list`: walks the link list. - ![](images/walk-link-list.png) - `hexdump-flexible`: displays the hexdump with user defined format. - ![](images/hexdump-flexible.png) - `hash`: calculates various hashes, or show known-collisions. - ![](images/hash.png) - `crc`: calculates various CRCs. - ![](images/crc.png) - `json`: pretty prints json. - ![](images/json.png) - `base-n-decode`/`base-n-encode`: decodes/encodes various baseN. - ![](images/base-n-decode.png) - `morse-decode`/`morse-encode`: decodes/encodes morse code. - ![](images/morse-decode.png) - `saveo`/`diffo`: saves and diffs the command outputs. - ![](images/saveo-diffo.png) - `memcmp`: compares the contents of the address A and B, whether virtual or physical. - ![](images/memcmp.png) - `memset`: sets the value to the memory range, whether virtual or physical. - `memcpy`: copies the contents from the address A to B, whether virtual or physical. - `memswap`: swaps the contents of the address A and B, whether virtual or physical. - `meminsert`: inserts the contents of the address A to B, whether virtual or physical. - ![](images/meminsert.png) - `strlen`: detects the length of the string. - ![](images/strlen.png) - `is-mem-zero`: checks the contents of address range are all 0x00 or 0xff. - ![](images/is-mem-zero.png) - `seq-length`: detects consecutive length of the same sequence. - ![](images/seq-length.png) - `strings`: searches for ASCII string from specific location. - ![](images/strings.png) - `xs`: dumps string like `x/s` command, but with hex-string style. - ![](images/xs.png) - `xc`: dumps address like `x/x` command, but with coloring at some intervals. - ![](images/xc.png) - `ii`: is a shortcut for `x/50i $pc` with opcode bytes. - It prints the value if it is memory access operation. - ![](images/ii.png) - `extra`: manages user specified command to execute when each step. - `comment`: manages user specified temporary comment. - `seccomp`: invokes `ceccomp` or `seccomp-tools`. - `onegadget`: invokes `one_gadget`. - ![](images/onegadget.png) - `rp`: invokes `rp++` with commonly used options. - `call-syscall`: calls system call with specified values. - ![](images/call-syscall.png) - `mmap`: allocates a new memory by `call-syscall`. - `munmap`: unmaps a memory by `call-syscall`. - `killthreads`: kills specific or all threads (for `pthread`). - `constgrep`: invokes `grep` under `/usr/include/`. - ![](images/constgrep.png) - `proc-dump`: dumps each file under `/proc/PID/`. - ![](images/proc-dump.png) - `up`/`down`: are wrappers for native `up`/`down`. - It shows also backtrace. - `time`: measures the time of the GDB command. - ![](images/time.png) - `multi-line`: executes multiple GDB commands in sequence. - ![](images/multi-line.png) - `cpuid`: shows the result of cpuid(eax=0,1,2...). - ![](images/cpuid.png) - `read-system-register-for-qemu-arm`: reads system register for old `qemu-system-arm`. - `read-system-register-for-kgdb`: reads system register for kgdb (x64/ARM64 only). - `capability`: shows the capabilities of the debugging process. - ![](images/capability.png) - `dasm`: disassembles the code by capstone. - ![](images/dasm.png) - `asm-list`: lists instructions. (x64/x86 only) - ![](images/asm-list.png) - This command uses x86data.js from https://github.com/asmjit/asmdb - `syscall-search`: searches for system call by regex. - ![](images/syscall-search.png) - `dwarf-exception-handler`: dumps the DWARF exception handler information. - ![](images/dwarf-exception-handler.png) - `magic`: displays useful addresses in glibc etc. - ![](images/magic.png) - `dynamic`: dumps the `_DYNAMIC` area. - ![](images/dynamic.png) - `link-map`: dumps useful members of `link_map` with iterating. - ![](images/link-map.png) - `dtor-dump`: dumps some destructor functions list. - ![](images/dtor-dump.png) - `ptr-mangle`: shows the mangled value that will be mangled by `PTR_MANGLE`. - `ptr-demangle`: shows the demangled value of the value mangled by `PTR_MANGLE`. - ![](images/ptr-mangle-demangle.png) - `search-mangled-ptr`: searches for the mangled value from RW memory. - ![](images/search-mangled-ptr.png) - `follow`: changes `follow-fork-mode` setting. - ![](images/follow.png) - `smart-cpp-function-name`: toggles `context.smart_cpp_function_name` setting. - `ret2dl-hint`: shows the structure used by return-to-dl-resolve as hint. - ![](images/ret2dl-hint.png) - `srop-hint`: shows the code for sigreturn-oriented-programming as hint. - ![](images/srop-hint.png) - `sigreturn`: displays stack values for sigreturn syscall. - ![](images/sigreturn.png) - `smart-memory-dump`: dumps all regions of the memory to each file. - ![](images/smart-memory-dump.png) - `load-file`: loads the file into memory. - `load-file-mmap`: loads the file into memory that allocated by `mmap`. - `search-cfi-gadgets`: searches for CFI-valid (for CET IBT) and controllable generally gadgets in the executable area. - ![](images/search-cfi-gadgets.png) - `symbols`: lists all symbols with coloring. - ![](images/symbols.png) - `types`: lists all types with compaction. - ![](images/types.png) - `dt`: makes it easier to use `ptype /ox TYPE` and `p ((TYPE*) ADDRESS)[0]`. - ![](images/dt.png) - This command is designed for several purposes. 1. When displaying very large struct, you may want to go through a pager because the results will not fit on one screen. However, using a pager, the color information disappears. This command calls the pager with preserving colors. 2. When `ptype /ox TYPE`, interpreting member type recursively often result is too long and difficult to read. This command keeps result compact by displaying only top-level members. 3. When `p ((TYPE*) ADDRESS)[0]` for large struct, the setting of `max-value-size` is too small to display. This command adjusts it automatically. 4. When debugging a binary written in the Golang, the offset information of the type is not displayed. This command also displays the offset. 5. When debugging a binary written in the Golang, the `p ((TYPE*) ADDRESS)[0]` command will be broken. This is because the Golang helper script is automatically loaded and overwrites the behavior of `p` command. This command creates the display results on the Python side, so we can display it without any problems. - `mte-tags`: displays the MTE tags for the specified address. - Supported on ARM64. - ![](images/mte-tags.png) - `iouring-dump`: dumps the area of iouring (x64 only). - This area is mapped to userland, but cannot be accessed from gdb. - Therefore, it executes the assembly code and retrieves the contents. - ![](images/iouring-dump.png) - `gef version`: shows software versions that GEF uses. - ![](images/gef-version.png) - `gef status`: shows architecture information used in GEF. - ![](images/gef-status.png) - `gef reset-breakpoint`: shows and resets all breakpoints. - `gef arch-list`: displays defined architecture information. - ![](images/gef-arch-list.png) - `gef pyobj-list`: displays defined global Python objects. - ![](images/gef-pyobj-list.png) - `gef avail-comm-list`: displays a list of commands which are available or not for the current architecture and gdb execution mode. - ![](images/gef-avail-comm-list.png) - `gef set-arch`: sets a specific architecture to GEF. - `gef check-update`: checks for GEF updates. - `binwalk-memory`: scans memory by `binwalk`. - ![](images/binwalk-memory.png) - `filetype-memory`: scans memory by `file` and `magika`. - ![](images/filetype-memory.png) - `sixel-memory`: shows image to terminal by `imagemagick`. - ![](images/sixel-memory.png) - If you have `pillow` and `pyzbar` installed, a barcode detection option is also available. - `stdio-dump`: dumps members of stdin/stdout/stderr. - ![](images/stdio-dump.png) - `peek-pageframe`: reads page frame data. - ![](images/peek-pageframe.png) - `peek-pageflags`: reads page flags of a page frame. - ![](images/peek-pageflags.png) - `angr`: finds simple constraints by `angr`. - ![](images/angr.png) - `history`: shows gdb command history easily. - ![](images/history.png) - `crc32rev`: performs CRC32 reverse calculation limited to ASCII character range. - ![](images/crc32rev.png) - `vdump`: visualizes memory data like an image. - ![](images/vdump.png) - `freq-analysis`: visualizes the frequency of occurrence of each byte. - `qemu-system-memory-region-dump`: dumps memory regions for `qemu-system`. - ![](images/qemu-system-memory-region-dump.png) - `find-syscall`: searches the syscall gadget. - ![](images/find-syscall.png) - `fpchain`: dumps chains from `__IO_list_all`. - ![](images/fpchain.png) - `stepi-for-kgdb`: is wrapper for AArch64 KGDB that avoids stepping into pending IRQ handlers. ### Other - The category is introduced in `gef help`. - ![](images/gef-help.png) - Combined into one file (from `gef-extras`). The following are moved from `gef-extras`. - `current-stack-frame`, `xref-telescope`, `bytearray`, and `bincompare`. - This is because a single file is more attractive for me than ease of maintenance. - The system-call table used by `syscall-args` is moved from `gef-extras`. - It was updated up to Linux kernel 6.19 for each architecture. - Removed some features that I don't use. - `$` - `ida-interact` - `gef-remote` - `pie` - `pcustom` - `ksymaddr` - `trace-run` - `shellcode` - Many bug fixes / formatting improvements / usability enhancements (made it easy for me to use). ## FAQ - See [docs/FAQ.md](docs/FAQ.md). ## Links - Why I decided to make this - [gefを改造した話](https://hackmd.io/@bata24/rJVtBJsrP) - The story behind each command, etc. - [bata24/gefの機能紹介とか](https://hackmd.io/@bata24/SycIO4qPi) - The story behind each command, etc. 2024 Edition - [bata24/gefの機能紹介とか 2024](https://hackmd.io/@bata24/SJOzjzqQ1e) - The story behind each command, etc. 2025 Edition - [bata24/gefの機能紹介とか 2025](https://hackmd.io/@bata24/rkut9rU7Zg) - Orynth project page - https://www.orynth.dev/projects/bata24-gef-8901 - Fan token symbol: `B24G` (details in [docs/FAQ.md](docs/FAQ.md)) ================================================ FILE: asmdb/x86data.js ================================================ // [x86data.js] // X86/X64 instruction-set data. // // [License] // Public Domain. // This file can be parsed as pure JSON, locate ${JSON:BEGIN} and ${JSON:END} // marks and strip everything outside, a sample JS function that would do the job: // // function strip(s) { // return s.replace(/(^.*\$\{JSON:BEGIN\}\s+)|(\/\/\s*\$\{JSON:END\}\s*.*$)/g, ""); // } // INSTRUCTIONS // ============ // // Each instruction definition consists of 5 strings: // // [0] - Instruction name. // [1] - Instruction operands. // [2] - Instruction encoding. // [3] - Instruction opcode. // [4] - Instruction metadata - CPU features, FLAGS (read/write), and other metadata. // // The definition tries to match Intel and AMD instruction set manuals, but there // are small differences to make the definition more informative and compact. // OPERANDS // ======== // // * "op" - Explicit operand, must always be part of the instruction. If a fixed // register (like "cl") is used, it means that the instruction uses this // register implicitly, but it must be specified anyway. // // * "" - Implicit operand - some assemblers allow implicit operands the be passed // explicitly for documenting purposes. And some assemblers like AsmJit's // Compiler infrastructure requires implicit operands to be passed explicitly // for register allocation purposes. // // * "{op}" - Optional operand. Mostly used by AVX_512: // // - {k} mask selector. // - {z} zeroing. // - {1tox} broadcast. // - {er} embedded-rounding. // - {sae} suppress-all-exceptions. // // * "?:Op" - Each operand can provide metadata that can be used to describe which // operands are used as a destination, and which operands are source-only. // Each instruction in general assumes that the first operand is always // read/write and all following operands are read-only. However, this is // not correct for all instructions, thus, instructions that don't match // this assumption must provide additional information: // // - "R:Op" - The operand is read-only. // - "w:Op" - The operand is write-only (does not zero-extend). // - "W:Op" - The operand is write-only (implicit zero-extend). // - "x:Op" - The operand is read/write (does not zero-extend). // - "X:Op" - The operand is read/write (implicit zero-extend). // // * Op[A:B] - Optional bit-range that describes which bits are read and written. // // * "~Op" - Operand is commutative with other operands prefixed by "~". Commutativity // means that all operands marked by '~' can be swapped and the result of the // instruction would be the same. // WHAT IS MISSING // =============== // // Here is a list of missing instructions to keep track of it: // // [ ] xlat/xlatb (function($export, $as) { "use strict"; $export[$as] = // ${JSON:BEGIN} { "architectures": [ "ANY", "X86", "X64" ], "extensions": [ { "name": "3DNOW" }, { "name": "3DNOW2" }, { "name": "ADX" }, { "name": "AESNI" }, { "name": "AMX_TILE" }, { "name": "AMX_BF16" }, { "name": "AMX_INT8" }, { "name": "AVX" }, { "name": "AVX_VNNI" }, { "name": "AVX2" }, { "name": "AVX512_4FMAPS" }, { "name": "AVX512_4VNNIW" }, { "name": "AVX512_BF16" }, { "name": "AVX512_BITALG" }, { "name": "AVX512_BW" }, { "name": "AVX512_CDI" }, { "name": "AVX512_DQ" }, { "name": "AVX512_ERI" }, { "name": "AVX512_F" }, { "name": "AVX512_FP16" }, { "name": "AVX512_IFMA" }, { "name": "AVX512_PFI" }, { "name": "AVX512_VBMI" }, { "name": "AVX512_VBMI2" }, { "name": "AVX512_VNNI" }, { "name": "AVX512_VL" }, { "name": "AVX512_VP2INTERSECT" }, { "name": "AVX512_VPOPCNTDQ" }, { "name": "BMI" }, { "name": "BMI2" }, { "name": "CET_IBT" }, { "name": "CET_SS" }, { "name": "CLDEMOTE" }, { "name": "CLFLUSH" }, { "name": "CLFLUSHOPT" }, { "name": "CLWB" }, { "name": "CLZERO" }, { "name": "CMOV" }, { "name": "CMPXCHG8B" }, { "name": "CMPXCHG16B" }, { "name": "ENCLV" }, { "name": "ENQCMD" }, { "name": "F16C" }, { "name": "FMA" }, { "name": "FMA4" }, { "name": "FSGSBASE" }, { "name": "FXSR" }, { "name": "GEODE" }, { "name": "HLE" }, { "name": "HRESET" }, { "name": "GFNI" }, { "name": "I486" }, { "name": "LAHFSAHF" }, { "name": "LWP" }, { "name": "LZCNT" }, { "name": "MCOMMIT" }, { "name": "MMX" }, { "name": "MMX2" }, { "name": "MONITOR" }, { "name": "MONITORX" }, { "name": "MOVBE" }, { "name": "MOVDIR64B" }, { "name": "MOVDIRI" }, { "name": "MPX" }, { "name": "MSR" }, { "name": "OSPKE" }, { "name": "PCLMULQDQ" }, { "name": "PCOMMIT" }, { "name": "PCONFIG" }, { "name": "POPCNT" }, { "name": "PREFETCHW" }, { "name": "PREFETCHWT1" }, { "name": "PTWRITE" }, { "name": "RDPID" }, { "name": "RDPRU" }, { "name": "RDRAND" }, { "name": "RDSEED" }, { "name": "RDTSC" }, { "name": "RDTSCP" }, { "name": "RTM" }, { "name": "SEAM" }, { "name": "SERIALIZE" }, { "name": "SHA" }, { "name": "SKINIT" }, { "name": "SMAP" }, { "name": "SMX" }, { "name": "SNP" }, { "name": "SSE" }, { "name": "SSE2" }, { "name": "SSE3" }, { "name": "SSE4_1" }, { "name": "SSE4_2" }, { "name": "SSE4A" }, { "name": "SSSE3" }, { "name": "SVM" }, { "name": "TBM" }, { "name": "TSX" }, { "name": "TSXLDTRK" }, { "name": "UINTR" }, { "name": "VAES" }, { "name": "VPCLMULQDQ" }, { "name": "VMX" }, { "name": "WAITPKG" }, { "name": "WBNOINVD" }, { "name": "XOP" }, { "name": "XSAVE" }, { "name": "XSAVEC" }, { "name": "XSAVEOPT" }, { "name": "XSAVES" } ], "attributes": [ { "name": "Control" , "type": "string" , "doc": "Describes control flow." }, { "name": "Volatile" , "type": "flag" , "doc": "Instruction can have side effects (hint for instruction scheduler)." }, { "name": "Deprecated" , "type": "flag" , "doc": "Deprecated instruction." }, { "name": "AltForm" , "type": "flag" , "doc": "Alternative form that is shorter, but has restrictions." }, { "name": "Lock" , "type": "flag" , "doc": "Can be used with LOCK prefix." }, { "name": "ImplicitLock" , "type": "flag" , "doc": "Instruction is always atomic, regardless of use of the LOCK prefix." }, { "name": "XAcquire" , "type": "flag" , "doc": "A hint used to start lock elision on the instruction memory operand address." }, { "name": "XRelease" , "type": "flag" , "doc": "A hint used to end lock elision on the instruction memory operand address." }, { "name": "REP" , "type": "flag" , "doc": "Can be used with REP (REPE/REPZ) prefix." }, { "name": "REPNE" , "type": "flag" , "doc": "Can be used with REPNE (REPNZ) prefix." }, { "name": "RepIgnored" , "type": "flag" , "doc": "Rep prefix can be used, but has no effect." }, { "name": "AliasOf" , "type": "string" , "doc": "Instruction is an alias to another instruction, must apply to all instructions within the same group." }, { "name": "EncodeAs" , "type": "string" , "doc": "Similar to AliasOf, but doesn't apply to all instructions in the group." } ], "specialRegs": [ { "name": "FLAGS.CF" , "group": "FLAGS.CF" , "doc": "Carry flag." }, { "name": "FLAGS.PF" , "group": "FLAGS.PF" , "doc": "Parity flag." }, { "name": "FLAGS.AF" , "group": "FLAGS.AF" , "doc": "Adjust flag." }, { "name": "FLAGS.ZF" , "group": "FLAGS.ZF" , "doc": "Zero flag." }, { "name": "FLAGS.SF" , "group": "FLAGS.SF" , "doc": "Sign flag." }, { "name": "FLAGS.TF" , "group": "FLAGS.TF" , "doc": "Trap flag." }, { "name": "FLAGS.IF" , "group": "FLAGS.IF" , "doc": "Interrupt enable flag." }, { "name": "FLAGS.DF" , "group": "FLAGS.DF" , "doc": "Direction flag." }, { "name": "FLAGS.OF" , "group": "FLAGS.OF" , "doc": "Overflow flag." }, { "name": "FLAGS.AC" , "group": "FLAGS.Other", "doc": "Alignment check flag." }, { "name": "FLAGS.IOPL" , "group": "FLAGS.Other", "doc": "I/O privilege level." }, { "name": "FLAGS.NT" , "group": "FLAGS.Other", "doc": "Nested task flag." }, { "name": "FLAGS.RF" , "group": "FLAGS.Other", "doc": "Resume flag." }, { "name": "FLAGS.VM" , "group": "FLAGS.Other", "doc": "Virtual 8086 mode flag." }, { "name": "FLAGS.VIF" , "group": "FLAGS.Other", "doc": "Virtual interrupt flag." }, { "name": "FLAGS.VIP" , "group": "FLAGS.Other", "doc": "Virtual interrupt pending." }, { "name": "FLAGS.CPUID" , "group": "FLAGS.Other", "doc": "CPUID instruction available." }, { "name": "X87CW.INVALID_OP" , "group": "X87CW.EXC" , "doc": "Invalid operation exception enable bit." }, { "name": "X87CW.DENORMAL" , "group": "X87CW.EXC" , "doc": "Dernormalized exception enable bit." }, { "name": "X87CW.ZERO_DIVIDE", "group": "X87CW.EXC" , "doc": "Division by zero exception enable bit." }, { "name": "X87CW.OVERFLOW" , "group": "X87CW.EXC" , "doc": "Overflow exception enable bit." }, { "name": "X87CW.UNDERFLOW" , "group": "X87CW.EXC" , "doc": "Underflow exception enable bit." }, { "name": "X87CW.PRECISION" , "group": "X87CW.EXC" , "doc": "Lost of precision exception enable bit." }, { "name": "X87CW.PC" , "group": "X87CW.PC" , "doc": "Precision control." }, { "name": "X87CW.RC" , "group": "X87CW.RC" , "doc": "Rounding control." }, { "name": "X87SW.INVALID_OP" , "group": "X87SW.EXC" , "doc": "Invalid operation exception flag." }, { "name": "X87SW.DENORMAL" , "group": "X87SW.EXC" , "doc": "Dernormalized exception flag." }, { "name": "X87SW.ZERO_DIVIDE", "group": "X87SW.EXC" , "doc": "Division by zero exception flag." }, { "name": "X87SW.OVERFLOW" , "group": "X87SW.EXC" , "doc": "Overflow exception flag." }, { "name": "X87SW.UNDERFLOW" , "group": "X87SW.EXC" , "doc": "Underflow exception flag." }, { "name": "X87SW.PRECISION" , "group": "X87SW.EXC" , "doc": "Lost of precision exception flag." }, { "name": "X87SW.STACK_FAULT", "group": "X87SW.EXC" , "doc": "Stack fault." }, { "name": "X87SW.EF" , "group": "X87SW.EXC" , "doc": "Exception flag." }, { "name": "X87SW.C0" , "group": "X87SW.C0" , "doc": "C0 condifion." }, { "name": "X87SW.C1" , "group": "X87SW.C1" , "doc": "C1 condifion." }, { "name": "X87SW.C2" , "group": "X87SW.C2" , "doc": "C2 condifion." }, { "name": "X87SW.TOP" , "group": "X87SW.TOP" , "doc": "Top of the FPU stack." }, { "name": "X87SW.C3" , "group": "X87SW.C3" , "doc": "C3 condifion." }, { "name": "MSR" , "group": "MSR" , "doc": "MSR register." }, { "name": "XCR" , "group": "XCR" , "doc": "XCR register." } ], "shortcuts": [ { "name": "CF" , "expand": "FLAGS.CF" }, { "name": "PF" , "expand": "FLAGS.PF" }, { "name": "AF" , "expand": "FLAGS.AF" }, { "name": "ZF" , "expand": "FLAGS.ZF" }, { "name": "SF" , "expand": "FLAGS.SF" }, { "name": "TF" , "expand": "FLAGS.TF" }, { "name": "IF" , "expand": "FLAGS.IF" }, { "name": "DF" , "expand": "FLAGS.DF" }, { "name": "OF" , "expand": "FLAGS.OF" }, { "name": "AC" , "expand": "FLAGS.AC" }, { "name": "C0" , "expand": "X87SW.C0" }, { "name": "C1" , "expand": "X87SW.C1" }, { "name": "C2" , "expand": "X87SW.C2" }, { "name": "C3" , "expand": "X87SW.C3" }, { "name": "_ILock" , "expand": "Lock|ImplicitLock" }, { "name": "_XLock" , "expand": "Lock|XAcquire|XRelease" }, { "name": "BND" , "expand": "REPNE|RepIgnored" }, { "name": "_Rep" , "expand": "REP|REPNE" }, { "name": "DummyRep" , "expand": "REP|REPNE|RepIgnored" } ], "registers": { "r8" : { "kind": "gp" , "any": "r8" , "names": ["al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", "r8-15b"] }, "r8hi": { "kind": "gp" , "names": ["ah", "ch", "dh", "bh"] }, "r16" : { "kind": "gp" , "any": "r16" , "names": ["ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8-15w"] }, "r32" : { "kind": "gp" , "any": "r32" , "names": ["eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8-15d"] }, "r64" : { "kind": "gp" , "any": "r64" , "names": ["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8-15"] }, "rxx" : { "kind": "gp" , "names": ["zax", "zcx", "zdx", "zbx", "zsp", "zbp", "zsi", "zdi"] }, "sreg": { "kind": "sreg", "any": "sreg" , "names": ["es", "cs", "ss", "ds", "fs", "gs" ] }, "creg": { "kind": "creg", "any": "creg" , "names": ["cr0-15"] }, "dreg": { "kind": "dreg", "any": "dreg" , "names": ["dr0-15"] }, "bnd" : { "kind": "bnd" , "any": "bnd" , "names": ["bnd0-3"] }, "st" : { "kind": "st" , "any": "st(i)", "names": ["st(0-7)"] }, "mm" : { "kind": "mm" , "any": "mm" , "names": ["mm0-7"] }, "k" : { "kind": "k" , "any": "k" , "names": ["k0-7"] }, "xmm" : { "kind": "vec" , "any": "xmm" , "names": ["xmm0-31"] }, "ymm" : { "kind": "vec" , "any": "ymm" , "names": ["ymm0-31"] }, "zmm" : { "kind": "vec" , "any": "zmm" , "names": ["zmm0-31"] }, "tmm" : { "kind": "tile", "any": "tmm" , "names": ["tmm0-7"] } }, "instructions": [ ["adc" , "x:al, ib/ub" , "I" , "14 ib" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "x:ax, iw/uw" , "I" , "66 15 iw" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:eax, id/ud" , "I" , "15 id" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:rax, id" , "I" , "REX.W 15 id" , "X64 AltForm OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "x:r8/m8, ib/ub" , "MI" , "80 /2 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "x:r16/m16, iw/uw" , "MI" , "66 81 /2 iw" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:r32/m32, id/ud" , "MI" , "81 /2 id" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:r64/m64, id" , "MI" , "REX.W 81 /2 id" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "x:r16/m16, ib" , "MI" , "66 83 /2 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:r32/m32, ib" , "MI" , "83 /2 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:r64/m64, ib" , "MI" , "REX.W 83 /2 ib" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "x:~r8/m8,~r8" , "MR" , "10 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "x:~r16/m16,~r16" , "MR" , "66 11 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:~r32/m32,~r32" , "MR" , "11 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:~r64/m64,~r64" , "MR" , "REX.W 11 /r" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "x:~r8,~r8/m8" , "RM" , "12 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "x:~r16,~r16/m16" , "RM" , "66 13 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:~r32,~r32/m32" , "RM" , "13 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=X"], ["adc" , "X:~r64,~r64/m64" , "RM" , "REX.W 13 /r" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=X"], ["add" , "x:al, ib/ub" , "I" , "04 ib" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "x:ax, iw/uw" , "I" , "66 05 iw" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:eax, id/ud" , "I" , "05 id" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:rax, id" , "I" , "REX.W 05 id" , "X64 AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "x:r8/m8, ib/ub" , "MI" , "80 /0 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "x:r16/m16, iw/uw" , "MI" , "66 81 /0 iw" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:r32/m32, id/ud" , "MI" , "81 /0 id" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:r64/m64, id" , "MI" , "REX.W 81 /0 id" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "x:r16/m16, ib" , "MI" , "66 83 /0 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:r32/m32, ib" , "MI" , "83 /0 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:r64/m64, ib" , "MI" , "REX.W 83 /0 ib" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "x:~r8/m8,~r8" , "MR" , "00 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "x:~r16/m16,~r16" , "MR" , "66 01 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:~r32/m32,~r32" , "MR" , "01 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:~r64/m64,~r64" , "MR" , "REX.W 01 /r" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "x:~r8,~r8/m8" , "RM" , "02 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "x:~r16,~r16/m16" , "RM" , "66 03 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:~r32,~r32/m32" , "RM" , "03 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["add" , "X:~r64,~r64/m64" , "RM" , "REX.W 03 /r" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["and" , "x:al, ib/ub" , "I" , "24 ib" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "x:ax, iw/uw" , "I" , "66 25 iw" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:eax, id/ud" , "I" , "25 id" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:rax, ud" , "I" , "25 id" , "X64 AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:rax, id" , "I" , "REX.W 25 id" , "X64 AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "x:r8/m8, ib/ub" , "MI" , "80 /4 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "x:r16/m16, iw/uw" , "MI" , "66 81 /4 iw" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:r32/m32, id/ud" , "MI" , "81 /4 id" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:r64, ud" , "MI" , "81 /4 id" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:r64/m64, id" , "MI" , "REX.W 81 /4 id" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "x:r16/m16, ib/ub" , "MI" , "66 83 /4 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:r32/m32, ib/ub" , "MI" , "83 /4 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:r64/m64, ib/ub" , "MI" , "REX.W 83 /4 ib" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "x:~r8/m8,~r8" , "MR" , "20 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "x:~r16/m16,~r16" , "MR" , "66 21 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:~r32/m32,~r32" , "MR" , "21 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:~r64/m64,~r64" , "MR" , "REX.W 21 /r" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "x:~r8,~r8/m8" , "RM" , "22 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "x:~r16,~r16/m16" , "RM" , "66 23 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:~r32,~r32/m32" , "RM" , "23 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["and" , "X:~r64,~r64/m64" , "RM" , "REX.W 23 /r" , "X64 OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["bound" , "R:r16, R:m32" , "RM" , "66 62 /r" , "X86 Deprecated"], ["bound" , "R:r32, R:m64" , "RM" , "62 /r" , "X86 Deprecated"], ["bsf" , "w:r16, r16/m16" , "RM" , "66 0F BC /r" , "ANY OF=U SF=U ZF=W AF=U PF=U CF=U"], ["bsf" , "W:r32, r32/m32" , "RM" , "0F BC /r" , "ANY OF=U SF=U ZF=W AF=U PF=U CF=U"], ["bsf" , "W:r64, r64/m64" , "RM" , "REX.W 0F BC /r" , "X64 OF=U SF=U ZF=W AF=U PF=U CF=U"], ["bsr" , "w:r16, r16/m16" , "RM" , "66 0F BD /r" , "ANY OF=U SF=U ZF=W AF=U PF=U CF=U"], ["bsr" , "W:r32, r32/m32" , "RM" , "0F BD /r" , "ANY OF=U SF=U ZF=W AF=U PF=U CF=U"], ["bsr" , "W:r64, r64/m64" , "RM" , "REX.W 0F BD /r" , "X64 OF=U SF=U ZF=W AF=U PF=U CF=U"], ["bswap" , "X:r16" , "O" , "66 0F C8+r" , "ANY"], ["bswap" , "X:r32" , "O" , "0F C8+r" , "ANY"], ["bswap" , "X:r64" , "O" , "REX.W 0F C8+r" , "X64"], ["bt" , "R:r16/m16, ib/ub" , "MI" , "66 0F BA /4 ib" , "ANY OF=U SF=U AF=U PF=U CF=W"], ["bt" , "R:r32/m32, ib/ub" , "MI" , "0F BA /4 ib" , "ANY OF=U SF=U AF=U PF=U CF=W"], ["bt" , "R:r64/m64, ib/ub" , "MI" , "REX.W 0F BA /4 ib" , "X64 OF=U SF=U AF=U PF=U CF=W"], ["bt" , "R:r16/m16, r16" , "MR" , "66 0F A3 /r" , "ANY OF=U SF=U AF=U PF=U CF=W"], ["bt" , "R:r32/m32, r32" , "MR" , "0F A3 /r" , "ANY OF=U SF=U AF=U PF=U CF=W"], ["bt" , "R:r64/m64, r64" , "MR" , "REX.W 0F A3 /r" , "X64 OF=U SF=U AF=U PF=U CF=W"], ["btc" , "x:r16/m16, ib/ub" , "MI" , "66 0F BA /7 ib" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["btc" , "X:r32/m32, ib/ub" , "MI" , "0F BA /7 ib" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["btc" , "X:r64/m64, ib/ub" , "MI" , "REX.W 0F BA /7 ib" , "X64 _XLock OF=U SF=U AF=U PF=U CF=W"], ["btc" , "x:r16/m16, r16" , "MR" , "66 0F BB /r" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["btc" , "X:r32/m32, r32" , "MR" , "0F BB /r" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["btc" , "X:r64/m64, r64" , "MR" , "REX.W 0F BB /r" , "X64 _XLock OF=U SF=U AF=U PF=U CF=W"], ["btr" , "x:r16/m16, ib/ub" , "MI" , "66 0F BA /6 ib" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["btr" , "X:r32/m32, ib/ub" , "MI" , "0F BA /6 ib" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["btr" , "X:r64/m64, ib/ub" , "MI" , "REX.W 0F BA /6 ib" , "X64 _XLock OF=U SF=U AF=U PF=U CF=W"], ["btr" , "x:r16/m16, r16" , "MR" , "66 0F B3 /r" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["btr" , "X:r32/m32, r32" , "MR" , "0F B3 /r" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["btr" , "X:r64/m64, r64" , "MR" , "REX.W 0F B3 /r" , "X64 _XLock OF=U SF=U AF=U PF=U CF=W"], ["bts" , "x:r16/m16, ib/ub" , "MI" , "66 0F BA /5 ib" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["bts" , "X:r32/m32, ib/ub" , "MI" , "0F BA /5 ib" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["bts" , "X:r64/m64, ib/ub" , "MI" , "REX.W 0F BA /5 ib" , "X64 _XLock OF=U SF=U AF=U PF=U CF=W"], ["bts" , "x:r16/m16, r16" , "MR" , "66 0F AB /r" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["bts" , "X:r32/m32, r32" , "MR" , "0F AB /r" , "ANY _XLock OF=U SF=U AF=U PF=U CF=W"], ["bts" , "X:r64/m64, r64" , "MR" , "REX.W 0F AB /r" , "X64 _XLock OF=U SF=U AF=U PF=U CF=W"], ["call" , "rel16" , "D" , "66 E8 cw" , "X86 BND Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["call" , "rel32" , "D" , "E8 cd" , "ANY BND Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["call" , "R:r16/m16" , "M" , "66 FF /2" , "X86 BND Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["call" , "R:r32/m32" , "M" , "FF /2" , "X86 BND Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["call" , "R:r64/m64" , "M" , "FF /2" , "X64 BND Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["cbw" , "x:" , "NONE" , "66 98" , "ANY"], ["cwde" , "X:" , "NONE" , "98" , "ANY"], ["cdqe" , "X:" , "NONE" , "REX.W 98" , "X64"], ["cwd" , "w:, " , "NONE" , "66 99" , "ANY"], ["cdq" , "W:, " , "NONE" , "99" , "ANY"], ["cqo" , "W:, " , "NONE" , "REX.W 99" , "X64"], ["cmovo" , "x:r16, r16/m16" , "RM" , "66 0F 40 /r" , "CMOV OF=R"], ["cmovo" , "X:r32, r32/m32" , "RM" , "0F 40 /r" , "CMOV OF=R"], ["cmovo" , "X:r64, r64/m64" , "RM" , "REX.W 0F 40 /r" , "CMOV X64 OF=R"], ["cmovno" , "x:r16, r16/m16" , "RM" , "66 0F 41 /r" , "CMOV OF=R"], ["cmovno" , "X:r32, r32/m32" , "RM" , "0F 41 /r" , "CMOV OF=R"], ["cmovno" , "X:r64, r64/m64" , "RM" , "REX.W 0F 41 /r" , "CMOV X64 OF=R"], ["cmovb/cmovnae/cmovc" , "x:r16, r16/m16" , "RM" , "66 0F 42 /r" , "CMOV CF=R"], ["cmovb/cmovnae/cmovc" , "X:r32, r32/m32" , "RM" , "0F 42 /r" , "CMOV CF=R"], ["cmovb/cmovnae/cmovc" , "X:r64, r64/m64" , "RM" , "REX.W 0F 42 /r" , "CMOV X64 CF=R"], ["cmovae/cmovnb/cmovnc", "x:r16, r16/m16" , "RM" , "66 0F 43 /r" , "CMOV CF=R"], ["cmovae/cmovnb/cmovnc", "X:r32, r32/m32" , "RM" , "0F 43 /r" , "CMOV CF=R"], ["cmovae/cmovnb/cmovnc", "X:r64, r64/m64" , "RM" , "REX.W 0F 43 /r" , "CMOV X64 CF=R"], ["cmove/cmovz" , "x:r16, r16/m16" , "RM" , "66 0F 44 /r" , "CMOV ZF=R"], ["cmove/cmovz" , "X:r32, r32/m32" , "RM" , "0F 44 /r" , "CMOV ZF=R"], ["cmove/cmovz" , "X:r64, r64/m64" , "RM" , "REX.W 0F 44 /r" , "CMOV X64 ZF=R"], ["cmovne/cmovnz" , "x:r16, r16/m16" , "RM" , "66 0F 45 /r" , "CMOV ZF=R"], ["cmovne/cmovnz" , "X:r32, r32/m32" , "RM" , "0F 45 /r" , "CMOV ZF=R"], ["cmovne/cmovnz" , "X:r64, r64/m64" , "RM" , "REX.W 0F 45 /r" , "CMOV X64 ZF=R"], ["cmovbe/cmovna" , "x:r16, r16/m16" , "RM" , "66 0F 46 /r" , "CMOV CF=R ZF=R"], ["cmovbe/cmovna" , "X:r32, r32/m32" , "RM" , "0F 46 /r" , "CMOV CF=R ZF=R"], ["cmovbe/cmovna" , "X:r64, r64/m64" , "RM" , "REX.W 0F 46 /r" , "CMOV X64 CF=R ZF=R"], ["cmova/cmovnbe" , "x:r16, r16/m16" , "RM" , "66 0F 47 /r" , "CMOV CF=R ZF=R"], ["cmova/cmovnbe" , "X:r32, r32/m32" , "RM" , "0F 47 /r" , "CMOV CF=R ZF=R"], ["cmova/cmovnbe" , "X:r64, r64/m64" , "RM" , "REX.W 0F 47 /r" , "CMOV X64 CF=R ZF=R"], ["cmovs" , "x:r16, r16/m16" , "RM" , "66 0F 48 /r" , "CMOV SF=R"], ["cmovs" , "X:r32, r32/m32" , "RM" , "0F 48 /r" , "CMOV SF=R"], ["cmovs" , "X:r64, r64/m64" , "RM" , "REX.W 0F 48 /r" , "CMOV X64 SF=R"], ["cmovns" , "x:r16, r16/m16" , "RM" , "66 0F 49 /r" , "CMOV SF=R"], ["cmovns" , "X:r32, r32/m32" , "RM" , "0F 49 /r" , "CMOV SF=R"], ["cmovns" , "X:r64, r64/m64" , "RM" , "REX.W 0F 49 /r" , "CMOV X64 SF=R"], ["cmovp/cmovpe" , "x:r16, r16/m16" , "RM" , "66 0F 4A /r" , "CMOV PF=R"], ["cmovp/cmovpe" , "X:r32, r32/m32" , "RM" , "0F 4A /r" , "CMOV PF=R"], ["cmovp/cmovpe" , "X:r64, r64/m64" , "RM" , "REX.W 0F 4A /r" , "CMOV X64 PF=R"], ["cmovnp/cmovpo" , "x:r16, r16/m16" , "RM" , "66 0F 4B /r" , "CMOV PF=R"], ["cmovnp/cmovpo" , "X:r32, r32/m32" , "RM" , "0F 4B /r" , "CMOV PF=R"], ["cmovnp/cmovpo" , "X:r64, r64/m64" , "RM" , "REX.W 0F 4B /r" , "CMOV X64 PF=R"], ["cmovl/cmovnge" , "x:r16, r16/m16" , "RM" , "66 0F 4C /r" , "CMOV SF=R OF=R"], ["cmovl/cmovnge" , "X:r32, r32/m32" , "RM" , "0F 4C /r" , "CMOV SF=R OF=R"], ["cmovl/cmovnge" , "X:r64, r64/m64" , "RM" , "REX.W 0F 4C /r" , "CMOV X64 SF=R OF=R"], ["cmovge/cmovnl" , "x:r16, r16/m16" , "RM" , "66 0F 4D /r" , "CMOV SF=R OF=R"], ["cmovge/cmovnl" , "X:r32, r32/m32" , "RM" , "0F 4D /r" , "CMOV SF=R OF=R"], ["cmovge/cmovnl" , "X:r64, r64/m64" , "RM" , "REX.W 0F 4D /r" , "CMOV X64 SF=R OF=R"], ["cmovle/cmovng" , "x:r16, r16/m16" , "RM" , "66 0F 4E /r" , "CMOV ZF=R SF=R OF=R"], ["cmovle/cmovng" , "X:r32, r32/m32" , "RM" , "0F 4E /r" , "CMOV ZF=R SF=R OF=R"], ["cmovle/cmovng" , "X:r64, r64/m64" , "RM" , "REX.W 0F 4E /r" , "CMOV X64 ZF=R SF=R OF=R"], ["cmovg/cmovnle" , "x:r16, r16/m16" , "RM" , "66 0F 4F /r" , "CMOV ZF=R SF=R OF=R"], ["cmovg/cmovnle" , "X:r32, r32/m32" , "RM" , "0F 4F /r" , "CMOV ZF=R SF=R OF=R"], ["cmovg/cmovnle" , "X:r64, r64/m64" , "RM" , "REX.W 0F 4F /r" , "CMOV X64 ZF=R SF=R OF=R"], ["cmp" , "R:al, ib/ub" , "I" , "3C ib" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:ax, iw/uw" , "I" , "66 3D iw" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:eax, id/ud" , "I" , "3D id" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:rax, id" , "I" , "REX.W 3D id" , "X64 AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r8/m8, ib/ub" , "MI" , "80 /7 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r16/m16, iw/uw" , "MI" , "66 81 /7 iw" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r32/m32, id/ud" , "MI" , "81 /7 id" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r64/m64, id" , "MI" , "REX.W 81 /7 id" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r16/m16, ib" , "MI" , "66 83 /7 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r32/m32, ib" , "MI" , "83 /7 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r64/m64, ib" , "MI" , "REX.W 83 /7 ib" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r8/m8, r8" , "MR" , "38 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r16/m16, r16" , "MR" , "66 39 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r32/m32, r32" , "MR" , "39 /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r64/m64, r64" , "MR" , "REX.W 39 /r" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r8, r8/m8" , "RM" , "3A /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r16, r16/m16" , "RM" , "66 3B /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r32, r32/m32" , "RM" , "3B /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmp" , "R:r64, r64/m64" , "RM" , "REX.W 3B /r" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmpsb" , "R:, R:" , "NONE" , "A6" , "ANY _Rep OF=W SF=W ZF=W AF=W PF=W CF=W DF=R"], ["cmpsw" , "R:, R:" , "NONE" , "66 A7" , "ANY _Rep OF=W SF=W ZF=W AF=W PF=W CF=W DF=R"], ["cmpsd" , "R:, R:" , "NONE" , "A7" , "ANY _Rep OF=W SF=W ZF=W AF=W PF=W CF=W DF=R"], ["cmpsq" , "R:, R:" , "NONE" , "REX.W A7" , "X64 _Rep OF=W SF=W ZF=W AF=W PF=W CF=W DF=R"], ["cmpxchg" , "x:r8/m8, r8, " , "MR" , "0F B0 /r" , "I486 _XLock Volatile OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmpxchg" , "x:r16/m16, r16, " , "MR" , "66 0F B1 /r" , "I486 _XLock Volatile OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmpxchg" , "X:r32/m32, r32, " , "MR" , "0F B1 /r" , "I486 _XLock Volatile OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmpxchg" , "X:r64/m64, r64, " , "MR" , "REX.W 0F B1 /r" , "I486 X64 _XLock Volatile OF=W SF=W ZF=W AF=W PF=W CF=W"], ["cmpxchg8b" , "X:m64, X:, X:, , " , "M" , "0F C7 /1" , "CMPXCHG8B _XLock Volatile ZF=W"], ["cmpxchg16b" , "X:m128, X:, X:, , " , "M" , "REX.W 0F C7 /1" , "CMPXCHG16B X64 _XLock Volatile ZF=W"], ["dec" , "x:r16" , "O" , "66 48+r" , "X86 OF=W SF=W ZF=W AF=W PF=W"], ["dec" , "X:r32" , "O" , "48+r" , "X86 OF=W SF=W ZF=W AF=W PF=W"], ["dec" , "x:r8/m8" , "M" , "FE /1" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W"], ["dec" , "x:r16/m16" , "M" , "66 FF /1" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W"], ["dec" , "X:r32/m32" , "M" , "FF /1" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W"], ["dec" , "X:r64/m64" , "M" , "REX.W FF /1" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W"], ["div" , "x:, r8/m8" , "M" , "F6 /6" , "ANY OF=U SF=U ZF=U AF=U PF=U CF=U"], ["div" , "x:, x:, r16/m16" , "M" , "66 F7 /6" , "ANY OF=U SF=U ZF=U AF=U PF=U CF=U"], ["div" , "X:, X:, r32/m32" , "M" , "F7 /6" , "ANY OF=U SF=U ZF=U AF=U PF=U CF=U"], ["div" , "X:, X:, r64/m64" , "M" , "REX.W F7 /6" , "X64 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["idiv" , "x:, r8/m8" , "M" , "F6 /7" , "ANY OF=U SF=U ZF=U AF=U PF=U CF=U"], ["idiv" , "x:, x:, r16/m16" , "M" , "66 F7 /7" , "ANY OF=U SF=U ZF=U AF=U PF=U CF=U"], ["idiv" , "X:, X:, r32/m32" , "M" , "F7 /7" , "ANY OF=U SF=U ZF=U AF=U PF=U CF=U"], ["idiv" , "X:, X:, r64/m64" , "M" , "REX.W F7 /7" , "X64 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["imul" , "x:, r8/m8" , "M" , "F6 /5" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "w:, x:, r16/m16" , "M" , "66 F7 /5" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "W:, X:, r32/m32" , "M" , "F7 /5" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "W:, X:, r64/m64" , "M" , "REX.W F7 /5" , "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "x:~r16,~r16/m16" , "RM" , "66 0F AF /r" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "X:~r32,~r32/m32" , "RM" , "0F AF /r" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "X:~r64,~r64/m64" , "RM" , "REX.W 0F AF /r" , "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "w:r16, r16/m16, ib" , "RMI" , "66 6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "W:r32, r32/m32, ib" , "RMI" , "6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "W:r64, r64/m64, ib" , "RMI" , "REX.W 6B /r ib" , "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "w:r16, r16/m16, iw/uw" , "RMI" , "66 69 /r iw" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "W:r32, r32/m32, id/ud" , "RMI" , "69 /r id" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], ["imul" , "W:r64, r64/m64, id" , "RMI" , "REX.W 69 /r id" , "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"], ["inc" , "x:r16" , "O" , "66 40+r" , "X86 OF=W SF=W ZF=W AF=W PF=W"], ["inc" , "X:r32" , "O" , "40+r" , "X86 OF=W SF=W ZF=W AF=W PF=W"], ["inc" , "x:r8/m8" , "M" , "FE /0" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W"], ["inc" , "x:r16/m16" , "M" , "66 FF /0" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W"], ["inc" , "X:r32/m32" , "M" , "FF /0" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W"], ["inc" , "X:r64/m64" , "M" , "REX.W FF /0" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W"], ["iret" , "" , "NONE" , "66 CF" , "ANY Control=Return OF=U SF=U ZF=U AF=U PF=U CF=U"], ["iretd" , "" , "NONE" , "CF" , "ANY Control=Return OF=U SF=U ZF=U AF=U PF=U CF=U"], ["iretq" , "" , "NONE" , "REX.W CF" , "X64 Control=Return OF=U SF=U ZF=U AF=U PF=U CF=U"], ["jo" , "rel8" , "D" , "70 cb" , "ANY BND Control=Branch OF=R"], ["jno" , "rel8" , "D" , "71 cb" , "ANY BND Control=Branch OF=R"], ["jb/jnae/jc" , "rel8" , "D" , "72 cb" , "ANY BND Control=Branch CF=R"], ["jae/jnb/jnc" , "rel8" , "D" , "73 cb" , "ANY BND Control=Branch CF=R"], ["je/jz" , "rel8" , "D" , "74 cb" , "ANY BND Control=Branch ZF=R"], ["jne/jnz" , "rel8" , "D" , "75 cb" , "ANY BND Control=Branch ZF=R"], ["jbe/jna" , "rel8" , "D" , "76 cb" , "ANY BND Control=Branch CF=R ZF=R"], ["ja/jnbe" , "rel8" , "D" , "77 cb" , "ANY BND Control=Branch CF=R ZF=R"], ["js" , "rel8" , "D" , "78 cb" , "ANY BND Control=Branch SF=R"], ["jns" , "rel8" , "D" , "79 cb" , "ANY BND Control=Branch SF=R"], ["jp/jpe" , "rel8" , "D" , "7A cb" , "ANY BND Control=Branch PF=R"], ["jnp/jpo" , "rel8" , "D" , "7B cb" , "ANY BND Control=Branch PF=R"], ["jl/jnge" , "rel8" , "D" , "7C cb" , "ANY BND Control=Branch SF=R OF=R"], ["jge/jnl" , "rel8" , "D" , "7D cb" , "ANY BND Control=Branch SF=R OF=R"], ["jle/jng" , "rel8" , "D" , "7E cb" , "ANY BND Control=Branch ZF=R SF=R OF=R"], ["jg/jnle" , "rel8" , "D" , "7F cb" , "ANY BND Control=Branch ZF=R SF=R OF=R"], ["jo" , "rel16" , "D" , "66 0F 80 cw" , "X86 BND Control=Branch OF=R"], ["jo" , "rel32" , "D" , "0F 80 cd" , "ANY BND Control=Branch OF=R"], ["jno" , "rel16" , "D" , "66 0F 81 cw" , "X86 BND Control=Branch OF=R"], ["jno" , "rel32" , "D" , "0F 81 cd" , "ANY BND Control=Branch OF=R"], ["jb/jnae/jc" , "rel16" , "D" , "66 0F 82 cw" , "X86 BND Control=Branch CF=R"], ["jb/jnae/jc" , "rel32" , "D" , "0F 82 cd" , "ANY BND Control=Branch CF=R"], ["jae/jnb/jnc" , "rel16" , "D" , "66 0F 83 cw" , "X86 BND Control=Branch CF=R"], ["jae/jnb/jnc" , "rel32" , "D" , "0F 83 cd" , "ANY BND Control=Branch CF=R"], ["je/jz" , "rel16" , "D" , "66 0F 84 cw" , "X86 BND Control=Branch ZF=R"], ["je/jz" , "rel32" , "D" , "0F 84 cd" , "ANY BND Control=Branch ZF=R"], ["jne/jnz" , "rel16" , "D" , "66 0F 85 cw" , "X86 BND Control=Branch ZF=R"], ["jne/jnz" , "rel32" , "D" , "0F 85 cd" , "ANY BND Control=Branch ZF=R"], ["jbe/jna" , "rel16" , "D" , "66 0F 86 cw" , "X86 BND Control=Branch CF=R ZF=R"], ["jbe/jna" , "rel32" , "D" , "0F 86 cd" , "ANY BND Control=Branch CF=R ZF=R"], ["ja/jnbe" , "rel16" , "D" , "66 0F 87 cw" , "X86 BND Control=Branch CF=R ZF=R"], ["ja/jnbe" , "rel32" , "D" , "0F 87 cd" , "ANY BND Control=Branch CF=R ZF=R"], ["js" , "rel16" , "D" , "66 0F 88 cw" , "X86 BND Control=Branch SF=R"], ["js" , "rel32" , "D" , "0F 88 cd" , "ANY BND Control=Branch SF=R"], ["jns" , "rel16" , "D" , "66 0F 89 cw" , "X86 BND Control=Branch SF=R"], ["jns" , "rel32" , "D" , "0F 89 cd" , "ANY BND Control=Branch SF=R"], ["jp/jpe" , "rel16" , "D" , "66 0F 8A cw" , "X86 BND Control=Branch PF=R"], ["jp/jpe" , "rel32" , "D" , "0F 8A cd" , "ANY BND Control=Branch PF=R"], ["jnp/jpo" , "rel16" , "D" , "66 0F 8B cw" , "X86 BND Control=Branch PF=R"], ["jnp/jpo" , "rel32" , "D" , "0F 8B cd" , "ANY BND Control=Branch PF=R"], ["jl/jnge" , "rel16" , "D" , "66 0F 8C cw" , "X86 BND Control=Branch SF=R OF=R"], ["jl/jnge" , "rel32" , "D" , "0F 8C cd" , "ANY BND Control=Branch SF=R OF=R"], ["jge/jnl" , "rel16" , "D" , "66 0F 8D cw" , "X86 BND Control=Branch SF=R OF=R"], ["jge/jnl" , "rel32" , "D" , "0F 8D cd" , "ANY BND Control=Branch SF=R OF=R"], ["jle/jng" , "rel16" , "D" , "66 0F 8E cw" , "X86 BND Control=Branch ZF=R SF=R OF=R"], ["jle/jng" , "rel32" , "D" , "0F 8E cd" , "ANY BND Control=Branch ZF=R SF=R OF=R"], ["jg/jnle" , "rel16" , "D" , "66 0F 8F cw" , "X86 BND Control=Branch ZF=R SF=R OF=R"], ["jg/jnle" , "rel32" , "D" , "0F 8F cd" , "ANY BND Control=Branch ZF=R SF=R OF=R"], ["jecxz" , "R:, rel8" , "D" , "67 E3 cb" , "X86 BND Control=Branch"], ["jecxz" , "R:, rel8" , "D" , "E3 cb" , "X86 BND Control=Branch"], ["jecxz" , "R:, rel8" , "D" , "67 E3 cb" , "X64 BND Control=Branch"], ["jecxz" , "R:, rel8" , "D" , "E3 cb" , "X64 BND Control=Branch"], ["jmp" , "rel8" , "D" , "EB cb" , "ANY BND Control=Jump"], ["jmp" , "rel16" , "D" , "66 E9 cw" , "X86 BND Control=Jump"], ["jmp" , "rel32" , "D" , "E9 cd" , "ANY BND Control=Jump"], ["jmp" , "R:r32/m32" , "D" , "FF /4" , "X86 BND Control=Jump"], ["jmp" , "R:r64/m64" , "D" , "FF /4" , "X64 BND Control=Jump"], ["lcall" , "iw, iw" , "II" , "66 9A iw iw" , "X86 Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["lcall" , "iw, id" , "II" , "9A id iw" , "X86 Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["lcall" , "R:m16_16" , "M" , "66 FF /3" , "ANY Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["lcall" , "R:m16_32" , "M" , "FF /3" , "ANY Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["lcall" , "R:m16_64" , "M" , "REX.W FF /3" , "X64 Control=Call OF=U SF=U ZF=U AF=U PF=U CF=U"], ["lea" , "w:r16, mem" , "RM" , "67 8D /r" , "ANY"], ["lea" , "W:r32, mem" , "RM" , "8D /r" , "ANY"], ["lea" , "W:r64, mem" , "RM" , "REX.W 8D /r" , "X64"], ["ljmp" , "iw, iw" , "II" , "66 EA iw iw" , "X86 Control=Jump"], ["ljmp" , "iw, id" , "II" , "EA id iw" , "X86 Control=Jump"], ["ljmp" , "R:m16_16" , "M" , "66 FF /5" , "ANY Control=Jump"], ["ljmp" , "R:m16_32" , "M" , "FF /5" , "ANY Control=Jump"], ["ljmp" , "R:m16_64" , "M" , "REX.W FF /5" , "X64 Control=Jump"], ["lodsb" , "w:, R:" , "NONE" , "AC" , "ANY _Rep DF=R"], ["lodsw" , "w:, R:" , "NONE" , "66 AD" , "ANY _Rep DF=R"], ["lodsd" , "W:, R:" , "NONE" , "AD" , "ANY _Rep DF=R"], ["lodsq" , "W:, R:" , "NONE" , "REX.W AD" , "X64 _Rep DF=R"], ["loop" , "x:, rel8" , "D" , "67 E2 cb" , "X86 Control=Branch"], ["loop" , "X:, rel8" , "D" , "E2 cb" , "X86 Control=Branch"], ["loop" , "X:, rel8" , "D" , "67 E2 cb" , "X64 Control=Branch"], ["loop" , "X:, rel8" , "D" , "E2 cb" , "X64 Control=Branch"], ["loope" , "x:, rel8" , "D" , "67 E1 cb" , "X86 Control=Branch ZF=R"], ["loope" , "X:, rel8" , "D" , "E1 cb" , "X86 Control=Branch ZF=R"], ["loope" , "X:, rel8" , "D" , "67 E1 cb" , "X64 Control=Branch ZF=R"], ["loope" , "X:, rel8" , "D" , "E1 cb" , "X64 Control=Branch ZF=R"], ["loopne" , "x:, rel8" , "D" , "67 E0 cb" , "X86 Control=Branch ZF=R"], ["loopne" , "X:, rel8" , "D" , "E0 cb" , "X86 Control=Branch ZF=R"], ["loopne" , "X:, rel8" , "D" , "67 E0 cb" , "X64 Control=Branch ZF=R"], ["loopne" , "X:, rel8" , "D" , "E0 cb" , "X64 Control=Branch ZF=R"], ["mov" , "w:r8/m8, r8" , "MR" , "88 /r" , "ANY XRelease"], ["mov" , "w:r16/m16, r16" , "MR" , "66 89 /r" , "ANY XRelease"], ["mov" , "W:r32/m32, r32" , "MR" , "89 /r" , "ANY XRelease"], ["mov" , "W:r64/m64, r64" , "MR" , "REX.W 89 /r" , "X64 XRelease"], ["mov" , "w:r8/m8, ib/ub" , "MI" , "C6 /0 ib" , "ANY XRelease"], ["mov" , "w:r16/m16, iw/uw" , "MI" , "66 C7 /0 iw" , "ANY XRelease"], ["mov" , "W:r32/m32, id/ud" , "MI" , "C7 /0 id" , "ANY XRelease"], ["mov" , "W:r64/m64, id" , "MI" , "REX.W C7 /0 id" , "X64 XRelease"], ["mov" , "w:r8, ib/ub" , "I" , "B0+r ib" , "ANY"], ["mov" , "w:r16, iw/uw" , "I" , "66 B8+r iw" , "ANY"], ["mov" , "W:r32, id/ud" , "I" , "B8+r id" , "ANY"], ["mov" , "W:r64, iq/uq" , "I" , "REX.W B8+r iq" , "X64"], ["mov" , "w:r8, r8/m8" , "RM" , "8A /r" , "ANY"], ["mov" , "w:r16, r16/m16" , "RM" , "66 8B /r" , "ANY"], ["mov" , "W:r32, r32/m32" , "RM" , "8B /r" , "ANY"], ["mov" , "W:r64, r64/m64" , "RM" , "REX.W 8B /r" , "X64"], ["mov" , "w:r16/m16, sreg" , "MR" , "66 8C /r" , "ANY"], ["mov" , "W:r32/m16, sreg" , "MR" , "8C /r" , "ANY"], ["mov" , "W:r64/m16, sreg" , "MR" , "REX.W 8C /r" , "X64"], ["mov" , "W:sreg, r16/m16" , "RM" , "66 8E /r" , "ANY"], ["mov" , "W:sreg, r32/m16" , "RM" , "8E /r" , "ANY"], ["mov" , "W:sreg, r64/m16" , "RM" , "REX.W 8E /r" , "X64"], ["mov" , "w:al, moff8" , "NONE" , "A0" , "ANY"], ["mov" , "w:ax, moff16" , "NONE" , "66 A1" , "ANY"], ["mov" , "W:eax, moff32" , "NONE" , "A1" , "ANY"], ["mov" , "W:rax, moff64" , "NONE" , "REX.W A1" , "X64"], ["mov" , "W:moff8, al" , "NONE" , "A2" , "ANY"], ["mov" , "W:moff16, ax" , "NONE" , "66 A3" , "ANY"], ["mov" , "W:moff32, eax" , "NONE" , "A3" , "ANY"], ["mov" , "W:moff64, rax" , "NONE" , "REX.W A3" , "X64"], ["mov" , "W:r32, creg" , "MR" , "0F 20 /r" , "X86 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["mov" , "W:r64, creg" , "MR" , "0F 20 /r" , "X64 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["mov" , "W:creg, r32" , "RM" , "0F 22 /r" , "X86 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["mov" , "W:creg, r64" , "RM" , "0F 22 /r" , "X64 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["mov" , "W:r32, dreg" , "MR" , "0F 21 /r" , "X86 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["mov" , "W:r64, dreg" , "MR" , "0F 21 /r" , "X64 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["mov" , "W:dreg, r32" , "RM" , "0F 23 /r" , "X86 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["mov" , "W:dreg, r64" , "RM" , "0F 23 /r" , "X64 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["movsb" , "W:, R:" , "NONE" , "A4" , "ANY _Rep DF=R"], ["movsw" , "W:, R:" , "NONE" , "66 A5" , "ANY _Rep DF=R"], ["movsd" , "W:, R:" , "NONE" , "A5" , "ANY _Rep DF=R"], ["movsq" , "W:, R:" , "NONE" , "REX.W A5" , "X64 _Rep DF=R"], ["movsx" , "w:r16, r8/m8" , "RM" , "66 0F BE /r" , "ANY"], ["movsx" , "W:r32, r8/m8" , "RM" , "0F BE /r" , "ANY"], ["movsx" , "W:r64, r8/m8" , "RM" , "REX.W 0F BE /r" , "X64"], ["movsx" , "W:r32, r16/m16" , "RM" , "0F BF /r" , "ANY"], ["movsx" , "W:r64, r16/m16" , "RM" , "REX.W 0F BF /r" , "X64"], ["movsxd" , "W:r16, r16/m16" , "RM" , "66 63 /r" , "X64"], ["movsxd" , "W:r32, r32/m32" , "RM" , "63 /r" , "X64"], ["movsxd" , "W:r64, r32/m32" , "RM" , "REX.W 63 /r" , "X64"], ["movzx" , "w:r16, r8/m8" , "RM" , "66 0F B6 /r" , "ANY"], ["movzx" , "W:r32, r8/m8" , "RM" , "0F B6 /r" , "ANY"], ["movzx" , "W:r64, r8/m8" , "RM" , "REX.W 0F B6 /r" , "X64"], ["movzx" , "W:r32, r16/m16" , "RM" , "0F B7 /r" , "ANY"], ["movzx" , "W:r64, r16/m16" , "RM" , "REX.W 0F B7 /r" , "X64"], ["mul" , "x:, r8/m8" , "M" , "F6 /4" , "ANY OF=W SF=U ZF=U AF=U PF=U CF=W"], ["mul" , "w:, x:, r16/m16" , "M" , "66 F7 /4" , "ANY OF=W SF=U ZF=U AF=U PF=U CF=W"], ["mul" , "W:, X:, r32/m32" , "M" , "F7 /4" , "ANY OF=W SF=U ZF=U AF=U PF=U CF=W"], ["mul" , "W:, X:, r64/m64" , "M" , "REX.W F7 /4" , "X64 OF=W SF=U ZF=U AF=U PF=U CF=W"], ["neg" , "x:r8/m8" , "M" , "F6 /3" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["neg" , "x:r16/m16" , "M" , "66 F7 /3" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["neg" , "X:r32/m32" , "M" , "F7 /3" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["neg" , "X:r64/m64" , "M" , "REX.W F7 /3" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["nop" , "" , "NONE" , "90" , ""], ["nop" , "R:r16/m16" , "M" , "66 0F 1F /0" , ""], ["nop" , "R:r32/m32" , "M" , "0F 1F /0" , ""], ["nop" , "R:r64/m64" , "M" , "REX.W 0F 1F /0" , ""], ["nop" , "R:r16/m16, r16" , "MR" , "66 0F 1F /r" , ""], ["nop" , "R:r32/m32, r32" , "MR" , "0F 1F /r" , ""], ["nop" , "R:r64/m64, r64" , "MR" , "REX.W 0F 1F /r" , ""], ["not" , "x:r8/m8" , "M" , "F6 /2" , "ANY _XLock"], ["not" , "x:r16/m16" , "M" , "66 F7 /2" , "ANY _XLock"], ["not" , "X:r32/m32" , "M" , "F7 /2" , "ANY _XLock"], ["not" , "X:r64/m64" , "M" , "REX.W F7 /2" , "X64 _XLock"], ["or" , "x:al, ib/ub" , "I" , "0C ib" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "x:ax, iw/uw" , "I" , "66 0D iw" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:eax, id/ud" , "I" , "0D id" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:rax, id" , "I" , "REX.W 0D id" , "X64 AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "x:r8/m8, ib/ub" , "MI" , "80 /1 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "x:r16/m16, iw/uw" , "MI" , "66 81 /1 iw" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:r32/m32, id/ud" , "MI" , "81 /1 id" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:r64/m64, id" , "MI" , "REX.W 81 /1 id" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "x:r16/m16, ib" , "MI" , "66 83 /1 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:r32/m32, ib" , "MI" , "83 /1 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:r64/m64, ib" , "MI" , "REX.W 83 /1 ib" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "x:~r8/m8,~r8" , "MR" , "08 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "x:~r16/m16,~r16" , "MR" , "66 09 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:~r32/m32,~r32" , "MR" , "09 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:~r64/m64,~r64" , "MR" , "REX.W 09 /r" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "x:~r8,~r8/m8" , "RM" , "0A /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "x:~r16,~r16/m16" , "RM" , "66 0B /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:~r32,~r32/m32" , "RM" , "0B /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["or" , "X:~r64,~r64/m64" , "RM" , "REX.W 0B /r" , "X64 OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["pop" , "w:r16/m16" , "M" , "66 8F /0" , "ANY"], ["pop" , "W:r32/m32" , "M" , "8F /0" , "X86"], ["pop" , "W:r64/m64" , "M" , "8F /0" , "X64"], ["pop" , "w:r16" , "O" , "66 58+r" , "ANY"], ["pop" , "W:r32" , "O" , "58+r" , "X86"], ["pop" , "W:r64" , "O" , "58+r" , "X64"], ["pop" , "W:ds" , "NONE" , "1F" , "X86"], ["pop" , "W:es" , "NONE" , "07" , "X86"], ["pop" , "W:ss" , "NONE" , "17" , "X86"], ["pop" , "W:fs" , "NONE" , "0F A1" , "ANY"], ["pop" , "W:gs" , "NONE" , "0F A9" , "ANY"], ["popa" , "" , "NONE" , "66 61" , "X86 Deprecated"], ["popad" , "" , "NONE" , "61" , "X86 Deprecated"], ["popf" , "" , "NONE" , "66 9D" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W DF=W IF=W TF=W"], ["popfd" , "" , "NONE" , "9D" , "X86 OF=W SF=W ZF=W AF=W PF=W CF=W DF=W IF=W TF=W"], ["popfq" , "" , "NONE" , "9D" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W DF=W IF=W TF=W"], ["push" , "R:r16/m16" , "M" , "66 FF /6" , "ANY"], ["push" , "R:r32/m32" , "M" , "FF /6" , "X86"], ["push" , "R:r64/m64" , "M" , "FF /6" , "X64"], ["push" , "R:r16" , "O" , "66 50+r" , "ANY"], ["push" , "R:r32" , "O" , "50+r" , "X86"], ["push" , "R:r64" , "O" , "50+r" , "X64"], ["push" , "ib" , "I" , "6A ib" , "ANY"], ["push" , "iw" , "I" , "66 68 iw" , "ANY"], ["push" , "id/ud" , "I" , "68 id" , "X86"], ["push" , "id" , "I" , "68 id" , "X64"], ["push" , "R:cs" , "NONE" , "0E" , "X86"], ["push" , "R:ss" , "NONE" , "16" , "X86"], ["push" , "R:ds" , "NONE" , "1E" , "X86"], ["push" , "R:es" , "NONE" , "06" , "X86"], ["push" , "R:fs" , "NONE" , "0F A0" , "ANY"], ["push" , "R:gs" , "NONE" , "0F A8" , "ANY"], ["pusha" , "" , "NONE" , "66 60" , "X86 Deprecated"], ["pushad" , "" , "NONE" , "60" , "X86 Deprecated"], ["pushf" , "" , "NONE" , "66 9C" , "ANY OF=R SF=R ZF=R AF=R PF=R CF=R DF=R IF=R TF=R"], ["pushfd" , "" , "NONE" , "9C" , "X86 OF=R SF=R ZF=R AF=R PF=R CF=R DF=R IF=R TF=R"], ["pushfq" , "" , "NONE" , "9C" , "X64 OF=R SF=R ZF=R AF=R PF=R CF=R DF=R IF=R TF=R"], ["rcl" , "x:r8/m8, 1" , "M" , "D0 /2" , "ANY AltForm CF=X OF=X"], ["rcl" , "x:r8/m8, cl" , "M" , "D2 /2" , "ANY CF=X OF=X"], ["rcl" , "x:r8/m8, ib/ub" , "MI" , "C0 /2 ib" , "ANY CF=X OF=X"], ["rcl" , "x:r16/m16, 1" , "M" , "66 D1 /2" , "ANY AltForm CF=X OF=X"], ["rcl" , "x:r16/m16, cl" , "M" , "66 D3 /2" , "ANY CF=X OF=X"], ["rcl" , "x:r16/m16, ib/ub" , "MI" , "66 C1 /2 ib" , "ANY CF=X OF=X"], ["rcl" , "X:r32/m32, 1" , "M" , "D1 /2" , "ANY AltForm CF=X OF=X"], ["rcl" , "X:r32/m32, cl" , "M" , "D3 /2" , "ANY CF=X OF=X"], ["rcl" , "X:r32/m32, ib/ub" , "MI" , "C1 /2 ib" , "ANY CF=X OF=X"], ["rcl" , "X:r64/m64, 1" , "M" , "REX.W D1 /2" , "X64 AltForm CF=X OF=X"], ["rcl" , "X:r64/m64, cl" , "M" , "REX.W D3 /2" , "X64 CF=X OF=X"], ["rcl" , "X:r64/m64, ib/ub" , "MI" , "REX.W C1 /2 ib" , "X64 CF=X OF=X"], ["rcr" , "x:r8/m8, 1" , "M" , "D0 /3" , "ANY AltForm CF=X OF=X"], ["rcr" , "x:r8/m8, cl" , "M" , "D2 /3" , "ANY CF=X OF=X"], ["rcr" , "x:r8/m8, ib/ub" , "MI" , "C0 /3 ib" , "ANY CF=X OF=X"], ["rcr" , "x:r16/m16, 1" , "M" , "66 D1 /3" , "ANY AltForm CF=X OF=X"], ["rcr" , "x:r16/m16, cl" , "M" , "66 D3 /3" , "ANY CF=X OF=X"], ["rcr" , "x:r16/m16, ib/ub" , "MI" , "66 C1 /3 ib" , "ANY CF=X OF=X"], ["rcr" , "X:r32/m32, 1" , "M" , "D1 /3" , "ANY AltForm CF=X OF=X"], ["rcr" , "X:r32/m32, cl" , "M" , "D3 /3" , "ANY CF=X OF=X"], ["rcr" , "X:r32/m32, ib/ub" , "MI" , "C1 /3 ib" , "ANY CF=X OF=X"], ["rcr" , "X:r64/m64, 1" , "M" , "REX.W D1 /3" , "X64 AltForm CF=X OF=X"], ["rcr" , "X:r64/m64, cl" , "M" , "REX.W D3 /3" , "X64 CF=X OF=X"], ["rcr" , "X:r64/m64, ib/ub" , "MI" , "REX.W C1 /3 ib" , "X64 CF=X OF=X"], ["ret" , "" , "NONE" , "C3" , "ANY BND DummyRep Control=Return"], ["ret" , "uw" , "I" , "C2 iw" , "ANY BND DummyRep Control=Return"], ["retf" , "" , "NONE" , "CB" , "ANY Control=Return"], ["retf" , "uw" , "I" , "CA iw" , "ANY Control=Return"], ["rol" , "x:r8/m8, 1" , "M" , "D0 /0" , "ANY AltForm CF=W OF=W"], ["rol" , "x:r8/m8, cl" , "M" , "D2 /0" , "ANY CF=W OF=W"], ["rol" , "x:r8/m8, ib/ub" , "MI" , "C0 /0 ib" , "ANY CF=W OF=W"], ["rol" , "x:r16/m16, 1" , "M" , "66 D1 /0" , "ANY AltForm CF=W OF=W"], ["rol" , "x:r16/m16, cl" , "M" , "66 D3 /0" , "ANY CF=W OF=W"], ["rol" , "x:r16/m16, ib/ub" , "MI" , "66 C1 /0 ib" , "ANY CF=W OF=W"], ["rol" , "X:r32/m32, 1" , "M" , "D1 /0" , "ANY AltForm CF=W OF=W"], ["rol" , "X:r32/m32, cl" , "M" , "D3 /0" , "ANY CF=W OF=W"], ["rol" , "X:r32/m32, ib/ub" , "MI" , "C1 /0 ib" , "ANY CF=W OF=W"], ["rol" , "X:r64/m64, 1" , "M" , "REX.W D1 /0" , "X64 AltForm CF=W OF=W"], ["rol" , "X:r64/m64, cl" , "M" , "REX.W D3 /0" , "X64 CF=W OF=W"], ["rol" , "X:r64/m64, ib/ub" , "MI" , "REX.W C1 /0 ib" , "X64 CF=W OF=W"], ["ror" , "x:r8/m8, 1" , "M" , "D0 /1" , "ANY AltForm CF=W OF=W"], ["ror" , "x:r8/m8, cl" , "M" , "D2 /1" , "ANY CF=W OF=W"], ["ror" , "x:r8/m8, ib/ub" , "MI" , "C0 /1 ib" , "ANY CF=W OF=W"], ["ror" , "x:r16/m16, 1" , "M" , "66 D1 /1" , "ANY AltForm CF=W OF=W"], ["ror" , "x:r16/m16, cl" , "M" , "66 D3 /1" , "ANY CF=W OF=W"], ["ror" , "x:r16/m16, ib/ub" , "MI" , "66 C1 /1 ib" , "ANY CF=W OF=W"], ["ror" , "X:r32/m32, 1" , "M" , "D1 /1" , "ANY AltForm CF=W OF=W"], ["ror" , "X:r32/m32, cl" , "M" , "D3 /1" , "ANY CF=W OF=W"], ["ror" , "X:r32/m32, ib/ub" , "MI" , "C1 /1 ib" , "ANY CF=W OF=W"], ["ror" , "X:r64/m64, 1" , "M" , "REX.W D1 /1" , "X64 AltForm CF=W OF=W"], ["ror" , "X:r64/m64, cl" , "M" , "REX.W D3 /1" , "X64 CF=W OF=W"], ["ror" , "X:r64/m64, ib/ub" , "MI" , "REX.W C1 /1 ib" , "X64 CF=W OF=W"], ["sar" , "x:r8/m8, 1" , "M" , "D0 /7" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "x:r8/m8, cl" , "M" , "D2 /7" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "x:r8/m8, ib/ub" , "MI" , "C0 /7 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "x:r16/m16, 1" , "M" , "66 D1 /7" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "x:r16/m16, cl" , "M" , "66 D3 /7" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "x:r16/m16, ib/ub" , "MI" , "66 C1 /7 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "X:r32/m32, 1" , "M" , "D1 /7" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "X:r32/m32, cl" , "M" , "D3 /7" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "X:r32/m32, ib/ub" , "MI" , "C1 /7 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "X:r64/m64, 1" , "M" , "REX.W D1 /7" , "X64 AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "X:r64/m64, cl" , "M" , "REX.W D3 /7" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sar" , "X:r64/m64, ib/ub" , "MI" , "REX.W C1 /7 ib" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sbb" , "x:al, ib/ub" , "I" , "1C ib" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "x:ax, iw/uw" , "I" , "66 1D iw" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:eax, id/ud" , "I" , "1D id" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:rax, id" , "I" , "REX.W 1D id" , "X64 AltForm OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "x:r8/m8, ib/ub" , "MI" , "80 /3 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "x:r16/m16, iw/uw" , "MI" , "66 81 /3 iw" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:r32/m32, id/ud" , "MI" , "81 /3 id" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:r64/m64, id" , "MI" , "REX.W 81 /3 id" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "x:r16/m16, ib" , "MI" , "66 83 /3 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:r32/m32, ib" , "MI" , "83 /3 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:r64/m64, ib" , "MI" , "REX.W 83 /3 ib" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "x:r8/m8, r8" , "MR" , "18 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "x:r16/m16, r16" , "MR" , "66 19 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:r32/m32, r32" , "MR" , "19 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:r64/m64, r64" , "MR" , "REX.W 19 /r" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "x:r8, r8/m8" , "RM" , "1A /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "x:r16, r16/m16" , "RM" , "66 1B /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:r32, r32/m32" , "RM" , "1B /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=X"], ["sbb" , "X:r64, r64/m64" , "RM" , "REX.W 1B /r" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=X"], ["scasb" , "R:, R:" , "NONE" , "AE" , "ANY _Rep OF=W SF=W ZF=W AF=W PF=W CF=W DF=R"], ["scasw" , "R:, R:" , "NONE" , "66 AF" , "ANY _Rep OF=W SF=W ZF=W AF=W PF=W CF=W DF=R"], ["scasd" , "R:, R:" , "NONE" , "AF" , "ANY _Rep OF=W SF=W ZF=W AF=W PF=W CF=W DF=R"], ["scasq" , "R:, R:" , "NONE" , "REX.W AF" , "X64 _Rep OF=W SF=W ZF=W AF=W PF=W CF=W DF=R"], ["seto" , "w:r8/m8" , "M" , "0F 90 /r" , "ANY OF=R"], ["setno" , "w:r8/m8" , "M" , "0F 91 /r" , "ANY OF=R"], ["setb/setnae/setc" , "w:r8/m8" , "M" , "0F 92 /r" , "ANY CF=R"], ["setae/setnb/setnc", "w:r8/m8" , "M" , "0F 93 /r" , "ANY CF=R"], ["sete/setz" , "w:r8/m8" , "M" , "0F 94 /r" , "ANY ZF=R"], ["setne/setnz" , "w:r8/m8" , "M" , "0F 95 /r" , "ANY ZF=R"], ["setbe/setna" , "w:r8/m8" , "M" , "0F 96 /r" , "ANY CF=R ZF=R"], ["seta/setnbe" , "w:r8/m8" , "M" , "0F 97 /r" , "ANY CF=R ZF=R"], ["sets" , "w:r8/m8" , "M" , "0F 98 /r" , "ANY SF=R"], ["setns" , "w:r8/m8" , "M" , "0F 99 /r" , "ANY SF=R"], ["setp/setpe" , "w:r8/m8" , "M" , "0F 9A /r" , "ANY PF=R"], ["setnp/setpo" , "w:r8/m8" , "M" , "0F 9B /r" , "ANY PF=R"], ["setl/setnge" , "w:r8/m8" , "M" , "0F 9C /r" , "ANY SF=R OF=R"], ["setge/setnl" , "w:r8/m8" , "M" , "0F 9D /r" , "ANY SF=R OF=R"], ["setle/setng" , "w:r8/m8" , "M" , "0F 9E /r" , "ANY ZF=R SF=R OF=R"], ["setg/setnle" , "w:r8/m8" , "M" , "0F 9F /r" , "ANY ZF=R SF=R OF=R"], ["shl/sal" , "x:r8/m8, 1" , "M" , "D0 /4" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "x:r8/m8, cl" , "M" , "D2 /4" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "x:r8/m8, ib/ub" , "MI" , "C0 /4 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "x:r16/m16, 1" , "M" , "66 D1 /4" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "x:r16/m16, cl" , "M" , "66 D3 /4" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "x:r16/m16, ib/ub" , "MI" , "66 C1 /4 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "X:r32/m32, 1" , "M" , "D1 /4" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "X:r32/m32, cl" , "M" , "D3 /4" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "X:r32/m32, ib/ub" , "MI" , "C1 /4 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "X:r64/m64, 1" , "M" , "REX.W D1 /4" , "X64 AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "X:r64/m64, cl" , "M" , "REX.W D3 /4" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shl/sal" , "X:r64/m64, ib/ub" , "MI" , "REX.W C1 /4 ib" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "x:r8/m8, 1" , "M" , "D0 /5" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "x:r8/m8, cl" , "M" , "D2 /5" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "x:r8/m8, ib/ub" , "MI" , "C0 /5 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "x:r16/m16, 1" , "M" , "66 D1 /5" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "x:r16/m16, cl" , "M" , "66 D3 /5" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "x:r16/m16, ib/ub" , "MI" , "66 C1 /5 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "X:r32/m32, 1" , "M" , "D1 /5" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "X:r32/m32, cl" , "M" , "D3 /5" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "X:r32/m32, ib/ub" , "MI" , "C1 /5 ib" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "X:r64/m64, 1" , "M" , "REX.W D1 /5" , "X64 AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "X:r64/m64, cl" , "M" , "REX.W D3 /5" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shr" , "X:r64/m64, ib/ub" , "MI" , "REX.W C1 /5 ib" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["shld" , "x:r16/m16, r16, cl" , "MR" , "66 0F A5 /r" , "ANY OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shld" , "x:r16/m16, r16, ib/ub" , "MRI" , "66 0F A4 /r ib" , "ANY OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shld" , "X:r32/m32, r32, cl" , "MR" , "0F A5 /r" , "ANY OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shld" , "X:r32/m32, r32, ib/ub" , "MRI" , "0F A4 /r ib" , "ANY OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shld" , "X:r64/m64, r64, cl" , "MR" , "REX.W 0F A5 /r" , "X64 OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shld" , "X:r64/m64, r64, ib/ub" , "MRI" , "REX.W 0F A4 /r ib" , "X64 OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shrd" , "x:r16/m16, r16, cl" , "MR" , "66 0F AD /r" , "ANY OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shrd" , "x:r16/m16, r16, ib/ub" , "MRI" , "66 0F AC /r ib" , "ANY OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shrd" , "X:r32/m32, r32, cl" , "MR" , "0F AD /r" , "ANY OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shrd" , "X:r32/m32, r32, ib/ub" , "MRI" , "0F AC /r ib" , "ANY OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shrd" , "X:r64/m64, r64, cl" , "MR" , "REX.W 0F AD /r" , "X64 OF=W SF=W ZF=W AF=U PF=W CF=W"], ["shrd" , "X:r64/m64, r64, ib/ub" , "MRI" , "REX.W 0F AC /r ib" , "X64 OF=W SF=W ZF=W AF=U PF=W CF=W"], ["stosb" , "W:, R:" , "NONE" , "AA" , "ANY _Rep DF=R"], ["stosw" , "W:, R:" , "NONE" , "66 AB" , "ANY _Rep DF=R"], ["stosd" , "W:, R:" , "NONE" , "AB" , "ANY _Rep DF=R"], ["stosq" , "W:, R:" , "NONE" , "REX.W AB" , "X64 _Rep DF=R"], ["sub" , "x:al, ib/ub" , "I" , "2C ib" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "x:ax, iw/uw" , "I" , "66 2D iw" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:eax, id/ud" , "I" , "2D id" , "ANY AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:rax, id" , "I" , "REX.W 2D id" , "X64 AltForm OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "x:r8/m8, ib/ub" , "MI" , "80 /5 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "x:r16/m16, iw/uw" , "MI" , "66 81 /5 iw" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:r32/m32, id/ud" , "MI" , "81 /5 id" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:r64/m64, id" , "MI" , "REX.W 81 /5 id" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "x:r16/m16, ib" , "MI" , "66 83 /5 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:r32/m32, ib" , "MI" , "83 /5 ib" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:r64/m64, ib" , "MI" , "REX.W 83 /5 ib" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "x:r8/m8, r8" , "MR" , "28 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "x:r16/m16, r16" , "MR" , "66 29 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:r32/m32, r32" , "MR" , "29 /r" , "ANY _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:r64/m64, r64" , "MR" , "REX.W 29 /r" , "X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "x:r8, r8/m8" , "RM" , "2A /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "x:r16, r16/m16" , "RM" , "66 2B /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:r32, r32/m32" , "RM" , "2B /r" , "ANY OF=W SF=W ZF=W AF=W PF=W CF=W"], ["sub" , "X:r64, r64/m64" , "RM" , "REX.W 2B /r" , "X64 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["test" , "R:al, ib/ub" , "I" , "A8 ib" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:ax, iw/uw" , "I" , "66 A9 iw" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:eax, id/ud" , "I" , "A9 id" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:rax, id" , "I" , "REX.W A9 id" , "X64 AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:r8/m8, ib/ub" , "MI" , "F6 /0 ib" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:r16/m16, iw/uw" , "MI" , "66 F7 /0 iw" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:r32/m32, id/ud" , "MI" , "F7 /0 id" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:r64/m64, id" , "MI" , "REX.W F7 /0 id" , "X64 OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:~r8/m8,~r8" , "MR" , "84 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:~r16/m16,~r16" , "MR" , "66 85 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:~r32/m32,~r32" , "MR" , "85 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["test" , "R:~r64/m64,~r64" , "MR" , "REX.W 85 /r" , "X64 OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["ud0" , "r32, r32/m32" , "RM" , "0F FF /r" , "ANY"], ["ud1" , "r32, r32/m32" , "RM" , "0F B9 /r" , "ANY"], ["ud2" , "" , "NONE" , "0F 0B" , "ANY"], ["xadd" , "x:r8/m8, x:r8" , "MR" , "0F C0 /r" , "I486 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["xadd" , "x:r16/m16, x:r16" , "MR" , "66 0F C1 /r" , "I486 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["xadd" , "X:r32/m32, X:r32" , "MR" , "0F C1 /r" , "I486 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["xadd" , "X:r64/m64, X:r64" , "MR" , "REX.W 0F C1 /r" , "I486 X64 _XLock OF=W SF=W ZF=W AF=W PF=W CF=W"], ["xchg" , "x:~ax, x:~r16" , "O" , "66 90+r" , "ANY AltForm"], ["xchg" , "X:~eax, X:~r32" , "O" , "90+r" , "ANY AltForm"], ["xchg" , "X:~rax, X:~r64" , "O" , "REX.W 90+r" , "X64 AltForm"], ["xchg" , "x:~r16, x:~ax" , "O" , "66 90+r" , "ANY AltForm"], ["xchg" , "X:~r32, X:~eax" , "O" , "90+r" , "ANY AltForm"], ["xchg" , "X:~r64, X:~rax" , "O" , "REX.W 90+r" , "X64 AltForm"], ["xchg" , "x:~r8/m8, x:~r8" , "MR" , "86 /r" , "ANY _ILock XAcquire"], ["xchg" , "x:~r16/m16, x:~r16" , "MR" , "66 87 /r" , "ANY _ILock XAcquire"], ["xchg" , "X:~r32/m32, X:~r32" , "MR" , "87 /r" , "ANY _ILock XAcquire"], ["xchg" , "X:~r64/m64, X:~r64" , "MR" , "REX.W 87 /r" , "X64 _ILock XAcquire"], ["xchg" , "x:~r8, x:~r8/m8" , "RM" , "86 /r" , "ANY _ILock"], ["xchg" , "x:~r16, x:~r16/m16" , "RM" , "66 87 /r" , "ANY _ILock"], ["xchg" , "X:~r32, X:~r32/m32" , "RM" , "87 /r" , "ANY _ILock"], ["xchg" , "X:~r64, X:~r64/m64" , "RM" , "REX.W 87 /r" , "X64 _ILock"], ["xor" , "x:al, ib/ub" , "I" , "34 ib" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "x:ax, iw/uw" , "I" , "66 35 iw" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:eax, id/ud" , "I" , "35 id" , "ANY AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:rax, id" , "I" , "REX.W 35 id" , "X64 AltForm OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "x:r8/m8, ib/ub" , "MI" , "80 /6 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "x:r16/m16, iw/uw" , "MI" , "66 81 /6 iw" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:r32/m32, id/ud" , "MI" , "81 /6 id" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:r64/m64, id" , "MI" , "REX.W 81 /6 id" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "x:r16/m16, ib" , "MI" , "66 83 /6 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:r32/m32, ib" , "MI" , "83 /6 ib" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:r64/m64, ib" , "MI" , "REX.W 83 /6 ib" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "x:~r8/m8, ~r8" , "MR" , "30 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "x:~r16/m16, ~r16" , "MR" , "66 31 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:~r32/m32, ~r32" , "MR" , "31 /r" , "ANY _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:~r64/m64, ~r64" , "MR" , "REX.W 31 /r" , "X64 _XLock OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "x:~r8, ~r8/m8" , "RM" , "32 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "x:~r16, ~r16/m16" , "RM" , "66 33 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:~r32, ~r32/m32" , "RM" , "33 /r" , "ANY OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["xor" , "X:~r64, ~r64/m64" , "RM" , "REX.W 33 /r" , "X64 OF=0 SF=W ZF=W AF=U PF=W CF=0"], ["aaa" , "x:" , "NONE" , "37" , "X86 Deprecated OF=U SF=U ZF=U AF=W PF=U CF=W"], ["aas" , "x:" , "NONE" , "3F" , "X86 Deprecated OF=U SF=U ZF=U AF=W PF=U CF=W"], ["aad" , "x:, ib/ub" , "I" , "D5 ib" , "X86 Deprecated OF=U SF=W ZF=W AF=U PF=W CF=U"], ["aam" , "x:, ib/ub" , "I" , "D4 ib" , "X86 Deprecated OF=U SF=W ZF=W AF=U PF=W CF=U"], ["daa" , "x:" , "NONE" , "27" , "X86 Deprecated OF=U SF=W ZF=W AF=W PF=W CF=W"], ["das" , "x:" , "NONE" , "2F" , "X86 Deprecated OF=U SF=W ZF=W AF=W PF=W CF=W"], ["enter" , "iw/uw, ib/ub" , "II" , "C8 iw ib" , "ANY Volatile"], ["leave" , "" , "NONE" , "C9" , "ANY Volatile"], ["in" , "w:al, ib/ub" , "I" , "E4 ib" , "ANY Volatile"], ["in" , "w:ax, ib/ub" , "I" , "66 E5 ib" , "ANY Volatile"], ["in" , "W:eax, ib/ub" , "I" , "E5 ib" , "ANY Volatile"], ["in" , "w:al, dx" , "NONE" , "EC" , "ANY Volatile"], ["in" , "w:ax, dx" , "NONE" , "66 ED" , "ANY Volatile"], ["in" , "W:eax, dx" , "NONE" , "ED" , "ANY Volatile"], ["insb" , "W:es:zdi, dx" , "NONE" , "6C" , "ANY _Rep Volatile"], ["insw" , "W:es:zdi, dx" , "NONE" , "66 6D" , "ANY _Rep Volatile"], ["insd" , "W:es:zdi, dx" , "NONE" , "6D" , "ANY _Rep Volatile"], ["out" , "ub, al" , "I" , "E6 ib" , "ANY Volatile"], ["out" , "ub, ax" , "I" , "66 E7 ib" , "ANY Volatile"], ["out" , "ub, eax" , "I" , "E7 ib" , "ANY Volatile"], ["out" , "R:dx, R:al" , "NONE" , "EE" , "ANY Volatile"], ["out" , "R:dx, R:ax" , "NONE" , "66 EF" , "ANY Volatile"], ["out" , "R:dx, R:eax" , "NONE" , "EF" , "ANY Volatile"], ["outsb" , "R:dx, R:ds:zsi" , "NONE" , "6E" , "ANY _Rep Volatile"], ["outsw" , "R:dx, R:ds:zsi" , "NONE" , "66 6F" , "ANY _Rep Volatile"], ["outsd" , "R:dx, R:ds:zsi" , "NONE" , "6F" , "ANY _Rep Volatile"], ["clc" , "" , "NONE" , "F8" , "ANY CF=0"], ["cld" , "" , "NONE" , "FC" , "ANY DF=0"], ["cmc" , "" , "NONE" , "F5" , "ANY CF=X"], ["stc" , "" , "NONE" , "F9" , "ANY CF=1"], ["std" , "" , "NONE" , "FD" , "ANY DF=1"], ["lahf" , "w:" , "NONE" , "9F" , "LAHFSAHF Volatile SF=R ZF=R AF=R PF=R CF=R"], ["sahf" , "R:" , "NONE" , "9E" , "LAHFSAHF Volatile SF=W ZF=W AF=W PF=W CF=W"], ["adcx" , "X:~r32, ~r32/m32" , "RM" , "66 0F 38 F6 /r" , "ADX CF=X"], ["adcx" , "X:~r64, ~r64/m64" , "RM" , "REX.W 66 0F 38 F6 /r" , "ADX X64 CF=X"], ["adox" , "X:~r32, ~r32/m32" , "RM" , "F3 0F 38 F6 /r" , "ADX OF=X"], ["adox" , "X:~r64, ~r64/m64" , "RM" , "REX.W F3 0F 38 F6 /r" , "ADX X64 OF=X"], ["lzcnt" , "w:r16, r16/m16" , "RM" , "66 F3 0F BD /r" , "LZCNT OF=U SF=U ZF=W AF=U PF=U CF=W"], ["lzcnt" , "W:r32, r32/m32" , "RM" , "F3 0F BD /r" , "LZCNT OF=U SF=U ZF=W AF=U PF=U CF=W"], ["lzcnt" , "W:r64, r64/m64" , "RM" , "REX.W F3 0F BD /r" , "LZCNT X64 OF=U SF=U ZF=W AF=U PF=U CF=W"], ["popcnt" , "w:r16, r16/m16" , "RM" , "66 F3 0F B8 /r" , "POPCNT OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"], ["popcnt" , "W:r32, r32/m32" , "RM" , "F3 0F B8 /r" , "POPCNT OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"], ["popcnt" , "W:r64, r64/m64" , "RM" , "REX.W F3 0F B8 /r" , "POPCNT X64 OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"], ["andn" , "W:r32, r32, r32/m32" , "RVM" , "VEX.LZ.0F38.W0 F2 /r" , "BMI OF=0 SF=W ZF=W AF=U PF=U CF=0"], ["andn" , "W:r64, r64, r64/m64" , "RVM" , "VEX.LZ.0F38.W1 F2 /r" , "BMI X64 OF=0 SF=W ZF=W AF=U PF=U CF=0"], ["bextr" , "W:r32, r32/m32, r32" , "RMV" , "VEX.LZ.0F38.W0 F7 /r" , "BMI OF=0 SF=U ZF=W AF=U PF=U CF=0"], ["bextr" , "W:r64, r64/m64, r64" , "RMV" , "VEX.LZ.0F38.W1 F7 /r" , "BMI X64 OF=0 SF=U ZF=W AF=U PF=U CF=0"], ["blsi" , "W:r32, r32/m32" , "VM" , "VEX.LZ.0F38.W0 F3 /3" , "BMI OF=0 SF=W ZF=W AF=U PF=U CF=W"], ["blsi" , "W:r64, r64/m64" , "VM" , "VEX.LZ.0F38.W1 F3 /3" , "BMI X64 OF=0 SF=W ZF=W AF=U PF=U CF=W"], ["blsmsk" , "W:r32, r32/m32" , "VM" , "VEX.LZ.0F38.W0 F3 /2" , "BMI OF=0 SF=W ZF=0 AF=U PF=U CF=W"], ["blsmsk" , "W:r64, r64/m64" , "VM" , "VEX.LZ.0F38.W1 F3 /2" , "BMI X64 OF=0 SF=W ZF=0 AF=U PF=U CF=W"], ["blsr" , "W:r32, r32/m32" , "VM" , "VEX.LZ.0F38.W0 F3 /1" , "BMI OF=0 SF=W ZF=W AF=U PF=U CF=W"], ["blsr" , "W:r64, r64/m64" , "VM" , "VEX.LZ.0F38.W1 F3 /1" , "BMI X64 OF=0 SF=W ZF=W AF=U PF=U CF=W"], ["bzhi" , "W:r32, r32/m32, r32" , "RMV" , "VEX.LZ.0F38.W0 F5 /r" , "BMI2 OF=0 SF=W ZF=W AF=U PF=U CF=W"], ["bzhi" , "W:r64, r64/m64, r64" , "RMV" , "VEX.LZ.0F38.W1 F5 /r" , "BMI2 X64 OF=0 SF=W ZF=W AF=U PF=U CF=W"], ["mulx" , "W:r32, W:r32, ~r32/m32, ~" , "RVM" , "VEX.LZ.F2.0F38.W0 F6 /r" , "BMI2"], ["mulx" , "W:r64, W:r64, ~r64/m64, ~" , "RVM" , "VEX.LZ.F2.0F38.W1 F6 /r" , "BMI2 X64"], ["pdep" , "W:r32, r32, r32/m32" , "RVM" , "VEX.LZ.F2.0F38.W0 F5 /r" , "BMI2"], ["pdep" , "W:r64, r64, r64/m64" , "RVM" , "VEX.LZ.F2.0F38.W1 F5 /r" , "BMI2 X64"], ["pext" , "W:r32, r32, r32/m32" , "RVM" , "VEX.LZ.F3.0F38.W0 F5 /r" , "BMI2"], ["pext" , "W:r64, r64, r64/m64" , "RVM" , "VEX.LZ.F3.0F38.W1 F5 /r" , "BMI2 X64"], ["rorx" , "W:r32, r32/m32, ib/ub" , "RMI" , "VEX.LZ.F2.0F3A.W0 F0 /r ib" , "BMI2"], ["rorx" , "W:r64, r64/m64, ib/ub" , "RMI" , "VEX.LZ.F2.0F3A.W1 F0 /r ib" , "BMI2 X64"], ["sarx" , "W:r32, r32/m32, r32" , "RMV" , "VEX.LZ.F3.0F38.W0 F7 /r" , "BMI2"], ["sarx" , "W:r64, r64/m64, r64" , "RMV" , "VEX.LZ.F3.0F38.W1 F7 /r" , "BMI2 X64"], ["shlx" , "W:r32, r32/m32, r32" , "RMV" , "VEX.LZ.66.0F38.W0 F7 /r" , "BMI2"], ["shlx" , "W:r64, r64/m64, r64" , "RMV" , "VEX.LZ.66.0F38.W1 F7 /r" , "BMI2 X64"], ["shrx" , "W:r32, r32/m32, r32" , "RMV" , "VEX.LZ.F2.0F38.W0 F7 /r" , "BMI2"], ["shrx" , "W:r64, r64/m64, r64" , "RMV" , "VEX.LZ.F2.0F38.W1 F7 /r" , "BMI2 X64"], ["tzcnt" , "w:r16, r16/m16" , "RM" , "66 F3 0F BC /r" , "BMI OF=U SF=U ZF=W AF=U PF=U CF=W"], ["tzcnt" , "W:r32, r32/m32" , "RM" , "F3 0F BC /r" , "BMI OF=U SF=U ZF=W AF=U PF=U CF=W"], ["tzcnt" , "W:r64, r64/m64" , "RM" , "REX.W F3 0F BC /r" , "BMI X64 OF=U SF=U ZF=W AF=U PF=U CF=W"], ["blci" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 02 /6" , "TBM"], ["blci" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 02 /6" , "TBM X64"], ["blcic" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 01 /5" , "TBM"], ["blcic" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 01 /5" , "TBM X64"], ["blsic" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 01 /6" , "TBM"], ["blsic" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 01 /6" , "TBM X64"], ["blcfill" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 01 /1" , "TBM"], ["blcfill" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 01 /1" , "TBM X64"], ["blsfill" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 01 /2" , "TBM"], ["blsfill" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 01 /2" , "TBM X64"], ["blcmsk" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 02 /1" , "TBM"], ["blcmsk" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 02 /1" , "TBM X64"], ["blcs" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 01 /3" , "TBM"], ["blcs" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 01 /3" , "TBM X64"], ["tzmsk" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 01 /4" , "TBM"], ["tzmsk" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 01 /4" , "TBM X64"], ["t1mskc" , "W:r32, r32/m32" , "VM" , "XOP.LZ.M09.W0 01 /7" , "TBM"], ["t1mskc" , "W:r64, r64/m64" , "VM" , "XOP.LZ.M09.W1 01 /7" , "TBM X64"], ["crc32" , "X:r32, r8/m8" , "RM" , "F2 0F 38 F0 /r" , "SSE4_2"], ["crc32" , "X:r32, r16/m16" , "RM" , "66 F2 0F 38 F1 /r" , "SSE4_2"], ["crc32" , "X:r32, r32/m32" , "RM" , "F2 0F 38 F1 /r" , "SSE4_2"], ["crc32" , "X:r64, r8/m8" , "RM" , "REX.W F2 0F 38 F0 /r" , "SSE4_2 X64"], ["crc32" , "X:r64, r64/m64" , "RM" , "REX.W F2 0F 38 F1 /r" , "SSE4_2 X64"], ["movbe" , "w:r16, m16" , "RM" , "66 0F 38 F0 /r" , "MOVBE"], ["movbe" , "W:r32, m32" , "RM" , "0F 38 F0 /r" , "MOVBE"], ["movbe" , "W:r64, m64" , "RM" , "REX.W 0F 38 F0 /r" , "MOVBE X64"], ["movbe" , "W:m16, r16" , "MR" , "66 0F 38 F1 /r" , "MOVBE"], ["movbe" , "W:m32, r32" , "MR" , "0F 38 F1 /r" , "MOVBE"], ["movbe" , "W:m64, r64" , "MR" , "REX.W 0F 38 F1 /r" , "MOVBE X64"], ["movdiri" , "W:m32, r32" , "MR" , "0F 38 F9 /r" , "MOVDIRI"], ["movdiri" , "W:m64, r64" , "MR" , "REX.W 0F 38 F9 /r" , "MOVDIRI X64"], ["movdir64b" , "W:es:r32, m512" , "RM" , "66 0F 38 F8 /r" , "MOVDIR64B"], ["movdir64b" , "W:es:r64, m512" , "RM" , "66 0F 38 F8 /r" , "MOVDIR64B X64"], ["ldmxcsr" , "R:m32" , "M" , "0F AE /2" , "SSE Volatile"], ["stmxcsr" , "W:m32" , "M" , "0F AE /3" , "SSE Volatile"], ["lfence" , "" , "NONE" , "0F AE E8" , "SSE2 Volatile"], ["mfence" , "" , "NONE" , "0F AE F0" , "SSE2 Volatile"], ["sfence" , "" , "NONE" , "0F AE F8" , "MMX2 Volatile"], ["prefetch" , "R:mem" , "M" , "0F 0D /0" , "3DNOW"], ["prefetchnta" , "R:mem" , "M" , "0F 18 /0" , "MMX2"], ["prefetcht0" , "R:mem" , "M" , "0F 18 /1" , "MMX2"], ["prefetcht1" , "R:mem" , "M" , "0F 18 /2" , "MMX2"], ["prefetcht2" , "R:mem" , "M" , "0F 18 /3" , "MMX2"], ["prefetchw" , "R:mem" , "M" , "0F 0D /1" , "PREFETCHW OF=U SF=U ZF=U AF=U PF=U CF=U"], ["prefetchwt1" , "R:mem" , "M" , "0F 0D /2" , "PREFETCHWT1 OF=U SF=U ZF=U AF=U PF=U CF=U"], ["cpuid" , "X:, W:, X:, W:" , "NONE" , "0F A2" , "I486 Volatile"], ["cldemote" , "R:mem" , "M" , "0F 1C /0" , "CLDEMOTE Volatile"], ["clflush" , "R:mem" , "M" , "0F AE /7" , "CLFLUSH Volatile"], ["clflushopt" , "R:mem" , "M" , "66 0F AE /7" , "CLFLUSHOPT Volatile"], ["clwb" , "R:mem" , "M" , "66 0F AE /6" , "CLWB Volatile"], ["clzero" , "R:" , "NONE" , "0F 01 FC" , "CLZERO Volatile"], ["ptwrite" , "R:r32/m32" , "M" , "F3 0F AE /4" , "PTWRITE Volatile"], ["ptwrite" , "R:r64/m64" , "M" , "REX.W F3 0F AE /4" , "PTWRITE X64 Volatile"], ["serialize" , "" , "NONE" , "0F 01 E8" , "SERIALIZE Volatile"], ["rdpid" , "W:r32" , "R" , "F3 0F C7 /7" , "RDPID X86 Volatile"], ["rdpid" , "W:r64" , "R" , "F3 0F C7 /7" , "RDPID X64 Volatile"], ["rdpkru" , "W:, W:, R:" , "NONE" , "0F 01 EE" , "OSPKE Volatile"], ["rdpru" , "W:, W:, R:" , "NONE" , "0F 01 FD" , "RDPRU Volatile"], ["rdtsc" , "W:, W:" , "NONE" , "0F 31" , "RDTSC Volatile"], ["rdtscp" , "W:, W:, W:" , "NONE" , "0F 01 F9" , "RDTSCP Volatile"], ["arpl" , "x:r16/m16, R:r16" , "MR" , "63 /r" , "X86 ZF=W"], ["cli" , "" , "NONE" , "FA" , "ANY Volatile IF=W"], ["getsec" , "" , "NONE" , "0F 37" , "SMX Volatile"], ["int" , "ib/ub" , "I" , "CD ib" , "ANY Volatile"], ["int3" , "" , "NONE" , "CC" , "ANY Volatile"], ["into" , "" , "NONE" , "CE" , "X86 Deprecated Volatile OF=R"], ["lar" , "w:r16, R:r16/m16" , "RM" , "66 0F 02 /r" , "ANY Volatile ZF=W"], ["lar" , "W:r32, R:r32/m16" , "RM" , "0F 02 /r" , "ANY Volatile ZF=W"], ["lds" , "x:r16, m16_16" , "RM" , "66 C5 /r" , "X86 Volatile"], ["lds" , "X:r32, m16_32" , "RM" , "C5 /r" , "X86 Volatile"], ["les" , "x:r16, m16_16" , "RM" , "66 C4 /r" , "X86 Volatile"], ["les" , "X:r32, m16_32" , "RM" , "C4 /r" , "X86 Volatile"], ["lfs" , "x:r16, m16_16" , "RM" , "66 0F B4 /r" , "ANY Volatile"], ["lfs" , "X:r32, m16_32" , "RM" , "0F B4 /r" , "ANY Volatile"], ["lfs" , "X:r64, m16_64" , "RM" , "REX.W 0F B4 /r" , "X64 Volatile"], ["lgs" , "x:r16, m16_16" , "RM" , "66 0F B5 /r" , "ANY Volatile"], ["lgs" , "X:r32, m16_32" , "RM" , "0F B5 /r" , "ANY Volatile"], ["lgs" , "X:r64, m16_64" , "RM" , "REX.W 0F B5 /r" , "X64 Volatile"], ["lsl" , "w:r16, R:r16/m16" , "RM" , "66 0F 03 /r" , "ANY Volatile ZF=W"], ["lsl" , "W:r32, R:r32/m16" , "RM" , "0F 03 /r" , "ANY Volatile ZF=W"], ["lsl" , "W:r64, R:r32/m16" , "RM" , "REX.W 0F 03 /r" , "X64 Volatile ZF=W"], ["lss" , "x:r16, m16_16" , "RM" , "66 0F B2 /r" , "ANY Volatile"], ["lss" , "X:r32, m16_32" , "RM" , "0F B2 /r" , "ANY Volatile"], ["lss" , "X:r64, m16_64" , "RM" , "REX.W 0F B2 /r" , "X64 Volatile"], ["pause" , "" , "NONE" , "F3 90" , "ANY Volatile"], ["rsm" , "" , "NONE" , "0F AA" , "X86 Volatile OF=U SF=U ZF=U AF=U PF=U CF=U"], ["sgdt" , "W:mem" , "M" , "0F 01 /0" , "ANY Volatile"], ["sidt" , "W:mem" , "M" , "0F 01 /1" , "ANY Volatile"], ["sldt" , "w:r16/m16" , "M" , "66 0F 00 /0" , "ANY Volatile"], ["sldt" , "W:r32/m16" , "M" , "0F 00 /0" , "ANY Volatile"], ["sldt" , "W:r64/m16" , "M" , "REX.W 0F 00 /0" , "X64 Volatile"], ["smsw" , "w:r16/m16" , "M" , "66 0F 01 /4" , "ANY Volatile"], ["smsw" , "W:r32/m16" , "M" , "0F 01 /4" , "ANY Volatile"], ["smsw" , "W:r64/m16" , "M" , "REX.W 0F 01 /4" , "X64 Volatile"], ["sti" , "" , "NONE" , "FB" , "ANY Volatile IF=1"], ["str" , "w:r16/m16" , "M" , "66 0F 00 /1" , "ANY Volatile"], ["str" , "W:r32/m16" , "M" , "0F 00 /1" , "ANY Volatile"], ["str" , "W:r64/m16" , "M" , "REX.W 0F 00 /1" , "X64 Volatile"], ["verr" , "R:r16/m16" , "M" , "0F 00 /4" , "ANY Volatile ZF=W"], ["verw" , "R:r16/m16" , "M" , "0F 00 /5" , "ANY Volatile ZF=W"], ["xlatb" , "" , "NONE" , "D7" , "ANY Volatile"], ["rdfsbase" , "W:r32" , "M" , "F3 0F AE /0" , "FSGSBASE X64 Volatile"], ["rdfsbase" , "W:r64" , "M" , "REX.W F3 0F AE /0" , "FSGSBASE X64 Volatile"], ["rdgsbase" , "W:r32" , "M" , "F3 0F AE /1" , "FSGSBASE X64 Volatile"], ["rdgsbase" , "W:r64" , "M" , "REX.W F3 0F AE /1" , "FSGSBASE X64 Volatile"], ["wrfsbase" , "R:r32" , "M" , "F3 0F AE /2" , "FSGSBASE X64 Volatile"], ["wrfsbase" , "R:r64" , "M" , "REX.W F3 0F AE /2" , "FSGSBASE X64 Volatile"], ["wrgsbase" , "R:r32" , "M" , "F3 0F AE /3" , "FSGSBASE X64 Volatile"], ["wrgsbase" , "R:r64" , "M" , "REX.W F3 0F AE /3" , "FSGSBASE X64 Volatile"], ["fxrstor" , "R:mem" , "NONE" , "0F AE /1" , "FXSR Volatile C0=W C1=W C2=W C3=W"], ["fxrstor64" , "R:mem" , "NONE" , "REX.W 0F AE /1" , "FXSR X64 Volatile C0=W C1=W C2=W C3=W"], ["fxsave" , "W:mem" , "NONE" , "0F AE /0" , "FXSR Volatile"], ["fxsave64" , "W:mem" , "NONE" , "REX.W 0F AE /0" , "FXSR X64 Volatile"], ["xgetbv" , "W:, W:, R:" , "NONE" , "0F 01 D0" , "XSAVE Volatile XCR=R"], ["xrstor" , "R:mem, , " , "M" , "0F AE /5" , "XSAVE Volatile XCR=R"], ["xrstor64" , "R:mem, , " , "M" , "REX.W 0F AE /5" , "XSAVE X64 Volatile XCR=R"], ["xrstors" , "R:mem, , " , "M" , "0F C7 /3" , "XSAVES Volatile XCR=R"], ["xrstors64" , "R:mem, , " , "M" , "REX.W 0F C7 /3" , "XSAVES X64 Volatile XCR=R"], ["xsave" , "W:mem, , " , "M" , "0F AE /4" , "XSAVE Volatile XCR=R"], ["xsave64" , "W:mem, , " , "M" , "REX.W 0F AE /4" , "XSAVE X64 Volatile XCR=R"], ["xsavec" , "W:mem, , " , "M" , "0F C7 /4" , "XSAVEC Volatile XCR=R"], ["xsavec64" , "W:mem, , " , "M" , "REX.W 0F C7 /4" , "XSAVEC X64 Volatile XCR=R"], ["xsaveopt" , "W:mem, , " , "M" , "0F AE /6" , "XSAVEOPT Volatile XCR=R"], ["xsaveopt64" , "W:mem, , " , "M" , "REX.W 0F AE /6" , "XSAVEOPT X64 Volatile XCR=R"], ["xsaves" , "W:mem, , " , "M" , "0F C7 /5" , "XSAVES Volatile XCR=R"], ["xsaves64" , "W:mem, , " , "M" , "REX.W 0F C7 /5" , "XSAVES X64 Volatile XCR=R"], ["bndcl" , "R:bnd, r32/m32" , "RM" , "F3 0F 1A /r" , "MPX X86"], ["bndcl" , "R:bnd, r64/m64" , "RM" , "F3 0F 1A /r" , "MPX X64"], ["bndcn" , "R:bnd, r32/m32" , "RM" , "F2 0F 1B /r" , "MPX X86"], ["bndcn" , "R:bnd, r64/m64" , "RM" , "F2 0F 1B /r" , "MPX X64"], ["bndcu" , "R:bnd, r32/m32" , "RM" , "F2 0F 1A /r" , "MPX X86"], ["bndcu" , "R:bnd, r64/m64" , "RM" , "F2 0F 1A /r" , "MPX X64"], ["bndldx" , "W:bnd, mib" , "RM" , "0F 1A /r" , "MPX"], ["bndmk" , "W:bnd, mem" , "RM" , "F3 0F 1B /r" , "MPX"], ["bndmov" , "W:bnd, bnd/mem" , "RM" , "66 0F 1A /r" , "MPX"], ["bndmov" , "W:bnd/mem, bnd" , "MR" , "66 0F 1B /r" , "MPX"], ["bndstx" , "W:mib, bnd" , "MR" , "0F 1B /r" , "MPX"], ["monitorx" , "R:, R:, R:" , "NONE" , "0F 01 FA" , "MONITORX Volatile"], ["mwaitx" , "R:, R:, R:" , "NONE" , "0F 01 FB" , "MONITORX Volatile"], ["mcommit" , "" , "NONE" , "F3 0F 01 FA" , "MCOMMIT Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["enqcmd" , "W:es:r32, m512" , "RM" , "F2 0F 38 F8 /r" , "ENQCMD X86 Volatile"], ["enqcmd" , "W:es:r64, m512" , "RM" , "F2 0F 38 F8 /r" , "ENQCMD X64 Volatile"], ["enqcmds" , "W:es:r32, m512" , "RM" , "F3 0F 38 F8 /r" , "ENQCMD X86 Volatile"], ["enqcmds" , "W:es:r64, m512" , "RM" , "F3 0F 38 F8 /r" , "ENQCMD X64 Volatile"], ["tpause" , "R:r32, , " , "M" , "66 0F AE /6" , "WAITPKG Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["umonitor" , "R:ds:r32" , "M" , "F3 0F AE /6" , "WAITPKG Volatile"], ["umonitor" , "R:ds:r64" , "M" , "F3 0F AE /6" , "WAITPKG X64 Volatile"], ["umwait" , "R:r32, , " , "M" , "F2 0F AE /6" , "WAITPKG Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["rdrand" , "w:r16" , "M" , "66 0F C7 /6" , "RDRAND Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["rdrand" , "W:r32" , "M" , "0F C7 /6" , "RDRAND Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["rdrand" , "W:r64" , "M" , "REX.W 0F C7 /6" , "RDRAND X64 Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["rdseed" , "w:r16" , "M" , "66 0F C7 /7" , "RDSEED Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["rdseed" , "W:r32" , "M" , "0F C7 /7" , "RDSEED Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["rdseed" , "W:r64" , "M" , "REX.W 0F C7 /7" , "RDSEED X64 Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["syscall" , "" , "NONE" , "0F 05" , "X64 Volatile"], ["sysenter" , "" , "NONE" , "0F 34" , "ANY Volatile"], ["llwpcb" , "R:r32" , "M" , "XOP.L0.P0.M09.W0 12 /0" , "LWP Volatile"], ["llwpcb" , "R:r64" , "M" , "XOP.L0.P0.M09.W1 12 /0" , "LWP X64 Volatile"], ["lwpins" , "R:r32, R:r32/m32, id/ud" , "VMI" , "XOP.L0.P0.M0A.W0 12 /0 id" , "LWP Volatile"], ["lwpins" , "R:r64, R:r32/m32, id/ud" , "VMI" , "XOP.L0.P0.M0A.W1 12 /0 id" , "LWP X64 Volatile"], ["lwpval" , "R:r32, R:r32/m32, id/ud" , "VMI" , "XOP.L0.P0.M0A.W0 12 /1 id" , "LWP Volatile"], ["lwpval" , "R:r64, R:r32/m32, id/ud" , "VMI" , "XOP.L0.P0.M0A.W1 12 /1 id" , "LWP X64 Volatile"], ["slwpcb" , "W:r32" , "M" , "XOP.L0.P0.M09.W0 12 /1" , "LWP Volatile"], ["slwpcb" , "W:r64" , "M" , "XOP.L0.P0.M09.W1 12 /1" , "LWP X64 Volatile"], ["xabort" , "ib/ub" , "I" , "C6 /7 ib" , "RTM Volatile"], ["xbegin" , "rel16" , "NONE" , "66 C7 /7 cw" , "RTM Volatile"], ["xbegin" , "rel32" , "NONE" , "C7 /7 cd" , "RTM Volatile"], ["xend" , "" , "NONE" , "0F 01 D5" , "RTM Volatile"], ["xtest" , "" , "NONE" , "0F 01 D6" , "TSX Volatile OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"], ["xresldtrk" , "" , "NONE" , "F2 0F 01 E9" , "TSXLDTRK Volatile"], ["xsusldtrk" , "" , "NONE" , "F2 0F 01 E8" , "TSXLDTRK Volatile"], ["endbr32" , "" , "NONE" , "F3 0F 1E FB" , "CET_IBT Volatile"], ["endbr64" , "" , "NONE" , "F3 0F 1E FA" , "CET_IBT Volatile"], ["clrssbsy" , "R:m64" , "M" , "F3 0F AE /6" , "CET_SS Volatile PRIVILEGE=L0 OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["setssbsy" , "" , "NONE" , "F3 0F 01 E8" , "CET_SS Volatile PRIVILEGE=L0"], ["incsspd" , "r32" , "M" , "F3 0F AE /5" , "CET_SS Volatile"], ["incsspq" , "r64" , "M" , "REX.W F3 0F AE /5" , "CET_SS X64 Volatile"], ["rdsspd" , "W:r32" , "M" , "F3 0F 1E /1" , "CET_SS Volatile"], ["rdsspq" , "W:r64" , "M" , "REX.W F3 0F 1E /1" , "CET_SS X64 Volatile"], ["rstorssp" , "R:m64" , "M" , "F3 0F 01 /5" , "CET_SS Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["saveprevssp" , "" , "NONE" , "F3 0F 01 EA" , "CET_SS Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["wrssd" , "W:r32/m32, r32" , "MR" , "0F 38 F6 /r" , "CET_SS Volatile"], ["wrssq" , "W:r64/m64, r64" , "MR" , "REX.W 0F 38 F6 /r" , "CET_SS X64 Volatile"], ["wrussd" , "W:r32/m32, r32" , "MR" , "66 0F 38 F5 /r" , "CET_SS Volatile"], ["wrussq" , "W:r64/m64, r64" , "MR" , "REX.W 66 0F 38 F5 /r" , "CET_SS X64 Volatile"], ["hreset" , "ib/ub, W:" , "I" , "F3 0F 3A F0 /0 ib" , "HRESET Volatile"], ["uiret" , "" , "NONE" , "F3 0F 01 EC" , "UINTR X64 Volatile"], ["clui" , "" , "NONE" , "F3 0F 01 EE" , "UINTR X64 Volatile"], ["stui" , "" , "NONE" , "F3 0F 01 EF" , "UINTR X64 Volatile"], ["testui" , "" , "NONE" , "F3 0F 01 ED" , "UINTR X64 Volatile OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"], ["senduipi" , "R:r64" , "R" , "F3 0F C7 /6" , "UINTR X64 Volatile"], ["seamcall" , "" , "NONE" , "66 0F 01 CF" , "SEAM Volatile"], ["seamops" , "" , "NONE" , "66 0F 01 CE" , "SEAM Volatile"], ["seamret" , "" , "NONE" , "66 0F 01 CD" , "SEAM Volatile"], ["tdcall" , "" , "NONE" , "66 0F 01 CC" , "SEAM Volatile"], ["clts" , "" , "NONE" , "0F 06" , "ANY Volatile PRIVILEGE=L0"], ["hlt" , "" , "NONE" , "F4" , "ANY Volatile PRIVILEGE=L0"], ["invd" , "" , "NONE" , "0F 08" , "I486 Volatile PRIVILEGE=L0"], ["invlpg" , "R:mem" , "M" , "0F 01 /7" , "I486 Volatile PRIVILEGE=L0"], ["invpcid" , "R:r32, R:m128" , "RM" , "66 0F 38 82 /r" , "I486 X86 Volatile PRIVILEGE=L0"], ["invpcid" , "R:r64, R:m128" , "RM" , "66 0F 38 82 /r" , "I486 X64 Volatile PRIVILEGE=L0"], ["lgdt" , "R:mem" , "M" , "0F 01 /2" , "ANY Volatile PRIVILEGE=L0"], ["lidt" , "R:mem" , "M" , "0F 01 /3" , "ANY Volatile PRIVILEGE=L0"], ["lldt" , "R:r16/m16" , "M" , "0F 00 /2" , "ANY Volatile PRIVILEGE=L0"], ["lmsw" , "R:r16/m16" , "M" , "0F 01 /6" , "ANY Volatile PRIVILEGE=L0"], ["ltr" , "R:r16/m16" , "M" , "0F 00 /3" , "ANY Volatile PRIVILEGE=L0"], ["pconfig" , "" , "NONE" , "0F 01 C5" , "PCONFIG Volatile PRIVILEGE=L0"], ["rdpmc" , "W:, W:, R:" , "NONE" , "0F 33" , "ANY Volatile PRIVILEGE=L0"], ["rdmsr" , "W:, W:, R:" , "NONE" , "0F 32" , "ANY Volatile PRIVILEGE=L0 MSR=R"], ["swapgs" , "" , "NONE" , "0F 01 F8" , "X64 Volatile PRIVILEGE=L0"], ["sysexit" , "" , "NONE" , "0F 35" , "ANY Volatile PRIVILEGE=L0"], ["sysexitq" , "" , "NONE" , "REX.W 0F 35" , "ANY Volatile PRIVILEGE=L0"], ["sysret" , "" , "NONE" , "0F 07" , "X64 Volatile PRIVILEGE=L0"], ["sysretq" , "" , "NONE" , "REX.W 0F 07" , "X64 Volatile PRIVILEGE=L0"], ["wbinvd" , "" , "NONE" , "0F 09" , "ANY Volatile PRIVILEGE=L0"], ["wbnoinvd" , "" , "NONE" , "F3 0F 09" , "WBNOINVD Volatile PRIVILEGE=L0"], ["wrmsr" , "R:, R:, R:" , "NONE" , "0F 30" , "ANY Volatile PRIVILEGE=L0 MSR=W"], ["xsetbv" , "R:, R:, R:" , "NONE" , "0F 01 D1" , "XSAVE Volatile PRIVILEGE=L0 XCR=W"], ["monitor" , "R:, R:, R:" , "NONE" , "0F 01 C8" , "MONITOR Volatile PRIVILEGE=L0"], ["mwait" , "R:, R:" , "NONE" , "0F 01 C9" , "MONITOR Volatile PRIVILEGE=L0"], ["clac" , "" , "NONE" , "0F 01 CA" , "SMAP Volatile PRIVILEGE=L0 AC=0"], ["stac" , "" , "NONE" , "0F 01 CB" , "SMAP Volatile PRIVILEGE=L0 AC=1"], ["skinit" , "X:" , "NONE" , "0F 01 DE" , "SKINIT Volatile PRIVILEGE=L0"], ["stgi" , "" , "NONE" , "0F 01 DC" , "SKINIT Volatile PRIVILEGE=L0"], ["psmash" , "" , "NONE" , "F3 0F 01 FF" , "SNP X64 Volatile PRIVILEGE=L0 OF=W SF=W ZF=W AF=W PF=W"], ["pvalidate" , "" , "NONE" , "F2 0F 01 FF" , "SNP Volatile PRIVILEGE=L0 OF=W SF=W ZF=W AF=W PF=W CF=W"], ["rmpadjust" , "" , "NONE" , "F3 0F 01 FE" , "SNP X64 Volatile PRIVILEGE=L0 OF=W SF=W ZF=W AF=W PF=W"], ["rmpupdate" , "" , "NONE" , "F2 0F 01 FE" , "SNP X64 Volatile PRIVILEGE=L0 OF=W SF=W ZF=W AF=W PF=W"], ["invept" , "R:r32, R:m128" , "RM" , "66 0F 38 80 /r" , "VMX X86 Volatile PRIVILEGE=L0"], ["invept" , "R:r64, R:m128" , "RM" , "66 0F 38 80 /r" , "VMX X64 Volatile PRIVILEGE=L0"], ["invvpid" , "R:r32, R:m128" , "RM" , "66 0F 38 81 /r" , "VMX X86 Volatile PRIVILEGE=L0"], ["invvpid" , "R:r64, R:m128" , "RM" , "66 0F 38 81 /r" , "VMX X64 Volatile PRIVILEGE=L0"], ["vmcall" , "" , "NONE" , "0F 01 C1" , "VMX Volatile PRIVILEGE=L0"], ["vmclear" , "W:m64" , "M" , "66 0F C7 /6" , "VMX Volatile PRIVILEGE=L0"], ["vmfunc" , "" , "NONE" , "0F 01 D4" , "VMX Volatile"], ["vmlaunch" , "" , "NONE" , "0F 01 C2" , "VMX Volatile PRIVILEGE=L0"], ["vmptrld" , "R:m64" , "M" , "0F C7 /6" , "VMX Volatile PRIVILEGE=L0"], ["vmptrst" , "W:m64" , "M" , "0F C7 /7" , "VMX Volatile PRIVILEGE=L0"], ["vmread" , "W:r32/m32, R:r32" , "MR" , "0F 78 /r" , "VMX X86 Volatile PRIVILEGE=L0"], ["vmread" , "W:r64/m64, R:r64" , "MR" , "0F 78 /r" , "VMX X64 Volatile PRIVILEGE=L0"], ["vmresume" , "" , "NONE" , "0F 01 C3" , "VMX Volatile PRIVILEGE=L0"], ["vmwrite" , "R:r32, R:r32/m32" , "RM" , "0F 79 /r" , "VMX X86 Volatile PRIVILEGE=L0"], ["vmwrite" , "R:r64, R:r64/m64" , "RM" , "0F 79 /r" , "VMX X64 Volatile PRIVILEGE=L0"], ["vmxon" , "R:m64" , "M" , "F3 0F C7 /6" , "VMX"], ["clgi" , "" , "NONE" , "0F 01 DD" , "SVM Volatile PRIVILEGE=L0"], ["invlpga" , "R:, R:" , "NONE" , "0F 01 DF" , "SVM X86 Volatile PRIVILEGE=L0"], ["invlpga" , "R:, R:" , "NONE" , "67 0F 01 DF" , "SVM X64 Volatile PRIVILEGE=L0"], ["invlpga" , "R:, R:" , "NONE" , "0F 01 DF" , "SVM X64 Volatile PRIVILEGE=L0"], ["vmload" , "R:" , "NONE" , "0F 01 DA" , "SVM X86 Volatile PRIVILEGE=L0"], ["vmload" , "R:" , "NONE" , "0F 01 DA" , "SVM X64 Volatile PRIVILEGE=L0"], ["vmmcall" , "" , "NONE" , "0F 01 D9" , "SVM Volatile"], ["vmrun" , "X:" , "NONE" , "0F 01 D8" , "SVM X86 Volatile PRIVILEGE=L0"], ["vmrun" , "X:" , "NONE" , "0F 01 D8" , "SVM X64 Volatile PRIVILEGE=L0"], ["vmsave" , "R:" , "NONE" , "0F 01 DB" , "SVM X86 Volatile PRIVILEGE=L0"], ["vmsave" , "R:" , "NONE" , "0F 01 DB" , "SVM X64 Volatile PRIVILEGE=L0"], ["f2xm1" , "" , "NONE" , "D9 F0" , "FPU C0=U C1=W C2=U C3=U"], ["fabs" , "" , "NONE" , "D9 E1" , "FPU C0=U C1=0 C2=U C3=U"], ["fadd" , "R:m32fp" , "M" , "D8 /0" , "FPU C0=U C1=W C2=U C3=U"], ["fadd" , "R:m64fp" , "M" , "DC /0" , "FPU C0=U C1=W C2=U C3=U"], ["fadd" , "st(0), st(i)" , "O" , "D8 C0+i" , "FPU C0=U C1=W C2=U C3=U"], ["fadd" , "st(i), st(0)" , "O" , "DC C0+i" , "FPU C0=U C1=W C2=U C3=U"], ["faddp" , "" , "NONE" , "DE C1" , "FPU_POP C0=U C1=W C2=U C3=U"], ["faddp" , "st(i)" , "O" , "DE C0+i" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fbld" , "R:m80dec" , "M" , "DF /4" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fbstp" , "W:m80bcd" , "M" , "DF /6" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fchs" , "" , "NONE" , "D9 E0" , "FPU C0=U C1=0 C2=U C3=U"], ["fclex" , "" , "NONE" , "9B DB E2" , "FPU C0=U C1=U C2=U C3=U"], ["fcmovb" , "st(i)" , "O" , "DA C0+i" , "FPU CMOV C0=U C1=W C2=U C3=U CF=R"], ["fcmovbe" , "st(i)" , "O" , "DA D0+i" , "FPU CMOV C0=U C1=W C2=U C3=U CF=R ZF=R"], ["fcmove" , "st(i)" , "O" , "DA C8+i" , "FPU CMOV C0=U C1=W C2=U C3=U ZF=R"], ["fcmovnb" , "st(i)" , "O" , "DB C0+i" , "FPU CMOV C0=U C1=W C2=U C3=U CF=R"], ["fcmovnbe" , "st(i)" , "O" , "DB D0+i" , "FPU CMOV C0=U C1=W C2=U C3=U CF=R ZF=R"], ["fcmovne" , "st(i)" , "O" , "DB C8+i" , "FPU CMOV C0=U C1=W C2=U C3=U ZF=R"], ["fcmovnu" , "st(i)" , "O" , "DB D8+i" , "FPU CMOV C0=U C1=W C2=U C3=U PF=R"], ["fcmovu" , "st(i)" , "O" , "DA D8+i" , "FPU CMOV C0=U C1=W C2=U C3=U PF=R"], ["fcom" , "" , "NONE" , "D8 D1" , "FPU C0=W C1=0 C2=W C3=W"], ["fcom" , "R:m32fp" , "M" , "D8 /2" , "FPU C0=W C1=0 C2=W C3=W"], ["fcom" , "R:m64fp" , "M" , "DC /2" , "FPU C0=W C1=0 C2=W C3=W"], ["fcom" , "R:st(i)" , "O" , "D8 D0+i" , "FPU C0=W C1=0 C2=W C3=W"], ["fcomi" , "R:st(i)" , "O" , "DB F0+i" , "FPU C1=0 ZF=W PF=W CF=W"], ["fcomip" , "R:st(i)" , "O" , "DF F0+i" , "FPU_POP C1=0 ZF=W PF=W CF=W"], ["fcomp" , "" , "NONE" , "D8 D9" , "FPU_POP C0=W C1=W C2=W C3=W"], ["fcomp" , "R:m32fp" , "M" , "D8 /3" , "FPU_POP C0=W C1=W C2=W C3=W"], ["fcomp" , "R:m64fp" , "M" , "DC /3" , "FPU_POP C0=W C1=W C2=W C3=W"], ["fcomp" , "R:st(i)" , "O" , "D8 D8+i" , "FPU_POP C0=W C1=W C2=W C3=W"], ["fcompp" , "" , "NONE" , "DE D9" , "FPU_POP=2 C0=W C1=W C2=W C3=W"], ["fcos" , "" , "NONE" , "D9 FF" , "FPU C0=U C1=W C2=W C3=U"], ["fdecstp" , "" , "NONE" , "D9 F6" , "FPU_TOP=-1 C0=U C1=W C2=U C3=U"], ["fdiv" , "R:m32fp" , "M" , "D8 /6" , "FPU C0=U C1=W C2=U C3=U"], ["fdiv" , "R:m64fp" , "M" , "DC /6" , "FPU C0=U C1=W C2=U C3=U"], ["fdiv" , "st(0), st(i)" , "O" , "D8 F0+i" , "FPU C0=U C1=W C2=U C3=U"], ["fdiv" , "st(i), st(0)" , "O" , "DC F8+i" , "FPU C0=U C1=W C2=U C3=U"], ["fdivp" , "" , "NONE" , "DE F9" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fdivp" , "st(i)" , "O" , "DE F8+i" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fdivr" , "R:m32fp" , "M" , "D8 /7" , "FPU C0=U C1=W C2=U C3=U"], ["fdivr" , "R:m64fp" , "M" , "DC /7" , "FPU C0=U C1=W C2=U C3=U"], ["fdivr" , "st(0), st(i)" , "O" , "D8 F8+i" , "FPU C0=U C1=W C2=U C3=U"], ["fdivr" , "st(i), st(0)" , "O" , "DC F0+i" , "FPU C0=U C1=W C2=U C3=U"], ["fdivrp" , "" , "NONE" , "DE F1" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fdivrp" , "st(i)" , "O" , "DE F0+i" , "FPU_POP C0=U C1=W C2=U C3=U"], ["ffree" , "st(i)" , "O" , "DD C0+i" , "FPU C0=U C1=U C2=U C3=U"], ["fiadd" , "R:m16int" , "M" , "DE /0" , "FPU C0=U C1=W C2=U C3=U"], ["fiadd" , "R:m32int" , "M" , "DA /0" , "FPU C0=U C1=W C2=U C3=U"], ["ficom" , "R:m16int" , "M" , "DE /2" , "FPU C0=W C1=0 C2=W C3=W"], ["ficom" , "R:m32int" , "M" , "DA /2" , "FPU C0=W C1=0 C2=W C3=W"], ["ficomp" , "R:m16int" , "M" , "DE /3" , "FPU C0=W C1=0 C2=W C3=W"], ["ficomp" , "R:m32int" , "M" , "DA /3" , "FPU C0=W C1=0 C2=W C3=W"], ["fidiv" , "R:m16int" , "M" , "DE /6" , "FPU C0=U C1=W C2=U C3=U"], ["fidiv" , "R:m32int" , "M" , "DA /6" , "FPU C0=U C1=W C2=U C3=U"], ["fidivr" , "R:m16int" , "M" , "DE /7" , "FPU C0=U C1=W C2=U C3=U"], ["fidivr" , "R:m32int" , "M" , "DA /7" , "FPU C0=U C1=W C2=U C3=U"], ["fild" , "R:m16int" , "M" , "DF /0" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fild" , "R:m32int" , "M" , "DB /0" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fild" , "R:m64int" , "M" , "DF /5" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fimul" , "R:m16int" , "M" , "DE /1" , "FPU C0=U C1=W C2=U C3=U"], ["fimul" , "R:m32int" , "M" , "DA /1" , "FPU C0=U C1=W C2=U C3=U"], ["fincstp" , "" , "NONE" , "D9 F7" , "FPU_TOP=+1 C0=U C1=0 C2=U C3=U"], ["finit" , "" , "NONE" , "9B DB E3" , "FPU C0=0 C1=0 C2=0 C3=0"], ["fist" , "W:m16int" , "M" , "DF /2" , "FPU C0=U C1=W C2=U C3=U"], ["fist" , "W:m32int" , "M" , "DB /2" , "FPU C0=U C1=W C2=U C3=U"], ["fistp" , "W:m16int" , "M" , "DF /3" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fistp" , "W:m32int" , "M" , "DB /3" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fistp" , "W:m64int" , "M" , "DF /7" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fisttp" , "W:m16int" , "M" , "DF /1" , "FPU_POP SSE3 C0=U C1=0 C2=U C3=U"], ["fisttp" , "W:m32int" , "M" , "DB /1" , "FPU_POP SSE3 C0=U C1=0 C2=U C3=U"], ["fisttp" , "W:m64int" , "M" , "DD /1" , "FPU_POP SSE3 C0=U C1=0 C2=U C3=U"], ["fisub" , "R:m16int" , "M" , "DE /4" , "FPU C0=U C1=W C2=U C3=U"], ["fisub" , "R:m32int" , "M" , "DA /4" , "FPU C0=U C1=W C2=U C3=U"], ["fisubr" , "R:m16int" , "M" , "DE /5" , "FPU C0=U C1=W C2=U C3=U"], ["fisubr" , "R:m32int" , "M" , "DA /5" , "FPU C0=U C1=W C2=U C3=U"], ["fld" , "R:m32fp" , "M" , "D9 /0" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fld" , "R:m64fp" , "M" , "DD /0" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fld" , "R:m80fp" , "M" , "DB /5" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fld" , "R:st(i)" , "O" , "D9 C0+i" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fld1" , "" , "NONE" , "D9 E8" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fldcw" , "R:m16" , "M" , "D9 /5" , "FPU C0=U C1=U C2=U C3=U"], ["fldenv" , "R:mem" , "M" , "D9 /4" , "FPU C0=W C1=W C2=W C3=W"], ["fldl2e" , "" , "NONE" , "D9 EA" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fldl2t" , "" , "NONE" , "D9 E9" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fldlg2" , "" , "NONE" , "D9 EC" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fldln2" , "" , "NONE" , "D9 ED" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fldpi" , "" , "NONE" , "D9 EB" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fldz" , "" , "NONE" , "D9 EE" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fmul" , "R:m32fp" , "M" , "D8 /1" , "FPU C0=U C1=W C2=U C3=U"], ["fmul" , "R:m64fp" , "M" , "DC /1" , "FPU C0=U C1=W C2=U C3=U"], ["fmul" , "st(0), st(i)" , "O" , "D8 C8+i" , "FPU C0=U C1=W C2=U C3=U"], ["fmul" , "st(i), st(0)" , "O" , "DC C8+i" , "FPU C0=U C1=W C2=U C3=U"], ["fmulp" , "" , "NONE" , "DE C9" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fmulp" , "st(i)" , "O" , "DE C8+i" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fnclex" , "" , "NONE" , "DB E2" , "FPU C0=U C1=U C2=U C3=U"], ["fninit" , "" , "NONE" , "DB E3" , "FPU C0=0 C1=0 C2=0 C3=0"], ["fnop" , "" , "NONE" , "D9 D0" , "FPU C0=U C1=U C2=U C3=U"], ["fnsave" , "W:mem" , "M" , "DD /6" , "FPU C0=W C1=W C2=W C3=W"], ["fnstcw" , "W:m16" , "M" , "D9 /7" , "FPU C0=U C1=U C2=U C3=U"], ["fnstenv" , "W:mem" , "M" , "D9 /6" , "FPU C0=U C1=U C2=U C3=U"], ["fnstsw" , "w:ax" , "NONE" , "DF E0" , "FPU C0=U C1=U C2=U C3=U"], ["fnstsw" , "W:m16" , "M" , "DD /7" , "FPU C0=U C1=U C2=U C3=U"], ["fpatan" , "" , "NONE" , "D9 F3" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fprem" , "" , "NONE" , "D9 F8" , "FPU C0=U C1=W C2=U C3=U"], ["fprem1" , "" , "NONE" , "D9 F5" , "FPU C0=W C1=W C2=W C3=W"], ["fptan" , "" , "NONE" , "D9 F2" , "FPU_POP C0=U C1=W C2=W C3=U"], ["frndint" , "" , "NONE" , "D9 FC" , "FPU C0=U C1=W C2=U C3=U"], ["frstor" , "R:mem" , "M" , "DD /4" , "FPU C0=W C1=W C2=W C3=W"], ["fsave" , "W:mem" , "M" , "9B DD /6" , "FPU C0=W C1=W C2=W C3=W"], ["fscale" , "" , "NONE" , "D9 FD" , "FPU C0=U C1=W C2=U C3=U"], ["fsin" , "" , "NONE" , "D9 FE" , "FPU C0=U C1=W C2=W C3=U"], ["fsincos" , "" , "NONE" , "D9 FB" , "FPU_PUSH C0=U C1=W C2=W C3=U"], ["fsqrt" , "" , "NONE" , "D9 FE" , "FPU C0=U C1=W C2=U C3=U"], ["fst" , "W:m32fp" , "M" , "D9 /2" , "FPU C0=U C1=W C2=U C3=U"], ["fst" , "W:m64fp" , "M" , "DD /2" , "FPU C0=U C1=W C2=U C3=U"], ["fst" , "W:st(i)" , "O" , "DD D0+i" , "FPU C0=U C1=W C2=U C3=U"], ["fstcw" , "W:m16" , "M" , "9B D9 /7" , "FPU C0=U C1=U C2=U C3=U"], ["fstenv" , "W:mem" , "M" , "9B D9 /6" , "FPU C0=U C1=U C2=U C3=U"], ["fstp" , "W:m32fp" , "M" , "D9 /3" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fstp" , "W:m64fp" , "M" , "DD /3" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fstp" , "W:m80fp" , "M" , "DB /7" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fstp" , "W:st(i)" , "O" , "DD D8+i" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fstsw" , "w:ax" , "NONE" , "9B DF E0" , "FPU C0=U C1=U C2=U C3=U"], ["fstsw" , "W:m16" , "M" , "9B DD /7" , "FPU C0=U C1=U C2=U C3=U"], ["fsub" , "R:m32fp" , "M" , "D8 /4" , "FPU C0=U C1=W C2=U C3=U"], ["fsub" , "R:m64fp" , "M" , "DC /4" , "FPU C0=U C1=W C2=U C3=U"], ["fsub" , "st(0), st(i)" , "O" , "D8 E0+i" , "FPU C0=U C1=W C2=U C3=U"], ["fsub" , "st(i), st(0)" , "O" , "DC E8+i" , "FPU C0=U C1=W C2=U C3=U"], ["fsubp" , "" , "NONE" , "DE E9" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fsubp" , "st(i)" , "O" , "DE E8+i" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fsubr" , "R:m32fp" , "M" , "D8 /5" , "FPU C0=U C1=W C2=U C3=U"], ["fsubr" , "R:m64fp" , "M" , "DC /5" , "FPU C0=U C1=W C2=U C3=U"], ["fsubr" , "st(0), st(i)" , "O" , "D8 E8+i" , "FPU C0=U C1=W C2=U C3=U"], ["fsubr" , "st(i), st(0)" , "O" , "DC E0+i" , "FPU C0=U C1=W C2=U C3=U"], ["fsubrp" , "" , "NONE" , "DE E1" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fsubrp" , "st(i)" , "O" , "DE E0+i" , "FPU_POP C0=U C1=W C2=U C3=U"], ["ftst" , "" , "NONE" , "D9 E4" , "FPU C0=W C1=0 C2=W C3=W"], ["fucom" , "" , "NONE" , "DD E1" , "FPU C0=W C1=0 C2=W C3=W"], ["fucom" , "R:st(i)" , "O" , "DD E0+i" , "FPU C0=W C1=0 C2=W C3=W"], ["fucomi" , "R:st(i)" , "O" , "DB E8+i" , "FPU C1=0 ZF=W PF=W CF=W"], ["fucomip" , "R:st(i)" , "O" , "DF E8+i" , "FPU_POP C1=0 ZF=W PF=W CF=W"], ["fucomp" , "" , "NONE" , "DD E9" , "FPU_POP C0=W C1=W C2=W C3=W"], ["fucomp" , "R:st(i)" , "O" , "DD E8+i" , "FPU_POP C0=W C1=W C2=W C3=W"], ["fucompp" , "" , "NONE" , "DA E9" , "FPU_POP=2 C0=W C1=W C2=W C3=W"], ["fwait/wait" , "" , "NONE" , "9B" , "FPU C0=U C1=U C2=U C3=U"], ["fxam" , "" , "NONE" , "D9 E5" , "FPU C0=W C1=W C2=W C3=W"], ["fxch" , "" , "NONE" , "D9 C9" , "FPU C0=U C1=0 C2=U C3=U"], ["fxch" , "st(i)" , "O" , "D9 C8+i" , "FPU C0=U C1=0 C2=U C3=U"], ["fxtract" , "" , "NONE" , "D9 F4" , "FPU_PUSH C0=U C1=W C2=U C3=U"], ["fyl2x" , "" , "NONE" , "D9 F1" , "FPU_POP C0=U C1=W C2=U C3=U"], ["fyl2xp1" , "" , "NONE" , "D9 F9" , "FPU_POP C0=U C1=W C2=U C3=U"], ["addpd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 58 /r" , "SSE2"], ["addps" , "X:~xmm, ~xmm/m128" , "RM" , "0F 58 /r" , "SSE"], ["addsd" , "x:xmm[63:0], xmm[63:0]/m64" , "RM" , "F2 0F 58 /r" , "SSE2"], ["addss" , "x:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 58 /r" , "SSE"], ["addsubpd" , "X:xmm, xmm/m128" , "RM" , "66 0F D0 /r" , "SSE3"], ["addsubps" , "X:xmm, xmm/m128" , "RM" , "F2 0F D0 /r" , "SSE3"], ["andnpd" , "X:xmm, xmm/m128" , "RM" , "66 0F 55 /r" , "SSE2"], ["andnps" , "X:xmm, xmm/m128" , "RM" , "0F 55 /r" , "SSE"], ["andpd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 54 /r" , "SSE2"], ["andps" , "X:~xmm, ~xmm/m128" , "RM" , "0F 54 /r" , "SSE"], ["blendpd" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 0D /r ib" , "SSE4_1"], ["blendps" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 0C /r ib" , "SSE4_1"], ["blendvpd" , "X:xmm, xmm/m128, " , "RM" , "66 0F 38 15 /r" , "SSE4_1"], ["blendvps" , "X:xmm, xmm/m128, " , "RM" , "66 0F 38 14 /r" , "SSE4_1"], ["cmppd" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F C2 /r ib" , "SSE2"], ["cmpps" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "0F C2 /r ib" , "SSE"], ["cmpsd" , "x:xmm[63:0], xmm[63:0]/m64, ib/ub" , "RMI" , "F2 0F C2 /r ib" , "SSE2"], ["cmpss" , "x:xmm[31:0], xmm[31:0]/m32, ib/ub" , "RMI" , "F3 0F C2 /r ib" , "SSE"], ["comisd" , "R:xmm[63:0], xmm[63:0]/m64" , "RM" , "66 0F 2F /r" , "SSE2 OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["comiss" , "R:xmm[31:0], xmm[31:0]/m32" , "RM" , "0F 2F /r" , "SSE OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["cvtdq2pd" , "W:xmm, xmm[63:0]/m64" , "RM" , "F3 0F E6 /r" , "SSE2"], ["cvtdq2ps" , "W:xmm, xmm/m128" , "RM" , "0F 5B /r" , "SSE2"], ["cvtpd2dq" , "W:xmm[63:0], xmm/m128" , "RM" , "F2 0F E6 /r" , "SSE2"], ["cvtpd2pi" , "W:mm, xmm/m128" , "RM" , "66 0F 2D /r" , "SSE2"], ["cvtpd2ps" , "W:xmm[63:0], xmm/m128" , "RM" , "66 0F 5A /r" , "SSE2"], ["cvtpi2pd" , "W:xmm, R:mm[63:0]/m64" , "RM" , "66 0F 2A /r" , "SSE2"], ["cvtpi2ps" , "w:xmm[63:0], mm/m64" , "RM" , "0F 2A /r" , "SSE"], ["cvtps2dq" , "W:xmm, xmm/m128" , "RM" , "66 0F 5B /r" , "SSE2"], ["cvtps2pd" , "W:xmm, xmm[63:0]/m64" , "RM" , "0F 5A /r" , "SSE2"], ["cvtps2pi" , "W:mm, xmm[63:0]/m64" , "RM" , "0F 2D /r" , "SSE"], ["cvtsd2si" , "W:r32, xmm[63:0]/m64" , "RM" , "F2 0F 2D /r" , "SSE2"], ["cvtsd2si" , "W:r64, xmm[63:0]/m64" , "RM" , "REX.W F2 0F 2D /r" , "SSE2 X64"], ["cvtsd2ss" , "w:xmm[31:0], xmm[63:0]/m64" , "RM" , "F2 0F 5A /r" , "SSE2"], ["cvtsi2sd" , "w:xmm[63:0], r32/m32" , "RM" , "F2 0F 2A /r" , "SSE2"], ["cvtsi2sd" , "w:xmm[63:0], r64/m64" , "RM" , "REX.W F2 0F 2A /r" , "SSE2 X64"], ["cvtsi2ss" , "w:xmm[31:0], r32/m32" , "RM" , "F3 0F 2A /r" , "SSE"], ["cvtsi2ss" , "w:xmm[31:0], r64/m64" , "RM" , "REX.W F3 0F 2A /r" , "SSE X64"], ["cvtss2sd" , "w:xmm[63:0], xmm[31:0]/m32" , "RM" , "F3 0F 5A /r" , "SSE2"], ["cvtss2si" , "W:r32, xmm[31:0]/m32" , "RM" , "F3 0F 2D /r" , "SSE"], ["cvtss2si" , "W:r64, xmm[31:0]/m32" , "RM" , "REX.W F3 0F 2D /r" , "SSE X64"], ["cvttpd2dq" , "W:xmm[63:0], xmm/m128" , "RM" , "66 0F E6 /r" , "SSE2"], ["cvttpd2pi" , "W:mm, xmm/m128" , "RM" , "66 0F 2C /r" , "SSE2"], ["cvttps2dq" , "W:xmm, xmm/m128" , "RM" , "F3 0F 5B /r" , "SSE2"], ["cvttps2pi" , "W:mm, xmm[63:0]/m64" , "RM" , "0F 2C /r" , "SSE"], ["cvttsd2si" , "W:r32, xmm[63:0]/m64" , "RM" , "F2 0F 2C /r" , "SSE2"], ["cvttsd2si" , "W:r64, xmm[63:0]/m64" , "RM" , "REX.W F2 0F 2C /r" , "SSE2 X64"], ["cvttss2si" , "W:r32, xmm[31:0]/m32" , "RM" , "F3 0F 2C /r" , "SSE"], ["cvttss2si" , "W:r64, xmm[31:0]/m32" , "RM" , "REX.W F3 0F 2C /r" , "SSE X64"], ["divpd" , "X:xmm, xmm/m128" , "RM" , "66 0F 5E /r" , "SSE2"], ["divps" , "X:xmm, xmm/m128" , "RM" , "0F 5E /r" , "SSE"], ["divsd" , "x:xmm[63:0], xmm[63:0]/m64" , "RM" , "F2 0F 5E /r" , "SSE2"], ["divss" , "x:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 5E /r" , "SSE"], ["dppd" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 41 /r ib" , "SSE4_1"], ["dpps" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 40 /r ib" , "SSE4_1"], ["extractps" , "W:r32/m32, xmm, ib/ub" , "MRI" , "66 0F 3A 17 /r ib" , "SSE4_1"], ["extrq" , "X:xmm, ib/ub, ib/ub" , "RII" , "66 0F 78 /0 ib ib" , "SSE4A"], ["extrq" , "X:xmm, xmm" , "RM" , "66 0F 79 /r" , "SSE4A"], ["haddpd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 7C /r" , "SSE3"], ["haddps" , "X:~xmm, ~xmm/m128" , "RM" , "F2 0F 7C /r" , "SSE3"], ["hsubpd" , "X:xmm, xmm/m128" , "RM" , "66 0F 7D /r" , "SSE3"], ["hsubps" , "X:xmm, xmm/m128" , "RM" , "F2 0F 7D /r" , "SSE3"], ["insertps" , "X:xmm, xmm[31:0]/m32, ib/ub" , "RMI" , "66 0F 3A 21 /r ib" , "SSE4_1"], ["insertq" , "X:xmm, xmm" , "RM" , "F2 0F 79 /r" , "SSE4A"], ["insertq" , "X:xmm, xmm, ib/ub, ib/ub" , "RMII" , "F2 0F 78 /r ib ib" , "SSE4A"], ["lddqu" , "W:xmm, m128" , "RM" , "F2 0F F0 /r" , "SSE3"], ["maskmovdqu" , "R:xmm, xmm, X:" , "RM" , "66 0F F7 /r" , "SSE2"], ["maskmovq" , "R:mm, mm, X:" , "RM" , "0F F7 /r" , "MMX2"], ["maxpd" , "X:xmm, xmm/m128" , "RM" , "66 0F 5F /r" , "SSE2"], ["maxps" , "X:xmm, xmm/m128" , "RM" , "0F 5F /r" , "SSE"], ["maxsd" , "x:xmm[63:0], xmm[63:0]/m64" , "RM" , "F2 0F 5F /r" , "SSE2"], ["maxss" , "x:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 5F /r" , "SSE"], ["minpd" , "X:xmm, xmm/m128" , "RM" , "66 0F 5D /r" , "SSE2"], ["minps" , "X:xmm, xmm/m128" , "RM" , "0F 5D /r" , "SSE"], ["minsd" , "x:xmm[63:0], xmm[63:0]/m64" , "RM" , "F2 0F 5D /r" , "SSE2"], ["minss" , "x:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 5D /r" , "SSE"], ["movapd" , "W:xmm, xmm/m128" , "RM" , "66 0F 28 /r" , "SSE2"], ["movapd" , "W:xmm/m128, xmm" , "MR" , "66 0F 29 /r" , "SSE2"], ["movaps" , "W:xmm, xmm/m128" , "RM" , "0F 28 /r" , "SSE"], ["movaps" , "W:xmm/m128, xmm" , "MR" , "0F 29 /r" , "SSE"], ["movd" , "W:mm[31:0], R:r32[31:0]/m32" , "RM" , "0F 6E /r" , "MMX"], ["movd" , "W:r32[31:0]/m32, R:mm[31:0]" , "MR" , "0F 7E /r" , "MMX"], ["movd" , "W:r32[31:0]/m32, xmm[31:0]" , "MR" , "66 0F 7E /r" , "SSE2"], ["movd" , "W:xmm[31:0], R:r32[31:0]/m32" , "RM" , "66 0F 6E /r" , "SSE2"], ["movddup" , "W:xmm, xmm[63:0]/m64" , "RM" , "F2 0F 12 /r" , "SSE3"], ["movdq2q" , "W:mm, xmm[63:0]" , "RM" , "F2 0F D6 /r" , "SSE2"], ["movdqa" , "W:xmm, xmm/m128" , "RM" , "66 0F 6F /r" , "SSE2"], ["movdqa" , "W:xmm/m128, xmm" , "MR" , "66 0F 7F /r" , "SSE2"], ["movdqu" , "W:xmm, xmm/m128" , "RM" , "F3 0F 6F /r" , "SSE2"], ["movdqu" , "W:xmm/m128, xmm" , "MR" , "F3 0F 7F /r" , "SSE2"], ["movhlps" , "w:xmm[63:0], xmm[127:64]" , "RM" , "0F 12 /r" , "SSE"], ["movhpd" , "W:m64, xmm[127:64]" , "MR" , "66 0F 17 /r" , "SSE2"], ["movhpd" , "w:xmm[127:64], m64" , "RM" , "66 0F 16 /r" , "SSE2"], ["movhps" , "W:m64, xmm[127:64]" , "MR" , "0F 17 /r" , "SSE"], ["movhps" , "w:xmm[127:64], m64" , "RM" , "0F 16 /r" , "SSE"], ["movlhps" , "w:xmm[127:64], xmm[63:0]" , "RM" , "0F 16 /r" , "SSE"], ["movlpd" , "W:m64, xmm[63:0]" , "MR" , "66 0F 13 /r" , "SSE2"], ["movlpd" , "w:xmm[63:0], m64" , "RM" , "66 0F 12 /r" , "SSE2"], ["movlps" , "W:m64, xmm[63:0]" , "MR" , "0F 13 /r" , "SSE"], ["movlps" , "w:xmm[63:0], m64" , "RM" , "0F 12 /r" , "SSE"], ["movmskpd" , "W:r32[1:0], xmm" , "RM" , "66 0F 50 /r" , "SSE2"], ["movmskps" , "W:r32[3:0], xmm" , "RM" , "0F 50 /r" , "SSE"], ["movntdq" , "W:m128, xmm" , "MR" , "66 0F E7 /r" , "SSE2"], ["movntdqa" , "W:xmm, m128" , "RM" , "66 0F 38 2A /r" , "SSE4_1"], ["movnti" , "W:m32, r32" , "MR" , "0F C3 /r" , "SSE2"], ["movnti" , "W:m64, r64" , "MR" , "REX.W 0F C3 /r" , "SSE2 X64"], ["movntpd" , "W:m128, xmm" , "MR" , "66 0F 2B /r" , "SSE2"], ["movntps" , "W:m128, xmm" , "MR" , "0F 2B /r" , "SSE"], ["movntq" , "W:m64, mm" , "MR" , "0F E7 /r" , "MMX2"], ["movntsd" , "W:m64, xmm[63:0]" , "RM" , "F2 0F 2B /r" , "SSE4A"], ["movntss" , "W:m32, xmm[31:0]" , "RM" , "F3 0F 2B /r" , "SSE4A"], ["movq" , "W:mm, mm/m64" , "RM" , "0F 6F /r" , "MMX"], ["movq" , "W:mm, r64/m64" , "RM" , "REX.W 0F 6E /r" , "MMX X64"], ["movq" , "W:mm/m64, mm" , "MR" , "0F 7F /r" , "MMX"], ["movq" , "W:r64/m64, mm" , "MR" , "REX.W 0F 7E /r" , "MMX X64"], ["movq" , "W:r64/m64, xmm[63:0]" , "MR" , "REX.W 66 0F 7E /r" , "SSE2 X64"], ["movq" , "W:xmm[63:0], r64[63:0]/m64" , "RM" , "REX.W 66 0F 6E /r" , "SSE2 X64"], ["movq" , "W:xmm[63:0], xmm[63:0]/m64" , "RM" , "F3 0F 7E /r" , "SSE2"], ["movq" , "W:xmm[63:0]/m64, xmm[63:0]" , "MR" , "66 0F D6 /r" , "SSE2"], ["movq2dq" , "W:xmm[63:0], mm" , "RM" , "F3 0F D6 /r" , "SSE2"], ["movsd" , "w:xmm[63:0], xmm[63:0]" , "RM" , "F2 0F 10 /r" , "SSE2"], ["movsd" , "W:xmm[63:0], m64" , "RM" , "F2 0F 10 /r" , "SSE2"], ["movsd" , "W:m64, xmm[63:0]" , "MR" , "F2 0F 11 /r" , "SSE2"], ["movshdup" , "W:xmm, xmm/m128" , "RM" , "F3 0F 16 /r" , "SSE3"], ["movsldup" , "W:xmm, xmm/m128" , "RM" , "F3 0F 12 /r" , "SSE3"], ["movss" , "w:xmm[31:0], xmm[31:0]" , "RM" , "F3 0F 10 /r" , "SSE"], ["movss" , "W:xmm[31:0], m32" , "RM" , "F3 0F 10 /r" , "SSE"], ["movss" , "W:m32, xmm[31:0]" , "MR" , "F3 0F 11 /r" , "SSE"], ["movupd" , "W:xmm, xmm/m128" , "RM" , "66 0F 10 /r" , "SSE2"], ["movupd" , "W:xmm/m128, xmm" , "MR" , "66 0F 11 /r" , "SSE2"], ["movups" , "W:xmm, xmm/m128" , "RM" , "0F 10 /r" , "SSE"], ["movups" , "W:xmm/m128, xmm" , "MR" , "0F 11 /r" , "SSE"], ["mpsadbw" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 42 /r ib" , "SSE4_1"], ["mulpd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 59 /r" , "SSE2"], ["mulps" , "X:~xmm, ~xmm/m128" , "RM" , "0F 59 /r" , "SSE"], ["mulsd" , "x:xmm[63:0], xmm[63:0]/m64" , "RM" , "F2 0F 59 /r" , "SSE2"], ["mulss" , "x:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 59 /r" , "SSE"], ["orpd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 56 /r" , "SSE2"], ["orps" , "X:~xmm, ~xmm/m128" , "RM" , "0F 56 /r" , "SSE"], ["pabsb" , "W:mm, mm/m64" , "RM" , "0F 38 1C /r" , "SSSE3"], ["pabsb" , "W:xmm, xmm/m128" , "RM" , "66 0F 38 1C /r" , "SSSE3"], ["pabsd" , "W:mm, mm/m64" , "RM" , "0F 38 1E /r" , "SSSE3"], ["pabsd" , "W:xmm, xmm/m128" , "RM" , "66 0F 38 1E /r" , "SSSE3"], ["pabsw" , "W:mm, mm/m64" , "RM" , "0F 38 1D /r" , "SSSE3"], ["pabsw" , "W:xmm, xmm/m128" , "RM" , "66 0F 38 1D /r" , "SSSE3"], ["packssdw" , "X:mm, mm/m64" , "RM" , "0F 6B /r" , "MMX"], ["packssdw" , "X:xmm, xmm/m128" , "RM" , "66 0F 6B /r" , "SSE2"], ["packsswb" , "X:mm, mm/m64" , "RM" , "0F 63 /r" , "MMX"], ["packsswb" , "X:xmm, xmm/m128" , "RM" , "66 0F 63 /r" , "SSE2"], ["packusdw" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 2B /r" , "SSE4_1"], ["packuswb" , "X:mm, mm/m64" , "RM" , "0F 67 /r" , "MMX"], ["packuswb" , "X:xmm, xmm/m128" , "RM" , "66 0F 67 /r" , "SSE2"], ["paddb" , "X:~mm, ~mm/m64" , "RM" , "0F FC /r" , "MMX"], ["paddb" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F FC /r" , "SSE2"], ["paddd" , "X:~mm, ~mm/m64" , "RM" , "0F FE /r" , "MMX"], ["paddd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F FE /r" , "SSE2"], ["paddq" , "X:~mm, ~mm/m64" , "RM" , "0F D4 /r" , "SSE2"], ["paddq" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F D4 /r" , "SSE2"], ["paddsb" , "X:~mm, ~mm/m64" , "RM" , "0F EC /r" , "MMX"], ["paddsb" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F EC /r" , "SSE2"], ["paddsw" , "X:~mm, ~mm/m64" , "RM" , "0F ED /r" , "MMX"], ["paddsw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F ED /r" , "SSE2"], ["paddusb" , "X:~mm, ~mm/m64" , "RM" , "0F DC /r" , "MMX"], ["paddusb" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F DC /r" , "SSE2"], ["paddusw" , "X:~mm, ~mm/m64" , "RM" , "0F DD /r" , "MMX"], ["paddusw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F DD /r" , "SSE2"], ["paddw" , "X:~mm, ~mm/m64" , "RM" , "0F FD /r" , "MMX"], ["paddw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F FD /r" , "SSE2"], ["palignr" , "X:mm, mm/m64, ib/ub" , "RMI" , "0F 3A 0F /r ib" , "SSE3"], ["palignr" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 0F /r ib" , "SSE3"], ["pand" , "X:~mm, ~mm/m64" , "RM" , "0F DB /r" , "MMX"], ["pand" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F DB /r" , "SSE2"], ["pandn" , "X:mm, mm/m64" , "RM" , "0F DF /r" , "MMX"], ["pandn" , "X:xmm, xmm/m128" , "RM" , "66 0F DF /r" , "SSE2"], ["pavgb" , "X:~mm, ~mm/m64" , "RM" , "0F E0 /r" , "MMX2"], ["pavgb" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F E0 /r" , "SSE2"], ["pavgw" , "X:~mm, ~mm/m64" , "RM" , "0F E3 /r" , "MMX2"], ["pavgw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F E3 /r" , "SSE2"], ["pblendvb" , "X:xmm, xmm/m128, " , "RM" , "66 0F E0 /r" , "SSE4_1"], ["pblendw" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 0E /r ib" , "SSE4_1"], ["pclmulqdq" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 44 /r ib" , "PCLMULQDQ"], ["pcmpeqb" , "X:~mm, ~mm/m64" , "RM" , "0F 74 /r" , "MMX"], ["pcmpeqb" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 74 /r" , "SSE2"], ["pcmpeqd" , "X:~mm, ~mm/m64" , "RM" , "0F 76 /r" , "MMX"], ["pcmpeqd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 76 /r" , "SSE2"], ["pcmpeqq" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 29 /r" , "SSE4_1"], ["pcmpeqw" , "X:~mm, ~mm/m64" , "RM" , "0F 75 /r" , "MMX"], ["pcmpeqw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 75 /r" , "SSE2"], ["pcmpestri" , "R:xmm, xmm/m128, ib/ub, W:,," , "RMI" , "66 0F 3A 61 /r ib" , "SSE4_2 OF=W SF=W ZF=W AF=0 PF=0 CF=W"], ["pcmpestrm" , "R:xmm, xmm/m128, ib/ub, W:,," , "RMI" , "66 0F 3A 60 /r ib" , "SSE4_2 OF=W SF=W ZF=W AF=0 PF=0 CF=W"], ["pcmpgtb" , "X:mm, mm/m64" , "RM" , "0F 64 /r" , "MMX"], ["pcmpgtb" , "X:xmm, xmm/m128" , "RM" , "66 0F 64 /r" , "SSE2"], ["pcmpgtd" , "X:mm, mm/m64" , "RM" , "0F 66 /r" , "MMX"], ["pcmpgtd" , "X:xmm, xmm/m128" , "RM" , "66 0F 66 /r" , "SSE2"], ["pcmpgtq" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 37 /r" , "SSE4_2"], ["pcmpgtw" , "X:mm, mm/m64" , "RM" , "0F 65 /r" , "MMX"], ["pcmpgtw" , "X:xmm, xmm/m128" , "RM" , "66 0F 65 /r" , "SSE2"], ["pcmpistri" , "R:xmm, xmm/m128, ib/ub, W:" , "RMI" , "66 0F 3A 63 /r ib" , "SSE4_2 OF=W SF=W ZF=W AF=0 PF=0 CF=W"], ["pcmpistrm" , "R:xmm, xmm/m128, ib/ub, W:" , "RMI" , "66 0F 3A 62 /r ib" , "SSE4_2 OF=W SF=W ZF=W AF=0 PF=0 CF=W"], ["pextrb" , "W:r32[7:0]/m8, xmm, ib/ub" , "MRI" , "66 0F 3A 14 /r ib" , "SSE4_1"], ["pextrd" , "W:r32[31:0]/m32, xmm, ib/ub" , "MRI" , "66 0F 3A 16 /r ib" , "SSE4_1"], ["pextrq" , "W:r64/m64, xmm, ib/ub" , "MRI" , "REX.W 66 0F 3A 16 /r ib" , "SSE4_1 X64"], ["pextrw" , "W:r32[15:0], mm, ib/ub" , "RMI" , "0F C5 /r ib" , "MMX2"], ["pextrw" , "W:r32[15:0], xmm, ib/ub" , "RMI" , "66 0F C5 /r ib" , "SSE2"], ["pextrw" , "W:r32[15:0]/m16, xmm, ib/ub" , "MRI" , "66 0F 3A 15 /r ib" , "SSE4_1"], ["phaddd" , "X:~mm, ~mm/m64" , "RM" , "0F 38 02 /r" , "SSSE3"], ["phaddd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 02 /r" , "SSSE3"], ["phaddsw" , "X:~mm, ~mm/m64" , "RM" , "0F 38 03 /r" , "SSSE3"], ["phaddsw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 03 /r" , "SSSE3"], ["phaddw" , "X:~mm, ~mm/m64" , "RM" , "0F 38 01 /r" , "SSSE3"], ["phaddw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 01 /r" , "SSSE3"], ["phminposuw" , "W:xmm[18:0], xmm/m128" , "RM" , "66 0F 38 41 /r" , "SSE4_1"], ["phsubd" , "X:mm, mm/m64" , "RM" , "0F 38 06 /r" , "SSSE3"], ["phsubd" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 06 /r" , "SSSE3"], ["phsubsw" , "X:mm, mm/m64" , "RM" , "0F 38 07 /r" , "SSSE3"], ["phsubsw" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 07 /r" , "SSSE3"], ["phsubw" , "X:mm, mm/m64" , "RM" , "0F 38 05 /r" , "SSSE3"], ["phsubw" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 05 /r" , "SSSE3"], ["pinsrb" , "X:xmm, r32[7:0]/m8, ib/ub" , "RMI" , "66 0F 3A 20 /r ib" , "SSE4_1"], ["pinsrd" , "X:xmm, r32[31:0]/m32, ib/ub" , "RMI" , "66 0F 3A 22 /r ib" , "SSE4_1"], ["pinsrq" , "X:xmm, r64/m64, ib/ub" , "RMI" , "REX.W 66 0F 3A 22 /r ib" , "SSE4_1 X64"], ["pinsrw" , "X:mm, r32[15:0]/m16, ib/ub" , "RMI" , "0F C4 /r ib" , "MMX2"], ["pinsrw" , "X:xmm, r32[15:0]/m16, ib/ub" , "RMI" , "66 0F C4 /r ib" , "SSE2"], ["pmaddubsw" , "X:~mm, ~mm/m64" , "RM" , "0F 38 04 /r" , "SSSE3"], ["pmaddubsw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 04 /r" , "SSSE3"], ["pmaddwd" , "X:~mm, ~mm/m64" , "RM" , "0F F5 /r" , "MMX"], ["pmaddwd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F F5 /r" , "SSE2"], ["pmaxsb" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 3C /r" , "SSE4_1"], ["pmaxsd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 3D /r" , "SSE4_1"], ["pmaxsw" , "X:~mm, ~mm/m64" , "RM" , "0F EE /r" , "MMX2"], ["pmaxsw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F EE /r" , "SSE2"], ["pmaxub" , "X:~mm, ~mm/m64" , "RM" , "0F DE /r" , "MMX2"], ["pmaxub" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F DE /r" , "SSE2"], ["pmaxud" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 3F /r" , "SSE4_1"], ["pmaxuw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 3E /r" , "SSE4_1"], ["pminsb" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 38 /r" , "SSE4_1"], ["pminsd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 39 /r" , "SSE4_1"], ["pminsw" , "X:~mm, ~mm/m64" , "RM" , "0F EA /r" , "MMX2"], ["pminsw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F EA /r" , "SSE2"], ["pminub" , "X:~mm, ~mm/m64" , "RM" , "0F DA /r" , "MMX2"], ["pminub" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F DA /r" , "SSE2"], ["pminud" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 3B /r" , "SSE4_1"], ["pminuw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 3A /r" , "SSE4_1"], ["pmovmskb" , "W:r32[7:0], mm" , "RM" , "0F D7 /r" , "MMX2"], ["pmovmskb" , "W:r32[15:0], xmm" , "RM" , "66 0F D7 /r" , "SSE2"], ["pmovsxbd" , "W:xmm, xmm[31:0]/m32" , "RM" , "66 0F 38 21 /r" , "SSE4_1"], ["pmovsxbq" , "W:xmm, xmm[15:0]/m16" , "RM" , "66 0F 38 22 /r" , "SSE4_1"], ["pmovsxbw" , "W:xmm, xmm[63:0]/m64" , "RM" , "66 0F 38 20 /r" , "SSE4_1"], ["pmovsxdq" , "W:xmm, xmm[63:0]/m64" , "RM" , "66 0F 38 25 /r" , "SSE4_1"], ["pmovsxwd" , "W:xmm, xmm[63:0]/m64" , "RM" , "66 0F 38 23 /r" , "SSE4_1"], ["pmovsxwq" , "W:xmm, xmm[31:0]/m32" , "RM" , "66 0F 38 24 /r" , "SSE4_1"], ["pmovzxbd" , "W:xmm, xmm[31:0]/m32" , "RM" , "66 0F 38 31 /r" , "SSE4_1"], ["pmovzxbq" , "W:xmm, xmm[15:0]/m16" , "RM" , "66 0F 38 32 /r" , "SSE4_1"], ["pmovzxbw" , "W:xmm, xmm[63:0]/m64" , "RM" , "66 0F 38 30 /r" , "SSE4_1"], ["pmovzxdq" , "W:xmm, xmm[63:0]/m64" , "RM" , "66 0F 38 35 /r" , "SSE4_1"], ["pmovzxwd" , "W:xmm, xmm[63:0]/m64" , "RM" , "66 0F 38 33 /r" , "SSE4_1"], ["pmovzxwq" , "W:xmm, xmm[31:0]/m32" , "RM" , "66 0F 38 34 /r" , "SSE4_1"], ["pmuldq" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 28 /r" , "SSE4_1"], ["pmulhrsw" , "X:~mm, ~mm/m64" , "RM" , "0F 38 0B /r" , "SSSE3"], ["pmulhrsw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 0B /r" , "SSSE3"], ["pmulhuw" , "X:~mm, ~mm/m64" , "RM" , "0F E4 /r" , "MMX2"], ["pmulhuw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F E4 /r" , "SSE2"], ["pmulhw" , "X:~mm, ~mm/m64" , "RM" , "0F E5 /r" , "MMX"], ["pmulhw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F E5 /r" , "SSE2"], ["pmulld" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 38 40 /r" , "SSE4_1"], ["pmullw" , "X:~mm, ~mm/m64" , "RM" , "0F D5 /r" , "MMX"], ["pmullw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F D5 /r" , "SSE2"], ["pmuludq" , "X:~mm, ~mm/m64" , "RM" , "0F F4 /r" , "SSE2"], ["pmuludq" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F F4 /r" , "SSE2"], ["por" , "X:~mm, ~mm/m64" , "RM" , "0F EB /r" , "MMX"], ["por" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F EB /r" , "SSE2"], ["psadbw" , "X:~mm, ~mm/m64" , "RM" , "0F F6 /r" , "MMX2"], ["psadbw" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F F6 /r" , "SSE2"], ["pshufb" , "X:mm, mm/m64" , "RM" , "0F 38 00 /r" , "SSSE3"], ["pshufb" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 00 /r" , "SSSE3"], ["pshufd" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 70 /r ib" , "SSE2"], ["pshufhw" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "F3 0F 70 /r ib" , "SSE2"], ["pshuflw" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "F2 0F 70 /r ib" , "SSE2"], ["pshufw" , "W:mm, mm/m64, ib/ub" , "RMI" , "0F 70 /r ib" , "MMX2"], ["psignb" , "X:mm, mm/m64" , "RM" , "0F 38 08 /r" , "SSSE3"], ["psignb" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 08 /r" , "SSSE3"], ["psignd" , "X:mm, mm/m64" , "RM" , "0F 38 0A /r" , "SSSE3"], ["psignd" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 0A /r" , "SSSE3"], ["psignw" , "X:mm, mm/m64" , "RM" , "0F 38 09 /r" , "SSSE3"], ["psignw" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 09 /r" , "SSSE3"], ["pslld" , "X:mm, ib/ub" , "MI" , "0F 72 /6 ib" , "MMX"], ["pslld" , "X:mm, mm/m64" , "RM" , "0F F2 /r" , "MMX"], ["pslld" , "X:xmm, ib/ub" , "MI" , "66 0F 72 /6 ib" , "SSE2"], ["pslld" , "X:xmm, xmm/m128" , "RM" , "66 0F F2 /r" , "SSE2"], ["pslldq" , "X:xmm, ib/ub" , "MI" , "66 0F 73 /7 ib" , "SSE2"], ["psllq" , "X:mm, ib/ub" , "MI" , "0F 73 /6 ib" , "MMX"], ["psllq" , "X:mm, mm/m64" , "RM" , "0F F3 /r" , "MMX"], ["psllq" , "X:xmm, ib/ub" , "MI" , "66 0F 73 /6 ib" , "SSE2"], ["psllq" , "X:xmm, xmm/m128" , "RM" , "66 0F F3 /r" , "SSE2"], ["psllw" , "X:mm, ib/ub" , "MI" , "0F 71 /6 ib" , "MMX"], ["psllw" , "X:mm, mm/m64" , "RM" , "0F F1 /r" , "MMX"], ["psllw" , "X:xmm, ib/ub" , "MI" , "66 0F 71 /6 ib" , "SSE2"], ["psllw" , "X:xmm, xmm/m128" , "RM" , "66 0F F1 /r" , "SSE2"], ["psrad" , "X:mm, ib/ub" , "MI" , "0F 72 /4 ib" , "MMX"], ["psrad" , "X:mm, mm/m64" , "RM" , "0F E2 /r" , "MMX"], ["psrad" , "X:xmm, ib/ub" , "MI" , "66 0F 72 /4 ib" , "SSE2"], ["psrad" , "X:xmm, xmm/m128" , "RM" , "66 0F E2 /r" , "SSE2"], ["psraw" , "X:mm, ib/ub" , "MI" , "0F 71 /4 ib" , "MMX"], ["psraw" , "X:mm, mm/m64" , "RM" , "0F E1 /r" , "MMX"], ["psraw" , "X:xmm, ib/ub" , "MI" , "66 0F 71 /4 ib" , "SSE2"], ["psraw" , "X:xmm, xmm/m128" , "RM" , "66 0F E1 /r" , "SSE2"], ["psrld" , "X:mm, ib/ub" , "MI" , "0F 72 /2 ib" , "MMX"], ["psrld" , "X:mm, mm/m64" , "RM" , "0F D2 /r" , "MMX"], ["psrld" , "X:xmm, ib/ub" , "MI" , "66 0F 72 /2 ib" , "SSE2"], ["psrld" , "X:xmm, xmm/m128" , "RM" , "66 0F D2 /r" , "SSE2"], ["psrldq" , "X:xmm, ib/ub" , "MI" , "66 0F 73 /3 ib" , "SSE2"], ["psrlq" , "X:mm, ib/ub" , "MI" , "0F 73 /2 ib" , "MMX"], ["psrlq" , "X:mm, mm/m64" , "RM" , "0F D3 /r" , "MMX"], ["psrlq" , "X:xmm, ib/ub" , "MI" , "66 0F 73 /2 ib" , "SSE2"], ["psrlq" , "X:xmm, xmm/m128" , "RM" , "66 0F D3 /r" , "SSE2"], ["psrlw" , "X:mm, ib/ub" , "MI" , "0F 71 /2 ib" , "MMX"], ["psrlw" , "X:mm, mm/m64" , "RM" , "0F D1 /r" , "MMX"], ["psrlw" , "X:xmm, ib/ub" , "MI" , "66 0F 71 /2 ib" , "SSE2"], ["psrlw" , "X:xmm, xmm/m128" , "RM" , "66 0F D1 /r" , "SSE2"], ["psubb" , "X:mm, mm/m64" , "RM" , "0F F8 /r" , "MMX"], ["psubb" , "X:xmm, xmm/m128" , "RM" , "66 0F F8 /r" , "SSE2"], ["psubd" , "X:mm, mm/m64" , "RM" , "0F FA /r" , "MMX"], ["psubd" , "X:xmm, xmm/m128" , "RM" , "66 0F FA /r" , "SSE2"], ["psubq" , "X:mm, mm/m64" , "RM" , "0F FB /r" , "SSE2"], ["psubq" , "X:xmm, xmm/m128" , "RM" , "66 0F FB /r" , "SSE2"], ["psubsb" , "X:mm, mm/m64" , "RM" , "0F E8 /r" , "MMX"], ["psubsb" , "X:xmm, xmm/m128" , "RM" , "66 0F E8 /r" , "SSE2"], ["psubsw" , "X:mm, mm/m64" , "RM" , "0F E9 /r" , "MMX"], ["psubsw" , "X:xmm, xmm/m128" , "RM" , "66 0F E9 /r" , "SSE2"], ["psubusb" , "X:mm, mm/m64" , "RM" , "0F D8 /r" , "MMX"], ["psubusb" , "X:xmm, xmm/m128" , "RM" , "66 0F D8 /r" , "SSE2"], ["psubusw" , "X:mm, mm/m64" , "RM" , "0F D9 /r" , "MMX"], ["psubusw" , "X:xmm, xmm/m128" , "RM" , "66 0F D9 /r" , "SSE2"], ["psubw" , "X:mm, mm/m64" , "RM" , "0F F9 /r" , "MMX"], ["psubw" , "X:xmm, xmm/m128" , "RM" , "66 0F F9 /r" , "SSE2"], ["ptest" , "R:~xmm, ~xmm/m128" , "RM" , "66 0F 38 17 /r" , "SSE4_1 OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["punpckhbw" , "X:mm, mm/m64" , "RM" , "0F 68 /r" , "MMX"], ["punpckhbw" , "X:xmm, xmm/m128" , "RM" , "66 0F 68 /r" , "SSE2"], ["punpckhdq" , "X:mm, mm/m64" , "RM" , "0F 6A /r" , "MMX"], ["punpckhdq" , "X:xmm, xmm/m128" , "RM" , "66 0F 6A /r" , "SSE2"], ["punpckhqdq" , "X:xmm, xmm/m128" , "RM" , "66 0F 6D /r" , "SSE2"], ["punpckhwd" , "X:mm, mm/m64" , "RM" , "0F 69 /r" , "MMX"], ["punpckhwd" , "X:xmm, xmm/m128" , "RM" , "66 0F 69 /r" , "SSE2"], ["punpcklbw" , "X:mm, mm/m32" , "RM" , "0F 60 /r" , "MMX"], ["punpcklbw" , "X:xmm, xmm/m128" , "RM" , "66 0F 60 /r" , "SSE2"], ["punpckldq" , "X:mm, mm/m32" , "RM" , "0F 62 /r" , "MMX"], ["punpckldq" , "X:xmm, xmm/m128" , "RM" , "66 0F 62 /r" , "SSE2"], ["punpcklqdq" , "X:xmm, xmm/m128" , "RM" , "66 0F 6C /r" , "SSE2"], ["punpcklwd" , "X:mm, mm/m32" , "RM" , "0F 61 /r" , "MMX"], ["punpcklwd" , "X:xmm, xmm/m128" , "RM" , "66 0F 61 /r" , "SSE2"], ["pxor" , "X:~mm, ~mm/m64" , "RM" , "0F EF /r" , "MMX"], ["pxor" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F EF /r" , "SSE2"], ["rcpps" , "W:xmm, xmm/m128" , "RM" , "0F 53 /r" , "SSE"], ["rcpss" , "w:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 53 /r" , "SSE"], ["roundpd" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 09 /r ib" , "SSE4_1"], ["roundps" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A 08 /r ib" , "SSE4_1"], ["roundsd" , "w:xmm[63:0], xmm[63:0]/m64, ib/ub" , "RMI" , "66 0F 3A 0B /r ib" , "SSE4_1"], ["roundss" , "w:xmm[31:0], xmm[31:0]/m32, ib/ub" , "RMI" , "66 0F 3A 0A /r ib" , "SSE4_1"], ["rsqrtps" , "W:xmm, xmm/m128" , "RM" , "0F 52 /r" , "SSE"], ["rsqrtss" , "w:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 52 /r" , "SSE"], ["shufpd" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F C6 /r ib" , "SSE2"], ["shufps" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "0F C6 /r ib" , "SSE"], ["sqrtpd" , "W:xmm, xmm/m128" , "RM" , "66 0F 51 /r" , "SSE2"], ["sqrtps" , "W:xmm, xmm/m128" , "RM" , "0F 51 /r" , "SSE"], ["sqrtsd" , "w:xmm[63:0], xmm[63:0]/m64" , "RM" , "F2 0F 51 /r" , "SSE2"], ["sqrtss" , "w:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 51 /r" , "SSE"], ["subpd" , "X:xmm, xmm/m128" , "RM" , "66 0F 5C /r" , "SSE2"], ["subps" , "X:xmm, xmm/m128" , "RM" , "0F 5C /r" , "SSE"], ["subsd" , "x:xmm[63:0], xmm[63:0]/m64" , "RM" , "F2 0F 5C /r" , "SSE2"], ["subss" , "x:xmm[31:0], xmm[31:0]/m32" , "RM" , "F3 0F 5C /r" , "SSE"], ["ucomisd" , "R:xmm[63:0], xmm[63:0]/m64" , "RM" , "66 0F 2E /r" , "SSE2 OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["ucomiss" , "R:xmm[31:0], xmm[31:0]/m32" , "RM" , "0F 2E /r" , "SSE OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["unpckhpd" , "X:xmm, xmm/m128" , "RM" , "66 0F 15 /r" , "SSE2"], ["unpckhps" , "X:xmm, xmm/m128" , "RM" , "0F 15 /r" , "SSE"], ["unpcklpd" , "X:xmm, xmm/m128" , "RM" , "66 0F 14 /r" , "SSE2"], ["unpcklps" , "X:xmm, xmm/m128" , "RM" , "0F 14 /r" , "SSE"], ["xorpd" , "X:~xmm, ~xmm/m128" , "RM" , "66 0F 57 /r" , "SSE2"], ["xorps" , "X:~xmm, ~xmm/m128" , "RM" , "0F 57 /r" , "SSE"], ["pavgusb" , "X:mm, mm/m64" , "RM" , "0F 0F /r BF" , "3DNOW"], ["pf2id" , "W:mm, mm/m64" , "RM" , "0F 0F /r 1D" , "3DNOW"], ["pf2iw" , "W:mm, mm/m64" , "RM" , "0F 0F /r 1C" , "3DNOW2"], ["pfacc" , "X:mm, mm/m64" , "RM" , "0F 0F /r AE" , "3DNOW"], ["pfadd" , "X:mm, mm/m64" , "RM" , "0F 0F /r 9E" , "3DNOW"], ["pfcmpeq" , "X:mm, mm/m64" , "RM" , "0F 0F /r B0" , "3DNOW"], ["pfcmpge" , "X:mm, mm/m64" , "RM" , "0F 0F /r 90" , "3DNOW"], ["pfcmpgt" , "X:mm, mm/m64" , "RM" , "0F 0F /r A0" , "3DNOW"], ["pfmax" , "X:mm, mm/m64" , "RM" , "0F 0F /r A4" , "3DNOW"], ["pfmin" , "X:mm, mm/m64" , "RM" , "0F 0F /r 94" , "3DNOW"], ["pfmul" , "X:mm, mm/m64" , "RM" , "0F 0F /r B4" , "3DNOW"], ["pfnacc" , "X:mm, mm/m64" , "RM" , "0F 0F /r 8A" , "3DNOW2"], ["pfpnacc" , "X:mm, mm/m64" , "RM" , "0F 0F /r 8E" , "3DNOW2"], ["pfrcp" , "W:mm, mm/m64" , "RM" , "0F 0F /r 96" , "3DNOW"], ["pfrcpit1" , "X:mm, mm/m64" , "RM" , "0F 0F /r A6" , "3DNOW"], ["pfrcpit2" , "X:mm, mm/m64" , "RM" , "0F 0F /r B6" , "3DNOW"], ["pfrcpv" , "X:mm, mm/m64" , "RM" , "0F 0F /r 86" , "GEODE"], ["pfrsqit1" , "W:mm, mm/m64" , "RM" , "0F 0F /r A7" , "3DNOW"], ["pfrsqrt" , "W:mm, mm/m64" , "RM" , "0F 0F /r 97" , "3DNOW"], ["pfrsqrtv" , "X:mm, mm/m64" , "RM" , "0F 0F /r 87" , "GEODE"], ["pfsub" , "X:mm, mm/m64" , "RM" , "0F 0F /r 9A" , "3DNOW"], ["pfsubr" , "X:mm, mm/m64" , "RM" , "0F 0F /r AA" , "3DNOW"], ["pi2fd" , "W:mm, mm/m64" , "RM" , "0F 0F /r 0D" , "3DNOW"], ["pi2fw" , "W:mm, mm/m64" , "RM" , "0F 0F /r 0C" , "3DNOW2"], ["pmulhrw" , "X:mm, mm/m64" , "RM" , "0F 0F /r B7" , "3DNOW"], ["pswapd" , "W:mm, mm/m64" , "RM" , "0F 0F /r BB" , "3DNOW2"], ["emms" , "" , "NONE" , "0F 77" , "MMX Volatile"], ["femms" , "" , "NONE" , "0F 0E" , "3DNOW Volatile"], ["aesdec" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 DE /r" , "AESNI"], ["aesdeclast" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 DF /r" , "AESNI"], ["aesenc" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 DC /r" , "AESNI"], ["aesenclast" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 DD /r" , "AESNI"], ["aesimc" , "W:xmm, xmm/m128" , "RM" , "66 0F 38 DB /r" , "AESNI"], ["aeskeygenassist" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A DF /r ib" , "AESNI"], ["sha1msg1" , "xmm, xmm/m128" , "RM" , "0F 38 C9 /r" , "SHA"], ["sha1msg2" , "xmm, xmm/m128" , "RM" , "0F 38 CA /r" , "SHA"], ["sha1nexte" , "xmm, xmm/m128" , "RM" , "0F 38 C8 /r" , "SHA"], ["sha1rnds4" , "xmm, xmm/m128, ib/ub" , "RMI" , "0F 3A CC /r ib" , "SHA"], ["sha256msg1" , "xmm, xmm/m128" , "RM" , "0F 38 CC /r" , "SHA"], ["sha256msg2" , "xmm, xmm/m128" , "RM" , "0F 38 CD /r" , "SHA"], ["sha256rnds2" , "xmm, xmm/m128, " , "RM" , "0F 38 CB /r" , "SHA"], ["gf2p8affineinvqb" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A CF /r ib" , "GFNI"], ["gf2p8affineqb" , "X:xmm, xmm/m128, ib/ub" , "RMI" , "66 0F 3A CE /r ib" , "GFNI"], ["gf2p8mulb" , "X:xmm, xmm/m128" , "RM" , "66 0F 38 CF /r" , "GFNI"], ["vaddpd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 58 /r" , "AVX"], ["vaddpd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 58 /r" , "AVX"], ["vaddps" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.0F.WIG 58 /r" , "AVX"], ["vaddps" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.0F.WIG 58 /r" , "AVX"], ["vaddsd" , "W:xmm, xmm, xmm[63:0]/m64" , "RVM" , "VEX.LIG.F2.0F.WIG 58 /r" , "AVX"], ["vaddss" , "W:xmm, xmm, xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 58 /r" , "AVX"], ["vaddsubpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG D0 /r" , "AVX"], ["vaddsubpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG D0 /r" , "AVX"], ["vaddsubps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.F2.0F.WIG D0 /r" , "AVX"], ["vaddsubps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.F2.0F.WIG D0 /r" , "AVX"], ["vaesdec" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG DE /r" , "AVX AESNI"], ["vaesdec" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG DE /r" , "VAES"], ["vaesdeclast" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG DF /r" , "AVX AESNI"], ["vaesdeclast" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG DF /r" , "VAES"], ["vaesenc" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG DC /r" , "AVX AESNI"], ["vaesenc" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG DC /r" , "VAES"], ["vaesenclast" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG DD /r" , "AVX AESNI"], ["vaesenclast" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG DD /r" , "VAES"], ["vaesimc" , "W:xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG DB /r" , "AVX AESNI"], ["vaeskeygenassist" , "W:xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG DF /r ib" , "AVX AESNI"], ["vandnpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 55 /r" , "AVX"], ["vandnpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 55 /r" , "AVX"], ["vandnps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.0F.WIG 55 /r" , "AVX"], ["vandnps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.0F.WIG 55 /r" , "AVX"], ["vandpd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 54 /r" , "AVX"], ["vandpd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 54 /r" , "AVX"], ["vandps" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.0F.WIG 54 /r" , "AVX"], ["vandps" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.0F.WIG 54 /r" , "AVX"], ["vblendpd" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 0D /r ib" , "AVX"], ["vblendpd" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.WIG 0D /r ib" , "AVX"], ["vblendps" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 0C /r ib" , "AVX"], ["vblendps" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.WIG 0C /r ib" , "AVX"], ["vblendvpd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 4B /r /is4", "AVX"], ["vblendvpd" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 4B /r /is4", "AVX"], ["vblendvps" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 4A /r /is4", "AVX"], ["vblendvps" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 4A /r /is4", "AVX"], ["vbroadcastf128" , "W:ymm, m128" , "RM" , "VEX.256.66.0F38.W0 1A /r" , "AVX"], ["vbroadcasti128" , "W:ymm, m128" , "RM" , "VEX.256.66.0F38.W0 5A /r" , "AVX2"], ["vbroadcastsd" , "W:ymm, m64" , "RM" , "VEX.256.66.0F38.W0 19 /r" , "AVX"], ["vbroadcastsd" , "W:ymm, xmm[63:0]" , "RM" , "VEX.256.66.0F38.W0 19 /r" , "AVX2"], ["vbroadcastss" , "W:xmm, m32" , "RM" , "VEX.128.66.0F38.W0 18 /r" , "AVX"], ["vbroadcastss" , "W:xmm, xmm[31:0]" , "RM" , "VEX.128.66.0F38.W0 18 /r" , "AVX2"], ["vbroadcastss" , "W:ymm, m32" , "RM" , "VEX.256.66.0F38.W0 18 /r" , "AVX"], ["vbroadcastss" , "W:ymm, xmm[31:0]" , "RM" , "VEX.256.66.0F38.W0 18 /r" , "AVX2"], ["vcmppd" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F.WIG C2 /r ib" , "AVX"], ["vcmppd" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F.WIG C2 /r ib" , "AVX"], ["vcmpps" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.0F.WIG C2 /r ib" , "AVX"], ["vcmpps" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.0F.WIG C2 /r ib" , "AVX"], ["vcmpsd" , "W:xmm, xmm, xmm[63:0]/m64, ib/ub" , "RVMI" , "VEX.LIG.F2.0F.WIG C2 /r ib" , "AVX"], ["vcmpss" , "W:xmm, xmm, xmm[31:0]/m32, ib/ub" , "RVMI" , "VEX.LIG.F3.0F.WIG C2 /r ib" , "AVX"], ["vcomisd" , "R:xmm[63:0], xmm[63:0]/m64" , "RM" , "VEX.LIG.66.0F.WIG 2F /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["vcomiss" , "R:xmm[31:0], xmm[31:0]/m32" , "RM" , "VEX.LIG.0F.WIG 2F /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["vcvtdq2pd" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.F3.0F.WIG E6 /r" , "AVX"], ["vcvtdq2pd" , "W:ymm, xmm/m128" , "RM" , "VEX.256.F3.0F.WIG E6 /r" , "AVX"], ["vcvtdq2ps" , "W:xmm, xmm/m128" , "RM" , "VEX.128.0F.WIG 5B /r" , "AVX"], ["vcvtdq2ps" , "W:ymm, ymm/m256" , "RM" , "VEX.256.0F.WIG 5B /r" , "AVX"], ["vcvtpd2dq" , "W:xmm[63:0], xmm/m128" , "RM" , "VEX.128.F2.0F.WIG E6 /r" , "AVX"], ["vcvtpd2dq" , "W:xmm, ymm/m256" , "RM" , "VEX.256.F2.0F.WIG E6 /r" , "AVX"], ["vcvtpd2ps" , "W:xmm[63:0], xmm/m128" , "RM" , "VEX.128.66.0F.WIG 5A /r" , "AVX"], ["vcvtpd2ps" , "W:xmm, ymm/m256" , "RM" , "VEX.256.66.0F.WIG 5A /r" , "AVX"], ["vcvtps2dq" , "W:xmm, xmm/m128" , "RM" , "VEX.128.66.0F.WIG 5B /r" , "AVX"], ["vcvtps2dq" , "W:ymm, ymm/m256" , "RM" , "VEX.256.66.0F.WIG 5B /r" , "AVX"], ["vcvtps2pd" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.0F.WIG 5A /r" , "AVX"], ["vcvtps2pd" , "W:ymm, xmm/m128" , "RM" , "VEX.256.0F.WIG 5A /r" , "AVX"], ["vcvtsd2si" , "W:r32, xmm[63:0]/m64" , "RM" , "VEX.LIG.F2.0F.W0 2D /r" , "AVX"], ["vcvtsd2si" , "W:r64, xmm[63:0]/m64" , "RM" , "VEX.LIG.F2.0F.W1 2D /r" , "AVX X64"], ["vcvtsd2ss" , "W:xmm, xmm[127:32], xmm[63:0]/m64" , "RVM" , "VEX.LIG.F2.0F.WIG 5A /r" , "AVX"], ["vcvtsi2sd" , "W:xmm, xmm[127:64], r32/m32" , "RVM" , "VEX.LIG.F2.0F.W0 2A /r" , "AVX"], ["vcvtsi2sd" , "W:xmm, xmm[127:64], r64/m64" , "RVM" , "VEX.LIG.F2.0F.W1 2A /r" , "AVX X64"], ["vcvtsi2ss" , "W:xmm, xmm[127:32], r32/m32" , "RVM" , "VEX.LIG.F3.0F.W0 2A /r" , "AVX"], ["vcvtsi2ss" , "W:xmm, xmm[127:32], r64/m64" , "RVM" , "VEX.LIG.F3.0F.W1 2A /r" , "AVX X64"], ["vcvtss2sd" , "W:xmm, xmm[127:64], xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 5A /r" , "AVX"], ["vcvtss2si" , "W:r32, xmm[31:0]/m32" , "RM" , "VEX.LIG.F3.0F.W0 2D /r" , "AVX"], ["vcvtss2si" , "W:r64, xmm[31:0]/m32" , "RM" , "VEX.LIG.F3.0F.W1 2D /r" , "AVX X64"], ["vcvttpd2dq" , "W:xmm[63:0], xmm/m128" , "RM" , "VEX.128.66.0F.WIG E6 /r" , "AVX"], ["vcvttpd2dq" , "W:xmm, ymm/m256" , "RM" , "VEX.256.66.0F.WIG E6 /r" , "AVX"], ["vcvttps2dq" , "W:xmm, xmm/m128" , "RM" , "VEX.128.F3.0F.WIG 5B /r" , "AVX"], ["vcvttps2dq" , "W:ymm, ymm/m256" , "RM" , "VEX.256.F3.0F.WIG 5B /r" , "AVX"], ["vcvttsd2si" , "W:r32, xmm[63:0]/m64" , "RM" , "VEX.LIG.F2.0F.W0 2C /r" , "AVX"], ["vcvttsd2si" , "W:r64, xmm[63:0]/m64" , "RM" , "VEX.LIG.F2.0F.W1 2C /r" , "AVX X64"], ["vcvttss2si" , "W:r32, xmm[31:0]/m32" , "RM" , "VEX.LIG.F3.0F.W0 2C /r" , "AVX"], ["vcvttss2si" , "W:r64, xmm[31:0]/m32" , "RM" , "VEX.LIG.F3.0F.W1 2C /r" , "AVX X64"], ["vdivpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 5E /r" , "AVX"], ["vdivpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 5E /r" , "AVX"], ["vdivps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.0F.WIG 5E /r" , "AVX"], ["vdivps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.0F.WIG 5E /r" , "AVX"], ["vdivsd" , "W:xmm, xmm, xmm[63:0]/m64" , "RVM" , "VEX.LIG.F2.0F.WIG 5E /r" , "AVX"], ["vdivss" , "W:xmm, xmm, xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 5E /r" , "AVX"], ["vdppd" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 41 /r ib" , "AVX"], ["vdpps" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 40 /r ib" , "AVX"], ["vdpps" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.WIG 40 /r ib" , "AVX"], ["vextractf128" , "W:xmm/m128, ymm, ib/ub" , "MRI" , "VEX.256.66.0F3A.W0 19 /r ib" , "AVX"], ["vextracti128" , "W:xmm/m128, ymm, ib/ub" , "MRI" , "VEX.256.66.0F3A.W0 39 /r ib" , "AVX2"], ["vextractps" , "W:r32[31:0]/m32, xmm, ib/ub" , "MRI" , "VEX.128.66.0F3A.WIG 17 /r ib" , "AVX"], ["vgatherdpd" , "X:xmm, vm32x, X:xmm" , "RMV" , "VEX.128.66.0F38.W1 92 /r" , "AVX2"], ["vgatherdpd" , "X:ymm, vm32x, X:ymm" , "RMV" , "VEX.256.66.0F38.W1 92 /r" , "AVX2"], ["vgatherdps" , "X:xmm, vm32x, X:xmm" , "RMV" , "VEX.128.66.0F38.W0 92 /r" , "AVX2"], ["vgatherdps" , "X:ymm, vm32y, X:ymm" , "RMV" , "VEX.256.66.0F38.W0 92 /r" , "AVX2"], ["vgatherqpd" , "X:xmm, vm64x, X:xmm" , "RMV" , "VEX.128.66.0F38.W1 93 /r" , "AVX2"], ["vgatherqpd" , "X:ymm, vm64y, X:ymm" , "RMV" , "VEX.256.66.0F38.W1 93 /r" , "AVX2"], ["vgatherqps" , "X:xmm, vm64x, X:xmm" , "RMV" , "VEX.128.66.0F38.W0 93 /r" , "AVX2"], ["vgatherqps" , "X:xmm, vm64y, X:xmm" , "RMV" , "VEX.256.66.0F38.W0 93 /r" , "AVX2"], ["vgf2p8affineinvqb", "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.W1 CF /r ib" , "AVX GFNI"], ["vgf2p8affineinvqb", "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.W1 CF /r ib" , "AVX GFNI"], ["vgf2p8affineqb" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.W1 CE /r ib" , "AVX GFNI"], ["vgf2p8affineqb" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.W1 CE /r ib" , "AVX GFNI"], ["vgf2p8mulb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 CF /r" , "AVX GFNI"], ["vgf2p8mulb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 CF /r" , "AVX GFNI"], ["vhaddpd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 7C /r" , "AVX"], ["vhaddpd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 7C /r" , "AVX"], ["vhaddps" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.F2.0F.WIG 7C /r" , "AVX"], ["vhaddps" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.F2.0F.WIG 7C /r" , "AVX"], ["vhsubpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 7D /r" , "AVX"], ["vhsubpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 7D /r" , "AVX"], ["vhsubps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.F2.0F.WIG 7D /r" , "AVX"], ["vhsubps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.F2.0F.WIG 7D /r" , "AVX"], ["vinsertf128" , "W:ymm, ymm, xmm/m128, ib/ub" , "RVMI" , "VEX.256.66.0F3A.W0 18 /r ib" , "AVX"], ["vinserti128" , "W:ymm, ymm, xmm/m128, ib/ub" , "RVMI" , "VEX.256.66.0F3A.W0 38 /r ib" , "AVX2"], ["vinsertps" , "W:xmm, xmm, xmm[31:0]/m32, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 21 /r ib" , "AVX"], ["vlddqu" , "W:xmm, m128" , "RM" , "VEX.128.F2.0F.WIG F0 /r" , "AVX"], ["vlddqu" , "W:ymm, m256" , "RM" , "VEX.256.F2.0F.WIG F0 /r" , "AVX"], ["vmaskmovdqu" , "R:xmm, xmm, X:" , "RM" , "VEX.128.66.0F.WIG F7 /r" , "AVX"], ["vmaskmovpd" , "X:m128, xmm, xmm" , "MVR" , "VEX.128.66.0F38.W0 2F /r" , "AVX"], ["vmaskmovpd" , "X:m256, ymm, ymm" , "MVR" , "VEX.256.66.0F38.W0 2F /r" , "AVX"], ["vmaskmovpd" , "W:xmm, xmm, m128" , "RVM" , "VEX.128.66.0F38.W0 2D /r" , "AVX"], ["vmaskmovpd" , "W:ymm, ymm, m256" , "RVM" , "VEX.256.66.0F38.W0 2D /r" , "AVX"], ["vmaskmovps" , "X:m128, xmm, xmm" , "MVR" , "VEX.128.66.0F38.W0 2E /r" , "AVX"], ["vmaskmovps" , "X:m256, ymm, ymm" , "MVR" , "VEX.256.66.0F38.W0 2E /r" , "AVX"], ["vmaskmovps" , "W:xmm, xmm, m128" , "RVM" , "VEX.128.66.0F38.W0 2C /r" , "AVX"], ["vmaskmovps" , "W:ymm, ymm, m256" , "RVM" , "VEX.256.66.0F38.W0 2C /r" , "AVX"], ["vmaxpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 5F /r" , "AVX"], ["vmaxpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 5F /r" , "AVX"], ["vmaxps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.0F.WIG 5F /r" , "AVX"], ["vmaxps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.0F.WIG 5F /r" , "AVX"], ["vmaxsd" , "W:xmm, xmm, xmm[63:0]/m64" , "RVM" , "VEX.LIG.F2.0F.WIG 5F /r" , "AVX"], ["vmaxss" , "W:xmm, xmm, xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 5F /r" , "AVX"], ["vminpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 5D /r" , "AVX"], ["vminpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 5D /r" , "AVX"], ["vminps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.0F.WIG 5D /r" , "AVX"], ["vminps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.0F.WIG 5D /r" , "AVX"], ["vminsd" , "W:xmm, xmm, xmm[63:0]/m64" , "RVM" , "VEX.LIG.F2.0F.WIG 5D /r" , "AVX"], ["vminss" , "W:xmm, xmm, xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 5D /r" , "AVX"], ["vmovapd" , "W:xmm, xmm/m128" , "RM" , "VEX.128.66.0F.WIG 28 /r" , "AVX"], ["vmovapd" , "W:xmm/m128, xmm" , "MR" , "VEX.128.66.0F.WIG 29 /r" , "AVX"], ["vmovapd" , "W:ymm, ymm/m256" , "RM" , "VEX.256.66.0F.WIG 28 /r" , "AVX"], ["vmovapd" , "W:ymm/m256, ymm" , "MR" , "VEX.256.66.0F.WIG 29 /r" , "AVX"], ["vmovaps" , "W:xmm, xmm/m128" , "RM" , "VEX.128.0F.WIG 28 /r" , "AVX"], ["vmovaps" , "W:xmm/m128, xmm" , "MR" , "VEX.128.0F.WIG 29 /r" , "AVX"], ["vmovaps" , "W:ymm, ymm/m256" , "RM" , "VEX.256.0F.WIG 28 /r" , "AVX"], ["vmovaps" , "W:ymm/m256, ymm" , "MR" , "VEX.256.0F.WIG 29 /r" , "AVX"], ["vmovd" , "W:r32[31:0]/m32, xmm[31:0]" , "MR" , "VEX.128.66.0F.W0 7E /r" , "AVX"], ["vmovd" , "W:xmm[31:0], r32[31:0]/m32" , "RM" , "VEX.128.66.0F.W0 6E /r" , "AVX"], ["vmovddup" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.F2.0F.WIG 12 /r" , "AVX"], ["vmovddup" , "W:ymm, ymm/m256" , "RM" , "VEX.256.F2.0F.WIG 12 /r" , "AVX"], ["vmovdqa" , "W:xmm, xmm/m128" , "RM" , "VEX.128.66.0F.WIG 6F /r" , "AVX"], ["vmovdqa" , "W:xmm/m128, xmm" , "MR" , "VEX.128.66.0F.WIG 7F /r" , "AVX"], ["vmovdqa" , "W:ymm, ymm/m256" , "RM" , "VEX.256.66.0F.WIG 6F /r" , "AVX"], ["vmovdqa" , "W:ymm/m256, ymm" , "MR" , "VEX.256.66.0F.WIG 7F /r" , "AVX"], ["vmovdqu" , "W:xmm, xmm/m128" , "RM" , "VEX.128.F3.0F.WIG 6F /r" , "AVX"], ["vmovdqu" , "W:xmm/m128, xmm" , "MR" , "VEX.128.F3.0F.WIG 7F /r" , "AVX"], ["vmovdqu" , "W:ymm, ymm/m256" , "RM" , "VEX.256.F3.0F.WIG 6F /r" , "AVX"], ["vmovdqu" , "W:ymm/m256, ymm" , "MR" , "VEX.256.F3.0F.WIG 7F /r" , "AVX"], ["vmovhlps" , "W:xmm, xmm[127:64], xmm[127:64]" , "RVM" , "VEX.128.0F.WIG 12 /r" , "AVX"], ["vmovhpd" , "W:m64, xmm[127:64]" , "MR" , "VEX.128.66.0F.WIG 17 /r" , "AVX"], ["vmovhpd" , "W:xmm, xmm[63:0], m64" , "RVM" , "VEX.128.66.0F.WIG 16 /r" , "AVX"], ["vmovhps" , "W:m64, xmm[127:64]" , "MR" , "VEX.128.0F.WIG 17 /r" , "AVX"], ["vmovhps" , "W:xmm, xmm[63:0], m64" , "RVM" , "VEX.128.0F.WIG 16 /r" , "AVX"], ["vmovlhps" , "W:xmm, xmm[63:0], xmm[63:0]" , "RVM" , "VEX.128.0F.WIG 16 /r" , "AVX"], ["vmovlpd" , "W:m64, xmm[63:0]" , "MR" , "VEX.128.66.0F.WIG 13 /r" , "AVX"], ["vmovlpd" , "W:xmm, xmm[127:64], m64" , "RVM" , "VEX.128.66.0F.WIG 12 /r" , "AVX"], ["vmovlps" , "W:m64, xmm[63:0]" , "MR" , "VEX.128.0F.WIG 13 /r" , "AVX"], ["vmovlps" , "W:xmm, xmm[127:64], m64" , "RVM" , "VEX.128.0F.WIG 12 /r" , "AVX"], ["vmovmskpd" , "W:r32[1:0], xmm" , "RM" , "VEX.128.66.0F.WIG 50 /r" , "AVX"], ["vmovmskpd" , "W:r32[3:0], ymm" , "RM" , "VEX.256.66.0F.WIG 50 /r" , "AVX"], ["vmovmskps" , "W:r32[3:0], xmm" , "RM" , "VEX.128.0F.WIG 50 /r" , "AVX"], ["vmovmskps" , "W:r32[7:0], ymm" , "RM" , "VEX.256.0F.WIG 50 /r" , "AVX"], ["vmovntdq" , "W:m128, xmm" , "MR" , "VEX.128.66.0F.WIG E7 /r" , "AVX"], ["vmovntdq" , "W:m256, ymm" , "MR" , "VEX.256.66.0F.WIG E7 /r" , "AVX"], ["vmovntdqa" , "W:xmm, m128" , "RM" , "VEX.128.66.0F38.WIG 2A /r" , "AVX"], ["vmovntdqa" , "W:ymm, m256" , "RM" , "VEX.256.66.0F38.WIG 2A /r" , "AVX2"], ["vmovntpd" , "W:m128, xmm" , "MR" , "VEX.128.66.0F.WIG 2B /r" , "AVX"], ["vmovntpd" , "W:m256, ymm" , "MR" , "VEX.256.66.0F.WIG 2B /r" , "AVX"], ["vmovntps" , "W:m128, xmm" , "MR" , "VEX.128.0F.WIG 2B /r" , "AVX"], ["vmovntps" , "W:m256, ymm" , "MR" , "VEX.256.0F.WIG 2B /r" , "AVX"], ["vmovq" , "W:r64/m64, xmm[63:0]" , "MR" , "VEX.128.66.0F.W1 7E /r" , "AVX X64"], ["vmovq" , "W:xmm[63:0], xmm[63:0]/m64" , "RM" , "VEX.128.F3.0F.WIG 7E /r" , "AVX"], ["vmovq" , "W:xmm[63:0], r64/m64" , "RM" , "VEX.128.66.0F.W1 6E /r" , "AVX X64"], ["vmovq" , "W:xmm[63:0]/m64, xmm[63:0]" , "MR" , "VEX.128.66.0F.WIG D6 /r" , "AVX"], ["vmovsd" , "W:m64, xmm[63:0]" , "MR" , "VEX.LIG.F2.0F.WIG 11 /r" , "AVX"], ["vmovsd" , "W:xmm[63:0], m64" , "RM" , "VEX.LIG.F2.0F.WIG 10 /r" , "AVX"], ["vmovsd" , "W:xmm, xmm[127:64], xmm[63:0]" , "MVR" , "VEX.LIG.F2.0F.WIG 11 /r" , "AVX"], ["vmovsd" , "W:xmm, xmm[127:64], xmm[63:0]" , "RVM" , "VEX.LIG.F2.0F.WIG 10 /r" , "AVX"], ["vmovshdup" , "W:xmm, xmm/m128" , "RM" , "VEX.128.F3.0F.WIG 16 /r" , "AVX"], ["vmovshdup" , "W:ymm, ymm/m256" , "RM" , "VEX.256.F3.0F.WIG 16 /r" , "AVX"], ["vmovsldup" , "W:xmm, xmm/m128" , "RM" , "VEX.128.F3.0F.WIG 12 /r" , "AVX"], ["vmovsldup" , "W:ymm, ymm/m256" , "RM" , "VEX.256.F3.0F.WIG 12 /r" , "AVX"], ["vmovss" , "W:m32, xmm[31:0]" , "MR" , "VEX.LIG.F3.0F.WIG 11 /r" , "AVX"], ["vmovss" , "W:xmm[31:0], m32" , "RM" , "VEX.LIG.F3.0F.WIG 10 /r" , "AVX"], ["vmovss" , "W:xmm, xmm[127:32], xmm[31:0]" , "MVR" , "VEX.LIG.F3.0F.WIG 11 /r" , "AVX"], ["vmovss" , "W:xmm, xmm[127:32], xmm[31:0]" , "RVM" , "VEX.LIG.F3.0F.WIG 10 /r" , "AVX"], ["vmovupd" , "W:xmm, xmm/m128" , "RM" , "VEX.128.0F.WIG 10 /r" , "AVX"], ["vmovupd" , "W:xmm/m128, xmm" , "MR" , "VEX.128.0F.WIG 11 /r" , "AVX"], ["vmovupd" , "W:ymm, ymm/m256" , "RM" , "VEX.256.0F.WIG 10 /r" , "AVX"], ["vmovupd" , "W:ymm/m256, ymm" , "MR" , "VEX.256.0F.WIG 11 /r" , "AVX"], ["vmovups" , "W:xmm, xmm/m128" , "RM" , "VEX.128.66.0F.WIG 10 /r" , "AVX"], ["vmovups" , "W:xmm/m128, xmm" , "MR" , "VEX.128.66.0F.WIG 11 /r" , "AVX"], ["vmovups" , "W:ymm, ymm/m256" , "RM" , "VEX.256.66.0F.WIG 10 /r" , "AVX"], ["vmovups" , "W:ymm/m256, ymm" , "MR" , "VEX.256.66.0F.WIG 11 /r" , "AVX"], ["vmpsadbw" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 42 /r ib" , "AVX"], ["vmpsadbw" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.WIG 42 /r ib" , "AVX2"], ["vmulpd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 59 /r" , "AVX"], ["vmulpd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 59 /r" , "AVX"], ["vmulps" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.0F.WIG 59 /r" , "AVX"], ["vmulps" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.0F.WIG 59 /r" , "AVX"], ["vmulsd" , "W:xmm, xmm, xmm[63:0]/m64" , "RVM" , "VEX.LIG.F2.0F.WIG 59 /r" , "AVX"], ["vmulss" , "W:xmm, xmm, xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 59 /r" , "AVX"], ["vorpd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 56 /r" , "AVX"], ["vorpd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 56 /r" , "AVX"], ["vorps" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.0F.WIG 56 /r" , "AVX"], ["vorps" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.0F.WIG 56 /r" , "AVX"], ["vpabsb" , "W:xmm, xmm/m128" , "RM" , "VEX.128.66.0F38.WIG 1C /r" , "AVX"], ["vpabsb" , "W:ymm, ymm/m256" , "RM" , "VEX.256.66.0F38.WIG 1C /r" , "AVX2"], ["vpabsd" , "W:xmm, xmm/m128" , "RM" , "VEX.128.66.0F38.WIG 1E /r" , "AVX"], ["vpabsd" , "W:ymm, ymm/m256" , "RM" , "VEX.256.66.0F38.WIG 1E /r" , "AVX2"], ["vpabsw" , "W:xmm, xmm/m128" , "RM" , "VEX.128.66.0F38.WIG 1D /r" , "AVX"], ["vpabsw" , "W:ymm, ymm/m256" , "RM" , "VEX.256.66.0F38.WIG 1D /r" , "AVX2"], ["vpackssdw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 6B /r" , "AVX"], ["vpackssdw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 6B /r" , "AVX2"], ["vpacksswb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 63 /r" , "AVX"], ["vpacksswb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 63 /r" , "AVX2"], ["vpackusdw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 2B /r" , "AVX"], ["vpackusdw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 2B /r" , "AVX2"], ["vpackuswb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 67 /r" , "AVX"], ["vpackuswb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 67 /r" , "AVX2"], ["vpaddb" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG FC /r" , "AVX"], ["vpaddb" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG FC /r" , "AVX2"], ["vpaddd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG FE /r" , "AVX"], ["vpaddd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG FE /r" , "AVX2"], ["vpaddq" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG D4 /r" , "AVX"], ["vpaddq" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG D4 /r" , "AVX2"], ["vpaddsb" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG EC /r" , "AVX"], ["vpaddsb" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG EC /r" , "AVX2"], ["vpaddsw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG ED /r" , "AVX"], ["vpaddsw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG ED /r" , "AVX2"], ["vpaddusb" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG DC /r" , "AVX"], ["vpaddusb" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG DC /r" , "AVX2"], ["vpaddusw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG DD /r" , "AVX"], ["vpaddusw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG DD /r" , "AVX2"], ["vpaddw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG FD /r" , "AVX"], ["vpaddw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG FD /r" , "AVX2"], ["vpalignr" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 0F /r ib" , "AVX"], ["vpalignr" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.WIG 0F /r ib" , "AVX2"], ["vpand" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG DB /r" , "AVX"], ["vpand" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG DB /r" , "AVX2"], ["vpandn" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG DF /r" , "AVX"], ["vpandn" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG DF /r" , "AVX2"], ["vpavgb" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG E0 /r" , "AVX"], ["vpavgb" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG E0 /r" , "AVX2"], ["vpavgw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG E3 /r" , "AVX"], ["vpavgw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG E3 /r" , "AVX2"], ["vpblendd" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.W0 02 /r ib" , "AVX2"], ["vpblendd" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.W0 02 /r ib" , "AVX2"], ["vpblendvb" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 4C /r /is4", "AVX"], ["vpblendvb" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 4C /r /is4", "AVX2"], ["vpblendw" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 0E /r ib" , "AVX"], ["vpblendw" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.WIG 0E /r ib" , "AVX2"], ["vpbroadcastb" , "W:xmm, xmm[7:0]/m8" , "RM" , "VEX.128.66.0F38.W0 78 /r" , "AVX2"], ["vpbroadcastb" , "W:ymm, xmm[7:0]/m8" , "RM" , "VEX.256.66.0F38.W0 78 /r" , "AVX2"], ["vpbroadcastd" , "W:xmm, xmm[31:0]/m32" , "RM" , "VEX.128.66.0F38.W0 58 /r" , "AVX2"], ["vpbroadcastd" , "W:ymm, xmm[31:0]/m32" , "RM" , "VEX.256.66.0F38.W0 58 /r" , "AVX2"], ["vpbroadcastq" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.66.0F38.W0 59 /r" , "AVX2"], ["vpbroadcastq" , "W:ymm, xmm[63:0]/m64" , "RM" , "VEX.256.66.0F38.W0 59 /r" , "AVX2"], ["vpbroadcastw" , "W:xmm, xmm[15:0]/m16" , "RM" , "VEX.128.66.0F38.W0 79 /r" , "AVX2"], ["vpbroadcastw" , "W:ymm, xmm[15:0]/m16" , "RM" , "VEX.256.66.0F38.W0 79 /r" , "AVX2"], ["vpclmulqdq" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F3A.WIG 44 /r ib" , "AVX PCLMULQDQ"], ["vpclmulqdq" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.WIG 44 /r ib" , "VPCLMULQDQ"], ["vpcmpeqb" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 74 /r" , "AVX"], ["vpcmpeqb" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 74 /r" , "AVX2"], ["vpcmpeqd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 76 /r" , "AVX"], ["vpcmpeqd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 76 /r" , "AVX2"], ["vpcmpeqq" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 29 /r" , "AVX"], ["vpcmpeqq" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 29 /r" , "AVX2"], ["vpcmpeqw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 75 /r" , "AVX"], ["vpcmpeqw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 75 /r" , "AVX2"], ["vpcmpestri" , "R:xmm, xmm/m128, ib/ub, W:,," , "RMI" , "VEX.128.66.0F3A.WIG 61 /r ib" , "AVX OF=W SF=W ZF=W AF=0 PF=0 CF=W"], ["vpcmpestrm" , "R:xmm, xmm/m128, ib/ub, W:,," , "RMI" , "VEX.128.66.0F3A.WIG 60 /r ib" , "AVX OF=W SF=W ZF=W AF=0 PF=0 CF=W"], ["vpcmpgtb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 64 /r" , "AVX"], ["vpcmpgtb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 64 /r" , "AVX2"], ["vpcmpgtd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 66 /r" , "AVX"], ["vpcmpgtd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 66 /r" , "AVX2"], ["vpcmpgtq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 37 /r" , "AVX"], ["vpcmpgtq" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 37 /r" , "AVX2"], ["vpcmpgtw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 65 /r" , "AVX"], ["vpcmpgtw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 65 /r" , "AVX2"], ["vpcmpistri" , "R:xmm, xmm/m128, ib/ub, W:" , "RMI" , "VEX.128.66.0F3A.WIG 63 /r ib" , "AVX OF=W SF=W ZF=W AF=0 PF=0 CF=W"], ["vpcmpistrm" , "R:xmm, xmm/m128, ib/ub, W:" , "RMI" , "VEX.128.66.0F3A.WIG 62 /r ib" , "AVX OF=W SF=W ZF=W AF=0 PF=0 CF=W"], ["vperm2f128" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.W0 06 /r ib" , "AVX"], ["vperm2i128" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F3A.W0 46 /r ib" , "AVX2"], ["vpermd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 36 /r" , "AVX2"], ["vpermilpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 0D /r" , "AVX"], ["vpermilpd" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "VEX.128.66.0F3A.W0 05 /r ib" , "AVX"], ["vpermilpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 0D /r" , "AVX"], ["vpermilpd" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.66.0F3A.W0 05 /r ib" , "AVX"], ["vpermilps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 0C /r" , "AVX"], ["vpermilps" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "VEX.128.66.0F3A.W0 04 /r ib" , "AVX"], ["vpermilps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 0C /r" , "AVX"], ["vpermilps" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.66.0F3A.W0 04 /r ib" , "AVX"], ["vpermpd" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.66.0F3A.W1 01 /r ib" , "AVX2"], ["vpermps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 16 /r" , "AVX2"], ["vpermq" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.66.0F3A.W1 00 /r ib" , "AVX2"], ["vpextrb" , "W:r32[7:0]/m8, xmm, ib/ub" , "MRI" , "VEX.128.66.0F3A.W0 14 /r ib" , "AVX"], ["vpextrd" , "W:r32/m32, xmm, ib/ub" , "MRI" , "VEX.128.66.0F3A.W0 16 /r ib" , "AVX"], ["vpextrq" , "W:r64/m64, xmm, ib/ub" , "MRI" , "VEX.128.66.0F3A.W1 16 /r ib" , "AVX X64"], ["vpextrw" , "W:r32[15:0], xmm, ib/ub" , "RMI" , "VEX.128.66.0F.W0 C5 /r ib" , "AVX"], ["vpextrw" , "W:r32[15:0]/m16, xmm, ib/ub" , "MRI" , "VEX.128.66.0F3A.W0 15 /r ib" , "AVX"], ["vpgatherdd" , "X:xmm, vm32x, X:xmm" , "RMV" , "VEX.128.66.0F38.W0 90 /r" , "AVX2"], ["vpgatherdd" , "X:ymm, vm32y, X:ymm" , "RMV" , "VEX.256.66.0F38.W0 90 /r" , "AVX2"], ["vpgatherdq" , "X:xmm, vm32x, X:xmm" , "RMV" , "VEX.128.66.0F38.W1 90 /r" , "AVX2"], ["vpgatherdq" , "X:ymm, vm32x, X:ymm" , "RMV" , "VEX.256.66.0F38.W1 90 /r" , "AVX2"], ["vpgatherqd" , "X:xmm, vm64x, X:xmm" , "RMV" , "VEX.128.66.0F38.W0 91 /r" , "AVX2"], ["vpgatherqd" , "X:xmm, vm64y, X:xmm" , "RMV" , "VEX.256.66.0F38.W0 91 /r" , "AVX2"], ["vpgatherqq" , "X:xmm, vm64x, X:xmm" , "RMV" , "VEX.128.66.0F38.W1 91 /r" , "AVX2"], ["vpgatherqq" , "X:ymm, vm64y, X:ymm" , "RMV" , "VEX.256.66.0F38.W1 91 /r" , "AVX2"], ["vphaddd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 02 /r" , "AVX"], ["vphaddd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 02 /r" , "AVX2"], ["vphaddsw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 03 /r" , "AVX"], ["vphaddsw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 03 /r" , "AVX2"], ["vphaddw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 01 /r" , "AVX"], ["vphaddw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 01 /r" , "AVX2"], ["vphminposuw" , "W:xmm[18:0], xmm/m128" , "RM" , "VEX.128.66.0F38.WIG 41 /r" , "AVX"], ["vphsubd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 06 /r" , "AVX"], ["vphsubd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 06 /r" , "AVX2"], ["vphsubsw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 07 /r" , "AVX"], ["vphsubsw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 07 /r" , "AVX2"], ["vphsubw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 05 /r" , "AVX"], ["vphsubw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 05 /r" , "AVX2"], ["vpinsrb" , "W:xmm, xmm, r32[7:0]/m8, ib/ub" , "RVMI" , "VEX.128.66.0F3A.W0 20 /r ib" , "AVX"], ["vpinsrd" , "W:xmm, xmm, r32/m32, ib/ub" , "RVMI" , "VEX.128.66.0F3A.W0 22 /r ib" , "AVX"], ["vpinsrq" , "W:xmm, xmm, r64/m64, ib/ub" , "RVMI" , "VEX.128.66.0F3A.W1 22 /r ib" , "AVX X64"], ["vpinsrw" , "W:xmm, xmm, r32[15:0]/m16, ib/ub" , "RVMI" , "VEX.128.66.0F.W0 C4 /r ib" , "AVX"], ["vpmaddubsw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 04 /r" , "AVX"], ["vpmaddubsw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 04 /r" , "AVX2"], ["vpmaddwd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG F5 /r" , "AVX"], ["vpmaddwd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG F5 /r" , "AVX2"], ["vpmaskmovd" , "X:m128, xmm, xmm" , "MVR" , "VEX.128.66.0F38.W0 8E /r" , "AVX2"], ["vpmaskmovd" , "X:m256, ymm, ymm" , "MVR" , "VEX.256.66.0F38.W0 8E /r" , "AVX2"], ["vpmaskmovd" , "W:xmm, xmm, m128" , "RVM" , "VEX.128.66.0F38.W0 8C /r" , "AVX2"], ["vpmaskmovd" , "W:ymm, ymm, m256" , "RVM" , "VEX.256.66.0F38.W0 8C /r" , "AVX2"], ["vpmaskmovq" , "X:m128, xmm, xmm" , "MVR" , "VEX.128.66.0F38.W1 8E /r" , "AVX2"], ["vpmaskmovq" , "X:m256, ymm, ymm" , "MVR" , "VEX.256.66.0F38.W1 8E /r" , "AVX2"], ["vpmaskmovq" , "W:xmm, xmm, m128" , "RVM" , "VEX.128.66.0F38.W1 8C /r" , "AVX2"], ["vpmaskmovq" , "W:ymm, ymm, m256" , "RVM" , "VEX.256.66.0F38.W1 8C /r" , "AVX2"], ["vpmaxsb" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 3C /r" , "AVX"], ["vpmaxsb" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 3C /r" , "AVX2"], ["vpmaxsd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 3D /r" , "AVX"], ["vpmaxsd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 3D /r" , "AVX2"], ["vpmaxsw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG EE /r" , "AVX"], ["vpmaxsw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG EE /r" , "AVX2"], ["vpmaxub" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG DE /r" , "AVX"], ["vpmaxub" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG DE /r" , "AVX2"], ["vpmaxud" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 3F /r" , "AVX"], ["vpmaxud" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 3F /r" , "AVX2"], ["vpmaxuw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 3E /r" , "AVX"], ["vpmaxuw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 3E /r" , "AVX2"], ["vpminsb" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 38 /r" , "AVX"], ["vpminsb" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 38 /r" , "AVX2"], ["vpminsd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 39 /r" , "AVX"], ["vpminsd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 39 /r" , "AVX2"], ["vpminsw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG EA /r" , "AVX"], ["vpminsw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG EA /r" , "AVX2"], ["vpminub" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG DA /r" , "AVX"], ["vpminub" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG DA /r" , "AVX2"], ["vpminud" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 3B /r" , "AVX"], ["vpminud" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 3B /r" , "AVX2"], ["vpminuw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 3A /r" , "AVX"], ["vpminuw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 3A /r" , "AVX2"], ["vpmovmskb" , "W:r32[15:0], xmm" , "RVM" , "VEX.128.66.0F.WIG D7 /r" , "AVX"], ["vpmovmskb" , "W:r32[31:0], ymm" , "RVM" , "VEX.256.66.0F.WIG D7 /r" , "AVX2"], ["vpmovsxbd" , "W:xmm, xmm[31:0]/m32" , "RM" , "VEX.128.66.0F38.WIG 21 /r" , "AVX"], ["vpmovsxbd" , "W:ymm, xmm[63:0]/m64" , "RM" , "VEX.256.66.0F38.WIG 21 /r" , "AVX2"], ["vpmovsxbq" , "W:xmm, xmm[15:0]/m16" , "RM" , "VEX.128.66.0F38.WIG 22 /r" , "AVX"], ["vpmovsxbq" , "W:ymm, xmm[31:0]/m32" , "RM" , "VEX.256.66.0F38.WIG 22 /r" , "AVX2"], ["vpmovsxbw" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.66.0F38.WIG 20 /r" , "AVX"], ["vpmovsxbw" , "W:ymm, xmm/m128" , "RM" , "VEX.256.66.0F38.WIG 20 /r" , "AVX2"], ["vpmovsxdq" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.66.0F38.WIG 25 /r" , "AVX"], ["vpmovsxdq" , "W:ymm, xmm/m128" , "RM" , "VEX.256.66.0F38.WIG 25 /r" , "AVX2"], ["vpmovsxwd" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.66.0F38.WIG 23 /r" , "AVX"], ["vpmovsxwd" , "W:ymm, xmm/m128" , "RM" , "VEX.256.66.0F38.WIG 23 /r" , "AVX2"], ["vpmovsxwq" , "W:xmm, xmm[31:0]/m32" , "RM" , "VEX.128.66.0F38.WIG 24 /r" , "AVX"], ["vpmovsxwq" , "W:ymm, xmm[63:0]/m64" , "RM" , "VEX.256.66.0F38.WIG 24 /r" , "AVX2"], ["vpmovzxbd" , "W:xmm, xmm[31:0]/m32" , "RM" , "VEX.128.66.0F38.WIG 31 /r" , "AVX"], ["vpmovzxbd" , "W:ymm, xmm[63:0]/m64" , "RM" , "VEX.256.66.0F38.WIG 31 /r" , "AVX2"], ["vpmovzxbq" , "W:xmm, xmm[15:0]/m16" , "RM" , "VEX.128.66.0F38.WIG 32 /r" , "AVX"], ["vpmovzxbq" , "W:ymm, xmm[31:0]/m32" , "RM" , "VEX.256.66.0F38.WIG 32 /r" , "AVX2"], ["vpmovzxbw" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.66.0F38.WIG 30 /r" , "AVX"], ["vpmovzxbw" , "W:ymm, xmm/m128" , "RM" , "VEX.256.66.0F38.WIG 30 /r" , "AVX2"], ["vpmovzxdq" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.66.0F38.WIG 35 /r" , "AVX"], ["vpmovzxdq" , "W:ymm, xmm/m128" , "RM" , "VEX.256.66.0F38.WIG 35 /r" , "AVX2"], ["vpmovzxwd" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.66.0F38.WIG 33 /r" , "AVX"], ["vpmovzxwd" , "W:ymm, xmm/m128" , "RM" , "VEX.256.66.0F38.WIG 33 /r" , "AVX2"], ["vpmovzxwq" , "W:xmm, xmm[31:0]/m32" , "RM" , "VEX.128.66.0F38.WIG 34 /r" , "AVX"], ["vpmovzxwq" , "W:ymm, xmm[63:0]/m64" , "RM" , "VEX.256.66.0F38.WIG 34 /r" , "AVX2"], ["vpmuldq" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 28 /r" , "AVX"], ["vpmuldq" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 28 /r" , "AVX2"], ["vpmulhrsw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 0B /r" , "AVX"], ["vpmulhrsw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 0B /r" , "AVX2"], ["vpmulhuw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG E4 /r" , "AVX"], ["vpmulhuw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG E4 /r" , "AVX2"], ["vpmulhw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG E5 /r" , "AVX"], ["vpmulhw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG E5 /r" , "AVX2"], ["vpmulld" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 40 /r" , "AVX"], ["vpmulld" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 40 /r" , "AVX2"], ["vpmullw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG D5 /r" , "AVX"], ["vpmullw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG D5 /r" , "AVX2"], ["vpmuludq" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG F4 /r" , "AVX"], ["vpmuludq" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG F4 /r" , "AVX2"], ["vpor" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG EB /r" , "AVX"], ["vpor" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG EB /r" , "AVX2"], ["vpsadbw" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG F6 /r" , "AVX"], ["vpsadbw" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG F6 /r" , "AVX2"], ["vpshufb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 00 /r" , "AVX"], ["vpshufb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 00 /r" , "AVX2"], ["vpshufd" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "VEX.128.66.0F.WIG 70 /r ib" , "AVX"], ["vpshufd" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.66.0F.WIG 70 /r ib" , "AVX2"], ["vpshufhw" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "VEX.128.F3.0F.WIG 70 /r ib" , "AVX"], ["vpshufhw" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.F3.0F.WIG 70 /r ib" , "AVX2"], ["vpshuflw" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "VEX.128.F2.0F.WIG 70 /r ib" , "AVX"], ["vpshuflw" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.F2.0F.WIG 70 /r ib" , "AVX2"], ["vpsignb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 08 /r" , "AVX"], ["vpsignb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 08 /r" , "AVX2"], ["vpsignd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 0A /r" , "AVX"], ["vpsignd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 0A /r" , "AVX2"], ["vpsignw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.WIG 09 /r" , "AVX"], ["vpsignw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.WIG 09 /r" , "AVX2"], ["vpslld" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 72 /6 ib" , "AVX"], ["vpslld" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG F2 /r" , "AVX"], ["vpslld" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 72 /6 ib" , "AVX2"], ["vpslld" , "W:ymm, ymm, xmm/m128" , "RVM" , "VEX.256.66.0F.WIG F2 /r" , "AVX2"], ["vpslldq" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 73 /7 ib" , "AVX"], ["vpslldq" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 73 /7 ib" , "AVX2"], ["vpsllq" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 73 /6 ib" , "AVX"], ["vpsllq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG F3 /r" , "AVX"], ["vpsllq" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 73 /6 ib" , "AVX2"], ["vpsllq" , "W:ymm, ymm, xmm/m128" , "RVM" , "VEX.256.66.0F.WIG F3 /r" , "AVX2"], ["vpsllvd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 47 /r" , "AVX2"], ["vpsllvd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 47 /r" , "AVX2"], ["vpsllvq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 47 /r" , "AVX2"], ["vpsllvq" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 47 /r" , "AVX2"], ["vpsllw" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 71 /6 ib" , "AVX"], ["vpsllw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG F1 /r" , "AVX"], ["vpsllw" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 71 /6 ib" , "AVX2"], ["vpsllw" , "W:ymm, ymm, xmm/m128" , "RVM" , "VEX.256.66.0F.WIG F1 /r" , "AVX2"], ["vpsrad" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 72 /4 ib" , "AVX"], ["vpsrad" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG E2 /r" , "AVX"], ["vpsrad" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 72 /4 ib" , "AVX2"], ["vpsrad" , "W:ymm, ymm, xmm/m128" , "RVM" , "VEX.256.66.0F.WIG E2 /r" , "AVX2"], ["vpsravd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 46 /r" , "AVX2"], ["vpsravd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 46 /r" , "AVX2"], ["vpsraw" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 71 /4 ib" , "AVX"], ["vpsraw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG E1 /r" , "AVX"], ["vpsraw" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 71 /4 ib" , "AVX2"], ["vpsraw" , "W:ymm, ymm, xmm/m128" , "RVM" , "VEX.256.66.0F.WIG E1 /r" , "AVX2"], ["vpsrld" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 72 /2 ib" , "AVX"], ["vpsrld" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG D2 /r" , "AVX"], ["vpsrld" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 72 /2 ib" , "AVX2"], ["vpsrld" , "W:ymm, ymm, xmm/m128" , "RVM" , "VEX.256.66.0F.WIG D2 /r" , "AVX2"], ["vpsrldq" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 73 /3 ib" , "AVX"], ["vpsrldq" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 73 /3 ib" , "AVX2"], ["vpsrlq" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 73 /2 ib" , "AVX"], ["vpsrlq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG D3 /r" , "AVX"], ["vpsrlq" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 73 /2 ib" , "AVX2"], ["vpsrlq" , "W:ymm, ymm, xmm/m128" , "RVM" , "VEX.256.66.0F.WIG D3 /r" , "AVX2"], ["vpsrlvd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 45 /r" , "AVX2"], ["vpsrlvd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 45 /r" , "AVX2"], ["vpsrlvq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 45 /r" , "AVX2"], ["vpsrlvq" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 45 /r" , "AVX2"], ["vpsrlw" , "W:xmm, xmm, ib/ub" , "VMI" , "VEX.128.66.0F.WIG 71 /2 ib" , "AVX"], ["vpsrlw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG D1 /r" , "AVX"], ["vpsrlw" , "W:ymm, ymm, ib/ub" , "VMI" , "VEX.256.66.0F.WIG 71 /2 ib" , "AVX2"], ["vpsrlw" , "W:ymm, ymm, xmm/m128" , "RVM" , "VEX.256.66.0F.WIG D1 /r" , "AVX2"], ["vpsubb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG F8 /r" , "AVX"], ["vpsubb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG F8 /r" , "AVX2"], ["vpsubd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG FA /r" , "AVX"], ["vpsubd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG FA /r" , "AVX2"], ["vpsubq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG FB /r" , "AVX"], ["vpsubq" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG FB /r" , "AVX2"], ["vpsubsb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG E8 /r" , "AVX"], ["vpsubsb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG E8 /r" , "AVX2"], ["vpsubsw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG E9 /r" , "AVX"], ["vpsubsw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG E9 /r" , "AVX2"], ["vpsubusb" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG D8 /r" , "AVX"], ["vpsubusb" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG D8 /r" , "AVX2"], ["vpsubusw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG D9 /r" , "AVX"], ["vpsubusw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG D9 /r" , "AVX2"], ["vpsubw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG F9 /r" , "AVX"], ["vpsubw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG F9 /r" , "AVX2"], ["vptest" , "R:~xmm, ~xmm/m128" , "RM" , "VEX.128.66.0F38.WIG 17 /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["vptest" , "R:~ymm, ~ymm/m256" , "RM" , "VEX.256.66.0F38.WIG 17 /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["vpunpckhbw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 68 /r" , "AVX"], ["vpunpckhbw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 68 /r" , "AVX2"], ["vpunpckhdq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 6A /r" , "AVX"], ["vpunpckhdq" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 6A /r" , "AVX2"], ["vpunpckhqdq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 6D /r" , "AVX"], ["vpunpckhqdq" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 6D /r" , "AVX2"], ["vpunpckhwd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 69 /r" , "AVX"], ["vpunpckhwd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 69 /r" , "AVX2"], ["vpunpcklbw" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 60 /r" , "AVX"], ["vpunpcklbw" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 60 /r" , "AVX2"], ["vpunpckldq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 62 /r" , "AVX"], ["vpunpckldq" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 62 /r" , "AVX2"], ["vpunpcklqdq" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 6C /r" , "AVX"], ["vpunpcklqdq" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 6C /r" , "AVX2"], ["vpunpcklwd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 61 /r" , "AVX"], ["vpunpcklwd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 61 /r" , "AVX2"], ["vpxor" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG EF /r" , "AVX"], ["vpxor" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG EF /r" , "AVX2"], ["vrcpps" , "W:xmm, xmm/m128" , "RM" , "VEX.128.0F.WIG 53 /r" , "AVX"], ["vrcpps" , "W:ymm, ymm/m256" , "RM" , "VEX.256.0F.WIG 53 /r" , "AVX"], ["vrcpss" , "W:xmm, xmm[127:32], xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 53 /r" , "AVX"], ["vroundpd" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "VEX.128.66.0F3A.WIG 09 /r ib" , "AVX"], ["vroundpd" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.66.0F3A.WIG 09 /r ib" , "AVX"], ["vroundps" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "VEX.128.66.0F3A.WIG 08 /r ib" , "AVX"], ["vroundps" , "W:ymm, ymm/m256, ib/ub" , "RMI" , "VEX.256.66.0F3A.WIG 08 /r ib" , "AVX"], ["vroundsd" , "W:xmm, xmm[127:64], xmm[63:0]/m64, ib/ub" , "RVMI" , "VEX.LIG.66.0F3A.WIG 0B /r ib" , "AVX"], ["vroundss" , "W:xmm, xmm[127:32], xmm[31:0]/m32, ib/ub" , "RVMI" , "VEX.LIG.66.0F3A.WIG 0A /r ib" , "AVX"], ["vrsqrtps" , "W:xmm, xmm/m128" , "RM" , "VEX.128.0F.WIG 52 /r" , "AVX"], ["vrsqrtps" , "W:ymm, ymm/m256" , "RM" , "VEX.256.0F.WIG 52 /r" , "AVX"], ["vrsqrtss" , "W:xmm, xmm[127:32], xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 52 /r" , "AVX"], ["vshufpd" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.66.0F.WIG C6 /r ib" , "AVX"], ["vshufpd" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.66.0F.WIG C6 /r ib" , "AVX"], ["vshufps" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "VEX.128.0F.WIG C6 /r ib" , "AVX"], ["vshufps" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI" , "VEX.256.0F.WIG C6 /r ib" , "AVX"], ["vsqrtpd" , "W:xmm, xmm/m128" , "RM" , "VEX.128.66.0F.WIG 51 /r" , "AVX"], ["vsqrtpd" , "W:ymm, ymm/m256" , "RM" , "VEX.256.66.0F.WIG 51 /r" , "AVX"], ["vsqrtps" , "W:xmm, xmm/m128" , "RM" , "VEX.128.0F.WIG 51 /r" , "AVX"], ["vsqrtps" , "W:ymm, ymm/m256" , "RM" , "VEX.256.0F.WIG 51 /r" , "AVX"], ["vsqrtsd" , "W:xmm, xmm[127:64], xmm[63:0]/m64" , "RVM" , "VEX.LIG.F2.0F.WIG 51 /r" , "AVX"], ["vsqrtss" , "W:xmm, xmm[127:32], xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 51 /r" , "AVX"], ["vsubpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 5C /r" , "AVX"], ["vsubpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 5C /r" , "AVX"], ["vsubps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.0F.WIG 5C /r" , "AVX"], ["vsubps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.0F.WIG 5C /r" , "AVX"], ["vsubsd" , "W:xmm, xmm, xmm[63:0]/m64" , "RVM" , "VEX.LIG.F2.0F.WIG 5C /r" , "AVX"], ["vsubss" , "W:xmm, xmm, xmm[31:0]/m32" , "RVM" , "VEX.LIG.F3.0F.WIG 5C /r" , "AVX"], ["vtestpd" , "R:~xmm, ~xmm/m128" , "RM" , "VEX.128.66.0F38.W0 0F /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["vtestpd" , "R:~ymm, ~ymm/m256" , "RM" , "VEX.256.66.0F38.W0 0F /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["vtestps" , "R:~xmm, ~xmm/m128" , "RM" , "VEX.128.66.0F38.W0 0E /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["vtestps" , "R:~ymm, ~ymm/m256" , "RM" , "VEX.256.66.0F38.W0 0E /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["vucomisd" , "R:xmm[63:0], xmm[63:0]/m64" , "RM" , "VEX.LIG.66.0F.WIG 2E /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["vucomiss" , "R:xmm[31:0], xmm[31:0]/m32" , "RM" , "VEX.LIG.0F.WIG 2E /r" , "AVX OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["vunpckhpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 15 /r" , "AVX"], ["vunpckhpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 15 /r" , "AVX"], ["vunpckhps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.0F.WIG 15 /r" , "AVX"], ["vunpckhps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.0F.WIG 15 /r" , "AVX"], ["vunpcklpd" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 14 /r" , "AVX"], ["vunpcklpd" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 14 /r" , "AVX"], ["vunpcklps" , "W:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.0F.WIG 14 /r" , "AVX"], ["vunpcklps" , "W:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.0F.WIG 14 /r" , "AVX"], ["vxorpd" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.66.0F.WIG 57 /r" , "AVX"], ["vxorpd" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.66.0F.WIG 57 /r" , "AVX"], ["vxorps" , "W:xmm,~xmm,~xmm/m128" , "RVM" , "VEX.128.0F.WIG 57 /r" , "AVX"], ["vxorps" , "W:ymm,~ymm,~ymm/m256" , "RVM" , "VEX.256.0F.WIG 57 /r" , "AVX"], ["vldmxcsr" , "R:m32" , "M" , "VEX.LZ.0F.WIG AE /2" , "AVX"], ["vstmxcsr" , "W:m32" , "M" , "VEX.LZ.0F.WIG AE /3" , "AVX"], ["vzeroall" , "" , "NONE" , "VEX.256.0F.WIG 77" , "AVX Volatile"], ["vzeroupper" , "" , "NONE" , "VEX.128.0F.WIG 77" , "AVX Volatile"], ["vcvtph2ps" , "W:xmm, xmm[63:0]/m64" , "RM" , "VEX.128.66.0F38.W0 13 /r" , "F16C"], ["vcvtph2ps" , "W:ymm, xmm/m128" , "RM" , "VEX.256.66.0F38.W0 13 /r" , "F16C"], ["vcvtps2ph" , "W:xmm[63:0]/m64, xmm, ib/ub" , "MRI" , "VEX.128.66.0F3A.W0 1D /r ib" , "F16C"], ["vcvtps2ph" , "W:xmm/m128, ymm, ib/ub" , "MRI" , "VEX.256.66.0F3A.W0 1D /r ib" , "F16C"], ["vfmadd132pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 98 /r" , "FMA"], ["vfmadd132pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 98 /r" , "FMA"], ["vfmadd132ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 98 /r" , "FMA"], ["vfmadd132ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 98 /r" , "FMA"], ["vfmadd132sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 99 /r" , "FMA"], ["vfmadd132ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 99 /r" , "FMA"], ["vfmadd213pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 A8 /r" , "FMA"], ["vfmadd213pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 A8 /r" , "FMA"], ["vfmadd213ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 A8 /r" , "FMA"], ["vfmadd213ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 A8 /r" , "FMA"], ["vfmadd213sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 A9 /r" , "FMA"], ["vfmadd213ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 A9 /r" , "FMA"], ["vfmadd231pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 B8 /r" , "FMA"], ["vfmadd231pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 B8 /r" , "FMA"], ["vfmadd231ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 B8 /r" , "FMA"], ["vfmadd231ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 B8 /r" , "FMA"], ["vfmadd231sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 B9 /r" , "FMA"], ["vfmadd231ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 B9 /r" , "FMA"], ["vfmaddsub132pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 96 /r" , "FMA"], ["vfmaddsub132pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 96 /r" , "FMA"], ["vfmaddsub132ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 96 /r" , "FMA"], ["vfmaddsub132ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 96 /r" , "FMA"], ["vfmaddsub213pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 A6 /r" , "FMA"], ["vfmaddsub213pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 A6 /r" , "FMA"], ["vfmaddsub213ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 A6 /r" , "FMA"], ["vfmaddsub213ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 A6 /r" , "FMA"], ["vfmaddsub231pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 B6 /r" , "FMA"], ["vfmaddsub231pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 B6 /r" , "FMA"], ["vfmaddsub231ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 B6 /r" , "FMA"], ["vfmaddsub231ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 B6 /r" , "FMA"], ["vfmsub132pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 9A /r" , "FMA"], ["vfmsub132pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 9A /r" , "FMA"], ["vfmsub132ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 9A /r" , "FMA"], ["vfmsub132ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 9A /r" , "FMA"], ["vfmsub132sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 9B /r" , "FMA"], ["vfmsub132ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 9B /r" , "FMA"], ["vfmsub213pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 AA /r" , "FMA"], ["vfmsub213pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 AA /r" , "FMA"], ["vfmsub213ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 AA /r" , "FMA"], ["vfmsub213ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 AA /r" , "FMA"], ["vfmsub213sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 AB /r" , "FMA"], ["vfmsub213ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 AB /r" , "FMA"], ["vfmsub231pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 BA /r" , "FMA"], ["vfmsub231pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 BA /r" , "FMA"], ["vfmsub231ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 BA /r" , "FMA"], ["vfmsub231ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 BA /r" , "FMA"], ["vfmsub231sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 BB /r" , "FMA"], ["vfmsub231ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 BB /r" , "FMA"], ["vfmsubadd132pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 97 /r" , "FMA"], ["vfmsubadd132pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 97 /r" , "FMA"], ["vfmsubadd132ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 97 /r" , "FMA"], ["vfmsubadd132ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 97 /r" , "FMA"], ["vfmsubadd213pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 A7 /r" , "FMA"], ["vfmsubadd213pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 A7 /r" , "FMA"], ["vfmsubadd213ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 A7 /r" , "FMA"], ["vfmsubadd213ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 A7 /r" , "FMA"], ["vfmsubadd231pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 B7 /r" , "FMA"], ["vfmsubadd231pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 B7 /r" , "FMA"], ["vfmsubadd231ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 B7 /r" , "FMA"], ["vfmsubadd231ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 B7 /r" , "FMA"], ["vfnmadd132pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 9C /r" , "FMA"], ["vfnmadd132pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 9C /r" , "FMA"], ["vfnmadd132ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 9C /r" , "FMA"], ["vfnmadd132ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 9C /r" , "FMA"], ["vfnmadd132sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 9D /r" , "FMA"], ["vfnmadd132ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 9D /r" , "FMA"], ["vfnmadd213pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 AC /r" , "FMA"], ["vfnmadd213pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 AC /r" , "FMA"], ["vfnmadd213ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 AC /r" , "FMA"], ["vfnmadd213ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 AC /r" , "FMA"], ["vfnmadd213sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 AD /r" , "FMA"], ["vfnmadd213ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 AD /r" , "FMA"], ["vfnmadd231pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 BC /r" , "FMA"], ["vfnmadd231pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 BC /r" , "FMA"], ["vfnmadd231ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 BC /r" , "FMA"], ["vfnmadd231ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 BC /r" , "FMA"], ["vfnmadd231sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 BD /r" , "FMA"], ["vfnmadd231ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 BD /r" , "FMA"], ["vfnmsub132pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 9E /r" , "FMA"], ["vfnmsub132pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 9E /r" , "FMA"], ["vfnmsub132ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 9E /r" , "FMA"], ["vfnmsub132ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 9E /r" , "FMA"], ["vfnmsub132sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 9F /r" , "FMA"], ["vfnmsub132ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 9F /r" , "FMA"], ["vfnmsub213pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 AE /r" , "FMA"], ["vfnmsub213pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 AE /r" , "FMA"], ["vfnmsub213ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 AE /r" , "FMA"], ["vfnmsub213ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 AE /r" , "FMA"], ["vfnmsub213sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 AF /r" , "FMA"], ["vfnmsub213ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 AF /r" , "FMA"], ["vfnmsub231pd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W1 BE /r" , "FMA"], ["vfnmsub231pd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W1 BE /r" , "FMA"], ["vfnmsub231ps" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 BE /r" , "FMA"], ["vfnmsub231ps" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 BE /r" , "FMA"], ["vfnmsub231sd" , "x:xmm[63:0], xmm[63:0], xmm[63:0]/m64" , "RVM" , "VEX.LIG.66.0F38.W1 BF /r" , "FMA"], ["vfnmsub231ss" , "x:xmm[31:0], xmm[31:0], xmm[31:0]/m32" , "RVM" , "VEX.LIG.66.0F38.W0 BF /r" , "FMA"], ["vfmaddpd" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 69 /r /is4", "FMA4"], ["vfmaddpd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 69 /r /is4", "FMA4"], ["vfmaddpd" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 69 /r /is4", "FMA4"], ["vfmaddpd" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 69 /r /is4", "FMA4"], ["vfmaddps" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 68 /r /is4", "FMA4"], ["vfmaddps" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 68 /r /is4", "FMA4"], ["vfmaddps" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 68 /r /is4", "FMA4"], ["vfmaddps" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 68 /r /is4", "FMA4"], ["vfmaddsd" , "W:xmm[63:0], xmm[63:0], xmm[63:0], xmm[63:0]/m64", "RVSM" , "VEX.128.66.0F3A.W1 6b /r /is4", "FMA4"], ["vfmaddsd" , "W:xmm[63:0], xmm[63:0], xmm[63:0]/m64, xmm[63:0]", "RVMS" , "VEX.128.66.0F3A.W0 6b /r /is4", "FMA4"], ["vfmaddss" , "W:xmm[31:0], xmm[31:0], xmm[31:0], xmm[31:0]/m32", "RVSM" , "VEX.128.66.0F3A.W1 6a /r /is4", "FMA4"], ["vfmaddss" , "W:xmm[31:0], xmm[31:0], xmm[31:0]/m32, xmm[31:0]", "RVMS" , "VEX.128.66.0F3A.W0 6a /r /is4", "FMA4"], ["vfmaddsubpd" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 5D /r /is4", "FMA4"], ["vfmaddsubpd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 5D /r /is4", "FMA4"], ["vfmaddsubpd" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 5D /r /is4", "FMA4"], ["vfmaddsubpd" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 5D /r /is4", "FMA4"], ["vfmaddsubps" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 5C /r /is4", "FMA4"], ["vfmaddsubps" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 5C /r /is4", "FMA4"], ["vfmaddsubps" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 5C /r /is4", "FMA4"], ["vfmaddsubps" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 5C /r /is4", "FMA4"], ["vfmsubaddpd" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 5F /r /is4", "FMA4"], ["vfmsubaddpd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 5F /r /is4", "FMA4"], ["vfmsubaddpd" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 5F /r /is4", "FMA4"], ["vfmsubaddpd" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 5F /r /is4", "FMA4"], ["vfmsubaddps" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 5E /r /is4", "FMA4"], ["vfmsubaddps" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 5E /r /is4", "FMA4"], ["vfmsubaddps" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 5E /r /is4", "FMA4"], ["vfmsubaddps" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 5E /r /is4", "FMA4"], ["vfmsubpd" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 6D /r /is4", "FMA4"], ["vfmsubpd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 6D /r /is4", "FMA4"], ["vfmsubpd" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 6D /r /is4", "FMA4"], ["vfmsubpd" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 6D /r /is4", "FMA4"], ["vfmsubps" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 6C /r /is4", "FMA4"], ["vfmsubps" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 6C /r /is4", "FMA4"], ["vfmsubps" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 6C /r /is4", "FMA4"], ["vfmsubps" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 6C /r /is4", "FMA4"], ["vfmsubsd" , "W:xmm[63:0], xmm[63:0], xmm[63:0], xmm[63:0]/m64", "RVSM" , "VEX.128.66.0F3A.W1 6F /r /is4", "FMA4"], ["vfmsubsd" , "W:xmm[63:0], xmm[63:0], xmm[63:0]/m64, xmm[63:0]", "RVMS" , "VEX.128.66.0F3A.W0 6F /r /is4", "FMA4"], ["vfmsubss" , "W:xmm[31:0], xmm[31:0], xmm[31:0], xmm[31:0]/m32", "RVSM" , "VEX.128.66.0F3A.W1 6E /r /is4", "FMA4"], ["vfmsubss" , "W:xmm[31:0], xmm[31:0], xmm[31:0]/m32, xmm[31:0]", "RVMS" , "VEX.128.66.0F3A.W0 6E /r /is4", "FMA4"], ["vfnmaddpd" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 79 /r /is4", "FMA4"], ["vfnmaddpd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 79 /r /is4", "FMA4"], ["vfnmaddpd" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 79 /r /is4", "FMA4"], ["vfnmaddpd" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 79 /r /is4", "FMA4"], ["vfnmaddps" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 78 /r /is4", "FMA4"], ["vfnmaddps" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 78 /r /is4", "FMA4"], ["vfnmaddps" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 78 /r /is4", "FMA4"], ["vfnmaddps" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 78 /r /is4", "FMA4"], ["vfnmaddsd" , "W:xmm[63:0], xmm[63:0], xmm[63:0], xmm[63:0]/m64", "RVSM" , "VEX.128.66.0F3A.W1 7B /r /is4", "FMA4"], ["vfnmaddsd" , "W:xmm[63:0], xmm[63:0], xmm[63:0]/m64, xmm[63:0]", "RVMS" , "VEX.128.66.0F3A.W0 7B /r /is4", "FMA4"], ["vfnmaddss" , "W:xmm[31:0], xmm[31:0], xmm[31:0], xmm[31:0]/m32", "RVSM" , "VEX.128.66.0F3A.W1 7A /r /is4", "FMA4"], ["vfnmaddss" , "W:xmm[31:0], xmm[31:0], xmm[31:0]/m32, xmm[31:0]", "RVMS" , "VEX.128.66.0F3A.W0 7A /r /is4", "FMA4"], ["vfnmsubpd" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 7D /r /is4", "FMA4"], ["vfnmsubpd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 7D /r /is4", "FMA4"], ["vfnmsubpd" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 7D /r /is4", "FMA4"], ["vfnmsubpd" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 7D /r /is4", "FMA4"], ["vfnmsubps" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "VEX.128.66.0F3A.W1 7C /r /is4", "FMA4"], ["vfnmsubps" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "VEX.128.66.0F3A.W0 7C /r /is4", "FMA4"], ["vfnmsubps" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "VEX.256.66.0F3A.W1 7C /r /is4", "FMA4"], ["vfnmsubps" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "VEX.256.66.0F3A.W0 7C /r /is4", "FMA4"], ["vfnmsubsd" , "W:xmm[63:0], xmm[63:0], xmm[63:0], xmm[63:0]/m64", "RVSM" , "VEX.128.66.0F3A.W1 7F /r /is4", "FMA4"], ["vfnmsubsd" , "W:xmm[63:0], xmm[63:0], xmm[63:0]/m64, xmm[63:0]", "RVMS" , "VEX.128.66.0F3A.W0 7F /r /is4", "FMA4"], ["vfnmsubss" , "W:xmm[31:0], xmm[31:0], xmm[31:0], xmm[31:0]/m32", "RVSM" , "VEX.128.66.0F3A.W1 7E /r /is4", "FMA4"], ["vfnmsubss" , "W:xmm[31:0], xmm[31:0], xmm[31:0]/m32, xmm[31:0]", "RVMS" , "VEX.128.66.0F3A.W0 7E /r /is4", "FMA4"], ["vfrczpd" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 81 /r" , "XOP"], ["vfrczpd" , "W:ymm, ymm/m256" , "RM" , "XOP.L1.P0.M09.W0 81 /r" , "XOP"], ["vfrczps" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 80 /r" , "XOP"], ["vfrczps" , "W:ymm, ymm/m256" , "RM" , "XOP.L1.P0.M09.W0 80 /r" , "XOP"], ["vfrczsd" , "W:xmm[63:0], xmm[63:0]/m64" , "RM" , "XOP.L0.P0.M09.W0 83 /r" , "XOP"], ["vfrczss" , "W:xmm[31:0], xmm[31:0]/m32" , "RM" , "XOP.L0.P0.M09.W0 82 /r" , "XOP"], ["vpcmov" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "XOP.L0.P0.M08.W1 A2 /r /is4" , "XOP"], ["vpcmov" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 A2 /r /is4" , "XOP"], ["vpcmov" , "W:ymm, ymm, ymm, ymm/m256" , "RVSM" , "XOP.L1.P0.M08.W1 A2 /r /is4" , "XOP"], ["vpcmov" , "W:ymm, ymm, ymm/m256, ymm" , "RVMS" , "XOP.L1.P0.M08.W0 A2 /r /is4" , "XOP"], ["vpcomb" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "XOP.L0.P0.M08.W0 CC /r ib" , "XOP"], ["vpcomd" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "XOP.L0.P0.M08.W0 CE /r ib" , "XOP"], ["vpcomq" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "XOP.L0.P0.M08.W0 CF /r ib" , "XOP"], ["vpcomub" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "XOP.L0.P0.M08.W0 EC /r ib" , "XOP"], ["vpcomud" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "XOP.L0.P0.M08.W0 EE /r ib" , "XOP"], ["vpcomuq" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "XOP.L0.P0.M08.W0 EF /r ib" , "XOP"], ["vpcomuw" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "XOP.L0.P0.M08.W0 ED /r ib" , "XOP"], ["vpcomw" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI" , "XOP.L0.P0.M08.W0 CD /r ib" , "XOP"], ["vpermil2pd" , "W:xmm, xmm, xmm/m128, xmm, i4/u4" , "RVMSI" , "VEX.L0.66.0F3A.W0 49 /r /is4" , "XOP"], ["vpermil2pd" , "W:xmm, xmm, xmm, xmm/m128, i4/u4" , "RVSMI" , "VEX.L0.66.0F3A.W1 49 /r /is4" , "XOP"], ["vpermil2pd" , "W:ymm, ymm, ymm/m256, ymm, i4/u4" , "RVMSI" , "VEX.L1.66.0F3A.W0 49 /r /is4" , "XOP"], ["vpermil2pd" , "W:ymm, ymm, ymm, ymm/m256, i4/u4" , "RVSMI" , "VEX.L1.66.0F3A.W1 49 /r /is4" , "XOP"], ["vpermil2ps" , "W:xmm, xmm, xmm/m128, xmm, i4/u4" , "RVMSI" , "VEX.L0.66.0F3A.W0 48 /r /is4" , "XOP"], ["vpermil2ps" , "W:xmm, xmm, xmm, xmm/m128, i4/u4" , "RVSMI" , "VEX.L0.66.0F3A.W1 48 /r /is4" , "XOP"], ["vpermil2ps" , "W:ymm, ymm, ymm/m256, ymm, i4/u4" , "RVMSI" , "VEX.L1.66.0F3A.W0 48 /r /is4" , "XOP"], ["vpermil2ps" , "W:ymm, ymm, ymm, ymm/m256, i4/u4" , "RVSMI" , "VEX.L1.66.0F3A.W1 48 /r /is4" , "XOP"], ["vphaddbd" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 C2 /r" , "XOP"], ["vphaddbq" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 C3 /r" , "XOP"], ["vphaddbw" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 C1 /r" , "XOP"], ["vphadddq" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 CB /r" , "XOP"], ["vphaddubd" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 D2 /r" , "XOP"], ["vphaddubq" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 D3 /r" , "XOP"], ["vphaddubw" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 D1 /r" , "XOP"], ["vphaddudq" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 DB /r" , "XOP"], ["vphadduwd" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 D6 /r" , "XOP"], ["vphadduwq" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 D7 /r" , "XOP"], ["vphaddwd" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 C6 /r" , "XOP"], ["vphaddwq" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 C7 /r" , "XOP"], ["vphsubbw" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 E1 /r" , "XOP"], ["vphsubdq" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 E3 /r" , "XOP"], ["vphsubwd" , "W:xmm, xmm/m128" , "RM" , "XOP.L0.P0.M09.W0 E2 /r" , "XOP"], ["vpmacsdd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 9E /r /is4" , "XOP"], ["vpmacsdqh" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 9F /r /is4" , "XOP"], ["vpmacsdql" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 97 /r /is4" , "XOP"], ["vpmacssdd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 8E /r /is4" , "XOP"], ["vpmacssdqh" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 8F /r /is4" , "XOP"], ["vpmacssdql" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 87 /r /is4" , "XOP"], ["vpmacsswd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 86 /r /is4" , "XOP"], ["vpmacssww" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 85 /r /is4" , "XOP"], ["vpmacswd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 96 /r /is4" , "XOP"], ["vpmacsww" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 95 /r /is4" , "XOP"], ["vpmadcsswd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 A6 /r /is4" , "XOP"], ["vpmadcswd" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 B6 /r /is4" , "XOP"], ["vpperm" , "W:xmm, xmm, xmm, xmm/m128" , "RVSM" , "XOP.L0.P0.M08.W1 A3 /r /is4" , "XOP"], ["vpperm" , "W:xmm, xmm, xmm/m128, xmm" , "RVMS" , "XOP.L0.P0.M08.W0 A3 /r /is4" , "XOP"], ["vprotb" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 90 /r" , "XOP"], ["vprotb" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "XOP.L0.P0.M08.W0 C0 /r ib" , "XOP"], ["vprotb" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 90 /r" , "XOP"], ["vprotd" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 92 /r" , "XOP"], ["vprotd" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "XOP.L0.P0.M08.W0 C2 /r ib" , "XOP"], ["vprotd" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 92 /r" , "XOP"], ["vprotq" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 93 /r" , "XOP"], ["vprotq" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "XOP.L0.P0.M08.W0 C3 /r ib" , "XOP"], ["vprotq" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 93 /r" , "XOP"], ["vprotw" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 91 /r" , "XOP"], ["vprotw" , "W:xmm, xmm/m128, ib/ub" , "RMI" , "XOP.L0.P0.M08.W0 C1 /r ib" , "XOP"], ["vprotw" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 91 /r" , "XOP"], ["vpshab" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 98 /r" , "XOP"], ["vpshab" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 98 /r" , "XOP"], ["vpshad" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 9A /r" , "XOP"], ["vpshad" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 9A /r" , "XOP"], ["vpshaq" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 9B /r" , "XOP"], ["vpshaq" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 9B /r" , "XOP"], ["vpshaw" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 99 /r" , "XOP"], ["vpshaw" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 99 /r" , "XOP"], ["vpshlb" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 94 /r" , "XOP"], ["vpshlb" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 94 /r" , "XOP"], ["vpshld" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 96 /r" , "XOP"], ["vpshld" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 96 /r" , "XOP"], ["vpshlq" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 97 /r" , "XOP"], ["vpshlq" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 97 /r" , "XOP"], ["vpshlw" , "W:xmm, xmm, xmm/m128" , "RVM" , "XOP.L0.P0.M09.W1 95 /r" , "XOP"], ["vpshlw" , "W:xmm, xmm/m128, xmm" , "RMV" , "XOP.L0.P0.M09.W0 95 /r" , "XOP"], ["vpdpbusd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 50 /r" , "AVX_VNNI"], ["vpdpbusd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 50 /r" , "AVX_VNNI"], ["vpdpbusds" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 51 /r" , "AVX_VNNI"], ["vpdpbusds" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 51 /r" , "AVX_VNNI"], ["vpdpwssd" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 52 /r" , "AVX_VNNI"], ["vpdpwssd" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 52 /r" , "AVX_VNNI"], ["vpdpwssds" , "X:xmm, xmm, xmm/m128" , "RVM" , "VEX.128.66.0F38.W0 53 /r" , "AVX_VNNI"], ["vpdpwssds" , "X:ymm, ymm, ymm/m256" , "RVM" , "VEX.256.66.0F38.W0 53 /r" , "AVX_VNNI"], ["kaddb" , "W:k[7:0] ,~k[7:0] ,~k[7:0]" , "RVM" , "VEX.L1.66.0F.W0 4A /r" , "AVX512_DQ"], ["kaddd" , "W:k[31:0],~k[31:0],~k[31:0]" , "RVM" , "VEX.L1.66.0F.W1 4A /r" , "AVX512_BW"], ["kaddq" , "W:k[63:0],~k[63:0],~k[63:0]" , "RVM" , "VEX.L1.0F.W1 4A /r" , "AVX512_BW"], ["kaddw" , "W:k[15:0],~k[15:0],~k[15:0]" , "RVM" , "VEX.L1.0F.W0 4A /r" , "AVX512_DQ"], ["kandb" , "W:k[7:0] ,~k[7:0] ,~k[7:0]" , "RVM" , "VEX.L1.66.0F.W0 41 /r" , "AVX512_DQ"], ["kandd" , "W:k[31:0],~k[31:0],~k[31:0]" , "RVM" , "VEX.L1.66.0F.W1 41 /r" , "AVX512_BW"], ["kandnb" , "W:k[7:0] , k[7:0] , k[7:0]" , "RVM" , "VEX.L1.66.0F.W0 42 /r" , "AVX512_DQ"], ["kandnd" , "W:k[31:0], k[31:0], k[31:0]" , "RVM" , "VEX.L1.66.0F.W1 42 /r" , "AVX512_BW"], ["kandnq" , "W:k[63:0], k[63:0], k[63:0]" , "RVM" , "VEX.L1.0F.W1 42 /r" , "AVX512_BW"], ["kandnw" , "W:k[15:0], k[15:0], k[15:0]" , "RVM" , "VEX.L1.0F.W0 42 /r" , "AVX512_F"], ["kandq" , "W:k[63:0],~k[63:0],~k[63:0]" , "RVM" , "VEX.L1.0F.W1 41 /r" , "AVX512_BW"], ["kandw" , "W:k[15:0],~k[15:0],~k[15:0]" , "RVM" , "VEX.L1.0F.W0 41 /r" , "AVX512_F"], ["kmovb" , "W:k[7:0], k[7:0]/m8" , "RM" , "VEX.L0.66.0F.W0 90 /r" , "AVX512_DQ"], ["kmovb" , "W:k[7:0], r32[7:0]" , "RM" , "VEX.L0.66.0F.W0 92 /r" , "AVX512_DQ"], ["kmovb" , "W:m8, k[7:0]" , "MR" , "VEX.L0.66.0F.W0 91 /r" , "AVX512_DQ"], ["kmovb" , "W:r32[7:0], k[7:0]" , "RM" , "VEX.L0.66.0F.W0 93 /r" , "AVX512_DQ"], ["kmovd" , "W:k[31:0], k[31:0]/m32" , "RM" , "VEX.L0.66.0F.W1 90 /r" , "AVX512_BW"], ["kmovd" , "W:k[31:0], r32[31:0]" , "RM" , "VEX.L0.F2.0F.W0 92 /r" , "AVX512_BW"], ["kmovd" , "W:m32, k[31:0]" , "MR" , "VEX.L0.66.0F.W1 91 /r" , "AVX512_BW"], ["kmovd" , "W:r32[31:0], k[31:0]" , "RM" , "VEX.L0.F2.0F.W0 93 /r" , "AVX512_BW"], ["kmovq" , "W:k[63:0], k[63:0]/m64" , "RM" , "VEX.L0.0F.W1 90 /r" , "AVX512_BW"], ["kmovq" , "W:k[63:0], r64" , "RM" , "VEX.L0.F2.0F.W1 92 /r" , "AVX512_BW X64"], ["kmovq" , "W:m64, k[63:0]" , "MR" , "VEX.L0.0F.W1 91 /r" , "AVX512_BW"], ["kmovq" , "W:r64, k[63:0]" , "RM" , "VEX.L0.F2.0F.W1 93 /r" , "AVX512_BW X64"], ["kmovw" , "W:k[15:0], k[15:0]/m16" , "RM" , "VEX.L0.0F.W0 90 /r" , "AVX512_F"], ["kmovw" , "W:k[15:0], r32[15:0]" , "RM" , "VEX.L0.0F.W0 92 /r" , "AVX512_F"], ["kmovw" , "W:m16, k[15:0]" , "MR" , "VEX.L0.0F.W0 91 /r" , "AVX512_F"], ["kmovw" , "W:r32[15:0], k[15:0]" , "RM" , "VEX.L0.0F.W0 93 /r" , "AVX512_F"], ["knotb" , "W:k[7:0] , k[7:0]" , "RM" , "VEX.L0.66.0F.W0 44 /r" , "AVX512_DQ"], ["knotd" , "W:k[31:0], k[31:0]" , "RM" , "VEX.L0.66.0F.W1 44 /r" , "AVX512_BW"], ["knotq" , "W:k[63:0] , k[63:0]" , "RM" , "VEX.L0.0F.W1 44 /r" , "AVX512_BW"], ["knotw" , "W:k[15:0], k[15:0]" , "RM" , "VEX.L0.0F.W0 44 /r" , "AVX512_F"], ["korb" , "W:k[7:0] , ~k[7:0] , ~k[7:0]" , "RVM" , "VEX.L1.66.0F.W0 45 /r" , "AVX512_DQ"], ["kord" , "W:k[31:0], ~k[31:0], ~k[31:0]" , "RVM" , "VEX.L1.66.0F.W1 45 /r" , "AVX512_BW"], ["korq" , "W:k[63:0], ~k[63:0], ~k[63:0]" , "RVM" , "VEX.L1.0F.W1 45 /r" , "AVX512_BW"], ["kortestb" , "R:~k[7:0] , ~k[7:0]" , "RM" , "VEX.L0.66.0F.W0 98 /r" , "AVX512_DQ OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["kortestd" , "R:~k[31:0], ~k[31:0]" , "RM" , "VEX.L0.66.0F.W1 98 /r" , "AVX512_BW OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["kortestq" , "R:~k[63:0], ~k[63:0]" , "RM" , "VEX.L0.0F.W1 98 /r" , "AVX512_BW OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["kortestw" , "R:~k[15:0], ~k[15:0]" , "RM" , "VEX.L0.0F.W0 98 /r" , "AVX512_F OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["korw" , "W:k[15:0], ~k[15:0], ~k[15:0]" , "RVM" , "VEX.L1.0F.W0 45 /r" , "AVX512_F"], ["kshiftlb" , "W:k[7:0] , k[7:0] , ib/ub" , "RMI" , "VEX.L0.66.0F3A.W0 32 /r ib" , "AVX512_DQ"], ["kshiftld" , "W:k[31:0], k[31:0], ib/ub" , "RMI" , "VEX.L0.66.0F3A.W0 33 /r ib" , "AVX512_BW"], ["kshiftlq" , "W:k[63:0], k[63:0] , ib/ub" , "RMI" , "VEX.L0.66.0F3A.W1 33 /r ib" , "AVX512_BW"], ["kshiftlw" , "W:k[15:0], k[15:0], ib/ub" , "RMI" , "VEX.L0.66.0F3A.W1 32 /r ib" , "AVX512_F"], ["kshiftrb" , "W:k[7:0] , k[7:0] , ib/ub" , "RMI" , "VEX.L0.66.0F3A.W0 30 /r ib" , "AVX512_DQ"], ["kshiftrd" , "W:k[31:0], k[31:0], ib/ub" , "RMI" , "VEX.L0.66.0F3A.W0 31 /r ib" , "AVX512_BW"], ["kshiftrq" , "W:k[63:0], k[63:0], ib/ub" , "RMI" , "VEX.L0.66.0F3A.W1 31 /r ib" , "AVX512_BW"], ["kshiftrw" , "W:k[15:0], k[15:0], ib/ub" , "RMI" , "VEX.L0.66.0F3A.W1 30 /r ib" , "AVX512_F"], ["ktestb" , "R:~k[7:0] , ~k[7:0]" , "RM" , "VEX.L0.66.0F.W0 99 /r" , "AVX512_DQ OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["ktestd" , "R:~k[31:0], ~k[31:0]" , "RM" , "VEX.L0.66.0F.W1 99 /r" , "AVX512_BW OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["ktestq" , "R:~k[63:0] , ~k[63:0]" , "RM" , "VEX.L0.0F.W1 99 /r" , "AVX512_BW OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["ktestw" , "R:~k[15:0], ~k[15:0]" , "RM" , "VEX.L0.0F.W0 99 /r" , "AVX512_DQ OF=0 SF=0 ZF=W AF=0 PF=0 CF=W"], ["kunpckbw" , "W:k[15:0], k[7:0] , k[7:0]" , "RVM" , "VEX.L1.66.0F.W0 4B /r" , "AVX512_F"], ["kunpckdq" , "W:k[63:0], k[31:0], k[31:0]" , "RVM" , "VEX.L1.0F.W1 4B /r" , "AVX512_BW"], ["kunpckwd" , "W:k[31:0], k[15:0], k[15:0]" , "RVM" , "VEX.L1.0F.W0 4B /r" , "AVX512_BW"], ["kxnorb" , "W:k[7:0] , k[7:0] , k[7:0]" , "RVM" , "VEX.L1.66.0F.W0 46 /r" , "AVX512_DQ"], ["kxnord" , "W:k[31:0], k[31:0], k[31:0]" , "RVM" , "VEX.L1.66.0F.W1 46 /r" , "AVX512_BW"], ["kxnorq" , "W:k[63:0], k[63:0], k[63:0]" , "RVM" , "VEX.L1.0F.W1 46 /r" , "AVX512_BW"], ["kxnorw" , "W:k[15:0], k[15:0], k[15:0]" , "RVM" , "VEX.L1.0F.W0 46 /r" , "AVX512_F"], ["kxorb" , "W:k[7:0] ,~k[7:0] ,~k[7:0]" , "RVM" , "VEX.L1.66.0F.W0 47 /r" , "AVX512_DQ"], ["kxord" , "W:k[31:0],~k[31:0],~k[31:0]" , "RVM" , "VEX.L1.66.0F.W1 47 /r" , "AVX512_BW"], ["kxorq" , "W:k[63:0],~k[63:0],~k[63:0]" , "RVM" , "VEX.L1.0F.W1 47 /r" , "AVX512_BW"], ["kxorw" , "W:k[15:0],~k[15:0],~k[15:0]" , "RVM" , "VEX.L1.0F.W0 47 /r" , "AVX512_F"], ["v4fmaddps" , "X:zmm {kz}, zmm, zmm+1, zmm+2, zmm+3, m128" , "RM-T1_4X", "EVEX.512.F2.0F38.W0 9A /r" , "AVX512_4FMAPS"], ["v4fmaddss" , "X:xmm {kz}, xmm, xmm+1, xmm+2, xmm+3, m128" , "RM-T1_4X", "EVEX.LIG.F2.0F38.W0 9B /r" , "AVX512_4FMAPS"], ["v4fnmaddps" , "X:zmm {kz}, zmm, zmm+1, zmm+2, zmm+3, m128" , "RM-T1_4X", "EVEX.512.F2.0F38.W0 AA /r" , "AVX512_4FMAPS"], ["v4fnmaddss" , "X:xmm {kz}, xmm, xmm+1, xmm+2, xmm+3, m128" , "RM-T1_4X", "EVEX.LIG.F2.0F38.W0 AB /r" , "AVX512_4FMAPS"], ["vaddpd" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 58 /r" , "AVX512_F-VL"], ["vaddpd" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 58 /r" , "AVX512_F-VL"], ["vaddpd" , "W:zmm {kz},~zmm,~zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F.W1 58 /r" , "AVX512_F"], ["vaddps" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 58 /r" , "AVX512_F-VL"], ["vaddps" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 58 /r" , "AVX512_F-VL"], ["vaddps" , "W:zmm {kz},~zmm,~zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.0F.W0 58 /r" , "AVX512_F"], ["vaddsd" , "W:xmm {kz}, xmm, xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 58 /r" , "AVX512_F"], ["vaddss" , "W:xmm {kz}, xmm, xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 58 /r" , "AVX512_F"], ["vaesdec" , "W:xmm, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG DE /r" , "AVX512_F-VL VAES"], ["vaesdec" , "W:ymm, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG DE /r" , "AVX512_F-VL VAES"], ["vaesdec" , "W:zmm, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG DE /r" , "AVX512_F VAES"], ["vaesdeclast" , "W:xmm, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG DF /r" , "AVX512_F-VL VAES"], ["vaesdeclast" , "W:ymm, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG DF /r" , "AVX512_F-VL VAES"], ["vaesdeclast" , "W:zmm, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG DF /r" , "AVX512_F VAES"], ["vaesenc" , "W:xmm, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG DC /r" , "AVX512_F-VL VAES"], ["vaesenc" , "W:ymm, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG DC /r" , "AVX512_F-VL VAES"], ["vaesenc" , "W:zmm, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG DC /r" , "AVX512_F VAES"], ["vaesenclast" , "W:xmm, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG DD /r" , "AVX512_F-VL VAES"], ["vaesenclast" , "W:ymm, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG DD /r" , "AVX512_F-VL VAES"], ["vaesenclast" , "W:zmm, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG DD /r" , "AVX512_F VAES"], ["valignd" , "W:xmm {kz}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W0 03 /r ib" , "AVX512_F-VL"], ["valignd" , "W:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W0 03 /r ib" , "AVX512_F-VL"], ["valignd" , "W:zmm {kz}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W0 03 /r ib" , "AVX512_F"], ["valignq" , "W:xmm {kz}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W1 03 /r ib" , "AVX512_F-VL"], ["valignq" , "W:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 03 /r ib" , "AVX512_F-VL"], ["valignq" , "W:zmm {kz}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 03 /r ib" , "AVX512_F"], ["vandnpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 55 /r" , "AVX512_DQ-VL"], ["vandnpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 55 /r" , "AVX512_DQ-VL"], ["vandnpd" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 55 /r" , "AVX512_DQ"], ["vandnps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.W0 55 /r" , "AVX512_DQ-VL"], ["vandnps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.W0 55 /r" , "AVX512_DQ-VL"], ["vandnps" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.W0 55 /r" , "AVX512_DQ"], ["vandpd" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 54 /r" , "AVX512_DQ-VL"], ["vandpd" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 54 /r" , "AVX512_DQ-VL"], ["vandpd" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 54 /r" , "AVX512_DQ"], ["vandps" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 54 /r" , "AVX512_DQ-VL"], ["vandps" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 54 /r" , "AVX512_DQ-VL"], ["vandps" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.0F.W0 54 /r" , "AVX512_DQ"], ["vblendmpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 65 /r" , "AVX512_F-VL"], ["vblendmpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 65 /r" , "AVX512_F-VL"], ["vblendmpd" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 65 /r" , "AVX512_F"], ["vblendmps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 65 /r" , "AVX512_F-VL"], ["vblendmps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 65 /r" , "AVX512_F-VL"], ["vblendmps" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 65 /r" , "AVX512_F"], ["vbroadcastf32x2" , "W:ymm {kz}, xmm[63:0]/m64" , "RM-T2" , "EVEX.256.66.0F38.W0 19 /r" , "AVX512_DQ-VL"], ["vbroadcastf32x2" , "W:zmm {kz}, xmm[63:0]/m64" , "RM-T2" , "EVEX.512.66.0F38.W0 19 /r" , "AVX512_DQ"], ["vbroadcastf32x4" , "W:ymm {kz}, m128" , "RM-T4" , "EVEX.256.66.0F38.W0 1A /r" , "AVX512_F"], ["vbroadcastf32x4" , "W:zmm {kz}, m128" , "RM-T4" , "EVEX.512.66.0F38.W0 1A /r" , "AVX512_F"], ["vbroadcastf32x8" , "W:zmm {kz}, m256" , "RM-T8" , "EVEX.512.66.0F38.W0 1B /r" , "AVX512_DQ"], ["vbroadcastf64x2" , "W:ymm {kz}, m128" , "RM-T2" , "EVEX.256.66.0F38.W1 1A /r" , "AVX512_DQ-VL"], ["vbroadcastf64x2" , "W:zmm {kz}, m128" , "RM-T2" , "EVEX.512.66.0F38.W1 1A /r" , "AVX512_DQ"], ["vbroadcastf64x4" , "W:zmm {kz}, m256" , "RM-T4" , "EVEX.512.66.0F38.W1 1B /r" , "AVX512_F"], ["vbroadcasti32x2" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-T2" , "EVEX.128.66.0F38.W0 59 /r" , "AVX512_DQ-VL"], ["vbroadcasti32x2" , "W:ymm {kz}, xmm[63:0]/m64" , "RM-T2" , "EVEX.256.66.0F38.W0 59 /r" , "AVX512_DQ-VL"], ["vbroadcasti32x2" , "W:zmm {kz}, xmm[63:0]/m64" , "RM-T2" , "EVEX.512.66.0F38.W0 59 /r" , "AVX512_DQ"], ["vbroadcasti32x4" , "W:ymm {kz}, m128" , "RM-T4" , "EVEX.256.66.0F38.W0 5A /r" , "AVX512_F-VL"], ["vbroadcasti32x4" , "W:zmm {kz}, m128" , "RM-T4" , "EVEX.512.66.0F38.W0 5A /r" , "AVX512_F"], ["vbroadcasti32x8" , "W:zmm {kz}, m256" , "RM-T8" , "EVEX.512.66.0F38.W0 5B /r" , "AVX512_DQ"], ["vbroadcasti64x2" , "W:ymm {kz}, m128" , "RM-T2" , "EVEX.256.66.0F38.W1 5A /r" , "AVX512_DQ-VL"], ["vbroadcasti64x2" , "W:zmm {kz}, m128" , "RM-T2" , "EVEX.512.66.0F38.W1 5A /r" , "AVX512_DQ"], ["vbroadcasti64x4" , "W:zmm {kz}, m256" , "RM-T4" , "EVEX.512.66.0F38.W1 5B /r" , "AVX512_F"], ["vbroadcastsd" , "W:ymm {kz}, xmm[63:0]/m64" , "RM-T1S" , "EVEX.256.66.0F38.W1 19 /r" , "AVX512_F-VL"], ["vbroadcastsd" , "W:zmm {kz}, xmm[63:0]/m64" , "RM-T1S" , "EVEX.512.66.0F38.W1 19 /r" , "AVX512_F"], ["vbroadcastss" , "W:xmm {kz}, xmm[31:0]/m32" , "RM-T1S" , "EVEX.128.66.0F38.W0 18 /r" , "AVX512_F-VL"], ["vbroadcastss" , "W:ymm {kz}, xmm[31:0]/m32" , "RM-T1S" , "EVEX.256.66.0F38.W0 18 /r" , "AVX512_F-VL"], ["vbroadcastss" , "W:zmm {kz}, xmm[31:0]/m32" , "RM-T1S" , "EVEX.512.66.0F38.W0 18 /r" , "AVX512_F"], ["vcmppd" , "W:k {kz}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F.W1 C2 /r ib" , "AVX512_F-VL"], ["vcmppd" , "W:k {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F.W1 C2 /r ib" , "AVX512_F-VL"], ["vcmppd" , "W:k {kz}, zmm, zmm/m512/b64, ib/ub {sae}" , "RVMI-FV" , "EVEX.512.66.0F.W1 C2 /r ib" , "AVX512_F"], ["vcmpps" , "W:k {kz}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FV" , "EVEX.128.0F.W0 C2 /r ib" , "AVX512_F-VL"], ["vcmpps" , "W:k {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.0F.W0 C2 /r ib" , "AVX512_F-VL"], ["vcmpps" , "W:k {kz}, zmm, zmm/m512/b32, ib/ub {sae}" , "RVMI-FV" , "EVEX.512.0F.W0 C2 /r ib" , "AVX512_F"], ["vcmpsd" , "W:k {kz}, xmm, xmm[63:0]/m64, ib/ub {sae}" , "RVMI-T1S", "EVEX.LIG.F2.0F.W1 C2 /r ib" , "AVX512_F"], ["vcmpss" , "W:k {kz}, xmm, xmm[31:0]/m32, ib/ub {sae}" , "RVMI-T1S", "EVEX.LIG.F3.0F.W0 C2 /r ib" , "AVX512_F"], ["vcomisd" , "R:xmm[63:0], xmm[63:0]/m64 {sae}" , "RM-T1S" , "EVEX.LIG.66.0F.W1 2F /r" , "AVX512_F OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["vcomiss" , "R:xmm[31:0], xmm[31:0]/m32 {sae}" , "RM-T1S" , "EVEX.LIG.0F.W0 2F /r" , "AVX512_F OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["vcompresspd" , "W:xmm/m128 {kz}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W1 8A /r" , "AVX512_F-VL"], ["vcompresspd" , "W:ymm/m256 {kz}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W1 8A /r" , "AVX512_F-VL"], ["vcompresspd" , "W:zmm/m512 {kz}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W1 8A /r" , "AVX512_F"], ["vcompressps" , "W:xmm/m128 {kz}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W0 8A /r" , "AVX512_F-VL"], ["vcompressps" , "W:ymm/m256 {kz}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W0 8A /r" , "AVX512_F-VL"], ["vcompressps" , "W:zmm/m512 {kz}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W0 8A /r" , "AVX512_F"], ["vcvtdq2pd" , "W:xmm {kz}, xmm[63:0]/m64/b32" , "RM-HV" , "EVEX.128.F3.0F.W0 E6 /r" , "AVX512_F-VL"], ["vcvtdq2pd" , "W:ymm {kz}, xmm/m128/b32" , "RM-HV" , "EVEX.256.F3.0F.W0 E6 /r" , "AVX512_F-VL"], ["vcvtdq2pd" , "W:zmm {kz}, ymm/m256/b32" , "RM-HV" , "EVEX.512.F3.0F.W0 E6 /r" , "AVX512_F"], ["vcvtdq2ps" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.0F.W0 5B /r" , "AVX512_F-VL"], ["vcvtdq2ps" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.0F.W0 5B /r" , "AVX512_F-VL"], ["vcvtdq2ps" , "W:zmm {kz}, zmm/m512/b32 {er}" , "RM-FV" , "EVEX.512.0F.W0 5B /r" , "AVX512_F"], ["vcvtne2ps2bf16" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.F2.0F38.W0 72 /r" , "AVX512_BF16-VL"], ["vcvtne2ps2bf16" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.F2.0F38.W0 72 /r" , "AVX512_BF16-VL"], ["vcvtne2ps2bf16" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.F2.0F38.W0 72 /r" , "AVX512_BF16-VL"], ["vcvtneps2bf16" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.F3.0F38.W0 72 /r" , "AVX512_BF16-VL"], ["vcvtneps2bf16" , "W:xmm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.F3.0F38.W0 72 /r" , "AVX512_BF16-VL"], ["vcvtneps2bf16" , "W:ymm {kz}, zmm/m512/b32" , "RM-FV" , "EVEX.512.F3.0F38.W0 72 /r" , "AVX512_BF16-VL"], ["vcvtpd2ps" , "W:xmm[63:0] {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F.W1 5A /r" , "AVX512_F-VL"], ["vcvtpd2ps" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F.W1 5A /r" , "AVX512_F-VL"], ["vcvtpd2ps" , "W:ymm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.66.0F.W1 5A /r" , "AVX512_F"], ["vcvtpd2dq" , "W:xmm[63:0] {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.F2.0F.W1 E6 /r" , "AVX512_F-VL"], ["vcvtpd2dq" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.F2.0F.W1 E6 /r" , "AVX512_F-VL"], ["vcvtpd2dq" , "W:ymm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.F2.0F.W1 E6 /r" , "AVX512_F"], ["vcvtpd2qq" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F.W1 7B /r" , "AVX512_DQ-VL"], ["vcvtpd2qq" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F.W1 7B /r" , "AVX512_DQ-VL"], ["vcvtpd2qq" , "W:zmm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.66.0F.W1 7B /r" , "AVX512_DQ"], ["vcvtpd2udq" , "W:xmm[63:0] {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.0F.W1 79 /r" , "AVX512_F-VL"], ["vcvtpd2udq" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.0F.W1 79 /r" , "AVX512_F-VL"], ["vcvtpd2udq" , "W:ymm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.0F.W1 79 /r" , "AVX512_F"], ["vcvtpd2uqq" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F.W1 79 /r" , "AVX512_DQ-VL"], ["vcvtpd2uqq" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F.W1 79 /r" , "AVX512_DQ-VL"], ["vcvtpd2uqq" , "W:zmm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.66.0F.W1 79 /r" , "AVX512_DQ"], ["vcvtph2ps" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-HVM" , "EVEX.128.66.0F38.W0 13 /r" , "AVX512_F-VL"], ["vcvtph2ps" , "W:ymm {kz}, xmm/m128" , "RM-HVM" , "EVEX.256.66.0F38.W0 13 /r" , "AVX512_F-VL"], ["vcvtph2ps" , "W:zmm {kz}, ymm/m256 {sae}" , "RM-HVM" , "EVEX.512.66.0F38.W0 13 /r" , "AVX512_F"], ["vcvtps2dq" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.0F.W0 5B /r" , "AVX512_F-VL"], ["vcvtps2dq" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.0F.W0 5B /r" , "AVX512_F-VL"], ["vcvtps2dq" , "W:zmm {kz}, zmm/m512/b32 {er}" , "RM-FV" , "EVEX.512.66.0F.W0 5B /r" , "AVX512_F"], ["vcvtps2pd" , "W:xmm {kz}, xmm[63:0]/m64/b32" , "RM-HV" , "EVEX.128.0F.W0 5A /r" , "AVX512_F-VL"], ["vcvtps2pd" , "W:ymm {kz}, xmm/m128/b32" , "RM-HV" , "EVEX.256.0F.W0 5A /r" , "AVX512_F-VL"], ["vcvtps2pd" , "W:zmm {kz}, ymm/m256/b32 {er}" , "RM-HV" , "EVEX.512.0F.W0 5A /r" , "AVX512_F"], ["vcvtps2ph" , "W:xmm[63:0]/m64 {kz}, xmm, ib/ub" , "MRI-HVM" , "EVEX.128.66.0F3A.W0 1D /r ib" , "AVX512_F-VL"], ["vcvtps2ph" , "W:xmm/m128 {kz}, ymm, ib/ub" , "MRI-HVM" , "EVEX.256.66.0F3A.W0 1D /r ib" , "AVX512_F-VL"], ["vcvtps2ph" , "W:ymm/m256 {kz}, zmm, ib/ub {sae}" , "MRI-HVM" , "EVEX.512.66.0F3A.W0 1D /r ib" , "AVX512_F"], ["vcvtps2qq" , "W:xmm {kz}, xmm[63:0]/m64/b32" , "RM-HV" , "EVEX.128.66.0F.W0 7B /r" , "AVX512_DQ-VL"], ["vcvtps2qq" , "W:ymm {kz}, xmm/m128/b32" , "RM-HV" , "EVEX.256.66.0F.W0 7B /r" , "AVX512_DQ-VL"], ["vcvtps2qq" , "W:zmm {kz}, ymm/m256/b32 {er}" , "RM-HV" , "EVEX.512.66.0F.W0 7B /r" , "AVX512_DQ"], ["vcvtps2udq" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.0F.W0 79 /r" , "AVX512_F-VL"], ["vcvtps2udq" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.0F.W0 79 /r" , "AVX512_F-VL"], ["vcvtps2udq" , "W:zmm {kz}, zmm/m512/b32 {er}" , "RM-FV" , "EVEX.512.0F.W0 79 /r" , "AVX512_F"], ["vcvtps2uqq" , "W:xmm {kz}, xmm[63:0]/m64/b32" , "RM-HV" , "EVEX.128.66.0F.W0 79 /r" , "AVX512_DQ-VL"], ["vcvtps2uqq" , "W:ymm {kz}, xmm/m128/b32" , "RM-HV" , "EVEX.256.66.0F.W0 79 /r" , "AVX512_DQ-VL"], ["vcvtps2uqq" , "W:zmm {kz}, ymm/m256/b32 {er}" , "RM-HV" , "EVEX.512.66.0F.W0 79 /r" , "AVX512_DQ"], ["vcvtqq2pd" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.F3.0F.W1 E6 /r" , "AVX512_DQ-VL"], ["vcvtqq2pd" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.F3.0F.W1 E6 /r" , "AVX512_DQ-VL"], ["vcvtqq2pd" , "W:zmm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.F3.0F.W1 E6 /r" , "AVX512_DQ"], ["vcvtqq2ps" , "W:xmm[63:0] {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.0F.W1 5B /r" , "AVX512_DQ-VL"], ["vcvtqq2ps" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.0F.W1 5B /r" , "AVX512_DQ-VL"], ["vcvtqq2ps" , "W:ymm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.0F.W1 5B /r" , "AVX512_DQ"], ["vcvtsd2si" , "W:r32, xmm[63:0]/m64 {er}" , "RM-T1F" , "EVEX.LIG.F2.0F.W0 2D /r" , "AVX512_F"], ["vcvtsd2si" , "W:r64, xmm[63:0]/m64 {er}" , "RM-T1F" , "EVEX.LIG.F2.0F.W1 2D /r" , "AVX512_F X64"], ["vcvtsd2ss" , "W:xmm {kz}, xmm[127:32], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 5A /r" , "AVX512_F"], ["vcvtsd2usi" , "W:r32, xmm[63:0]/m64 {er}" , "RM-T1F" , "EVEX.LIG.F2.0F.W0 79 /r" , "AVX512_F"], ["vcvtsd2usi" , "W:r64, xmm[63:0]/m64 {er}" , "RM-T1F" , "EVEX.LIG.F2.0F.W1 79 /r" , "AVX512_F X64"], ["vcvtsi2sd" , "W:xmm, xmm[127:64], r32/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W0 2A /r" , "AVX512_F"], ["vcvtsi2sd" , "W:xmm, xmm[127:64], r64/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 2A /r" , "AVX512_F X64"], ["vcvtsi2ss" , "W:xmm, xmm[127:32], r32/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 2A /r" , "AVX512_F"], ["vcvtsi2ss" , "W:xmm, xmm[127:32], r64/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W1 2A /r" , "AVX512_F X64"], ["vcvtss2sd" , "W:xmm {kz}, xmm[127:64], xmm[31:0]/m32 {sae}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 5A /r" , "AVX512_F"], ["vcvtss2si" , "W:r32, xmm[31:0]/m32 {er}" , "RM-T1F" , "EVEX.LIG.F3.0F.W0 2D /r" , "AVX512_F"], ["vcvtss2si" , "W:r64, xmm[31:0]/m32 {er}" , "RM-T1F" , "EVEX.LIG.F3.0F.W1 2D /r" , "AVX512_F X64"], ["vcvtss2usi" , "W:r32, xmm[31:0]/m32 {er}" , "RM-T1F" , "EVEX.LIG.F3.0F.W0 79 /r" , "AVX512_F"], ["vcvtss2usi" , "W:r64, xmm[31:0]/m32 {er}" , "RM-T1F" , "EVEX.LIG.F3.0F.W1 79 /r" , "AVX512_F X64"], ["vcvttpd2dq" , "W:xmm[63:0] {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F.W1 E6 /r" , "AVX512_F-VL"], ["vcvttpd2dq" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F.W1 E6 /r" , "AVX512_F-VL"], ["vcvttpd2dq" , "W:ymm {kz}, zmm/m512/b64 {sae}" , "RM-FV" , "EVEX.512.66.0F.W1 E6 /r" , "AVX512_F"], ["vcvttpd2qq" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F.W1 7A /r" , "AVX512_F-VL"], ["vcvttpd2qq" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F.W1 7A /r" , "AVX512_F-VL"], ["vcvttpd2qq" , "W:zmm {kz}, zmm/m512/b64 {sae}" , "RM-FV" , "EVEX.512.66.0F.W1 7A /r" , "AVX512_F"], ["vcvttpd2udq" , "W:xmm[63:0] {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.0F.W1 78 /r" , "AVX512_F-VL"], ["vcvttpd2udq" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.0F.W1 78 /r" , "AVX512_F-VL"], ["vcvttpd2udq" , "W:ymm {kz}, zmm/m512/b64 {sae}" , "RM-FV" , "EVEX.512.0F.W1 78 /r" , "AVX512_F"], ["vcvttpd2uqq" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F.W1 78 /r" , "AVX512_DQ-VL"], ["vcvttpd2uqq" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F.W1 78 /r" , "AVX512_DQ-VL"], ["vcvttpd2uqq" , "W:zmm {kz}, zmm/m512/b64 {sae}" , "RM-FV" , "EVEX.512.66.0F.W1 78 /r" , "AVX512_DQ"], ["vcvttps2dq" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.F3.0F.W0 5B /r" , "AVX512_F-VL"], ["vcvttps2dq" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.F3.0F.W0 5B /r" , "AVX512_F-VL"], ["vcvttps2dq" , "W:zmm {kz}, zmm/m512/b32 {sae}" , "RM-FV" , "EVEX.512.F3.0F.W0 5B /r" , "AVX512_F"], ["vcvttps2qq" , "W:xmm {kz}, xmm[63:0]/m64/b32" , "RM-HV" , "EVEX.128.66.0F.W0 7A /r" , "AVX512_DQ-VL"], ["vcvttps2qq" , "W:ymm {kz}, xmm/m128/b32" , "RM-HV" , "EVEX.256.66.0F.W0 7A /r" , "AVX512_DQ-VL"], ["vcvttps2qq" , "W:zmm {kz}, ymm/m256/b32 {sae}" , "RM-HV" , "EVEX.512.66.0F.W0 7A /r" , "AVX512_DQ"], ["vcvttps2udq" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.0F.W0 78 /r" , "AVX512_F-VL"], ["vcvttps2udq" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.0F.W0 78 /r" , "AVX512_F-VL"], ["vcvttps2udq" , "W:zmm {kz}, zmm/m512/b32 {sae}" , "RM-FV" , "EVEX.512.0F.W0 78 /r" , "AVX512_F"], ["vcvttps2uqq" , "W:xmm {kz}, xmm[63:0]/m64/b32" , "RM-HV" , "EVEX.128.66.0F.W0 78 /r" , "AVX512_DQ-VL"], ["vcvttps2uqq" , "W:ymm {kz}, xmm/m128/b32" , "RM-HV" , "EVEX.256.66.0F.W0 78 /r" , "AVX512_DQ-VL"], ["vcvttps2uqq" , "W:zmm {kz}, ymm/m256/b32 {sae}" , "RM-HV" , "EVEX.512.66.0F.W0 78 /r" , "AVX512_DQ"], ["vcvttsd2si" , "W:r32, xmm[63:0]/m64 {sae}" , "RM-T1F" , "EVEX.LIG.F2.0F.W0 2C /r" , "AVX512_F"], ["vcvttsd2si" , "W:r64, xmm[63:0]/m64 {sae}" , "RM-T1F" , "EVEX.LIG.F2.0F.W1 2C /r" , "AVX512_F X64"], ["vcvttsd2usi" , "W:r32, xmm[63:0]/m64 {sae}" , "RM-T1F" , "EVEX.LIG.F2.0F.W0 78 /r" , "AVX512_F"], ["vcvttsd2usi" , "W:r64, xmm[63:0]/m64 {sae}" , "RM-T1F" , "EVEX.LIG.F2.0F.W1 78 /r" , "AVX512_F X64"], ["vcvttss2si" , "W:r32, xmm[31:0]/m32 {sae}" , "RM-T1F" , "EVEX.LIG.F3.0F.W0 2C /r" , "AVX512_F"], ["vcvttss2si" , "W:r64, xmm[31:0]/m32 {sae}" , "RM-T1F" , "EVEX.LIG.F3.0F.W1 2C /r" , "AVX512_F X64"], ["vcvttss2usi" , "W:r32, xmm[31:0]/m32 {sae}" , "RM-T1F" , "EVEX.LIG.F3.0F.W0 78 /r" , "AVX512_F"], ["vcvttss2usi" , "W:r64, xmm[31:0]/m32 {sae}" , "RM-T1F" , "EVEX.LIG.F3.0F.W1 78 /r" , "AVX512_F X64"], ["vcvtudq2pd" , "W:xmm {kz}, xmm[63:0]/m64/b32" , "RM-HV" , "EVEX.128.F3.0F.W0 7A /r" , "AVX512_F-VL"], ["vcvtudq2pd" , "W:ymm {kz}, xmm/m128/b32" , "RM-HV" , "EVEX.256.F3.0F.W0 7A /r" , "AVX512_F-VL"], ["vcvtudq2pd" , "W:zmm {kz}, ymm/m256/b32" , "RM-HV" , "EVEX.512.F3.0F.W0 7A /r" , "AVX512_F"], ["vcvtudq2ps" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.F2.0F.W0 7A /r" , "AVX512_F-VL"], ["vcvtudq2ps" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.F2.0F.W0 7A /r" , "AVX512_F-VL"], ["vcvtudq2ps" , "W:zmm {kz}, zmm/m512/b32 {er}" , "RM-FV" , "EVEX.512.F2.0F.W0 7A /r" , "AVX512_F"], ["vcvtuqq2pd" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.F3.0F.W1 7A /r" , "AVX512_DQ-VL"], ["vcvtuqq2pd" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.F3.0F.W1 7A /r" , "AVX512_DQ-VL"], ["vcvtuqq2pd" , "W:zmm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.F3.0F.W1 7A /r" , "AVX512_DQ"], ["vcvtuqq2ps" , "W:xmm[63:0] {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.F2.0F.W1 7A /r" , "AVX512_DQ-VL"], ["vcvtuqq2ps" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.F2.0F.W1 7A /r" , "AVX512_DQ-VL"], ["vcvtuqq2ps" , "W:ymm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.F2.0F.W1 7A /r" , "AVX512_DQ"], ["vcvtusi2sd" , "W:xmm, xmm[127:64], r32/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W0 7B /r" , "AVX512_F"], ["vcvtusi2sd" , "W:xmm, xmm[127:64], r64/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 7B /r" , "AVX512_F X64"], ["vcvtusi2ss" , "W:xmm, xmm[127:32], r32/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 7B /r" , "AVX512_F"], ["vcvtusi2ss" , "W:xmm, xmm[127:32], r64/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W1 7B /r" , "AVX512_F X64"], ["vdbpsadbw" , "W:xmm {kz}, xmm, xmm/m128, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W0 42 /r ib" , "AVX512_BW-VL"], ["vdbpsadbw" , "W:ymm {kz}, ymm, ymm/m256, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W0 42 /r ib" , "AVX512_BW-VL"], ["vdbpsadbw" , "W:zmm {kz}, zmm, zmm/m512, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W0 42 /r ib" , "AVX512_BW"], ["vdivpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 5E /r" , "AVX512_F-VL"], ["vdivpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 5E /r" , "AVX512_F-VL"], ["vdivpd" , "W:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F.W1 5E /r" , "AVX512_F"], ["vdivps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 5E /r" , "AVX512_F-VL"], ["vdivps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 5E /r" , "AVX512_F-VL"], ["vdivps" , "W:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.0F.W0 5E /r" , "AVX512_F"], ["vdivsd" , "W:xmm {kz}, xmm, xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 5E /r" , "AVX512_F"], ["vdivss" , "W:xmm {kz}, xmm, xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 5E /r" , "AVX512_F"], ["vdpbf16ps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.F3.0F38.W0 52 /r" , "AVX512_BF16-VL"], ["vdpbf16ps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.F3.0F38.W0 52 /r" , "AVX512_BF16-VL"], ["vdpbf16ps" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.F3.0F38.W0 52 /r" , "AVX512_BF16-VL"], ["vexp2pd" , "W:zmm {kz}, zmm/m512/b64 {sae}" , "RM-FV" , "EVEX.512.66.0F38.W1 C8 /r" , "AVX512_ERI"], ["vexp2ps" , "W:zmm {kz}, zmm/m512/b32 {sae}" , "RM-FV" , "EVEX.512.66.0F38.W0 C8 /r" , "AVX512_ERI"], ["vexpandpd" , "W:xmm {kz}, xmm/m128" , "RM-T1S" , "EVEX.128.66.0F38.W1 88 /r" , "AVX512_F-VL"], ["vexpandpd" , "W:ymm {kz}, ymm/m256" , "RM-T1S" , "EVEX.256.66.0F38.W1 88 /r" , "AVX512_F-VL"], ["vexpandpd" , "W:zmm {kz}, zmm/m512" , "RM-T1S" , "EVEX.512.66.0F38.W1 88 /r" , "AVX512_F"], ["vexpandps" , "W:xmm {kz}, xmm/m128" , "RM-T1S" , "EVEX.128.66.0F38.W0 88 /r" , "AVX512_F-VL"], ["vexpandps" , "W:ymm {kz}, ymm/m256" , "RM-T1S" , "EVEX.256.66.0F38.W0 88 /r" , "AVX512_F-VL"], ["vexpandps" , "W:zmm {kz}, zmm/m512" , "RM-T1S" , "EVEX.512.66.0F38.W0 88 /r" , "AVX512_F"], ["vextractf32x4" , "W:xmm/m128 {kz}, ymm, ib/ub" , "MRI-T4" , "EVEX.256.66.0F3A.W0 19 /r ib" , "AVX512_F-VL"], ["vextractf32x4" , "W:xmm/m128 {kz}, zmm, ib/ub" , "MRI-T4" , "EVEX.512.66.0F3A.W0 19 /r ib" , "AVX512_F"], ["vextractf32x8" , "W:ymm/m256 {kz}, zmm, ib/ub" , "MRI-T8" , "EVEX.512.66.0F3A.W0 1B /r ib" , "AVX512_DQ"], ["vextractf64x2" , "W:xmm/m128 {kz}, ymm, ib/ub" , "MRI-T2" , "EVEX.256.66.0F3A.W1 19 /r ib" , "AVX512_DQ-VL"], ["vextractf64x2" , "W:xmm/m128 {kz}, zmm, ib/ub" , "MRI-T2" , "EVEX.512.66.0F3A.W1 19 /r ib" , "AVX512_DQ"], ["vextractf64x4" , "W:ymm/m256 {kz}, zmm, ib/ub" , "MRI-T4" , "EVEX.512.66.0F3A.W1 1B /r ib" , "AVX512_F"], ["vextracti32x4" , "W:xmm/m128 {kz}, ymm, ib/ub" , "MRI-T4" , "EVEX.256.66.0F3A.W0 39 /r ib" , "AVX512_F-VL"], ["vextracti32x4" , "W:xmm/m128 {kz}, zmm, ib/ub" , "MRI-T4" , "EVEX.512.66.0F3A.W0 39 /r ib" , "AVX512_F"], ["vextracti32x8" , "W:ymm/m256 {kz}, zmm, ib/ub" , "MRI-T8" , "EVEX.512.66.0F3A.W0 3B /r ib" , "AVX512_DQ"], ["vextracti64x2" , "W:xmm/m128 {kz}, ymm, ib/ub" , "MRI-T2" , "EVEX.256.66.0F3A.W1 39 /r ib" , "AVX512_DQ-VL"], ["vextracti64x2" , "W:xmm/m128 {kz}, zmm, ib/ub" , "MRI-T2" , "EVEX.512.66.0F3A.W1 39 /r ib" , "AVX512_DQ"], ["vextracti64x4" , "W:ymm/m256 {kz}, zmm, ib/ub" , "MRI-T4" , "EVEX.512.66.0F3A.W1 3B /r ib" , "AVX512_F"], ["vextractps" , "W:r32[31:0]/m32, xmm, ib/ub" , "MRI-T1S" , "EVEX.128.66.0F3A.WIG 17 /r ib", "AVX512_F"], ["vfixupimmpd" , "X:xmm {kz}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W1 54 /r ib" , "AVX512_F-VL"], ["vfixupimmpd" , "X:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 54 /r ib" , "AVX512_F-VL"], ["vfixupimmpd" , "X:zmm {kz}, zmm, zmm/m512/b64, ib/ub {sae}" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 54 /r ib" , "AVX512_F"], ["vfixupimmps" , "X:xmm {kz}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W0 54 /r ib" , "AVX512_F-VL"], ["vfixupimmps" , "X:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W0 54 /r ib" , "AVX512_F-VL"], ["vfixupimmps" , "X:zmm {kz}, zmm, zmm/m512/b32, ib/ub {sae}" , "RVMI-FV" , "EVEX.512.66.0F3A.W0 54 /r ib" , "AVX512_F"], ["vfixupimmsd" , "X:xmm {kz},xmm[127:64],xmm[63:0]/m64,ib/ub {sae}", "RVMI-T1S", "EVEX.LIG.66.0F3A.W1 55 /r ib" , "AVX512_F"], ["vfixupimmss" , "X:xmm {kz},xmm[127:32],xmm[31:0]/m32,ib/ub {sae}", "RVMI-T1S", "EVEX.LIG.66.0F3A.W0 55 /r ib" , "AVX512_F"], ["vfmadd132pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 98 /r" , "AVX512_F-VL"], ["vfmadd132pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 98 /r" , "AVX512_F-VL"], ["vfmadd132pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 98 /r" , "AVX512_F"], ["vfmadd132ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 98 /r" , "AVX512_F-VL"], ["vfmadd132ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 98 /r" , "AVX512_F-VL"], ["vfmadd132ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 98 /r" , "AVX512_F"], ["vfmadd132sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 99 /r" , "AVX512_F"], ["vfmadd132ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 99 /r" , "AVX512_F"], ["vfmadd213pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 A8 /r" , "AVX512_F-VL"], ["vfmadd213pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 A8 /r" , "AVX512_F-VL"], ["vfmadd213pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 A8 /r" , "AVX512_F"], ["vfmadd213ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 A8 /r" , "AVX512_F-VL"], ["vfmadd213ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 A8 /r" , "AVX512_F-VL"], ["vfmadd213ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 A8 /r" , "AVX512_F"], ["vfmadd213sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 A9 /r" , "AVX512_F"], ["vfmadd213ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 A9 /r" , "AVX512_F"], ["vfmadd231pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 B8 /r" , "AVX512_F-VL"], ["vfmadd231pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 B8 /r" , "AVX512_F-VL"], ["vfmadd231pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 B8 /r" , "AVX512_F"], ["vfmadd231ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 B8 /r" , "AVX512_F-VL"], ["vfmadd231ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 B8 /r" , "AVX512_F-VL"], ["vfmadd231ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 B8 /r" , "AVX512_F"], ["vfmadd231sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 B9 /r" , "AVX512_F"], ["vfmadd231ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 B9 /r" , "AVX512_F"], ["vfmaddsub132pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 96 /r" , "AVX512_F-VL"], ["vfmaddsub132pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 96 /r" , "AVX512_F-VL"], ["vfmaddsub132pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 96 /r" , "AVX512_F"], ["vfmaddsub132ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 96 /r" , "AVX512_F-VL"], ["vfmaddsub132ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 96 /r" , "AVX512_F-VL"], ["vfmaddsub132ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 96 /r" , "AVX512_F"], ["vfmaddsub213pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 A6 /r" , "AVX512_F-VL"], ["vfmaddsub213pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 A6 /r" , "AVX512_F-VL"], ["vfmaddsub213pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 A6 /r" , "AVX512_F"], ["vfmaddsub213ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 A6 /r" , "AVX512_F-VL"], ["vfmaddsub213ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 A6 /r" , "AVX512_F-VL"], ["vfmaddsub213ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 A6 /r" , "AVX512_F"], ["vfmaddsub231pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 B6 /r" , "AVX512_F-VL"], ["vfmaddsub231pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 B6 /r" , "AVX512_F-VL"], ["vfmaddsub231pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 B6 /r" , "AVX512_F"], ["vfmaddsub231ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 B6 /r" , "AVX512_F-VL"], ["vfmaddsub231ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 B6 /r" , "AVX512_F-VL"], ["vfmaddsub231ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 B6 /r" , "AVX512_F"], ["vfmsub132pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 9A /r" , "AVX512_F-VL"], ["vfmsub132pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 9A /r" , "AVX512_F-VL"], ["vfmsub132pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 9A /r" , "AVX512_F"], ["vfmsub132ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 9A /r" , "AVX512_F-VL"], ["vfmsub132ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 9A /r" , "AVX512_F-VL"], ["vfmsub132ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 9A /r" , "AVX512_F"], ["vfmsub132sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 9B /r" , "AVX512_F"], ["vfmsub132ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 9B /r" , "AVX512_F"], ["vfmsub213pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 AA /r" , "AVX512_F-VL"], ["vfmsub213pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 AA /r" , "AVX512_F-VL"], ["vfmsub213pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 AA /r" , "AVX512_F"], ["vfmsub213ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 AA /r" , "AVX512_F-VL"], ["vfmsub213ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 AA /r" , "AVX512_F-VL"], ["vfmsub213ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 AA /r" , "AVX512_F"], ["vfmsub213sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 AB /r" , "AVX512_F"], ["vfmsub213ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 AB /r" , "AVX512_F"], ["vfmsub231pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 BA /r" , "AVX512_F-VL"], ["vfmsub231pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 BA /r" , "AVX512_F-VL"], ["vfmsub231pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 BA /r" , "AVX512_F"], ["vfmsub231ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 BA /r" , "AVX512_F-VL"], ["vfmsub231ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 BA /r" , "AVX512_F-VL"], ["vfmsub231ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 BA /r" , "AVX512_F"], ["vfmsub231sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 BB /r" , "AVX512_F"], ["vfmsub231ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 BB /r" , "AVX512_F"], ["vfmsubadd132pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 97 /r" , "AVX512_F-VL"], ["vfmsubadd132pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 97 /r" , "AVX512_F-VL"], ["vfmsubadd132pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 97 /r" , "AVX512_F"], ["vfmsubadd132ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 97 /r" , "AVX512_F-VL"], ["vfmsubadd132ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 97 /r" , "AVX512_F-VL"], ["vfmsubadd132ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 97 /r" , "AVX512_F"], ["vfmsubadd213pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 A7 /r" , "AVX512_F-VL"], ["vfmsubadd213pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 A7 /r" , "AVX512_F-VL"], ["vfmsubadd213pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 A7 /r" , "AVX512_F"], ["vfmsubadd213ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 A7 /r" , "AVX512_F-VL"], ["vfmsubadd213ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 A7 /r" , "AVX512_F-VL"], ["vfmsubadd213ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 A7 /r" , "AVX512_F"], ["vfmsubadd231pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 B7 /r" , "AVX512_F-VL"], ["vfmsubadd231pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 B7 /r" , "AVX512_F-VL"], ["vfmsubadd231pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 B7 /r" , "AVX512_F"], ["vfmsubadd231ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 B7 /r" , "AVX512_F-VL"], ["vfmsubadd231ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 B7 /r" , "AVX512_F-VL"], ["vfmsubadd231ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 B7 /r" , "AVX512_F"], ["vfnmadd132pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 9C /r" , "AVX512_F-VL"], ["vfnmadd132pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 9C /r" , "AVX512_F-VL"], ["vfnmadd132pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 9C /r" , "AVX512_F"], ["vfnmadd132ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 9C /r" , "AVX512_F-VL"], ["vfnmadd132ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 9C /r" , "AVX512_F-VL"], ["vfnmadd132ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 9C /r" , "AVX512_F"], ["vfnmadd132sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 9D /r" , "AVX512_F"], ["vfnmadd132ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 9D /r" , "AVX512_F"], ["vfnmadd213pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 AC /r" , "AVX512_F-VL"], ["vfnmadd213pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 AC /r" , "AVX512_F-VL"], ["vfnmadd213pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 AC /r" , "AVX512_F"], ["vfnmadd213ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 AC /r" , "AVX512_F-VL"], ["vfnmadd213ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 AC /r" , "AVX512_F-VL"], ["vfnmadd213ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 AC /r" , "AVX512_F"], ["vfnmadd213sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 AD /r" , "AVX512_F"], ["vfnmadd213ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 AD /r" , "AVX512_F"], ["vfnmadd231pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 BC /r" , "AVX512_F-VL"], ["vfnmadd231pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 BC /r" , "AVX512_F-VL"], ["vfnmadd231pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 BC /r" , "AVX512_F"], ["vfnmadd231ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 BC /r" , "AVX512_F-VL"], ["vfnmadd231ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 BC /r" , "AVX512_F-VL"], ["vfnmadd231ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 BC /r" , "AVX512_F"], ["vfnmadd231sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 BD /r" , "AVX512_F"], ["vfnmadd231ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 BD /r" , "AVX512_F"], ["vfnmsub132pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 9E /r" , "AVX512_F-VL"], ["vfnmsub132pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 9E /r" , "AVX512_F-VL"], ["vfnmsub132pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 9E /r" , "AVX512_F"], ["vfnmsub132ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 9E /r" , "AVX512_F-VL"], ["vfnmsub132ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 9E /r" , "AVX512_F-VL"], ["vfnmsub132ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 9E /r" , "AVX512_F"], ["vfnmsub132sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 9F /r" , "AVX512_F"], ["vfnmsub132ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 9F /r" , "AVX512_F"], ["vfnmsub213pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 AE /r" , "AVX512_F-VL"], ["vfnmsub213pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 AE /r" , "AVX512_F-VL"], ["vfnmsub213pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 AE /r" , "AVX512_F"], ["vfnmsub213ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 AE /r" , "AVX512_F-VL"], ["vfnmsub213ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 AE /r" , "AVX512_F-VL"], ["vfnmsub213ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 AE /r" , "AVX512_F"], ["vfnmsub213sd" , "x:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 AF /r" , "AVX512_F"], ["vfnmsub213ss" , "x:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 AF /r" , "AVX512_F"], ["vfnmsub231pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 BE /r" , "AVX512_F-VL"], ["vfnmsub231pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 BE /r" , "AVX512_F-VL"], ["vfnmsub231pd" , "X:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 BE /r" , "AVX512_F"], ["vfnmsub231ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 BE /r" , "AVX512_F-VL"], ["vfnmsub231ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 BE /r" , "AVX512_F-VL"], ["vfnmsub231ps" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 BE /r" , "AVX512_F"], ["vfnmsub231sd" , "X:xmm[63:0] {kz}, xmm[63:0], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 BF /r" , "AVX512_F"], ["vfnmsub231ss" , "X:xmm[31:0] {kz}, xmm[31:0], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 BF /r" , "AVX512_F"], ["vfpclasspd" , "W:k {k}, xmm/m128/b64, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W1 66 /r ib" , "AVX512_DQ-VL"], ["vfpclasspd" , "W:k {k}, ymm/m256/b64, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W1 66 /r ib" , "AVX512_DQ-VL"], ["vfpclasspd" , "W:k {k}, zmm/m512/b64, ib/ub" , "RMI-FV" , "EVEX.512.66.0F3A.W1 66 /r ib" , "AVX512_DQ"], ["vfpclassps" , "W:k {k}, xmm/m128/b32, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W0 66 /r ib" , "AVX512_DQ-VL"], ["vfpclassps" , "W:k {k}, ymm/m256/b32, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W0 66 /r ib" , "AVX512_DQ-VL"], ["vfpclassps" , "W:k {k}, zmm/m512/b32, ib/ub" , "RMI-FV" , "EVEX.512.66.0F3A.W0 66 /r ib" , "AVX512_DQ"], ["vfpclasssd" , "W:k {k}, xmm[63:0]/m64, ib/ub" , "RMI-T1S" , "EVEX.LIG.66.0F3A.W1 67 /r ib" , "AVX512_DQ"], ["vfpclassss" , "W:k {k}, xmm[31:0]/m32, ib/ub" , "RMI-T1S" , "EVEX.LIG.66.0F3A.W0 67 /r ib" , "AVX512_DQ"], ["vgatherdpd" , "X:xmm {k}, vm32x" , "RM-T1S" , "EVEX.128.66.0F38.W1 92 /r" , "AVX512_F-VL"], ["vgatherdpd" , "X:ymm {k}, vm32x" , "RM-T1S" , "EVEX.256.66.0F38.W1 92 /r" , "AVX512_F-VL"], ["vgatherdpd" , "X:zmm {k}, vm32y" , "RM-T1S" , "EVEX.512.66.0F38.W1 92 /r" , "AVX512_F"], ["vgatherdps" , "X:xmm {k}, vm32x" , "RM-T1S" , "EVEX.128.66.0F38.W0 92 /r" , "AVX512_F-VL"], ["vgatherdps" , "X:ymm {k}, vm32y" , "RM-T1S" , "EVEX.256.66.0F38.W0 92 /r" , "AVX512_F-VL"], ["vgatherdps" , "X:zmm {k}, vm32z" , "RM-T1S" , "EVEX.512.66.0F38.W0 92 /r" , "AVX512_F"], ["vgatherpf0dpd" , "R:vm32y {k}" , "M-T1S" , "EVEX.512.66.0F38.W1 C6 /1" , "AVX512_PFI"], ["vgatherpf0dps" , "R:vm32z {k}" , "M-T1S" , "EVEX.512.66.0F38.W0 C6 /1" , "AVX512_PFI"], ["vgatherpf0qpd" , "R:vm64z {k}" , "M-T1S" , "EVEX.512.66.0F38.W1 C7 /1" , "AVX512_PFI"], ["vgatherpf0qps" , "R:vm64z {k}" , "M-T1S" , "EVEX.512.66.0F38.W0 C7 /1" , "AVX512_PFI"], ["vgatherpf1dpd" , "R:vm32y {k}" , "M-T1S" , "EVEX.512.66.0F38.W1 C6 /2" , "AVX512_PFI"], ["vgatherpf1dps" , "R:vm32z {k}" , "M-T1S" , "EVEX.512.66.0F38.W0 C6 /2" , "AVX512_PFI"], ["vgatherpf1qpd" , "R:vm64z {k}" , "M-T1S" , "EVEX.512.66.0F38.W1 C7 /2" , "AVX512_PFI"], ["vgatherpf1qps" , "R:vm64z {k}" , "M-T1S" , "EVEX.512.66.0F38.W0 C7 /2" , "AVX512_PFI"], ["vgatherqpd" , "X:xmm {k}, vm64x" , "RM-T1S" , "EVEX.128.66.0F38.W1 93 /r" , "AVX512_F-VL"], ["vgatherqpd" , "X:ymm {k}, vm64y" , "RM-T1S" , "EVEX.256.66.0F38.W1 93 /r" , "AVX512_F-VL"], ["vgatherqpd" , "X:zmm {k}, vm64z" , "RM-T1S" , "EVEX.512.66.0F38.W1 93 /r" , "AVX512_F"], ["vgatherqps" , "X:xmm {k}, vm64x" , "RM-T1S" , "EVEX.128.66.0F38.W0 93 /r" , "AVX512_F-VL"], ["vgatherqps" , "X:xmm {k}, vm64y" , "RM-T1S" , "EVEX.256.66.0F38.W0 93 /r" , "AVX512_F-VL"], ["vgatherqps" , "X:ymm {k}, vm64z" , "RM-T1S" , "EVEX.512.66.0F38.W0 93 /r" , "AVX512_F"], ["vgetexppd" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F38.W1 42 /r" , "AVX512_F-VL"], ["vgetexppd" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F38.W1 42 /r" , "AVX512_F-VL"], ["vgetexppd" , "W:zmm {kz}, zmm/m512/b64 {sae}" , "RM-FV" , "EVEX.512.66.0F38.W1 42 /r" , "AVX512_F"], ["vgetexpps" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.0F38.W0 42 /r" , "AVX512_F-VL"], ["vgetexpps" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.0F38.W0 42 /r" , "AVX512_F-VL"], ["vgetexpps" , "W:zmm {kz}, zmm/m512/b32 {sae}" , "RM-FV" , "EVEX.512.66.0F38.W0 42 /r" , "AVX512_F"], ["vgetexpsd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]/m64 {sae}" , "RM-T1S" , "EVEX.LIG.66.0F38.W1 43 /r" , "AVX512_F"], ["vgetexpss" , "W:xmm {kz}, xmm[127:32], xmm[31:0]/m32 {sae}" , "RM-T1S" , "EVEX.LIG.66.0F38.W0 43 /r" , "AVX512_F"], ["vgetmantpd" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W1 26 /r ib" , "AVX512_F-VL"], ["vgetmantpd" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W1 26 /r ib" , "AVX512_F-VL"], ["vgetmantpd" , "W:zmm {kz}, zmm/m512/b64, ib/ub {sae}" , "RMI-FV" , "EVEX.512.66.0F3A.W1 26 /r ib" , "AVX512_F"], ["vgetmantps" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W0 26 /r ib" , "AVX512_F-VL"], ["vgetmantps" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W0 26 /r ib" , "AVX512_F-VL"], ["vgetmantps" , "W:zmm {kz}, zmm/m512/b32, ib/ub {sae}" , "RMI-FV" , "EVEX.512.66.0F3A.W0 26 /r ib" , "AVX512_F"], ["vgetmantsd" , "W:xmm {kz},xmm[127:64],xmm[63:0]/m64,ib/ub {sae}", "RMI-T1S" , "EVEX.LIG.66.0F3A.W1 27 /r ib" , "AVX512_F"], ["vgetmantss" , "W:xmm {kz},xmm[127:32],xmm[31:0]/m32,ib/ub {sae}", "RMI-T1S" , "EVEX.LIG.66.0F3A.W0 27 /r ib" , "AVX512_F"], ["vgf2p8affineinvqb", "W:xmm {kz}, xmm, xmm/m128, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W1 CF /r ib" , "AVX512_F-VL GFNI"], ["vgf2p8affineinvqb", "W:ymm {kz}, ymm, ymm/m256, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W1 CF /r ib" , "AVX512_F-VL GFNI"], ["vgf2p8affineinvqb", "W:zmm {kz}, zmm, zmm/m512, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W1 CF /r ib" , "AVX512_F GFNI"], ["vgf2p8affineqb" , "W:xmm {kz}, xmm, xmm/m128, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W1 CE /r ib" , "AVX512_F-VL GFNI"], ["vgf2p8affineqb" , "W:ymm {kz}, ymm, ymm/m256, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W1 CE /r ib" , "AVX512_F-VL GFNI"], ["vgf2p8affineqb" , "W:zmm {kz}, zmm, zmm/m512, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W1 CE /r ib" , "AVX512_F GFNI"], ["vgf2p8mulb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W0 CF /r" , "AVX512_F-VL GFNI"], ["vgf2p8mulb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W0 CF /r" , "AVX512_F-VL GFNI"], ["vgf2p8mulb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W0 CF /r" , "AVX512_F GFNI"], ["vinsertf32x4" , "W:ymm {kz}, ymm, xmm/m128, ib/ub" , "RVMI-T4" , "EVEX.256.66.0F3A.W0 18 /r ib" , "AVX512_F-VL"], ["vinsertf32x4" , "W:zmm {kz}, zmm, xmm/m128, ib/ub" , "RVMI-T4" , "EVEX.512.66.0F3A.W0 18 /r ib" , "AVX512_F"], ["vinsertf32x8" , "W:zmm {kz}, zmm, ymm/m256, ib/ub" , "RVMI-T8" , "EVEX.512.66.0F3A.W0 1A /r ib" , "AVX512_DQ"], ["vinsertf64x2" , "W:ymm {kz}, ymm, xmm/m128, ib/ub" , "RVMI-T2" , "EVEX.256.66.0F3A.W1 18 /r ib" , "AVX512_DQ-VL"], ["vinsertf64x2" , "W:zmm {kz}, zmm, xmm/m128, ib/ub" , "RVMI-T2" , "EVEX.512.66.0F3A.W1 18 /r ib" , "AVX512_DQ"], ["vinsertf64x4" , "W:zmm {kz}, zmm, ymm/m256, ib/ub" , "RVMI-T4" , "EVEX.512.66.0F3A.W1 1A /r ib" , "AVX512_F"], ["vinserti32x4" , "W:ymm {kz}, ymm, xmm/m128, ib/ub" , "RVMI-T4" , "EVEX.256.66.0F3A.W0 38 /r ib" , "AVX512_F-VL"], ["vinserti32x4" , "W:zmm {kz}, zmm, xmm/m128, ib/ub" , "RVMI-T4" , "EVEX.512.66.0F3A.W0 38 /r ib" , "AVX512_F"], ["vinserti32x8" , "W:zmm {kz}, zmm, ymm/m256, ib/ub" , "RVMI-T8" , "EVEX.512.66.0F3A.W0 3A /r ib" , "AVX512_DQ"], ["vinserti64x2" , "W:ymm {kz}, ymm, xmm/m128, ib/ub" , "RVMI-T2" , "EVEX.256.66.0F3A.W1 38 /r ib" , "AVX512_DQ-VL"], ["vinserti64x2" , "W:zmm {kz}, zmm, xmm/m128, ib/ub" , "RVMI-T2" , "EVEX.512.66.0F3A.W1 38 /r ib" , "AVX512_DQ"], ["vinserti64x4" , "W:zmm {kz}, zmm, ymm/m256, ib/ub" , "RVMI-T4" , "EVEX.512.66.0F3A.W1 3A /r ib" , "AVX512_F"], ["vinsertps" , "W:xmm, xmm, xmm[31:0]/m32, ib/ub" , "RVMI-T1S", "EVEX.128.66.0F3A.W0 21 /r ib" , "AVX512_F"], ["vmaxpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 5F /r" , "AVX512_F-VL"], ["vmaxpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 5F /r" , "AVX512_F-VL"], ["vmaxpd" , "W:zmm {kz}, zmm, zmm/m512/b64 {sae}" , "RVM-FV" , "EVEX.512.66.0F.W1 5F /r" , "AVX512_F"], ["vmaxps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 5F /r" , "AVX512_F-VL"], ["vmaxps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 5F /r" , "AVX512_F-VL"], ["vmaxps" , "W:zmm {kz}, zmm, zmm/m512/b32 {sae}" , "RVM-FV" , "EVEX.512.0F.W0 5F /r" , "AVX512_F"], ["vmaxsd" , "W:xmm {kz}, xmm, xmm[63:0]/m64 {sae}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 5F /r" , "AVX512_F-VL"], ["vmaxss" , "W:xmm {kz}, xmm, xmm[31:0]/m32 {sae}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 5F /r" , "AVX512_F-VL"], ["vminpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 5D /r" , "AVX512_F-VL"], ["vminpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 5D /r" , "AVX512_F-VL"], ["vminpd" , "W:zmm {kz}, zmm, zmm/m512/b64 {sae}" , "RVM-FV" , "EVEX.512.66.0F.W1 5D /r" , "AVX512_F"], ["vminps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 5D /r" , "AVX512_F-VL"], ["vminps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 5D /r" , "AVX512_F-VL"], ["vminps" , "W:zmm {kz}, zmm, zmm/m512/b32 {sae}" , "RVM-FV" , "EVEX.512.0F.W0 5D /r" , "AVX512_F"], ["vminsd" , "W:xmm {kz}, xmm, xmm[63:0]/m64 {sae}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 5D /r" , "AVX512_F-VL"], ["vminss" , "W:xmm {kz}, xmm, xmm[31:0]/m32 {sae}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 5D /r" , "AVX512_F-VL"], ["vmovapd" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F.W1 28 /r" , "AVX512_F-VL"], ["vmovapd" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.66.0F.W1 29 /r" , "AVX512_F-VL"], ["vmovapd" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F.W1 28 /r" , "AVX512_F-VL"], ["vmovapd" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.66.0F.W1 29 /r" , "AVX512_F-VL"], ["vmovapd" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F.W1 28 /r" , "AVX512_F"], ["vmovapd" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.66.0F.W1 29 /r" , "AVX512_F"], ["vmovaps" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.0F.W0 28 /r" , "AVX512_F-VL"], ["vmovaps" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.0F.W0 29 /r" , "AVX512_F-VL"], ["vmovaps" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.0F.W0 28 /r" , "AVX512_F-VL"], ["vmovaps" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.0F.W0 29 /r" , "AVX512_F-VL"], ["vmovaps" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.0F.W0 28 /r" , "AVX512_F"], ["vmovaps" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.0F.W0 29 /r" , "AVX512_F"], ["vmovd" , "W:r32/m32, xmm[31:0]" , "MR-T1S" , "EVEX.128.66.0F.W0 7E /r" , "AVX512_F"], ["vmovd" , "W:xmm[31:0], r32/m32" , "RM-T1S" , "EVEX.128.66.0F.W0 6E /r" , "AVX512_F"], ["vmovddup" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-DUP" , "EVEX.128.F2.0F.W1 12 /r" , "AVX512_F-VL"], ["vmovddup" , "W:ymm {kz}, ymm/m256" , "RM-DUP" , "EVEX.256.F2.0F.W1 12 /r" , "AVX512_F-VL"], ["vmovddup" , "W:zmm {kz}, zmm/m512" , "RM-DUP" , "EVEX.512.F2.0F.W1 12 /r" , "AVX512_F"], ["vmovdqa32" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F.W0 6F /r" , "AVX512_F-VL"], ["vmovdqa32" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.66.0F.W0 7F /r" , "AVX512_F-VL"], ["vmovdqa32" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F.W0 6F /r" , "AVX512_F-VL"], ["vmovdqa32" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.66.0F.W0 7F /r" , "AVX512_F-VL"], ["vmovdqa32" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F.W0 6F /r" , "AVX512_F"], ["vmovdqa32" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.66.0F.W0 7F /r" , "AVX512_F"], ["vmovdqa64" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F.W1 6F /r" , "AVX512_F-VL"], ["vmovdqa64" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.66.0F.W1 7F /r" , "AVX512_F-VL"], ["vmovdqa64" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F.W1 6F /r" , "AVX512_F-VL"], ["vmovdqa64" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.66.0F.W1 7F /r" , "AVX512_F-VL"], ["vmovdqa64" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F.W1 6F /r" , "AVX512_F"], ["vmovdqa64" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.66.0F.W1 7F /r" , "AVX512_F"], ["vmovdqu16" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.F2.0F.W1 6F /r" , "AVX512_BW-VL"], ["vmovdqu16" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.F2.0F.W1 7F /r" , "AVX512_BW-VL"], ["vmovdqu16" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.F2.0F.W1 6F /r" , "AVX512_BW-VL"], ["vmovdqu16" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.F2.0F.W1 7F /r" , "AVX512_BW-VL"], ["vmovdqu16" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.F2.0F.W1 6F /r" , "AVX512_BW"], ["vmovdqu16" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.F2.0F.W1 7F /r" , "AVX512_BW"], ["vmovdqu32" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.F3.0F.W0 6F /r" , "AVX512_F-VL"], ["vmovdqu32" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.F3.0F.W0 7F /r" , "AVX512_F-VL"], ["vmovdqu32" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.F3.0F.W0 6F /r" , "AVX512_F-VL"], ["vmovdqu32" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.F3.0F.W0 7F /r" , "AVX512_F-VL"], ["vmovdqu32" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.F3.0F.W0 6F /r" , "AVX512_F"], ["vmovdqu32" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.F3.0F.W0 7F /r" , "AVX512_F"], ["vmovdqu64" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.F3.0F.W1 6F /r" , "AVX512_F-VL"], ["vmovdqu64" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.F3.0F.W1 7F /r" , "AVX512_F-VL"], ["vmovdqu64" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.F3.0F.W1 6F /r" , "AVX512_F-VL"], ["vmovdqu64" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.F3.0F.W1 7F /r" , "AVX512_F-VL"], ["vmovdqu64" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.F3.0F.W1 6F /r" , "AVX512_F"], ["vmovdqu64" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.F3.0F.W1 7F /r" , "AVX512_F"], ["vmovdqu8" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.F2.0F.W0 6F /r" , "AVX512_BW-VL"], ["vmovdqu8" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.F2.0F.W0 7F /r" , "AVX512_BW-VL"], ["vmovdqu8" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.F2.0F.W0 6F /r" , "AVX512_BW-VL"], ["vmovdqu8" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.F2.0F.W0 7F /r" , "AVX512_BW-VL"], ["vmovdqu8" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.F2.0F.W0 6F /r" , "AVX512_BW"], ["vmovdqu8" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.F2.0F.W0 7F /r" , "AVX512_BW"], ["vmovhlps" , "W:xmm, xmm[127:64], xmm[127:64]" , "RVM" , "EVEX.128.0F.W0 12 /r" , "AVX512_F"], ["vmovhpd" , "W:m64, xmm[127:64]" , "MR-T1S" , "EVEX.128.66.0F.W1 17 /r" , "AVX512_F"], ["vmovhpd" , "W:xmm, xmm[63:0], m64" , "RVM-T1S" , "EVEX.128.66.0F.W1 16 /r" , "AVX512_F"], ["vmovhps" , "W:m64, xmm[127:64]" , "MR-T2" , "EVEX.128.0F.W0 17 /r" , "AVX512_F"], ["vmovhps" , "W:xmm, xmm[63:0], m64" , "RVM-T2" , "EVEX.128.0F.W0 16 /r" , "AVX512_F"], ["vmovlhps" , "W:xmm, xmm[63:0], xmm[63:0]" , "RVM" , "EVEX.128.0F.W0 16 /r" , "AVX512_F"], ["vmovlpd" , "W:m64, xmm[63:0]" , "MR-T1S" , "EVEX.128.66.0F.W1 13 /r" , "AVX512_F"], ["vmovlpd" , "W:xmm, xmm[127:64], m64" , "RVM-T1S" , "EVEX.128.66.0F.W1 12 /r" , "AVX512_F"], ["vmovlps" , "W:m64, xmm[63:0]" , "MR-T2" , "EVEX.128.0F.W0 13 /r" , "AVX512_F"], ["vmovlps" , "W:xmm, xmm[127:64], m64" , "RVM-T2" , "EVEX.128.0F.W0 12 /r" , "AVX512_F"], ["vmovntdq" , "W:m128, xmm" , "MR-FVM" , "EVEX.128.66.0F.W0 E7 /r" , "AVX512_F-VL"], ["vmovntdq" , "W:m256, ymm" , "MR-FVM" , "EVEX.256.66.0F.W0 E7 /r" , "AVX512_F-VL"], ["vmovntdq" , "W:m512, zmm" , "MR-FVM" , "EVEX.512.66.0F.W0 E7 /r" , "AVX512_F"], ["vmovntdqa" , "W:xmm, m128" , "RM-FVM" , "EVEX.128.66.0F38.W0 2A /r" , "AVX512_F-VL"], ["vmovntdqa" , "W:ymm, m256" , "RM-FVM" , "EVEX.256.66.0F38.W0 2A /r" , "AVX512_F-VL"], ["vmovntdqa" , "W:zmm, m512" , "RM-FVM" , "EVEX.512.66.0F38.W0 2A /r" , "AVX512_F"], ["vmovntpd" , "W:m128, xmm" , "MR-FVM" , "EVEX.128.66.0F.W1 2B /r" , "AVX512_F-VL"], ["vmovntpd" , "W:m256, ymm" , "MR-FVM" , "EVEX.256.66.0F.W1 2B /r" , "AVX512_F-VL"], ["vmovntpd" , "W:m512, zmm" , "MR-FVM" , "EVEX.512.66.0F.W1 2B /r" , "AVX512_F"], ["vmovntps" , "W:m128, xmm" , "MR-FVM" , "EVEX.128.66.0F.W0 2B /r" , "AVX512_F-VL"], ["vmovntps" , "W:m256, ymm" , "MR-FVM" , "EVEX.256.66.0F.W0 2B /r" , "AVX512_F-VL"], ["vmovntps" , "W:m512, zmm" , "MR-FVM" , "EVEX.512.0F.W0 2B /r" , "AVX512_F"], ["vmovq" , "W:r64/m64, xmm[63:0]" , "MR-T1S" , "EVEX.128.66.0F.W1 7E /r" , "AVX512_F X64"], ["vmovq" , "W:xmm[63:0], r64/m64" , "RM-T1S" , "EVEX.128.66.0F.W1 6E /r" , "AVX512_F X64"], ["vmovq" , "W:xmm[63:0], xmm[63:0]/m64" , "RM-T1S" , "EVEX.128.F3.0F.W1 7E /r" , "AVX512_F"], ["vmovq" , "W:xmm[63:0]/m64, xmm[63:0]" , "MR-T1S" , "EVEX.128.66.0F.W1 D6 /r" , "AVX512_F"], ["vmovsd" , "W:m64, xmm[63:0]" , "MR-T1S" , "EVEX.LIG.F2.0F.W1 11 /r" , "AVX512_F"], ["vmovsd" , "W:xmm[63:0] {kz}, m64" , "MR-T1S" , "EVEX.LIG.F2.0F.W1 10 /r" , "AVX512_F"], ["vmovsd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]" , "MVR" , "EVEX.LIG.F2.0F.W1 11 /r" , "AVX512_F"], ["vmovsd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]" , "RVM" , "EVEX.LIG.F2.0F.W1 10 /r" , "AVX512_F"], ["vmovshdup" , "W:xmm {kz}, xmm/m128" , "RVM-FVM" , "EVEX.128.F3.0F.W0 16 /r" , "AVX512_F-VL"], ["vmovshdup" , "W:ymm {kz}, ymm/m256" , "RVM-FVM" , "EVEX.256.F3.0F.W0 16 /r" , "AVX512_F-VL"], ["vmovshdup" , "W:zmm {kz}, zmm/m512" , "RVM-FVM" , "EVEX.512.F3.0F.W0 16 /r" , "AVX512_F"], ["vmovsldup" , "W:xmm {kz}, xmm/m128" , "RVM-FVM" , "EVEX.128.F3.0F.W0 12 /r" , "AVX512_F-VL"], ["vmovsldup" , "W:ymm {kz}, ymm/m256" , "RVM-FVM" , "EVEX.256.F3.0F.W0 12 /r" , "AVX512_F-VL"], ["vmovsldup" , "W:zmm {kz}, zmm/m512" , "RVM-FVM" , "EVEX.512.F3.0F.W0 12 /r" , "AVX512_F"], ["vmovss" , "W:m32, xmm[31:0]" , "MR-T1S" , "EVEX.LIG.F3.0F.W0 11 /r" , "AVX512_F"], ["vmovss" , "W:xmm[31:0] {kz}, m32" , "MR-T1S" , "EVEX.LIG.F3.0F.W0 10 /r" , "AVX512_F"], ["vmovss" , "W:xmm {kz}, xmm[127:32], xmm[31:0]" , "MVR" , "EVEX.LIG.F3.0F.W0 11 /r" , "AVX512_F"], ["vmovss" , "W:xmm {kz}, xmm[127:32], xmm[31:0]" , "RVM" , "EVEX.LIG.F3.0F.W0 10 /r" , "AVX512_F"], ["vmovupd" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F.W1 10 /r" , "AVX512_F-VL"], ["vmovupd" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.66.0F.W1 11 /r" , "AVX512_F-VL"], ["vmovupd" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F.W1 11 /r" , "AVX512_F-VL"], ["vmovupd" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.66.0F.W1 10 /r" , "AVX512_F-VL"], ["vmovupd" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F.W1 10 /r" , "AVX512_F"], ["vmovupd" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.66.0F.W1 11 /r" , "AVX512_F"], ["vmovups" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.0F.W0 10 /r" , "AVX512_F-VL"], ["vmovups" , "W:xmm/m128 {kz}, xmm" , "MR-FVM" , "EVEX.128.0F.W0 11 /r" , "AVX512_F-VL"], ["vmovups" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.0F.W0 10 /r" , "AVX512_F-VL"], ["vmovups" , "W:ymm/m256 {kz}, ymm" , "MR-FVM" , "EVEX.256.0F.W0 11 /r" , "AVX512_F-VL"], ["vmovups" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.0F.W0 10 /r" , "AVX512_F"], ["vmovups" , "W:zmm/m512 {kz}, zmm" , "MR-FVM" , "EVEX.512.0F.W0 11 /r" , "AVX512_F"], ["vmulpd" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 59 /r" , "AVX512_F-VL"], ["vmulpd" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 59 /r" , "AVX512_F-VL"], ["vmulpd" , "W:zmm {kz},~zmm,~zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F.W1 59 /r" , "AVX512_F"], ["vmulps" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 59 /r" , "AVX512_F-VL"], ["vmulps" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 59 /r" , "AVX512_F-VL"], ["vmulps" , "W:zmm {kz},~zmm,~zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.0F.W0 59 /r" , "AVX512_F"], ["vmulsd" , "W:xmm {kz}, xmm, xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 59 /r" , "AVX512_F"], ["vmulss" , "W:xmm {kz}, xmm, xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 59 /r" , "AVX512_F"], ["vorpd" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 56 /r" , "AVX512_DQ-VL"], ["vorpd" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 56 /r" , "AVX512_DQ-VL"], ["vorpd" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 56 /r" , "AVX512_DQ"], ["vorps" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 56 /r" , "AVX512_DQ-VL"], ["vorps" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 56 /r" , "AVX512_DQ-VL"], ["vorps" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.0F.W0 56 /r" , "AVX512_DQ"], ["vp2intersectd" , "W:k, W:k+1, xmm, xmm/m128/b32" , "RVM" , "EVEX.128.F2.0F38.W0 68 /r" , "AVX512_VP2INTERSECT"], ["vp2intersectd" , "W:k, W:k+1, ymm, ymm/m256/b32" , "RVM" , "EVEX.256.F2.0F38.W0 68 /r" , "AVX512_VP2INTERSECT"], ["vp2intersectd" , "W:k, W:k+1, zmm, zmm/m512/b32" , "RVM" , "EVEX.512.F2.0F38.W0 68 /r" , "AVX512_VP2INTERSECT"], ["vp2intersectq" , "W:k, W:k+1, xmm, xmm/m128/b64" , "RVM" , "EVEX.128.F2.0F38.W1 68 /r" , "AVX512_VP2INTERSECT"], ["vp2intersectq" , "W:k, W:k+1, ymm, ymm/m256/b64" , "RVM" , "EVEX.256.F2.0F38.W1 68 /r" , "AVX512_VP2INTERSECT"], ["vp2intersectq" , "W:k, W:k+1, zmm, zmm/m512/b64" , "RVM" , "EVEX.512.F2.0F38.W1 68 /r" , "AVX512_VP2INTERSECT"], ["vp4dpwssd" , "W:zmm {kz}, zmm, zmm+1, zmm+2, zmm+3, m128" , "RM-T1_4X", "EVEX.512.F2.0F38.W0 52 /r" , "AVX512_4VNNIW"], ["vp4dpwssds" , "W:zmm {kz}, zmm, zmm+1, zmm+2, zmm+3, m128" , "RM-T1_4X", "EVEX.512.F2.0F38.W0 53 /r" , "AVX512_4VNNIW"], ["vpabsb" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F38 1C /r" , "AVX512_BW-VL"], ["vpabsb" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F38 1C /r" , "AVX512_BW-VL"], ["vpabsb" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F38 1C /r" , "AVX512_BW"], ["vpabsd" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.0F38.W0 1E /r" , "AVX512_F-VL"], ["vpabsd" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.0F38.W0 1E /r" , "AVX512_F-VL"], ["vpabsd" , "W:zmm {kz}, zmm/m512/b32" , "RM-FV" , "EVEX.512.66.0F38.W0 1E /r" , "AVX512_F"], ["vpabsq" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F38.W1 1F /r" , "AVX512_F-VL"], ["vpabsq" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F38.W1 1F /r" , "AVX512_F-VL"], ["vpabsq" , "W:zmm {kz}, zmm/m512/b64" , "RM-FV" , "EVEX.512.66.0F38.W1 1F /r" , "AVX512_F"], ["vpabsw" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F38 1D /r" , "AVX512_BW-VL"], ["vpabsw" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F38 1D /r" , "AVX512_BW-VL"], ["vpabsw" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F38 1D /r" , "AVX512_BW"], ["vpackssdw" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 6B /r" , "AVX512_BW-VL"], ["vpackssdw" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 6B /r" , "AVX512_BW-VL"], ["vpackssdw" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 6B /r" , "AVX512_BW"], ["vpacksswb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG 63 /r" , "AVX512_BW-VL"], ["vpacksswb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG 63 /r" , "AVX512_BW-VL"], ["vpacksswb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG 63 /r" , "AVX512_BW"], ["vpackusdw" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 2B /r" , "AVX512_BW-VL"], ["vpackusdw" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 2B /r" , "AVX512_BW-VL"], ["vpackusdw" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 2B /r" , "AVX512_BW"], ["vpackuswb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG 67 /r" , "AVX512_BW-VL"], ["vpackuswb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG 67 /r" , "AVX512_BW-VL"], ["vpackuswb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG 67 /r" , "AVX512_BW"], ["vpaddb" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG FC /r" , "AVX512_BW-VL"], ["vpaddb" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG FC /r" , "AVX512_BW-VL"], ["vpaddb" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG FC /r" , "AVX512_BW"], ["vpaddd" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 FE /r" , "AVX512_F-VL"], ["vpaddd" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 FE /r" , "AVX512_F-VL"], ["vpaddd" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 FE /r" , "AVX512_F"], ["vpaddq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 D4 /r" , "AVX512_F-VL"], ["vpaddq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 D4 /r" , "AVX512_F-VL"], ["vpaddq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 D4 /r" , "AVX512_F"], ["vpaddsb" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG EC /r" , "AVX512_BW-VL"], ["vpaddsb" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG EC /r" , "AVX512_BW-VL"], ["vpaddsb" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG EC /r" , "AVX512_BW"], ["vpaddsw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG ED /r" , "AVX512_BW-VL"], ["vpaddsw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG ED /r" , "AVX512_BW-VL"], ["vpaddsw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG ED /r" , "AVX512_BW"], ["vpaddusb" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG DC /r" , "AVX512_BW-VL"], ["vpaddusb" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG DC /r" , "AVX512_BW-VL"], ["vpaddusb" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG DC /r" , "AVX512_BW"], ["vpaddusw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG DD /r" , "AVX512_BW-VL"], ["vpaddusw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG DD /r" , "AVX512_BW-VL"], ["vpaddusw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG DD /r" , "AVX512_BW"], ["vpaddw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG FD /r" , "AVX512_BW-VL"], ["vpaddw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG FD /r" , "AVX512_BW-VL"], ["vpaddw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG FD /r" , "AVX512_BW"], ["vpalignr" , "W:xmm {kz}, xmm, xmm/m128, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.WIG 0F /r ib", "AVX512_BW-VL"], ["vpalignr" , "W:ymm {kz}, ymm, ymm/m256, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.WIG 0F /r ib", "AVX512_BW-VL"], ["vpalignr" , "W:zmm {kz}, zmm, zmm/m512, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.WIG 0F /r ib", "AVX512_BW"], ["vpandd" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 DB /r" , "AVX512_F-VL"], ["vpandd" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 DB /r" , "AVX512_F-VL"], ["vpandd" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 DB /r" , "AVX512_F"], ["vpandnd" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 DF /r" , "AVX512_F-VL"], ["vpandnd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 DF /r" , "AVX512_F-VL"], ["vpandnd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 DF /r" , "AVX512_F"], ["vpandnq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 DF /r" , "AVX512_F-VL"], ["vpandnq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 DF /r" , "AVX512_F-VL"], ["vpandnq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 DF /r" , "AVX512_F"], ["vpandq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 DB /r" , "AVX512_F-VL"], ["vpandq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 DB /r" , "AVX512_F-VL"], ["vpandq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 DB /r" , "AVX512_F"], ["vpavgb" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG E0 /r" , "AVX512_BW-VL"], ["vpavgb" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG E0 /r" , "AVX512_BW-VL"], ["vpavgb" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG E0 /r" , "AVX512_BW"], ["vpavgw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG E3 /r" , "AVX512_BW-VL"], ["vpavgw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG E3 /r" , "AVX512_BW-VL"], ["vpavgw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG E3 /r" , "AVX512_BW"], ["vpblendmb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W0 66 /r" , "AVX512_BW-VL"], ["vpblendmb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W0 66 /r" , "AVX512_BW-VL"], ["vpblendmb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W0 66 /r" , "AVX512_BW"], ["vpblendmd" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 64 /r" , "AVX512_F-VL"], ["vpblendmd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 64 /r" , "AVX512_F-VL"], ["vpblendmd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 64 /r" , "AVX512_F"], ["vpblendmq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 64 /r" , "AVX512_F-VL"], ["vpblendmq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 64 /r" , "AVX512_F-VL"], ["vpblendmq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 64 /r" , "AVX512_F"], ["vpblendmw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W1 66 /r" , "AVX512_BW-VL"], ["vpblendmw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W1 66 /r" , "AVX512_BW-VL"], ["vpblendmw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W1 66 /r" , "AVX512_BW"], ["vpbroadcastb" , "W:xmm {kz}, r32[7:0]" , "RM-T1S" , "EVEX.128.66.0F38.W0 7A /r" , "AVX512_BW-VL"], ["vpbroadcastb" , "W:xmm {kz}, xmm[7:0]/m8" , "RM-T1S" , "EVEX.128.66.0F38.W0 78 /r" , "AVX512_BW-VL"], ["vpbroadcastb" , "W:ymm {kz}, r32[7:0]" , "RM-T1S" , "EVEX.256.66.0F38.W0 7A /r" , "AVX512_BW-VL"], ["vpbroadcastb" , "W:ymm {kz}, xmm[7:0]/m8" , "RM-T1S" , "EVEX.256.66.0F38.W0 78 /r" , "AVX512_BW-VL"], ["vpbroadcastb" , "W:zmm {kz}, r32[7:0]" , "RM-T1S" , "EVEX.512.66.0F38.W0 7A /r" , "AVX512_BW"], ["vpbroadcastb" , "W:zmm {kz}, xmm[7:0]/m8" , "RM-T1S" , "EVEX.512.66.0F38.W0 78 /r" , "AVX512_BW"], ["vpbroadcastd" , "W:xmm {kz}, r32[31:0]" , "RM-T1S" , "EVEX.128.66.0F38.W0 7C /r" , "AVX512_F-VL"], ["vpbroadcastd" , "W:xmm {kz}, xmm[31:0]/m32" , "RM-T1S" , "EVEX.128.66.0F38.W0 58 /r" , "AVX512_F-VL"], ["vpbroadcastd" , "W:ymm {kz}, r32[31:0]" , "RM-T1S" , "EVEX.256.66.0F38.W0 7C /r" , "AVX512_F-VL"], ["vpbroadcastd" , "W:ymm {kz}, xmm[31:0]/m32" , "RM-T1S" , "EVEX.256.66.0F38.W0 58 /r" , "AVX512_F-VL"], ["vpbroadcastd" , "W:zmm {kz}, r32[31:0]" , "RM-T1S" , "EVEX.512.66.0F38.W0 7C /r" , "AVX512_F"], ["vpbroadcastd" , "W:zmm {kz}, xmm[31:0]/m32" , "RM-T1S" , "EVEX.512.66.0F38.W0 58 /r" , "AVX512_F"], ["vpbroadcastmb2q" , "W:xmm, k" , "RM" , "EVEX.128.F3.0F38.W1 2A /r" , "AVX512_CDI-VL"], ["vpbroadcastmb2q" , "W:ymm, k" , "RM" , "EVEX.256.F3.0F38.W1 2A /r" , "AVX512_CDI-VL"], ["vpbroadcastmb2q" , "W:zmm, k" , "RM" , "EVEX.512.F3.0F38.W1 2A /r" , "AVX512_CDI"], ["vpbroadcastmw2d" , "W:xmm, k" , "RM" , "EVEX.128.F3.0F38.W0 3A /r" , "AVX512_CDI-VL"], ["vpbroadcastmw2d" , "W:ymm, k" , "RM" , "EVEX.256.F3.0F38.W0 3A /r" , "AVX512_CDI-VL"], ["vpbroadcastmw2d" , "W:zmm, k" , "RM" , "EVEX.512.F3.0F38.W0 3A /r" , "AVX512_CDI"], ["vpbroadcastq" , "W:xmm {kz}, r64" , "RM-T1S" , "EVEX.128.66.0F38.W1 7C /r" , "AVX512_F-VL X64"], ["vpbroadcastq" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-T1S" , "EVEX.128.66.0F38.W1 59 /r" , "AVX512_F-VL"], ["vpbroadcastq" , "W:ymm {kz}, r64" , "RM-T1S" , "EVEX.256.66.0F38.W1 7C /r" , "AVX512_F-VL X64"], ["vpbroadcastq" , "W:ymm {kz}, xmm[63:0]/m64" , "RM-T1S" , "EVEX.256.66.0F38.W1 59 /r" , "AVX512_F-VL"], ["vpbroadcastq" , "W:zmm {kz}, r64" , "RM-T1S" , "EVEX.512.66.0F38.W1 7C /r" , "AVX512_F X64"], ["vpbroadcastq" , "W:zmm {kz}, xmm[63:0]/m64" , "RM-T1S" , "EVEX.512.66.0F38.W1 59 /r" , "AVX512_F"], ["vpbroadcastw" , "W:xmm {kz}, r32[15:0]" , "RM-T1S" , "EVEX.128.66.0F38.W0 7B /r" , "AVX512_BW-VL"], ["vpbroadcastw" , "W:xmm {kz}, xmm[15:0]/m16" , "RM-T1S" , "EVEX.128.66.0F38.W0 79 /r" , "AVX512_BW-VL"], ["vpbroadcastw" , "W:ymm {kz}, r32[15:0]" , "RM-T1S" , "EVEX.256.66.0F38.W0 7B /r" , "AVX512_BW-VL"], ["vpbroadcastw" , "W:ymm {kz}, xmm[15:0]/m16" , "RM-T1S" , "EVEX.256.66.0F38.W0 79 /r" , "AVX512_BW-VL"], ["vpbroadcastw" , "W:zmm {kz}, r32[15:0]" , "RM-T1S" , "EVEX.512.66.0F38.W0 7B /r" , "AVX512_BW"], ["vpbroadcastw" , "W:zmm {kz}, xmm[15:0]/m16" , "RM-T1S" , "EVEX.512.66.0F38.W0 79 /r" , "AVX512_BW"], ["vpclmulqdq" , "W:xmm, xmm, xmm/m128, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.WIG 44 /r ib", "AVX512_F-VL VPCLMULQDQ"], ["vpclmulqdq" , "W:ymm, ymm, ymm/m256, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.WIG 44 /r ib", "AVX512_F-VL VPCLMULQDQ"], ["vpclmulqdq" , "W:zmm, zmm, zmm/m512, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.WIG 44 /r ib", "AVX512_F VPCLMULQDQ"], ["vpcmpb" , "W:k {k}, xmm, xmm/m128, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W0 3F /r ib" , "AVX512_BW-VL"], ["vpcmpb" , "W:k {k}, ymm, ymm/m256, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W0 3F /r ib" , "AVX512_BW-VL"], ["vpcmpb" , "W:k {k}, zmm, zmm/m512, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W0 3F /r ib" , "AVX512_BW"], ["vpcmpd" , "W:k {k}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W0 1F /r ib" , "AVX512_F-VL"], ["vpcmpd" , "W:k {k}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W0 1F /r ib" , "AVX512_F-VL"], ["vpcmpd" , "W:k {k}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W0 1F /r ib" , "AVX512_F"], ["vpcmpeqb" , "W:k {k},~xmm,~xmm/m128" , "RVM-FV" , "EVEX.128.66.0F.WIG 74 /r" , "AVX512_BW-VL"], ["vpcmpeqb" , "W:k {k},~ymm,~ymm/m256" , "RVM-FV" , "EVEX.256.66.0F.WIG 74 /r" , "AVX512_BW-VL"], ["vpcmpeqb" , "W:k {k},~zmm,~zmm/m512" , "RVM-FV" , "EVEX.512.66.0F.WIG 74 /r" , "AVX512_BW"], ["vpcmpeqd" , "W:k {k},~xmm,~xmm/m128/b32" , "RVM-FVM" , "EVEX.128.66.0F.W0 76 /r" , "AVX512_F-VL"], ["vpcmpeqd" , "W:k {k},~ymm,~ymm/m256/b32" , "RVM-FVM" , "EVEX.256.66.0F.W0 76 /r" , "AVX512_F-VL"], ["vpcmpeqd" , "W:k {k},~zmm,~zmm/m512/b32" , "RVM-FVM" , "EVEX.512.66.0F.W0 76 /r" , "AVX512_F"], ["vpcmpeqq" , "W:k {k},~xmm,~xmm/m128/b64" , "RVM-FVM" , "EVEX.128.66.0F38.W1 29 /r" , "AVX512_F-VL"], ["vpcmpeqq" , "W:k {k},~ymm,~ymm/m256/b64" , "RVM-FVM" , "EVEX.256.66.0F38.W1 29 /r" , "AVX512_F-VL"], ["vpcmpeqq" , "W:k {k},~zmm,~zmm/m512/b64" , "RVM-FVM" , "EVEX.512.66.0F38.W1 29 /r" , "AVX512_F"], ["vpcmpeqw" , "W:k {k},~xmm,~xmm/m128" , "RVM-FV" , "EVEX.128.66.0F.WIG 75 /r" , "AVX512_BW-VL"], ["vpcmpeqw" , "W:k {k},~ymm,~ymm/m256" , "RVM-FV" , "EVEX.256.66.0F.WIG 75 /r" , "AVX512_BW-VL"], ["vpcmpeqw" , "W:k {k},~zmm,~zmm/m512" , "RVM-FV" , "EVEX.512.66.0F.WIG 75 /r" , "AVX512_BW"], ["vpcmpgtb" , "W:k {k}, xmm, xmm/m128" , "RVM-FV" , "EVEX.128.66.0F.WIG 64 /r" , "AVX512_BW-VL"], ["vpcmpgtb" , "W:k {k}, ymm, ymm/m256" , "RVM-FV" , "EVEX.256.66.0F.WIG 64 /r" , "AVX512_BW-VL"], ["vpcmpgtb" , "W:k {k}, zmm, zmm/m512" , "RVM-FV" , "EVEX.512.66.0F.WIG 64 /r" , "AVX512_BW"], ["vpcmpgtd" , "W:k {k}, xmm, xmm/m128/b32" , "RVM-FVM" , "EVEX.128.66.0F.W0 66 /r" , "AVX512_F-VL"], ["vpcmpgtd" , "W:k {k}, ymm, ymm/m256/b32" , "RVM-FVM" , "EVEX.256.66.0F.W0 66 /r" , "AVX512_F-VL"], ["vpcmpgtd" , "W:k {k}, zmm, zmm/m512/b32" , "RVM-FVM" , "EVEX.512.66.0F.W0 66 /r" , "AVX512_F"], ["vpcmpgtq" , "W:k {k}, xmm, xmm/m128/b64" , "RVM-FVM" , "EVEX.128.66.0F38.W1 37 /r" , "AVX512_F-VL"], ["vpcmpgtq" , "W:k {k}, ymm, ymm/m256/b64" , "RVM-FVM" , "EVEX.256.66.0F38.W1 37 /r" , "AVX512_F-VL"], ["vpcmpgtq" , "W:k {k}, zmm, zmm/m512/b64" , "RVM-FVM" , "EVEX.512.66.0F38.W1 37 /r" , "AVX512_F"], ["vpcmpgtw" , "W:k {k}, xmm, xmm/m128" , "RVM-FV" , "EVEX.128.66.0F.WIG 65 /r" , "AVX512_BW-VL"], ["vpcmpgtw" , "W:k {k}, ymm, ymm/m256" , "RVM-FV" , "EVEX.256.66.0F.WIG 65 /r" , "AVX512_BW-VL"], ["vpcmpgtw" , "W:k {k}, zmm, zmm/m512" , "RVM-FV" , "EVEX.512.66.0F.WIG 65 /r" , "AVX512_BW"], ["vpcmpq" , "W:k {k}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W1 1F /r ib" , "AVX512_F-VL"], ["vpcmpq" , "W:k {k}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 1F /r ib" , "AVX512_F-VL"], ["vpcmpq" , "W:k {k}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 1F /r ib" , "AVX512_F"], ["vpcmpub" , "W:k {k}, xmm, xmm/m128, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W0 3E /r ib" , "AVX512_BW-VL"], ["vpcmpub" , "W:k {k}, ymm, ymm/m256, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W0 3E /r ib" , "AVX512_BW-VL"], ["vpcmpub" , "W:k {k}, zmm, zmm/m512, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W0 3E /r ib" , "AVX512_BW"], ["vpcmpud" , "W:k {k}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W0 1E /r ib" , "AVX512_F-VL"], ["vpcmpud" , "W:k {k}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W0 1E /r ib" , "AVX512_F-VL"], ["vpcmpud" , "W:k {k}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W0 1E /r ib" , "AVX512_F"], ["vpcmpuq" , "W:k {k}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W1 1E /r ib" , "AVX512_F-VL"], ["vpcmpuq" , "W:k {k}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 1E /r ib" , "AVX512_F-VL"], ["vpcmpuq" , "W:k {k}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 1E /r ib" , "AVX512_F"], ["vpcmpuw" , "W:k {k}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W1 3E /r ib" , "AVX512_BW-VL"], ["vpcmpuw" , "W:k {k}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W1 3E /r ib" , "AVX512_BW-VL"], ["vpcmpuw" , "W:k {k}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W1 3E /r ib" , "AVX512_BW"], ["vpcmpw" , "W:k {k}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W1 3F /r ib" , "AVX512_BW-VL"], ["vpcmpw" , "W:k {k}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W1 3F /r ib" , "AVX512_BW-VL"], ["vpcmpw" , "W:k {k}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W1 3F /r ib" , "AVX512_BW"], ["vpcompressb" , "W:xmm/m128 {kz}, xmm" , "RVM-T1S" , "EVEX.128.66.0F38.W0 63 /r" , "AVX512_VBMI2-VL"], ["vpcompressb" , "W:ymm/m256 {kz}, ymm" , "RVM-T1S" , "EVEX.256.66.0F38.W0 63 /r" , "AVX512_VBMI2-VL"], ["vpcompressb" , "W:zmm/m512 {kz}, zmm" , "RVM-T1S" , "EVEX.512.66.0F38.W0 63 /r" , "AVX512_VBMI2"], ["vpcompressw" , "W:xmm/m128 {kz}, xmm" , "RVM-T1S" , "EVEX.128.66.0F38.W1 63 /r" , "AVX512_VBMI2-VL"], ["vpcompressw" , "W:ymm/m256 {kz}, ymm" , "RVM-T1S" , "EVEX.256.66.0F38.W1 63 /r" , "AVX512_VBMI2-VL"], ["vpcompressw" , "W:zmm/m512 {kz}, zmm" , "RVM-T1S" , "EVEX.512.66.0F38.W1 63 /r" , "AVX512_VBMI2"], ["vpcompressd" , "W:xmm/m128 {kz}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W0 8B /r" , "AVX512_F-VL"], ["vpcompressd" , "W:ymm/m256 {kz}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W0 8B /r" , "AVX512_F-VL"], ["vpcompressd" , "W:zmm/m512 {kz}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W0 8B /r" , "AVX512_F"], ["vpcompressq" , "W:xmm/m128 {kz}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W1 8B /r" , "AVX512_F-VL"], ["vpcompressq" , "W:ymm/m256 {kz}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W1 8B /r" , "AVX512_F-VL"], ["vpcompressq" , "W:zmm/m512 {kz}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W1 8B /r" , "AVX512_F"], ["vpconflictd" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.0F38.W0 C4 /r" , "AVX512_CDI-VL"], ["vpconflictd" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.0F38.W0 C4 /r" , "AVX512_CDI-VL"], ["vpconflictd" , "W:zmm {kz}, zmm/m512/b32" , "RM-FV" , "EVEX.512.66.0F38.W0 C4 /r" , "AVX512_CDI"], ["vpconflictq" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.0F38.W1 C4 /r" , "AVX512_CDI-VL"], ["vpconflictq" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.0F38.W1 C4 /r" , "AVX512_CDI-VL"], ["vpconflictq" , "W:zmm {kz}, zmm/m512/b32" , "RM-FV" , "EVEX.512.66.0F38.W1 C4 /r" , "AVX512_CDI"], ["vpdpbusd" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 50 /r" , "AVX512_VNNI-VL"], ["vpdpbusd" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 50 /r" , "AVX512_VNNI-VL"], ["vpdpbusd" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 50 /r" , "AVX512_VNNI"], ["vpdpbusds" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 51 /r" , "AVX512_VNNI-VL"], ["vpdpbusds" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 51 /r" , "AVX512_VNNI-VL"], ["vpdpbusds" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 51 /r" , "AVX512_VNNI"], ["vpdpwssd" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 52 /r" , "AVX512_VNNI-VL"], ["vpdpwssd" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 52 /r" , "AVX512_VNNI-VL"], ["vpdpwssd" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 52 /r" , "AVX512_VNNI"], ["vpdpwssds" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 53 /r" , "AVX512_VNNI-VL"], ["vpdpwssds" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 53 /r" , "AVX512_VNNI-VL"], ["vpdpwssds" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 53 /r" , "AVX512_VNNI"], ["vpexpandb" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F38.W0 62 /r" , "AVX512_VBMI2-VL"], ["vpexpandb" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F38.W0 62 /r" , "AVX512_VBMI2-VL"], ["vpexpandb" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F38.W0 62 /r" , "AVX512_VBMI2"], ["vpexpandw" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F38.W1 62 /r" , "AVX512_VBMI2-VL"], ["vpexpandw" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F38.W1 62 /r" , "AVX512_VBMI2-VL"], ["vpexpandw" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F38.W1 62 /r" , "AVX512_VBMI2"], ["vpermb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W0 8D /r" , "AVX512_VBMI-VL"], ["vpermb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W0 8D /r" , "AVX512_VBMI-VL"], ["vpermb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W0 8D /r" , "AVX512_VBMI"], ["vpermd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 36 /r" , "AVX512_F-VL"], ["vpermd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 36 /r" , "AVX512_F"], ["vpermi2b" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W0 75 /r" , "AVX512_VBMI-VL"], ["vpermi2b" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W0 75 /r" , "AVX512_VBMI-VL"], ["vpermi2b" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W0 75 /r" , "AVX512_VBMI"], ["vpermi2d" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 76 /r" , "AVX512_F-VL"], ["vpermi2d" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 76 /r" , "AVX512_F-VL"], ["vpermi2d" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 76 /r" , "AVX512_F"], ["vpermi2pd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 77 /r" , "AVX512_F-VL"], ["vpermi2pd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 77 /r" , "AVX512_F-VL"], ["vpermi2pd" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 77 /r" , "AVX512_F"], ["vpermi2ps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 77 /r" , "AVX512_F-VL"], ["vpermi2ps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 77 /r" , "AVX512_F-VL"], ["vpermi2ps" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 77 /r" , "AVX512_F"], ["vpermi2q" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 76 /r" , "AVX512_F-VL"], ["vpermi2q" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 76 /r" , "AVX512_F-VL"], ["vpermi2q" , "X:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 76 /r" , "AVX512_F"], ["vpermi2w" , "X:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W1 75 /r" , "AVX512_BW-VL"], ["vpermi2w" , "X:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W1 75 /r" , "AVX512_BW-VL"], ["vpermi2w" , "X:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W1 75 /r" , "AVX512_BW"], ["vpermilpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 0D /r" , "AVX512_F-VL"], ["vpermilpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 0D /r" , "AVX512_F-VL"], ["vpermilpd" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 0D /r" , "AVX512_F"], ["vpermilpd" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W1 05 /r ib" , "AVX512_F-VL"], ["vpermilpd" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W1 05 /r ib" , "AVX512_F-VL"], ["vpermilpd" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "RMI-FV" , "EVEX.512.66.0F3A.W1 05 /r ib" , "AVX512_F"], ["vpermilps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 0C /r" , "AVX512_F-VL"], ["vpermilps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 0C /r" , "AVX512_F-VL"], ["vpermilps" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 0C /r" , "AVX512_F"], ["vpermilps" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W0 04 /r ib" , "AVX512_F-VL"], ["vpermilps" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W0 04 /r ib" , "AVX512_F-VL"], ["vpermilps" , "W:zmm {kz}, zmm/m512/b32, ib/ub" , "RMI-FV" , "EVEX.512.66.0F3A.W0 04 /r ib" , "AVX512_F"], ["vpermpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 16 /r" , "AVX512_F-VL"], ["vpermpd" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 16 /r" , "AVX512_F"], ["vpermpd" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W1 01 /r ib" , "AVX512_F-VL"], ["vpermpd" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "RMI-FV" , "EVEX.512.66.0F3A.W1 01 /r ib" , "AVX512_F"], ["vpermps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 16 /r" , "AVX512_F-VL"], ["vpermps" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 16 /r" , "AVX512_F"], ["vpermq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 36 /r" , "AVX512_F-VL"], ["vpermq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 36 /r" , "AVX512_F"], ["vpermq" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W1 00 /r ib" , "AVX512_F-VL"], ["vpermq" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "RMI-FV" , "EVEX.512.66.0F3A.W1 00 /r ib" , "AVX512_F"], ["vpermt2b" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W0 7D /r" , "AVX512_VBMI-VL"], ["vpermt2b" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W0 7D /r" , "AVX512_VBMI-VL"], ["vpermt2b" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W0 7D /r" , "AVX512_VBMI"], ["vpermt2d" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 7E /r" , "AVX512_F-VL"], ["vpermt2d" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 7E /r" , "AVX512_F-VL"], ["vpermt2d" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 7E /r" , "AVX512_F"], ["vpermt2pd" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 7F /r" , "AVX512_F-VL"], ["vpermt2pd" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 7F /r" , "AVX512_F-VL"], ["vpermt2pd" , "X:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 7F /r" , "AVX512_F"], ["vpermt2ps" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 7F /r" , "AVX512_F-VL"], ["vpermt2ps" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 7F /r" , "AVX512_F-VL"], ["vpermt2ps" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 7F /r" , "AVX512_F"], ["vpermt2q" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 7E /r" , "AVX512_F-VL"], ["vpermt2q" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 7E /r" , "AVX512_F-VL"], ["vpermt2q" , "X:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 7E /r" , "AVX512_F"], ["vpermt2w" , "X:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W1 7D /r" , "AVX512_BW-VL"], ["vpermt2w" , "X:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W1 7D /r" , "AVX512_BW-VL"], ["vpermt2w" , "X:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W1 7D /r" , "AVX512_BW"], ["vpermw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W1 8D /r" , "AVX512_BW-VL"], ["vpermw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W1 8D /r" , "AVX512_BW-VL"], ["vpermw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W1 8D /r" , "AVX512_BW"], ["vpexpandd" , "W:xmm {kz}, xmm/m128" , "RM-T1S" , "EVEX.128.66.0F38.W0 89 /r" , "AVX512_F-VL"], ["vpexpandd" , "W:ymm {kz}, ymm/m256" , "RM-T1S" , "EVEX.256.66.0F38.W0 89 /r" , "AVX512_F-VL"], ["vpexpandd" , "W:zmm {kz}, zmm/m512" , "RM-T1S" , "EVEX.512.66.0F38.W0 89 /r" , "AVX512_F"], ["vpexpandq" , "W:xmm {kz}, xmm/m128" , "RM-T1S" , "EVEX.128.66.0F38.W1 89 /r" , "AVX512_F-VL"], ["vpexpandq" , "W:ymm {kz}, ymm/m256" , "RM-T1S" , "EVEX.256.66.0F38.W1 89 /r" , "AVX512_F-VL"], ["vpexpandq" , "W:zmm {kz}, zmm/m512" , "RM-T1S" , "EVEX.512.66.0F38.W1 89 /r" , "AVX512_F"], ["vpextrb" , "W:r32[7:0]/m8 , xmm, ib/ub" , "MRI-T1S" , "EVEX.128.66.0F3A.WIG 14 /r ib", "AVX512_BW"], ["vpextrd" , "W:r32/m32, xmm, ib/ub" , "MRI-T1S" , "EVEX.128.66.0F3A.W0 16 /r ib" , "AVX512_DQ"], ["vpextrq" , "W:r64/m64, xmm, ib/ub" , "MRI-T1S" , "EVEX.128.66.0F3A.W1 16 /r ib" , "AVX512_DQ X64"], ["vpextrw" , "W:r32[15:0], xmm, ib/ub" , "RMI" , "EVEX.128.66.0F.WIG C5 /r ib" , "AVX512_BW"], ["vpextrw" , "W:r32[15:0]/m16, xmm, ib/ub" , "MRI-T1S" , "EVEX.128.66.0F3A.WIG 15 /r ib", "AVX512_BW"], ["vpgatherdd" , "X:xmm {k}, vm32x" , "RM-T1S" , "EVEX.128.66.0F38.W0 90" , "AVX512_F-VL"], ["vpgatherdd" , "X:ymm {k}, vm32y" , "RM-T1S" , "EVEX.256.66.0F38.W0 90" , "AVX512_F-VL"], ["vpgatherdd" , "X:zmm {k}, vm32z" , "RM-T1S" , "EVEX.512.66.0F38.W0 90" , "AVX512_F"], ["vpgatherdq" , "X:xmm {k}, vm32x" , "RM-T1S" , "EVEX.128.66.0F38.W1 90" , "AVX512_F-VL"], ["vpgatherdq" , "X:ymm {k}, vm32x" , "RM-T1S" , "EVEX.256.66.0F38.W1 90" , "AVX512_F-VL"], ["vpgatherdq" , "X:zmm {k}, vm32y" , "RM-T1S" , "EVEX.512.66.0F38.W1 90" , "AVX512_F"], ["vpgatherqd" , "X:xmm {k}, vm64x" , "RM-T1S" , "EVEX.128.66.0F38.W0 91" , "AVX512_F-VL"], ["vpgatherqd" , "X:xmm {k}, vm64y" , "RM-T1S" , "EVEX.256.66.0F38.W0 91" , "AVX512_F-VL"], ["vpgatherqd" , "X:ymm {k}, vm64z" , "RM-T1S" , "EVEX.512.66.0F38.W0 91" , "AVX512_F"], ["vpgatherqq" , "X:xmm {k}, vm64x" , "RM-T1S" , "EVEX.128.66.0F38.W1 91" , "AVX512_F-VL"], ["vpgatherqq" , "X:ymm {k}, vm64y" , "RM-T1S" , "EVEX.256.66.0F38.W1 91" , "AVX512_F-VL"], ["vpgatherqq" , "X:zmm {k}, vm64z" , "RM-T1S" , "EVEX.512.66.0F38.W1 91" , "AVX512_F"], ["vpinsrb" , "W:xmm {kz}, xmm, r32[7:0]/m8, ib/ub" , "RVMI-T1S", "EVEX.128.66.0F3A.WIG 20 /r ib", "AVX512_BW"], ["vpinsrd" , "W:xmm {kz}, xmm, r32/m32, ib/ub" , "RVMI-T1S", "EVEX.128.66.0F3A.W0 22 /r ib" , "AVX512_DQ"], ["vpinsrq" , "W:xmm {kz}, xmm, r64/m64, ib/ub" , "RVMI-T1S", "EVEX.128.66.0F3A.W1 22 /r ib" , "AVX512_DQ X64"], ["vpinsrw" , "W:xmm {kz}, xmm, r32[15:0]/m16, ib/ub" , "RVMI-T1S", "EVEX.128.66.0F.WIG C4 /r ib" , "AVX512_BW"], ["vplzcntd" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.0F38.W0 44 /r" , "AVX512_CDI-VL"], ["vplzcntd" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.0F38.W0 44 /r" , "AVX512_CDI-VL"], ["vplzcntd" , "W:zmm {kz}, zmm/m512/b32" , "RM-FV" , "EVEX.512.66.0F38.W0 44 /r" , "AVX512_CDI"], ["vplzcntq" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F38.W1 44 /r" , "AVX512_CDI-VL"], ["vplzcntq" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F38.W1 44 /r" , "AVX512_CDI-VL"], ["vplzcntq" , "W:zmm {kz}, zmm/m512/b64" , "RM-FV" , "EVEX.512.66.0F38.W1 44 /r" , "AVX512_CDI"], ["vpmadd52luq" , "X:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 B4 /r" , "AVX512_IFMA-VL"], ["vpmadd52luq" , "X:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 B4 /r" , "AVX512_IFMA-VL"], ["vpmadd52luq" , "X:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 B4 /r" , "AVX512_IFMA"], ["vpmadd52huq" , "X:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 B5 /r" , "AVX512_IFMA-VL"], ["vpmadd52huq" , "X:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 B5 /r" , "AVX512_IFMA-VL"], ["vpmadd52huq" , "X:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 B5 /r" , "AVX512_IFMA"], ["vpmaddubsw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG 04 /r" , "AVX512_BW-VL"], ["vpmaddubsw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG 04 /r" , "AVX512_BW-VL"], ["vpmaddubsw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG 04 /r" , "AVX512_BW"], ["vpmaddwd" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG F5 /r" , "AVX512_BW-VL"], ["vpmaddwd" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG F5 /r" , "AVX512_BW-VL"], ["vpmaddwd" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG F5 /r" , "AVX512_BW"], ["vpmaxsb" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG 3C /r" , "AVX512_BW-VL"], ["vpmaxsb" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG 3C /r" , "AVX512_BW-VL"], ["vpmaxsb" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG 3C /r" , "AVX512_BW"], ["vpmaxsd" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 3D /r" , "AVX512_F-VL"], ["vpmaxsd" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 3D /r" , "AVX512_F-VL"], ["vpmaxsd" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 3D /r" , "AVX512_F"], ["vpmaxsq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 3D /r" , "AVX512_F-VL"], ["vpmaxsq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 3D /r" , "AVX512_F-VL"], ["vpmaxsq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 3D /r" , "AVX512_F"], ["vpmaxsw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG EE /r" , "AVX512_BW-VL"], ["vpmaxsw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG EE /r" , "AVX512_BW-VL"], ["vpmaxsw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG EE /r" , "AVX512_BW"], ["vpmaxub" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG DE /r" , "AVX512_BW-VL"], ["vpmaxub" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG DE /r" , "AVX512_BW-VL"], ["vpmaxub" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG DE /r" , "AVX512_BW"], ["vpmaxud" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 3F /r" , "AVX512_F-VL"], ["vpmaxud" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 3F /r" , "AVX512_F-VL"], ["vpmaxud" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 3F /r" , "AVX512_F"], ["vpmaxuq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 3F /r" , "AVX512_F-VL"], ["vpmaxuq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 3F /r" , "AVX512_F-VL"], ["vpmaxuq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 3F /r" , "AVX512_F"], ["vpmaxuw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG 3E /r" , "AVX512_BW-VL"], ["vpmaxuw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG 3E /r" , "AVX512_BW-VL"], ["vpmaxuw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG 3E /r" , "AVX512_BW"], ["vpminsb" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG 38 /r" , "AVX512_BW-VL"], ["vpminsb" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG 38 /r" , "AVX512_BW-VL"], ["vpminsb" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG 38 /r" , "AVX512_BW"], ["vpminsd" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 39 /r" , "AVX512_F-VL"], ["vpminsd" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 39 /r" , "AVX512_F-VL"], ["vpminsd" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 39 /r" , "AVX512_F"], ["vpminsq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 39 /r" , "AVX512_F-VL"], ["vpminsq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 39 /r" , "AVX512_F-VL"], ["vpminsq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 39 /r" , "AVX512_F"], ["vpminsw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG EA /r" , "AVX512_BW-VL"], ["vpminsw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG EA /r" , "AVX512_BW-VL"], ["vpminsw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG EA /r" , "AVX512_BW"], ["vpminub" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F DA /r" , "AVX512_BW-VL"], ["vpminub" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F DA /r" , "AVX512_BW-VL"], ["vpminub" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F DA /r" , "AVX512_BW"], ["vpminud" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 3B /r" , "AVX512_F-VL"], ["vpminud" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 3B /r" , "AVX512_F-VL"], ["vpminud" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 3B /r" , "AVX512_F"], ["vpminuq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 3B /r" , "AVX512_F-VL"], ["vpminuq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 3B /r" , "AVX512_F-VL"], ["vpminuq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 3B /r" , "AVX512_F"], ["vpminuw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38 3A /r" , "AVX512_BW-VL"], ["vpminuw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38 3A /r" , "AVX512_BW-VL"], ["vpminuw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38 3A /r" , "AVX512_BW"], ["vpmovb2m" , "W:k, xmm" , "RM" , "EVEX.128.F3.0F38.W0 29 /r" , "AVX512_BW-VL"], ["vpmovb2m" , "W:k, ymm" , "RM" , "EVEX.256.F3.0F38.W0 29 /r" , "AVX512_BW-VL"], ["vpmovb2m" , "W:k, zmm" , "RM" , "EVEX.512.F3.0F38.W0 29 /r" , "AVX512_BW"], ["vpmovd2m" , "W:k, xmm" , "RM" , "EVEX.128.F3.0F38.W0 39 /r" , "AVX512_DQ-VL"], ["vpmovd2m" , "W:k, ymm" , "RM" , "EVEX.256.F3.0F38.W0 39 /r" , "AVX512_DQ-VL"], ["vpmovd2m" , "W:k, zmm" , "RM" , "EVEX.512.F3.0F38.W0 39 /r" , "AVX512_DQ"], ["vpmovdb" , "W:xmm[31:0]/m32 {kz}, xmm" , "MR-QVM" , "EVEX.128.F3.0F38.W0 31 /r" , "AVX512_F-VL"], ["vpmovdb" , "W:xmm[63:0]/m64 {kz}, ymm" , "MR-QVM" , "EVEX.256.F3.0F38.W0 31 /r" , "AVX512_F-VL"], ["vpmovdb" , "W:xmm/m128 {kz}, zmm" , "MR-QVM" , "EVEX.512.F3.0F38.W0 31 /r" , "AVX512_F"], ["vpmovdw" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 33 /r" , "AVX512_F-VL"], ["vpmovdw" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 33 /r" , "AVX512_F-VL"], ["vpmovdw" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 33 /r" , "AVX512_F"], ["vpmovm2b" , "W:xmm, k" , "RM" , "EVEX.128.F3.0F38.W0 28 /r" , "AVX512_BW-VL"], ["vpmovm2b" , "W:ymm, k" , "RM" , "EVEX.256.F3.0F38.W0 28 /r" , "AVX512_BW-VL"], ["vpmovm2b" , "W:zmm, k" , "RM" , "EVEX.512.F3.0F38.W0 28 /r" , "AVX512_BW"], ["vpmovm2d" , "W:xmm, k" , "RM" , "EVEX.128.F3.0F38.W0 38 /r" , "AVX512_DQ-VL"], ["vpmovm2d" , "W:ymm, k" , "RM" , "EVEX.256.F3.0F38.W0 38 /r" , "AVX512_DQ-VL"], ["vpmovm2d" , "W:zmm, k" , "RM" , "EVEX.512.F3.0F38.W0 38 /r" , "AVX512_DQ"], ["vpmovm2q" , "W:xmm, k" , "RM" , "EVEX.128.F3.0F38.W1 38 /r" , "AVX512_DQ-VL"], ["vpmovm2q" , "W:ymm, k" , "RM" , "EVEX.256.F3.0F38.W1 38 /r" , "AVX512_DQ-VL"], ["vpmovm2q" , "W:zmm, k" , "RM" , "EVEX.512.F3.0F38.W1 38 /r" , "AVX512_DQ"], ["vpmovm2w" , "W:xmm, k" , "RM" , "EVEX.128.F3.0F38.W1 28 /r" , "AVX512_BW-VL"], ["vpmovm2w" , "W:ymm, k" , "RM" , "EVEX.256.F3.0F38.W1 28 /r" , "AVX512_BW-VL"], ["vpmovm2w" , "W:zmm, k" , "RM" , "EVEX.512.F3.0F38.W1 28 /r" , "AVX512_BW"], ["vpmovq2m" , "W:k, xmm" , "RM" , "EVEX.128.F3.0F38.W1 39 /r" , "AVX512_DQ-VL"], ["vpmovq2m" , "W:k, ymm" , "RM" , "EVEX.256.F3.0F38.W1 39 /r" , "AVX512_DQ-VL"], ["vpmovq2m" , "W:k, zmm" , "RM" , "EVEX.512.F3.0F38.W1 39 /r" , "AVX512_DQ"], ["vpmovqb" , "W:xmm[15:0]/m16 {kz}, xmm" , "MR-OVM" , "EVEX.128.F3.0F38.W0 32 /r" , "AVX512_F-VL"], ["vpmovqb" , "W:xmm[31:0]/m32 {kz}, ymm" , "MR-OVM" , "EVEX.256.F3.0F38.W0 32 /r" , "AVX512_F-VL"], ["vpmovqb" , "W:xmm[63:0]/m64 {kz}, zmm" , "MR-OVM" , "EVEX.512.F3.0F38.W0 32 /r" , "AVX512_F"], ["vpmovqd" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 35 /r" , "AVX512_F-VL"], ["vpmovqd" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 35 /r" , "AVX512_F-VL"], ["vpmovqd" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 35 /r" , "AVX512_F"], ["vpmovqw" , "W:xmm[31:0]/m32 {kz}, xmm" , "MR-QVM" , "EVEX.128.F3.0F38.W0 34 /r" , "AVX512_F-VL"], ["vpmovqw" , "W:xmm[63:0]/m64 {kz}, ymm" , "MR-QVM" , "EVEX.256.F3.0F38.W0 34 /r" , "AVX512_F-VL"], ["vpmovqw" , "W:xmm/m128 {kz}, zmm" , "MR-QVM" , "EVEX.512.F3.0F38.W0 34 /r" , "AVX512_F"], ["vpmovsdb" , "W:xmm[31:0]/m32 {kz}, xmm" , "MR-QVM" , "EVEX.128.F3.0F38.W0 21 /r" , "AVX512_F-VL"], ["vpmovsdb" , "W:xmm[63:0]/m64 {kz}, ymm" , "MR-QVM" , "EVEX.256.F3.0F38.W0 21 /r" , "AVX512_F-VL"], ["vpmovsdb" , "W:xmm/m128 {kz}, zmm" , "MR-QVM" , "EVEX.512.F3.0F38.W0 21 /r" , "AVX512_F"], ["vpmovsdw" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 23 /r" , "AVX512_F-VL"], ["vpmovsdw" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 23 /r" , "AVX512_F-VL"], ["vpmovsdw" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 23 /r" , "AVX512_F"], ["vpmovsqb" , "W:xmm[15:0]/m16 {kz}, xmm" , "MR-OVM" , "EVEX.128.F3.0F38.W0 22 /r" , "AVX512_F-VL"], ["vpmovsqb" , "W:xmm[31:0]/m32 {kz}, ymm" , "MR-OVM" , "EVEX.256.F3.0F38.W0 22 /r" , "AVX512_F-VL"], ["vpmovsqb" , "W:xmm[63:0]/m64 {kz}, zmm" , "MR-OVM" , "EVEX.512.F3.0F38.W0 22 /r" , "AVX512_F"], ["vpmovsqd" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 25 /r" , "AVX512_F-VL"], ["vpmovsqd" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 25 /r" , "AVX512_F-VL"], ["vpmovsqd" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 25 /r" , "AVX512_F"], ["vpmovsqw" , "W:xmm[31:0]/m32 {kz}, xmm" , "MR-QVM" , "EVEX.128.F3.0F38.W0 24 /r" , "AVX512_F-VL"], ["vpmovsqw" , "W:xmm[63:0]/m64 {kz}, ymm" , "MR-QVM" , "EVEX.256.F3.0F38.W0 24 /r" , "AVX512_F-VL"], ["vpmovsqw" , "W:xmm/m128 {kz}, zmm" , "MR-QVM" , "EVEX.512.F3.0F38.W0 24 /r" , "AVX512_F"], ["vpmovswb" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 20 /r" , "AVX512_BW-VL"], ["vpmovswb" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 20 /r" , "AVX512_BW-VL"], ["vpmovswb" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 20 /r" , "AVX512_BW"], ["vpmovsxbd" , "W:xmm {kz}, xmm[31:0]/m32" , "RM-QVM" , "EVEX.128.66.0F38.WIG 21 /r" , "AVX512_F-VL"], ["vpmovsxbd" , "W:ymm {kz}, xmm[63:0]/m64" , "RM-QVM" , "EVEX.256.66.0F38.WIG 21 /r" , "AVX512_F-VL"], ["vpmovsxbd" , "W:zmm {kz}, xmm/m128" , "RM-QVM" , "EVEX.512.66.0F38.WIG 21 /r" , "AVX512_F"], ["vpmovsxbq" , "W:xmm {kz}, xmm[15:0]/m16" , "RM-OVM" , "EVEX.128.66.0F38.WIG 22 /r" , "AVX512_F-VL"], ["vpmovsxbq" , "W:ymm {kz}, xmm[31:0]/m32" , "RM-OVM" , "EVEX.256.66.0F38.WIG 22 /r" , "AVX512_F-VL"], ["vpmovsxbq" , "W:zmm {kz}, xmm[63:0]/m64" , "RM-OVM" , "EVEX.512.66.0F38.WIG 22 /r" , "AVX512_F"], ["vpmovsxbw" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-HVM" , "EVEX.128.66.0F38.WIG 20 /r" , "AVX512_BW-VL"], ["vpmovsxbw" , "W:ymm {kz}, xmm/m128" , "RM-HVM" , "EVEX.256.66.0F38.WIG 20 /r" , "AVX512_BW-VL"], ["vpmovsxbw" , "W:zmm {kz}, ymm/m256" , "RM-HVM" , "EVEX.512.66.0F38.WIG 20 /r" , "AVX512_BW"], ["vpmovsxdq" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-HVM" , "EVEX.128.66.0F38.W0 25 /r" , "AVX512_F-VL"], ["vpmovsxdq" , "W:ymm {kz}, xmm/m128" , "RM-HVM" , "EVEX.256.66.0F38.W0 25 /r" , "AVX512_F-VL"], ["vpmovsxdq" , "W:zmm {kz}, ymm/m256" , "RM-HVM" , "EVEX.512.66.0F38.W0 25 /r" , "AVX512_F"], ["vpmovsxwd" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-HVM" , "EVEX.128.66.0F38.WIG 23 /r" , "AVX512_F-VL"], ["vpmovsxwd" , "W:ymm {kz}, xmm/m128" , "RM-HVM" , "EVEX.256.66.0F38.WIG 23 /r" , "AVX512_F-VL"], ["vpmovsxwd" , "W:zmm {kz}, ymm/m256" , "RM-HVM" , "EVEX.512.66.0F38.WIG 23 /r" , "AVX512_F"], ["vpmovsxwq" , "W:xmm {kz}, xmm[31:0]/m32" , "RM-QVM" , "EVEX.128.66.0F38.WIG 24 /r" , "AVX512_F-VL"], ["vpmovsxwq" , "W:ymm {kz}, xmm[63:0]/m64" , "RM-QVM" , "EVEX.256.66.0F38.WIG 24 /r" , "AVX512_F-VL"], ["vpmovsxwq" , "W:zmm {kz}, xmm/m128" , "RM-QVM" , "EVEX.512.66.0F38.WIG 24 /r" , "AVX512_F"], ["vpmovusdb" , "W:xmm[31:0]/m32 {kz}, xmm" , "MR-QVM" , "EVEX.128.F3.0F38.W0 11 /r" , "AVX512_F-VL"], ["vpmovusdb" , "W:xmm[63:0]/m64 {kz}, ymm" , "MR-QVM" , "EVEX.256.F3.0F38.W0 11 /r" , "AVX512_F-VL"], ["vpmovusdb" , "W:xmm/m128 {kz}, zmm" , "MR-QVM" , "EVEX.512.F3.0F38.W0 11 /r" , "AVX512_F"], ["vpmovusdw" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 13 /r" , "AVX512_F-VL"], ["vpmovusdw" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 13 /r" , "AVX512_F-VL"], ["vpmovusdw" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 13 /r" , "AVX512_F"], ["vpmovusqb" , "W:xmm[15:0]/m16 {kz}, xmm" , "MR-OVM" , "EVEX.128.F3.0F38.W0 12 /r" , "AVX512_F-VL"], ["vpmovusqb" , "W:xmm[31:0]/m32 {kz}, ymm" , "MR-OVM" , "EVEX.256.F3.0F38.W0 12 /r" , "AVX512_F-VL"], ["vpmovusqb" , "W:xmm[63:0]/m64 {kz}, zmm" , "MR-OVM" , "EVEX.512.F3.0F38.W0 12 /r" , "AVX512_F"], ["vpmovusqd" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 15 /r" , "AVX512_F-VL"], ["vpmovusqd" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 15 /r" , "AVX512_F-VL"], ["vpmovusqd" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 15 /r" , "AVX512_F"], ["vpmovusqw" , "W:xmm[31:0]/m32 {kz}, xmm" , "MR-QVM" , "EVEX.128.F3.0F38.W0 14 /r" , "AVX512_F-VL"], ["vpmovusqw" , "W:xmm[63:0]/m64 {kz}, ymm" , "MR-QVM" , "EVEX.256.F3.0F38.W0 14 /r" , "AVX512_F-VL"], ["vpmovusqw" , "W:xmm/m128 {kz}, zmm" , "MR-QVM" , "EVEX.512.F3.0F38.W0 14 /r" , "AVX512_F"], ["vpmovuswb" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 10 /r" , "AVX512_BW-VL"], ["vpmovuswb" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 10 /r" , "AVX512_BW-VL"], ["vpmovuswb" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 10 /r" , "AVX512_BW"], ["vpmovw2m" , "W:k, xmm" , "RM" , "EVEX.128.F3.0F38.W1 29 /r" , "AVX512_BW-VL"], ["vpmovw2m" , "W:k, ymm" , "RM" , "EVEX.256.F3.0F38.W1 29 /r" , "AVX512_BW-VL"], ["vpmovw2m" , "W:k, zmm" , "RM" , "EVEX.512.F3.0F38.W1 29 /r" , "AVX512_BW"], ["vpmovwb" , "W:xmm[63:0]/m64 {kz}, xmm" , "MR-HVM" , "EVEX.128.F3.0F38.W0 30 /r" , "AVX512_BW-VL"], ["vpmovwb" , "W:xmm/m128 {kz}, ymm" , "MR-HVM" , "EVEX.256.F3.0F38.W0 30 /r" , "AVX512_BW-VL"], ["vpmovwb" , "W:ymm/m256 {kz}, zmm" , "MR-HVM" , "EVEX.512.F3.0F38.W0 30 /r" , "AVX512_BW"], ["vpmovzxbd" , "W:xmm {kz}, xmm[31:0]/m32" , "RM-QVM" , "EVEX.128.66.0F38.WIG 31 /r" , "AVX512_F-VL"], ["vpmovzxbd" , "W:ymm {kz}, xmm[63:0]/m64" , "RM-QVM" , "EVEX.256.66.0F38.WIG 31 /r" , "AVX512_F-VL"], ["vpmovzxbd" , "W:zmm {kz}, xmm/m128" , "RM-QVM" , "EVEX.512.66.0F38.WIG 31 /r" , "AVX512_F"], ["vpmovzxbq" , "W:xmm {kz}, xmm[15:0]/m16" , "RM-OVM" , "EVEX.128.66.0F38.WIG 32 /r" , "AVX512_F-VL"], ["vpmovzxbq" , "W:ymm {kz}, xmm[31:0]/m32" , "RM-OVM" , "EVEX.256.66.0F38.WIG 32 /r" , "AVX512_F-VL"], ["vpmovzxbq" , "W:zmm {kz}, xmm[63:0]/m64" , "RM-OVM" , "EVEX.512.66.0F38.WIG 32 /r" , "AVX512_F"], ["vpmovzxbw" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-HVM" , "EVEX.128.66.0F38.WIG 30 /r" , "AVX512_BW-VL"], ["vpmovzxbw" , "W:ymm {kz}, xmm/m128" , "RM-HVM" , "EVEX.256.66.0F38.WIG 30 /r" , "AVX512_BW-VL"], ["vpmovzxbw" , "W:zmm {kz}, ymm/m256" , "RM-HVM" , "EVEX.512.66.0F38.WIG 30 /r" , "AVX512_BW"], ["vpmovzxdq" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-HVM" , "EVEX.128.66.0F38.W0 35 /r" , "AVX512_F-VL"], ["vpmovzxdq" , "W:ymm {kz}, xmm/m128" , "RM-HVM" , "EVEX.256.66.0F38.W0 35 /r" , "AVX512_F-VL"], ["vpmovzxdq" , "W:zmm {kz}, ymm/m256" , "RM-HVM" , "EVEX.512.66.0F38.W0 35 /r" , "AVX512_F"], ["vpmovzxwd" , "W:xmm {kz}, xmm[63:0]/m64" , "RM-HVM" , "EVEX.128.66.0F38.WIG 33 /r" , "AVX512_F-VL"], ["vpmovzxwd" , "W:ymm {kz}, xmm/m128" , "RM-HVM" , "EVEX.256.66.0F38.WIG 33 /r" , "AVX512_F-VL"], ["vpmovzxwd" , "W:zmm {kz}, ymm/m256" , "RM-HVM" , "EVEX.512.66.0F38.WIG 33 /r" , "AVX512_F"], ["vpmovzxwq" , "W:xmm {kz}, xmm[31:0]/m32" , "RM-QVM" , "EVEX.128.66.0F38.WIG 34 /r" , "AVX512_F-VL"], ["vpmovzxwq" , "W:ymm {kz}, xmm[63:0]/m64" , "RM-QVM" , "EVEX.256.66.0F38.WIG 34 /r" , "AVX512_F-VL"], ["vpmovzxwq" , "W:zmm {kz}, xmm/m128" , "RM-QVM" , "EVEX.512.66.0F38.WIG 34 /r" , "AVX512_F"], ["vpmuldq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 28 /r" , "AVX512_F-VL"], ["vpmuldq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 28 /r" , "AVX512_F-VL"], ["vpmuldq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 28 /r" , "AVX512_F"], ["vpmulhrsw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG 0B /r" , "AVX512_BW-VL"], ["vpmulhrsw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG 0B /r" , "AVX512_BW-VL"], ["vpmulhrsw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG 0B /r" , "AVX512_BW"], ["vpmulhuw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG E4 /r" , "AVX512_BW-VL"], ["vpmulhuw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG E4 /r" , "AVX512_BW-VL"], ["vpmulhuw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG E4 /r" , "AVX512_BW"], ["vpmulhw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG E5 /r" , "AVX512_BW-VL"], ["vpmulhw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG E5 /r" , "AVX512_BW-VL"], ["vpmulhw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG E5 /r" , "AVX512_BW"], ["vpmulld" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 40 /r" , "AVX512_F-VL"], ["vpmulld" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 40 /r" , "AVX512_F-VL"], ["vpmulld" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 40 /r" , "AVX512_F"], ["vpmullq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 40 /r" , "AVX512_DQ-VL"], ["vpmullq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 40 /r" , "AVX512_DQ-VL"], ["vpmullq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 40 /r" , "AVX512_DQ"], ["vpmullw" , "W:xmm {kz},~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG D5 /r" , "AVX512_BW-VL"], ["vpmullw" , "W:ymm {kz},~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG D5 /r" , "AVX512_BW-VL"], ["vpmullw" , "W:zmm {kz},~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG D5 /r" , "AVX512_BW"], ["vpmultishiftqb" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 83 /r" , "AVX512_VBMI-VL"], ["vpmultishiftqb" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 83 /r" , "AVX512_VBMI-VL"], ["vpmultishiftqb" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 83 /r" , "AVX512_VBMI"], ["vpmuludq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 F4 /r" , "AVX512_F-VL"], ["vpmuludq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 F4 /r" , "AVX512_F-VL"], ["vpmuludq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 F4 /r" , "AVX512_F"], ["vpopcntb" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F38.W0 54 /r" , "AVX512_BITALG-VL"], ["vpopcntb" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F38.W0 54 /r" , "AVX512_BITALG-VL"], ["vpopcntb" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F38.W0 54 /r" , "AVX512_BITALG"], ["vpopcntd" , "W:xmm {kz}, xmm/m128/b32" , "RM-FVM" , "EVEX.128.66.0F38.W0 55 /r" , "AVX512_VPOPCNTDQ-VL"], ["vpopcntd" , "W:ymm {kz}, ymm/m256/b32" , "RM-FVM" , "EVEX.256.66.0F38.W0 55 /r" , "AVX512_VPOPCNTDQ-VL"], ["vpopcntd" , "W:zmm {kz}, zmm/m512/b32" , "RM-FVM" , "EVEX.512.66.0F38.W0 55 /r" , "AVX512_VPOPCNTDQ"], ["vpopcntq" , "W:xmm {kz}, xmm/m128/b64" , "RM-FVM" , "EVEX.128.66.0F38.W1 55 /r" , "AVX512_VPOPCNTDQ-VL"], ["vpopcntq" , "W:ymm {kz}, ymm/m256/b64" , "RM-FVM" , "EVEX.256.66.0F38.W1 55 /r" , "AVX512_VPOPCNTDQ-VL"], ["vpopcntq" , "W:zmm {kz}, zmm/m512/b64" , "RM-FVM" , "EVEX.512.66.0F38.W1 55 /r" , "AVX512_VPOPCNTDQ"], ["vpopcntw" , "W:xmm {kz}, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F38.W1 54 /r" , "AVX512_BITALG-VL"], ["vpopcntw" , "W:ymm {kz}, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F38.W1 54 /r" , "AVX512_BITALG-VL"], ["vpopcntw" , "W:zmm {kz}, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F38.W1 54 /r" , "AVX512_BITALG"], ["vpord" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 EB /r" , "AVX512_F-VL"], ["vpord" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 EB /r" , "AVX512_F-VL"], ["vpord" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 EB /r" , "AVX512_F"], ["vporq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 EB /r" , "AVX512_F-VL"], ["vporq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 EB /r" , "AVX512_F-VL"], ["vporq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 EB /r" , "AVX512_F"], ["vprold" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W0 72 /1 ib" , "AVX512_F-VL"], ["vprold" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W0 72 /1 ib" , "AVX512_F-VL"], ["vprold" , "W:zmm {kz}, zmm/m512/b32, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W0 72 /1 ib" , "AVX512_F"], ["vprolq" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W1 72 /1 ib" , "AVX512_F-VL"], ["vprolq" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W1 72 /1 ib" , "AVX512_F-VL"], ["vprolq" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W1 72 /1 ib" , "AVX512_F"], ["vprolvd" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 15 /r" , "AVX512_F-VL"], ["vprolvd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 15 /r" , "AVX512_F-VL"], ["vprolvd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 15 /r" , "AVX512_F"], ["vprolvq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 15 /r" , "AVX512_F-VL"], ["vprolvq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 15 /r" , "AVX512_F-VL"], ["vprolvq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 15 /r" , "AVX512_F"], ["vprord" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W0 72 /0 ib" , "AVX512_F-VL"], ["vprord" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W0 72 /0 ib" , "AVX512_F-VL"], ["vprord" , "W:zmm {kz}, zmm/m512/b32, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W0 72 /0 ib" , "AVX512_F"], ["vprorq" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W1 72 /0 ib" , "AVX512_F-VL"], ["vprorq" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W1 72 /0 ib" , "AVX512_F-VL"], ["vprorq" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W1 72 /0 ib" , "AVX512_F"], ["vprorvd" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 14 /r" , "AVX512_F-VL"], ["vprorvd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 14 /r" , "AVX512_F-VL"], ["vprorvd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 14 /r" , "AVX512_F"], ["vprorvq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 14 /r" , "AVX512_F-VL"], ["vprorvq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 14 /r" , "AVX512_F-VL"], ["vprorvq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 14 /r" , "AVX512_F"], ["vpsadbw" , "W:xmm,~xmm,~xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG F6 /r" , "AVX512_BW-VL"], ["vpsadbw" , "W:ymm,~ymm,~ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG F6 /r" , "AVX512_BW-VL"], ["vpsadbw" , "W:zmm,~zmm,~zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG F6 /r" , "AVX512_BW"], ["vpscatterdd" , "W:vm32x {k}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W0 A0 /r" , "AVX512_F-VL"], ["vpscatterdd" , "W:vm32y {k}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W0 A0 /r" , "AVX512_F-VL"], ["vpscatterdd" , "W:vm32z {k}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W0 A0 /r" , "AVX512_F"], ["vpscatterdq" , "W:vm32x {k}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W1 A0 /r" , "AVX512_F-VL"], ["vpscatterdq" , "W:vm32x {k}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W1 A0 /r" , "AVX512_F-VL"], ["vpscatterdq" , "W:vm32y {k}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W1 A0 /r" , "AVX512_F"], ["vpscatterqd" , "W:vm64x {k}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W0 A1 /r" , "AVX512_F-VL"], ["vpscatterqd" , "W:vm64y {k}, xmm" , "MR-T1S" , "EVEX.256.66.0F38.W0 A1 /r" , "AVX512_F-VL"], ["vpscatterqd" , "W:vm64z {k}, ymm" , "MR-T1S" , "EVEX.512.66.0F38.W0 A1 /r" , "AVX512_F"], ["vpscatterqq" , "W:vm64x {k}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W1 A1 /r" , "AVX512_F-VL"], ["vpscatterqq" , "W:vm64y {k}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W1 A1 /r" , "AVX512_F-VL"], ["vpscatterqq" , "W:vm64z {k}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W1 A1 /r" , "AVX512_F"], ["vpshldd" , "W:xmm {kz}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W0 71 /r ib" , "AVX512_VBMI2-VL"], ["vpshldd" , "W:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W0 71 /r ib" , "AVX512_VBMI2-VL"], ["vpshldd" , "W:zmm {kz}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W0 71 /r ib" , "AVX512_VBMI2"], ["vpshldq" , "W:xmm {kz}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W1 71 /r ib" , "AVX512_VBMI2-VL"], ["vpshldq" , "W:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W1 71 /r ib" , "AVX512_VBMI2-VL"], ["vpshldq" , "W:zmm {kz}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W1 71 /r ib" , "AVX512_VBMI2"], ["vpshldvd" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FVM" , "EVEX.128.66.0F38.W0 71 /r" , "AVX512_VBMI2-VL"], ["vpshldvd" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FVM" , "EVEX.256.66.0F38.W0 71 /r" , "AVX512_VBMI2-VL"], ["vpshldvd" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FVM" , "EVEX.512.66.0F38.W0 71 /r" , "AVX512_VBMI2"], ["vpshldvq" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FVM" , "EVEX.128.66.0F38.W1 71 /r" , "AVX512_VBMI2-VL"], ["vpshldvq" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FVM" , "EVEX.256.66.0F38.W1 71 /r" , "AVX512_VBMI2-VL"], ["vpshldvq" , "X:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FVM" , "EVEX.512.66.0F38.W1 71 /r" , "AVX512_VBMI2"], ["vpshldvw" , "X:xmm {kz}, xmm, xmm/m128" , "RVM-FV" , "EVEX.128.66.0F38.W1 70 /r" , "AVX512_VBMI2-VL"], ["vpshldvw" , "X:ymm {kz}, ymm, ymm/m256" , "RVM-FV" , "EVEX.256.66.0F38.W1 70 /r" , "AVX512_VBMI2-VL"], ["vpshldvw" , "X:zmm {kz}, zmm, zmm/m512" , "RVM-FV" , "EVEX.512.66.0F38.W1 70 /r" , "AVX512_VBMI2"], ["vpshldw" , "W:xmm {kz}, xmm, xmm/m128, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W1 70 /r ib" , "AVX512_VBMI2-VL"], ["vpshldw" , "W:ymm {kz}, ymm, ymm/m256, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 70 /r ib" , "AVX512_VBMI2-VL"], ["vpshldw" , "W:zmm {kz}, zmm, zmm/m512, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 70 /r ib" , "AVX512_VBMI2"], ["vpshrdd" , "W:xmm {kz}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W0 73 /r ib" , "AVX512_VBMI2-VL"], ["vpshrdd" , "W:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W0 73 /r ib" , "AVX512_VBMI2-VL"], ["vpshrdd" , "W:zmm {kz}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W0 73 /r ib" , "AVX512_VBMI2"], ["vpshrdq" , "W:xmm {kz}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FVM", "EVEX.128.66.0F3A.W1 73 /r ib" , "AVX512_VBMI2-VL"], ["vpshrdq" , "W:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FVM", "EVEX.256.66.0F3A.W1 73 /r ib" , "AVX512_VBMI2-VL"], ["vpshrdq" , "W:zmm {kz}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FVM", "EVEX.512.66.0F3A.W1 73 /r ib" , "AVX512_VBMI2"], ["vpshrdvd" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FVM" , "EVEX.128.66.0F38.W0 73 /r" , "AVX512_VBMI2-VL"], ["vpshrdvd" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FVM" , "EVEX.256.66.0F38.W0 73 /r" , "AVX512_VBMI2-VL"], ["vpshrdvd" , "X:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FVM" , "EVEX.512.66.0F38.W0 73 /r" , "AVX512_VBMI2"], ["vpshrdvq" , "X:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FVM" , "EVEX.128.66.0F38.W1 73 /r" , "AVX512_VBMI2-VL"], ["vpshrdvq" , "X:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FVM" , "EVEX.256.66.0F38.W1 73 /r" , "AVX512_VBMI2-VL"], ["vpshrdvq" , "X:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FVM" , "EVEX.512.66.0F38.W1 73 /r" , "AVX512_VBMI2"], ["vpshrdvw" , "X:xmm {kz}, xmm, xmm/m128" , "RVM-FV" , "EVEX.128.66.0F38.W1 72 /r" , "AVX512_VBMI2-VL"], ["vpshrdvw" , "X:ymm {kz}, ymm, ymm/m256" , "RVM-FV" , "EVEX.256.66.0F38.W1 72 /r" , "AVX512_VBMI2-VL"], ["vpshrdvw" , "X:zmm {kz}, zmm, zmm/m512" , "RVM-FV" , "EVEX.512.66.0F38.W1 72 /r" , "AVX512_VBMI2"], ["vpshrdw" , "W:xmm {kz}, xmm, xmm/m128, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W1 72 /r ib" , "AVX512_VBMI2-VL"], ["vpshrdw" , "W:ymm {kz}, ymm, ymm/m256, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 72 /r ib" , "AVX512_VBMI2-VL"], ["vpshrdw" , "W:zmm {kz}, zmm, zmm/m512, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 72 /r ib" , "AVX512_VBMI2"], ["vpshufb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.WIG 00 /r" , "AVX512_BW-VL"], ["vpshufb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.WIG 00 /r" , "AVX512_BW-VL"], ["vpshufb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.WIG 00 /r" , "AVX512_BW"], ["vpshufbitqmb" , "W:k {k}, xmm, xmm/m128" , "RM-FVM" , "EVEX.128.66.0F38.W0 8F /r" , "AVX512_BITALG-VL"], ["vpshufbitqmb" , "W:k {k}, ymm, ymm/m256" , "RM-FVM" , "EVEX.256.66.0F38.W0 8F /r" , "AVX512_BITALG-VL"], ["vpshufbitqmb" , "W:k {k}, zmm, zmm/m512" , "RM-FVM" , "EVEX.512.66.0F38.W0 8F /r" , "AVX512_BITALG"], ["vpshufd" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "RMI-FV" , "EVEX.128.66.0F.W0 70 /r ib" , "AVX512_F-VL"], ["vpshufd" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "RMI-FV" , "EVEX.256.66.0F.W0 70 /r ib" , "AVX512_F-VL"], ["vpshufd" , "W:zmm {kz}, zmm/m512/b32, ib/ub" , "RMI-FV" , "EVEX.512.66.0F.W0 70 /r ib" , "AVX512_F"], ["vpshufhw" , "W:xmm {kz}, xmm/m128, ib/ub" , "RMI-FVM" , "EVEX.128.F3.0F.WIG 70 /r ib" , "AVX512_BW-VL"], ["vpshufhw" , "W:ymm {kz}, ymm/m256, ib/ub" , "RMI-FVM" , "EVEX.256.F3.0F.WIG 70 /r ib" , "AVX512_BW-VL"], ["vpshufhw" , "W:zmm {kz}, zmm/m512, ib/ub" , "RMI-FVM" , "EVEX.512.F3.0F.WIG 70 /r ib" , "AVX512_BW"], ["vpshuflw" , "W:xmm {kz}, xmm/m128, ib/ub" , "RMI-FVM" , "EVEX.128.F2.0F.WIG 70 /r ib" , "AVX512_BW-VL"], ["vpshuflw" , "W:ymm {kz}, ymm/m256, ib/ub" , "RMI-FVM" , "EVEX.256.F2.0F.WIG 70 /r ib" , "AVX512_BW-VL"], ["vpshuflw" , "W:zmm {kz}, zmm/m512, ib/ub" , "RMI-FVM" , "EVEX.512.F2.0F.WIG 70 /r ib" , "AVX512_BW"], ["vpslld" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.W0 F2 /r" , "AVX512_F-VL"], ["vpslld" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W0 72 /6 ib" , "AVX512_F-VL"], ["vpslld" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.W0 F2 /r" , "AVX512_F-VL"], ["vpslld" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W0 72 /6 ib" , "AVX512_F-VL"], ["vpslld" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.W0 F2 /r" , "AVX512_F"], ["vpslld" , "W:zmm {kz}, zmm/m512/b32, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W0 72 /6 ib" , "AVX512_F"], ["vpslldq" , "W:xmm, xmm/m128, ib/ub" , "VMI-FVM" , "EVEX.128.66.0F.WIG 73 /7 ib" , "AVX512_BW-VL"], ["vpslldq" , "W:ymm, ymm/m256, ib/ub" , "VMI-FVM" , "EVEX.256.66.0F.WIG 73 /7 ib" , "AVX512_BW-VL"], ["vpslldq" , "W:zmm, zmm/m512, ib/ub" , "VMI-FVM" , "EVEX.512.66.0F.WIG 73 /7 ib" , "AVX512_BW"], ["vpsllq" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.W1 F3 /r" , "AVX512_F-VL"], ["vpsllq" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W1 73 /6 ib" , "AVX512_F-VL"], ["vpsllq" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.W1 F3 /r" , "AVX512_F-VL"], ["vpsllq" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W1 73 /6 ib" , "AVX512_F-VL"], ["vpsllq" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.W1 F3 /r" , "AVX512_F"], ["vpsllq" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W1 73 /6 ib" , "AVX512_F"], ["vpsllvd" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 47 /r" , "AVX512_F-VL"], ["vpsllvd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 47 /r" , "AVX512_F-VL"], ["vpsllvd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 47 /r" , "AVX512_F"], ["vpsllvq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 47 /r" , "AVX512_F-VL"], ["vpsllvq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 47 /r" , "AVX512_F-VL"], ["vpsllvq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 47 /r" , "AVX512_F"], ["vpsllvw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W1 12 /r" , "AVX512_BW-VL"], ["vpsllvw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W1 12 /r" , "AVX512_BW-VL"], ["vpsllvw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W1 12 /r" , "AVX512_BW"], ["vpsllw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.WIG F1 /r" , "AVX512_BW-VL"], ["vpsllw" , "W:xmm {kz}, xmm/m128, ib/ub" , "VMI-FVM" , "EVEX.128.66.0F.WIG 71 /6 ib" , "AVX512_BW-VL"], ["vpsllw" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.WIG F1 /r" , "AVX512_BW-VL"], ["vpsllw" , "W:ymm {kz}, ymm/m256, ib/ub" , "VMI-FVM" , "EVEX.256.66.0F.WIG 71 /6 ib" , "AVX512_BW-VL"], ["vpsllw" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.WIG F1 /r" , "AVX512_BW"], ["vpsllw" , "W:zmm {kz}, zmm/m512, ib/ub" , "VMI-FVM" , "EVEX.512.66.0F.WIG 71 /6 ib" , "AVX512_BW"], ["vpsrad" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.W0 E2 /r" , "AVX512_F-VL"], ["vpsrad" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W0 72 /4 ib" , "AVX512_F-VL"], ["vpsrad" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.W0 E2 /r" , "AVX512_F-VL"], ["vpsrad" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W0 72 /4 ib" , "AVX512_F-VL"], ["vpsrad" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.W0 E2 /r" , "AVX512_F"], ["vpsrad" , "W:zmm {kz}, zmm/m512/b32, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W0 72 /4 ib" , "AVX512_F"], ["vpsraq" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.W1 E2 /r" , "AVX512_F-VL"], ["vpsraq" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W1 72 /4 ib" , "AVX512_F-VL"], ["vpsraq" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.W1 E2 /r" , "AVX512_F-VL"], ["vpsraq" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W1 72 /4 ib" , "AVX512_F-VL"], ["vpsraq" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.W1 E2 /r" , "AVX512_F"], ["vpsraq" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W1 72 /4 ib" , "AVX512_F"], ["vpsravd" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 46 /r" , "AVX512_F-VL"], ["vpsravd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 46 /r" , "AVX512_F-VL"], ["vpsravd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 46 /r" , "AVX512_F"], ["vpsravq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 46 /r" , "AVX512_F-VL"], ["vpsravq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 46 /r" , "AVX512_F-VL"], ["vpsravq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 46 /r" , "AVX512_F"], ["vpsravw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W1 11 /r" , "AVX512_BW-VL"], ["vpsravw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W1 11 /r" , "AVX512_BW-VL"], ["vpsravw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W1 11 /r" , "AVX512_BW"], ["vpsraw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.WIG E1 /r" , "AVX512_BW-VL"], ["vpsraw" , "W:xmm {kz}, xmm/m128, ib/ub" , "VMI-FVM" , "EVEX.128.66.0F.WIG 71 /4 ib" , "AVX512_BW-VL"], ["vpsraw" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.WIG E1 /r" , "AVX512_BW-VL"], ["vpsraw" , "W:ymm {kz}, ymm/m256, ib/ub" , "VMI-FVM" , "EVEX.256.66.0F.WIG 71 /4 ib" , "AVX512_BW-VL"], ["vpsraw" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.WIG E1 /r" , "AVX512_BW"], ["vpsraw" , "W:zmm {kz}, zmm/m512, ib/ub" , "VMI-FVM" , "EVEX.512.66.0F.WIG 71 /4 ib" , "AVX512_BW"], ["vpsrld" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.W0 D2 /r" , "AVX512_F-VL"], ["vpsrld" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W0 72 /2 ib" , "AVX512_F-VL"], ["vpsrld" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.W0 D2 /r" , "AVX512_F-VL"], ["vpsrld" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W0 72 /2 ib" , "AVX512_F-VL"], ["vpsrld" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.W0 D2 /r" , "AVX512_F"], ["vpsrld" , "W:zmm {kz}, zmm/m512/b32, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W0 72 /2 ib" , "AVX512_F"], ["vpsrldq" , "W:xmm, xmm/m128, ib/ub" , "VMI-FVM" , "EVEX.128.66.0F.WIG 73 /3 ib" , "AVX512_BW-VL"], ["vpsrldq" , "W:ymm, ymm/m256, ib/ub" , "VMI-FVM" , "EVEX.256.66.0F.WIG 73 /3 ib" , "AVX512_BW-VL"], ["vpsrldq" , "W:zmm, zmm/m512, ib/ub" , "VMI-FVM" , "EVEX.512.66.0F.WIG 73 /3 ib" , "AVX512_BW"], ["vpsrlq" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.W1 D3 /r" , "AVX512_F-VL"], ["vpsrlq" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "VMI-FV" , "EVEX.128.66.0F.W1 73 /2 ib" , "AVX512_F-VL"], ["vpsrlq" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.W1 D3 /r" , "AVX512_F-VL"], ["vpsrlq" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "VMI-FV" , "EVEX.256.66.0F.W1 73 /2 ib" , "AVX512_F-VL"], ["vpsrlq" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.W1 D3 /r" , "AVX512_F"], ["vpsrlq" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "VMI-FV" , "EVEX.512.66.0F.W1 73 /2 ib" , "AVX512_F"], ["vpsrlvd" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 45 /r" , "AVX512_F-VL"], ["vpsrlvd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 45 /r" , "AVX512_F-VL"], ["vpsrlvd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 45 /r" , "AVX512_F"], ["vpsrlvq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 45 /r" , "AVX512_F-VL"], ["vpsrlvq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 45 /r" , "AVX512_F-VL"], ["vpsrlvq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 45 /r" , "AVX512_F"], ["vpsrlvw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W1 10 /r" , "AVX512_BW-VL"], ["vpsrlvw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W1 10 /r" , "AVX512_BW-VL"], ["vpsrlvw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W1 10 /r" , "AVX512_BW"], ["vpsrlw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-M128", "EVEX.128.66.0F.WIG D1 /r" , "AVX512_BW-VL"], ["vpsrlw" , "W:xmm {kz}, xmm/m128, ib/ub" , "VMI-FVM" , "EVEX.128.66.0F.WIG 71 /2 ib" , "AVX512_BW-VL"], ["vpsrlw" , "W:ymm {kz}, ymm, xmm/m128" , "RVM-M128", "EVEX.256.66.0F.WIG D1 /r" , "AVX512_BW-VL"], ["vpsrlw" , "W:ymm {kz}, ymm/m256, ib/ub" , "VMI-FVM" , "EVEX.256.66.0F.WIG 71 /2 ib" , "AVX512_BW-VL"], ["vpsrlw" , "W:zmm {kz}, zmm, xmm/m128" , "RVM-M128", "EVEX.512.66.0F.WIG D1 /r" , "AVX512_BW"], ["vpsrlw" , "W:zmm {kz}, zmm/m512, ib/ub" , "VMI-FVM" , "EVEX.512.66.0F.WIG 71 /2 ib" , "AVX512_BW"], ["vpsubb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG F8 /r" , "AVX512_BW-VL"], ["vpsubb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG F8 /r" , "AVX512_BW-VL"], ["vpsubb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG F8 /r" , "AVX512_BW"], ["vpsubd" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 FA /r" , "AVX512_F-VL"], ["vpsubd" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 FA /r" , "AVX512_F-VL"], ["vpsubd" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 FA /r" , "AVX512_F"], ["vpsubq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 FB /r" , "AVX512_F-VL"], ["vpsubq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 FB /r" , "AVX512_F-VL"], ["vpsubq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 FB /r" , "AVX512_F"], ["vpsubsb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG E8 /r" , "AVX512_BW-VL"], ["vpsubsb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG E8 /r" , "AVX512_BW-VL"], ["vpsubsb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG E8 /r" , "AVX512_BW"], ["vpsubsw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG E9 /r" , "AVX512_BW-VL"], ["vpsubsw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG E9 /r" , "AVX512_BW-VL"], ["vpsubsw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG E9 /r" , "AVX512_BW"], ["vpsubusb" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG D8 /r" , "AVX512_BW-VL"], ["vpsubusb" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG D8 /r" , "AVX512_BW-VL"], ["vpsubusb" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG D8 /r" , "AVX512_BW"], ["vpsubusw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG D9 /r" , "AVX512_BW-VL"], ["vpsubusw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG D9 /r" , "AVX512_BW-VL"], ["vpsubusw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG D9 /r" , "AVX512_BW"], ["vpsubw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG F9 /r" , "AVX512_BW-VL"], ["vpsubw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG F9 /r" , "AVX512_BW-VL"], ["vpsubw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG F9 /r" , "AVX512_BW"], ["vpternlogd" , "X:xmm {kz}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W0 25 /r ib" , "AVX512_F-VL"], ["vpternlogd" , "X:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W0 25 /r ib" , "AVX512_F-VL"], ["vpternlogd" , "X:zmm {kz}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W0 25 /r ib" , "AVX512_F"], ["vpternlogq" , "X:xmm {kz}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W1 25 /r ib" , "AVX512_F-VL"], ["vpternlogq" , "X:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 25 /r ib" , "AVX512_F-VL"], ["vpternlogq" , "X:zmm {kz}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 25 /r ib" , "AVX512_F"], ["vptestmb" , "W:k {k}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W0 26 /r" , "AVX512_BW-VL"], ["vptestmb" , "W:k {k}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W0 26 /r" , "AVX512_BW-VL"], ["vptestmb" , "W:k {k}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W0 26 /r" , "AVX512_BW"], ["vptestmd" , "W:k {k}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 27 /r" , "AVX512_F-VL"], ["vptestmd" , "W:k {k}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 27 /r" , "AVX512_F-VL"], ["vptestmd" , "W:k {k}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F38.W0 27 /r" , "AVX512_F"], ["vptestmq" , "W:k {k}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 27 /r" , "AVX512_F-VL"], ["vptestmq" , "W:k {k}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 27 /r" , "AVX512_F-VL"], ["vptestmq" , "W:k {k}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F38.W1 27 /r" , "AVX512_F"], ["vptestmw" , "W:k {k}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F38.W1 26 /r" , "AVX512_BW-VL"], ["vptestmw" , "W:k {k}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F38.W1 26 /r" , "AVX512_BW-VL"], ["vptestmw" , "W:k {k}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F38.W1 26 /r" , "AVX512_BW"], ["vptestnmb" , "W:k {k}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.F3.0F38.W0 26 /r" , "AVX512_BW-VL"], ["vptestnmb" , "W:k {k}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.F3.0F38.W0 26 /r" , "AVX512_BW-VL"], ["vptestnmb" , "W:k {k}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.F3.0F38.W0 26 /r" , "AVX512_BW"], ["vptestnmd" , "W:k {k}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.F3.0F38.W0 27 /r" , "AVX512_F-VL"], ["vptestnmd" , "W:k {k}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.F3.0F38.W0 27 /r" , "AVX512_F-VL"], ["vptestnmd" , "W:k {k}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.F3.0F38.W0 27 /r" , "AVX512_F"], ["vptestnmq" , "W:k {k}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.F3.0F38.W1 27 /r" , "AVX512_F-VL"], ["vptestnmq" , "W:k {k}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.F3.0F38.W1 27 /r" , "AVX512_F-VL"], ["vptestnmq" , "W:k {k}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.F3.0F38.W1 27 /r" , "AVX512_F"], ["vptestnmw" , "W:k {k}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.F3.0F38.W1 26 /r" , "AVX512_BW-VL"], ["vptestnmw" , "W:k {k}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.F3.0F38.W1 26 /r" , "AVX512_BW-VL"], ["vptestnmw" , "W:k {k}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.F3.0F38.W1 26 /r" , "AVX512_BW"], ["vpunpckhbw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG 68 /r" , "AVX512_BW-VL"], ["vpunpckhbw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG 68 /r" , "AVX512_BW-VL"], ["vpunpckhbw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG 68 /r" , "AVX512_BW"], ["vpunpckhdq" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 6A /r" , "AVX512_F-VL"], ["vpunpckhdq" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 6A /r" , "AVX512_F-VL"], ["vpunpckhdq" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 6A /r" , "AVX512_F"], ["vpunpckhqdq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 6D /r" , "AVX512_F-VL"], ["vpunpckhqdq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 6D /r" , "AVX512_F-VL"], ["vpunpckhqdq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 6D /r" , "AVX512_F"], ["vpunpckhwd" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG 69 /r" , "AVX512_BW-VL"], ["vpunpckhwd" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG 69 /r" , "AVX512_BW-VL"], ["vpunpckhwd" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG 69 /r" , "AVX512_BW"], ["vpunpcklbw" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG 60 /r" , "AVX512_BW-VL"], ["vpunpcklbw" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG 60 /r" , "AVX512_BW-VL"], ["vpunpcklbw" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG 60 /r" , "AVX512_BW"], ["vpunpckldq" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 62 /r" , "AVX512_F-VL"], ["vpunpckldq" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 62 /r" , "AVX512_F-VL"], ["vpunpckldq" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 62 /r" , "AVX512_F"], ["vpunpcklqdq" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 6C /r" , "AVX512_F-VL"], ["vpunpcklqdq" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 6C /r" , "AVX512_F-VL"], ["vpunpcklqdq" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 6C /r" , "AVX512_F"], ["vpunpcklwd" , "W:xmm {kz}, xmm, xmm/m128" , "RVM-FVM" , "EVEX.128.66.0F.WIG 61 /r" , "AVX512_BW-VL"], ["vpunpcklwd" , "W:ymm {kz}, ymm, ymm/m256" , "RVM-FVM" , "EVEX.256.66.0F.WIG 61 /r" , "AVX512_BW-VL"], ["vpunpcklwd" , "W:zmm {kz}, zmm, zmm/m512" , "RVM-FVM" , "EVEX.512.66.0F.WIG 61 /r" , "AVX512_BW"], ["vpxord" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F.W0 EF /r" , "AVX512_F-VL"], ["vpxord" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F.W0 EF /r" , "AVX512_F-VL"], ["vpxord" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.66.0F.W0 EF /r" , "AVX512_F"], ["vpxorq" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 EF /r" , "AVX512_F-VL"], ["vpxorq" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 EF /r" , "AVX512_F-VL"], ["vpxorq" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 EF /r" , "AVX512_F"], ["vrangepd" , "W:xmm {kz}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W1 50 /r ib" , "AVX512_DQ-VL"], ["vrangepd" , "W:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 50 /r ib" , "AVX512_DQ-VL"], ["vrangepd" , "W:zmm {kz}, zmm, zmm/m512/b64, ib/ub {sae}" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 50 /r ib" , "AVX512_DQ"], ["vrangeps" , "W:xmm {kz}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F3A.W0 50 /r ib" , "AVX512_DQ-VL"], ["vrangeps" , "W:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W0 50 /r ib" , "AVX512_DQ-VL"], ["vrangeps" , "W:zmm {kz}, zmm, zmm/m512/b32, ib/ub {sae}" , "RVMI-FV" , "EVEX.512.66.0F3A.W0 50 /r ib" , "AVX512_DQ"], ["vrangesd" , "W:xmm {kz},xmm[127:64],xmm[63:0]/m64,ib/ub {sae}", "RVMI-T1S", "EVEX.LIG.66.0F3A.W1 51 /r ib" , "AVX512_DQ"], ["vrangess" , "W:xmm {kz},xmm[127:32],xmm[31:0]/m32,ib/ub {sae}", "RVMI-T1S", "EVEX.LIG.66.0F3A.W0 51 /r ib" , "AVX512_DQ"], ["vrcp14pd" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F38.W1 4C /r" , "AVX512_F-VL"], ["vrcp14pd" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F38.W1 4C /r" , "AVX512_F-VL"], ["vrcp14pd" , "W:zmm {kz}, zmm/m512/b64" , "RM-FV" , "EVEX.512.66.0F38.W1 4C /r" , "AVX512_F"], ["vrcp14ps" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.0F38.W0 4C /r" , "AVX512_F-VL"], ["vrcp14ps" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.0F38.W0 4C /r" , "AVX512_F-VL"], ["vrcp14ps" , "W:zmm {kz}, zmm/m512/b32" , "RM-FV" , "EVEX.512.66.0F38.W0 4C /r" , "AVX512_F"], ["vrcp14sd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]/m64" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 4D /r" , "AVX512_F"], ["vrcp14ss" , "W:xmm {kz}, xmm[127:32], xmm[31:0]/m32" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 4D /r" , "AVX512_F"], ["vrcp28pd" , "W:zmm {kz}, zmm/m512/b64 {sae}" , "RM-FV" , "EVEX.512.66.0F38.W1 CA /r" , "AVX512_ERI"], ["vrcp28ps" , "W:zmm {kz}, zmm/m512/b32 {sae}" , "RM-FV" , "EVEX.512.66.0F38.W0 CA /r" , "AVX512_ERI"], ["vrcp28sd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]/m64 {sae}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 CB /r" , "AVX512_ERI"], ["vrcp28ss" , "W:xmm {kz}, xmm[127:32], xmm[31:0]/m32 {sae}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 CB /r" , "AVX512_ERI"], ["vreducepd" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W1 56 /r ib" , "AVX512_DQ-VL"], ["vreducepd" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W1 56 /r ib" , "AVX512_DQ-VL"], ["vreducepd" , "W:zmm {kz}, zmm/m512/b64, ib/ub" , "RMI-FV" , "EVEX.512.66.0F3A.W1 56 /r ib" , "AVX512_DQ"], ["vreduceps" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W0 56 /r ib" , "AVX512_DQ-VL"], ["vreduceps" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W0 56 /r ib" , "AVX512_DQ-VL"], ["vreduceps" , "W:zmm {kz}, zmm/m512/b32, ib/ub" , "RMI-FV" , "EVEX.512.66.0F3A.W0 56 /r ib" , "AVX512_DQ"], ["vreducesd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]/m64, ib/ub" , "RVMI-T1S", "EVEX.LIG.66.0F3A.W1 57 /r ib" , "AVX512_DQ"], ["vreducess" , "W:xmm {kz}, xmm[127:32], xmm[31:0]/m32, ib/ub" , "RVMI-T1S", "EVEX.LIG.66.0F3A.W0 57 /r ib" , "AVX512_DQ"], ["vrndscalepd" , "W:xmm {kz}, xmm/m128/b64, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W1 09 /r ib" , "AVX512_F-VL"], ["vrndscalepd" , "W:ymm {kz}, ymm/m256/b64, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W1 09 /r ib" , "AVX512_F-VL"], ["vrndscalepd" , "W:zmm {kz}, zmm/m512/b64, ib/ub {sae}" , "RMI-FV" , "EVEX.512.66.0F3A.W1 09 /r ib" , "AVX512_F"], ["vrndscaleps" , "W:xmm {kz}, xmm/m128/b32, ib/ub" , "RMI-FV" , "EVEX.128.66.0F3A.W0 08 /r ib" , "AVX512_F-VL"], ["vrndscaleps" , "W:ymm {kz}, ymm/m256/b32, ib/ub" , "RMI-FV" , "EVEX.256.66.0F3A.W0 08 /r ib" , "AVX512_F-VL"], ["vrndscaleps" , "W:zmm {kz}, zmm/m512/b32, ib/ub {sae}" , "RMI-FV" , "EVEX.512.66.0F3A.W0 08 /r ib" , "AVX512_F"], ["vrndscalesd" , "W:xmm {kz},xmm[127:64],xmm[63:0]/m64,ib/ub {sae}", "RVMI-T1S", "EVEX.LIG.66.0F3A.W1 0B /r ib" , "AVX512_F"], ["vrndscaless" , "W:xmm {kz},xmm[127:32],xmm[31:0]/m32,ib/ub {sae}", "RVMI-T1S", "EVEX.LIG.66.0F3A.W0 0A /r ib" , "AVX512_F"], ["vrsqrt14pd" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F38.W1 4E /r" , "AVX512_F-VL"], ["vrsqrt14pd" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F38.W1 4E /r" , "AVX512_F-VL"], ["vrsqrt14pd" , "W:zmm {kz}, zmm/m512/b64" , "RM-FV" , "EVEX.512.66.0F38.W1 4E /r" , "AVX512_F"], ["vrsqrt14ps" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.0F38.W0 4E /r" , "AVX512_F-VL"], ["vrsqrt14ps" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.0F38.W0 4E /r" , "AVX512_F-VL"], ["vrsqrt14ps" , "W:zmm {kz}, zmm/m512/b32" , "RM-FV" , "EVEX.512.66.0F38.W0 4E /r" , "AVX512_F"], ["vrsqrt14sd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]/m64" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 4F /r" , "AVX512_F"], ["vrsqrt14ss" , "W:xmm {kz}, xmm[127:32], xmm[31:0]/m32" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 4F /r" , "AVX512_F"], ["vrsqrt28pd" , "W:zmm {kz}, zmm/m512/b64 {sae}" , "RM-FV" , "EVEX.512.66.0F38.W1 CC /r" , "AVX512_ERI"], ["vrsqrt28ps" , "W:zmm {kz}, zmm/m512/b32 {sae}" , "RM-FV" , "EVEX.512.66.0F38.W0 CC /r" , "AVX512_ERI"], ["vrsqrt28sd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]/m64 {sae}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 CD /r" , "AVX512_ERI"], ["vrsqrt28ss" , "W:xmm {kz}, xmm[127:32], xmm[31:0]/m32 {sae}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 CD /r" , "AVX512_ERI"], ["vscalefpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F38.W1 2C /r" , "AVX512_F-VL"], ["vscalefpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F38.W1 2C /r" , "AVX512_F-VL"], ["vscalefpd" , "W:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W1 2C /r" , "AVX512_F"], ["vscalefps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.66.0F38.W0 2C /r" , "AVX512_F-VL"], ["vscalefps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.66.0F38.W0 2C /r" , "AVX512_F-VL"], ["vscalefps" , "W:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.66.0F38.W0 2C /r" , "AVX512_F"], ["vscalefsd" , "W:xmm {kz}, xmm, xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W1 2D /r" , "AVX512_F"], ["vscalefss" , "W:xmm {kz}, xmm, xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.66.0F38.W0 2D /r" , "AVX512_F"], ["vscatterdpd" , "W:vm32x {k}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W1 A2 /r" , "AVX512_F-VL"], ["vscatterdpd" , "W:vm32x {k}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W1 A2 /r" , "AVX512_F-VL"], ["vscatterdpd" , "W:vm32y {k}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W1 A2 /r" , "AVX512_F"], ["vscatterdps" , "W:vm32x {k}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W0 A2 /r" , "AVX512_F-VL"], ["vscatterdps" , "W:vm32y {k}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W0 A2 /r" , "AVX512_F-VL"], ["vscatterdps" , "W:vm32z {k}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W0 A2 /r" , "AVX512_F"], ["vscatterpf0dpd" , "R:vm32y {k}" , "M-T1S" , "EVEX.512.66.0F38.W1 C6 /5" , "AVX512_PFI"], ["vscatterpf0dps" , "R:vm32z {k}" , "M-T1S" , "EVEX.512.66.0F38.W0 C6 /5" , "AVX512_PFI"], ["vscatterpf0qpd" , "R:vm64z {k}" , "M-T1S" , "EVEX.512.66.0F38.W1 C7 /5" , "AVX512_PFI"], ["vscatterpf0qps" , "R:vm64z {k}" , "M-T1S" , "EVEX.512.66.0F38.W0 C7 /5" , "AVX512_PFI"], ["vscatterpf1dpd" , "R:vm32y {k}" , "M-T1S" , "EVEX.512.66.0F38.W1 C6 /6" , "AVX512_PFI"], ["vscatterpf1dps" , "R:vm32z {k}" , "M-T1S" , "EVEX.512.66.0F38.W0 C6 /6" , "AVX512_PFI"], ["vscatterpf1qpd" , "R:vm64z {k}" , "M-T1S" , "EVEX.512.66.0F38.W1 C7 /6" , "AVX512_PFI"], ["vscatterpf1qps" , "R:vm64z {k}" , "M-T1S" , "EVEX.512.66.0F38.W0 C7 /6" , "AVX512_PFI"], ["vscatterqpd" , "W:vm64x {k}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W1 A3" , "AVX512_F-VL"], ["vscatterqpd" , "W:vm64y {k}, ymm" , "MR-T1S" , "EVEX.256.66.0F38.W1 A3" , "AVX512_F-VL"], ["vscatterqpd" , "W:vm64z {k}, zmm" , "MR-T1S" , "EVEX.512.66.0F38.W1 A3" , "AVX512_F"], ["vscatterqps" , "W:vm64x {k}, xmm" , "MR-T1S" , "EVEX.128.66.0F38.W0 A3" , "AVX512_F-VL"], ["vscatterqps" , "W:vm64y {k}, xmm" , "MR-T1S" , "EVEX.256.66.0F38.W0 A3" , "AVX512_F-VL"], ["vscatterqps" , "W:vm64z {k}, ymm" , "MR-T1S" , "EVEX.512.66.0F38.W0 A3" , "AVX512_F"], ["vshuff32x4" , "W:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W0 23 /r ib" , "AVX512_F-VL"], ["vshuff32x4" , "W:zmm {kz}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W0 23 /r ib" , "AVX512_F"], ["vshuff64x2" , "W:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 23 /r ib" , "AVX512_F-VL"], ["vshuff64x2" , "W:zmm {kz}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 23 /r ib" , "AVX512_F"], ["vshufi32x4" , "W:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W0 43 /r ib" , "AVX512_F-VL"], ["vshufi32x4" , "W:zmm {kz}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W0 43 /r ib" , "AVX512_F"], ["vshufi64x2" , "W:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F3A.W1 43 /r ib" , "AVX512_F-VL"], ["vshufi64x2" , "W:zmm {kz}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F3A.W1 43 /r ib" , "AVX512_F"], ["vshufpd" , "W:xmm {kz}, xmm, xmm/m128/b64, ib/ub" , "RVMI-FV" , "EVEX.128.66.0F.W1 C6 /r ib" , "AVX512_F-VL"], ["vshufpd" , "W:ymm {kz}, ymm, ymm/m256/b64, ib/ub" , "RVMI-FV" , "EVEX.256.66.0F.W1 C6 /r ib" , "AVX512_F-VL"], ["vshufpd" , "W:zmm {kz}, zmm, zmm/m512/b64, ib/ub" , "RVMI-FV" , "EVEX.512.66.0F.W1 C6 /r ib" , "AVX512_F"], ["vshufps" , "W:xmm {kz}, xmm, xmm/m128/b32, ib/ub" , "RVMI-FV" , "EVEX.128.0F.W0 C6 /r ib" , "AVX512_F-VL"], ["vshufps" , "W:ymm {kz}, ymm, ymm/m256/b32, ib/ub" , "RVMI-FV" , "EVEX.256.0F.W0 C6 /r ib" , "AVX512_F-VL"], ["vshufps" , "W:zmm {kz}, zmm, zmm/m512/b32, ib/ub" , "RVMI-FV" , "EVEX.512.0F.W0 C6 /r ib" , "AVX512_F"], ["vsqrtpd" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.0F.W1 51 /r" , "AVX512_F-VL"], ["vsqrtpd" , "W:ymm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.0F.W1 51 /r" , "AVX512_F-VL"], ["vsqrtpd" , "W:zmm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.66.0F.W1 51 /r" , "AVX512_F"], ["vsqrtps" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.0F.W0 51 /r" , "AVX512_F-VL"], ["vsqrtps" , "W:ymm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.0F.W0 51 /r" , "AVX512_F-VL"], ["vsqrtps" , "W:zmm {kz}, zmm/m512/b32 {er}" , "RM-FV" , "EVEX.512.0F.W0 51 /r" , "AVX512_F"], ["vsqrtsd" , "W:xmm {kz}, xmm[127:64], xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 51 /r" , "AVX512_F"], ["vsqrtss" , "W:xmm {kz}, xmm[127:32], xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 51 /r" , "AVX512_F"], ["vsubpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 5C /r" , "AVX512_F-VL"], ["vsubpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 5C /r" , "AVX512_F-VL"], ["vsubpd" , "W:zmm {kz}, zmm, zmm/m512/b64 {er}" , "RVM-FV" , "EVEX.512.66.0F.W1 5C /r" , "AVX512_F"], ["vsubps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 5C /r" , "AVX512_F-VL"], ["vsubps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 5C /r" , "AVX512_F-VL"], ["vsubps" , "W:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.0F.W0 5C /r" , "AVX512_F"], ["vsubsd" , "W:xmm {kz}, xmm, xmm[63:0]/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.0F.W1 5C /r" , "AVX512_F"], ["vsubss" , "W:xmm {kz}, xmm, xmm[31:0]/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.0F.W0 5C /r" , "AVX512_F"], ["vucomisd" , "R:xmm[63:0], xmm[63:0]/m64 {sae}" , "RM-T1S" , "EVEX.LIG.66.0F.W1 2E /r" , "AVX512_F OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["vucomiss" , "R:xmm[31:0], xmm[31:0]/m32 {sae}" , "RM-T1S" , "EVEX.LIG.0F.W0 2E /r" , "AVX512_F OF=0 SF=0 ZF=W AF=0 PF=W CF=W"], ["vunpckhpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 15 /r" , "AVX512_F-VL"], ["vunpckhpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 15 /r" , "AVX512_F-VL"], ["vunpckhpd" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 15 /r" , "AVX512_F"], ["vunpckhps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 15 /r" , "AVX512_F-VL"], ["vunpckhps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 15 /r" , "AVX512_F-VL"], ["vunpckhps" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.0F.W0 15 /r" , "AVX512_F"], ["vunpcklpd" , "W:xmm {kz}, xmm, xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 14 /r" , "AVX512_F-VL"], ["vunpcklpd" , "W:ymm {kz}, ymm, ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 14 /r" , "AVX512_F-VL"], ["vunpcklpd" , "W:zmm {kz}, zmm, zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 14 /r" , "AVX512_F"], ["vunpcklps" , "W:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 14 /r" , "AVX512_F-VL"], ["vunpcklps" , "W:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 14 /r" , "AVX512_F-VL"], ["vunpcklps" , "W:zmm {kz}, zmm, zmm/m512/b32" , "RVM-FV" , "EVEX.512.0F.W0 14 /r" , "AVX512_F"], ["vxorpd" , "W:xmm {kz},~xmm,~xmm/m128/b64" , "RVM-FV" , "EVEX.128.66.0F.W1 57 /r" , "AVX512_DQ-VL"], ["vxorpd" , "W:ymm {kz},~ymm,~ymm/m256/b64" , "RVM-FV" , "EVEX.256.66.0F.W1 57 /r" , "AVX512_DQ-VL"], ["vxorpd" , "W:zmm {kz},~zmm,~zmm/m512/b64" , "RVM-FV" , "EVEX.512.66.0F.W1 57 /r" , "AVX512_DQ"], ["vxorps" , "W:xmm {kz},~xmm,~xmm/m128/b32" , "RVM-FV" , "EVEX.128.0F.W0 57 /r" , "AVX512_DQ-VL"], ["vxorps" , "W:ymm {kz},~ymm,~ymm/m256/b32" , "RVM-FV" , "EVEX.256.0F.W0 57 /r" , "AVX512_DQ-VL"], ["vxorps" , "W:zmm {kz},~zmm,~zmm/m512/b32" , "RVM-FV" , "EVEX.512.0F.W0 57 /r" , "AVX512_DQ"], ["vaddph" , "W:xmm {kz},~xmm,~xmm/m128/b16" , "RVM-FV" , "EVEX.128.NP.MAP5.W0 58 /r" , "AVX512_FP16-VL"], ["vaddph" , "W:ymm {kz},~ymm,~ymm/m256/b16" , "RVM-FV" , "EVEX.256.NP.MAP5.W0 58 /r" , "AVX512_FP16-VL"], ["vaddph" , "W:zmm {kz},~zmm,~zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.NP.MAP5.W0 58 /r" , "AVX512_FP16"], ["vaddsh" , "W:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 58 /r" , "AVX512_FP16"], ["vcmpph" , "W:k {k}, xmm, xmm/m128/b16, ib/ub" , "RVMI-FV" , "EVEX.128.NP.0F3A.W0 C2 /r ib" , "AVX512_FP16-VL"], ["vcmpph" , "W:k {k}, ymm, ymm/m256/b16, ib/ub" , "RVMI-FV" , "EVEX.256.NP.0F3A.W0 C2 /r ib" , "AVX512_FP16-VL"], ["vcmpph" , "W:k {k}, zmm, zmm/m512/b16, ib/ub {sae}" , "RVMI-FV" , "EVEX.512.NP.0F3A.W0 C2 /r ib" , "AVX512_FP16"], ["vcmpsh" , "W:k {k}, xmm[15:0], xmm[15:0]/m16, ib/ub {sae}" , "RVMI-T1S", "EVEX.LIG.F3.0F3A.W0 C2 /r ib" , "AVX512_FP16"], ["vcomish" , "R:xmm[15:0], xmm[15:0]/m16 {sae}" , "RM-T1S" , "EVEX.LIG.NP.MAP5.W0 2F /r" , "AVX512_FP16"], ["vcvtdq2ph" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.NP.MAP5.W0 5B /r" , "AVX512_FP16-VL"], ["vcvtdq2ph" , "W:xmm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.NP.MAP5.W0 5B /r" , "AVX512_FP16-VL"], ["vcvtdq2ph" , "W:ymm {kz}, zmm/m512/b32 {er}" , "RM-FV" , "EVEX.512.NP.MAP5.W0 5B /r" , "AVX512_FP16"], ["vcvtpd2ph" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.66.MAP5.W1 5A /r" , "AVX512_FP16-VL"], ["vcvtpd2ph" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.66.MAP5.W1 5A /r" , "AVX512_FP16-VL"], ["vcvtpd2ph" , "W:xmm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.66.MAP5.W1 5A /r" , "AVX512_FP16"], ["vcvtph2dq" , "W:xmm {kz}, xmm/m64/b16" , "RM-HV" , "EVEX.128.66.MAP5.W0 5B /r" , "AVX512_FP16-VL"], ["vcvtph2dq" , "W:ymm {kz}, xmm/m128/b16" , "RM-HV" , "EVEX.256.66.MAP5.W0 5B /r" , "AVX512_FP16-VL"], ["vcvtph2dq" , "W:zmm {kz}, ymm/m256/b16 {er}" , "RM-HV" , "EVEX.512.66.MAP5.W0 5B /r" , "AVX512_FP16"], ["vcvtph2pd" , "W:xmm {kz}, xmm/m32/b16" , "RM-QV" , "EVEX.128.NP.MAP5.W0 5A /r" , "AVX512_FP16-VL"], ["vcvtph2pd" , "W:ymm {kz}, xmm/m64/b16" , "RM-QV" , "EVEX.256.NP.MAP5.W0 5A /r" , "AVX512_FP16-VL"], ["vcvtph2pd" , "W:zmm {kz}, xmm/m128/b16 {sae}" , "RM-QV" , "EVEX.512.NP.MAP5.W0 5A /r" , "AVX512_FP16"], ["vcvtph2psx" , "W:xmm {kz}, xmm/m64/b16" , "RM-HV" , "EVEX.128.66.MAP6.W0 13 /r" , "AVX512_FP16-VL"], ["vcvtph2psx" , "W:ymm {kz}, xmm/m128/b16" , "RM-HV" , "EVEX.256.66.MAP6.W0 13 /r" , "AVX512_FP16-VL"], ["vcvtph2psx" , "W:zmm {kz}, ymm/m256/b16 {sae}" , "RM-HV" , "EVEX.512.66.MAP6.W0 13 /r" , "AVX512_FP16"], ["vcvtph2qq" , "W:xmm {kz}, xmm/m32/b16" , "RM-QV" , "EVEX.128.66.MAP5.W0 7B /r" , "AVX512_FP16-VL"], ["vcvtph2qq" , "W:ymm {kz}, xmm/m64/b16" , "RM-QV" , "EVEX.256.66.MAP5.W0 7B /r" , "AVX512_FP16-VL"], ["vcvtph2qq" , "W:zmm {kz}, xmm/m128/b16 {er}" , "RM-QV" , "EVEX.512.66.MAP5.W0 7B /r" , "AVX512_FP16"], ["vcvtph2udq" , "W:xmm {kz}, xmm/m64/b16" , "RM-HV" , "EVEX.128.NP.MAP5.W0 79 /r" , "AVX512_FP16-VL"], ["vcvtph2udq" , "W:ymm {kz}, xmm/m128/b16" , "RM-HV" , "EVEX.256.NP.MAP5.W0 79 /r" , "AVX512_FP16-VL"], ["vcvtph2udq" , "W:zmm {kz}, ymm/m256/b16 {er}" , "RM-HV" , "EVEX.512.NP.MAP5.W0 79 /r" , "AVX512_FP16"], ["vcvtph2uqq" , "W:xmm {kz}, xmm/m32/b16" , "RM-QV" , "EVEX.128.66.MAP5.W0 79 /r" , "AVX512_FP16-VL"], ["vcvtph2uqq" , "W:ymm {kz}, xmm/m64/b16" , "RM-QV" , "EVEX.256.66.MAP5.W0 79 /r" , "AVX512_FP16-VL"], ["vcvtph2uqq" , "W:zmm {kz}, xmm/m128/b16 {er}" , "RM-QV" , "EVEX.512.66.MAP5.W0 79 /r" , "AVX512_FP16"], ["vcvtph2uw" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.NP.MAP5.W0 7D /r" , "AVX512_FP16-VL"], ["vcvtph2uw" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.NP.MAP5.W0 7D /r" , "AVX512_FP16-VL"], ["vcvtph2uw" , "W:zmm {kz}, zmm/m512/b16 {er}" , "RM-FV" , "EVEX.512.NP.MAP5.W0 7D /r" , "AVX512_FP16"], ["vcvtph2w" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.66.MAP5.W0 7D /r" , "AVX512_FP16-VL"], ["vcvtph2w" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.66.MAP5.W0 7D /r" , "AVX512_FP16-VL"], ["vcvtph2w" , "W:zmm {kz}, zmm/m512/b16 {er}" , "RM-FV" , "EVEX.512.66.MAP5.W0 7D /r" , "AVX512_FP16"], ["vcvtps2phx" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.66.MAP5.W0 1D /r" , "AVX512_FP16-VL"], ["vcvtps2phx" , "W:xmm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.66.MAP5.W0 1D /r" , "AVX512_FP16-VL"], ["vcvtps2phx" , "W:ymm {kz}, zmm/m512/b32 {er}" , "RM-FV" , "EVEX.512.66.MAP5.W0 1D /r" , "AVX512_FP16"], ["vcvtqq2ph" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.NP.MAP5.W1 5B /r" , "AVX512_FP16-VL"], ["vcvtqq2ph" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.NP.MAP5.W1 5B /r" , "AVX512_FP16-VL"], ["vcvtqq2ph" , "W:xmm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.NP.MAP5.W1 5B /r" , "AVX512_FP16"], ["vcvtsd2sh" , "W:xmm {kz}, xmm, xmm/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F2.MAP5.W1 5A /r" , "AVX512_FP16"], ["vcvtsh2sd" , "W:xmm {kz}, xmm, xmm/m16 {sae}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 5A /r" , "AVX512_FP16"], ["vcvtsh2si" , "W:r32, xmm/m16 {er}" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W0 2D /r" , "AVX512_FP16"], ["vcvtsh2si" , "W:r64, xmm/m16 {er}" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W1 2D /r" , "AVX512_FP16 X64"], ["vcvtsh2ss" , "W:xmm {kz}, xmm, xmm/m16 {sae}" , "RVM-T1S" , "EVEX.LIG.NP.MAP6.W0 13 /r" , "AVX512_FP16"], ["vcvtsh2usi" , "W:r32, xmm/m16 {er}" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W0 79 /r" , "AVX512_FP16"], ["vcvtsh2usi" , "W:r64, xmm/m16 {er}" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W1 79 /r" , "AVX512_FP16 X64"], ["vcvtsi2sh" , "W:xmm, xmm, r32/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 2A /r" , "AVX512_FP16"], ["vcvtsi2sh" , "W:xmm, xmm, r64/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W1 2A /r" , "AVX512_FP16 X64"], ["vcvtss2sh" , "W:xmm {kz}, xmm, xmm/m32 {er}" , "RVM-T1S" , "EVEX.LIG.NP.MAP5.W0 1D /r" , "AVX512_FP16"], ["vcvttph2dq" , "W:xmm {kz}, xmm/m64/b16" , "RM-HV" , "EVEX.128.F3.MAP5.W0 5B /r" , "AVX512_FP16-VL"], ["vcvttph2dq" , "W:ymm {kz}, xmm/m128/b16" , "RM-HV" , "EVEX.256.F3.MAP5.W0 5B /r" , "AVX512_FP16-VL"], ["vcvttph2dq" , "W:zmm {kz}, ymm/m256/b16 {sae}" , "RM-HV" , "EVEX.512.F3.MAP5.W0 5B /r" , "AVX512_FP16"], ["vcvttph2qq" , "W:xmm {kz}, xmm/m32/b16" , "RM-QV" , "EVEX.128.66.MAP5.W0 7A /r" , "AVX512_FP16-VL"], ["vcvttph2qq" , "W:ymm {kz}, xmm/m64/b16" , "RM-QV" , "EVEX.256.66.MAP5.W0 7A /r" , "AVX512_FP16-VL"], ["vcvttph2qq" , "W:zmm {kz}, xmm/m128/b16 {sae}" , "RM-QV" , "EVEX.512.66.MAP5.W0 7A /r" , "AVX512_FP16"], ["vcvttph2udq" , "W:xmm {kz}, xmm/m64/b16" , "RM-HV" , "EVEX.128.NP.MAP5.W0 78 /r" , "AVX512_FP16-VL"], ["vcvttph2udq" , "W:ymm {kz}, xmm/m128/b16" , "RM-HV" , "EVEX.256.NP.MAP5.W0 78 /r" , "AVX512_FP16-VL"], ["vcvttph2udq" , "W:zmm {kz}, ymm/m256/b16 {sae}" , "RM-HV" , "EVEX.512.NP.MAP5.W0 78 /r" , "AVX512_FP16"], ["vcvttph2uqq" , "W:xmm {kz}, xmm/m32/b16" , "RM-QV" , "EVEX.128.66.MAP5.W0 78 /r" , "AVX512_FP16-VL"], ["vcvttph2uqq" , "W:ymm {kz}, xmm/m64/b16" , "RM-QV" , "EVEX.256.66.MAP5.W0 78 /r" , "AVX512_FP16-VL"], ["vcvttph2uqq" , "W:zmm {kz}, xmm/m128/b16 {sae}" , "RM-QV" , "EVEX.512.66.MAP5.W0 78 /r" , "AVX512_FP16"], ["vcvttph2uw" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.NP.MAP5.W0 7C /r" , "AVX512_FP16-VL"], ["vcvttph2uw" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.NP.MAP5.W0 7C /r" , "AVX512_FP16-VL"], ["vcvttph2uw" , "W:zmm {kz}, zmm/m512/b16 {sae}" , "RM-FV" , "EVEX.512.NP.MAP5.W0 7C /r" , "AVX512_FP16"], ["vcvttph2w" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.66.MAP5.W0 7C /r" , "AVX512_FP16-VL"], ["vcvttph2w" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.66.MAP5.W0 7C /r" , "AVX512_FP16-VL"], ["vcvttph2w" , "W:zmm {kz}, zmm/m512/b16 {sae}" , "RM-FV" , "EVEX.512.66.MAP5.W0 7C /r" , "AVX512_FP16"], ["vcvttsh2si" , "W:r32, xmm/m16 {sae}" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W0 2C /r" , "AVX512_FP16"], ["vcvttsh2si" , "W:r64, xmm/m16 {sae}" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W1 2C /r" , "AVX512_FP16 X64"], ["vcvttsh2usi" , "W:r32, xmm/m16 {sae}" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W0 78 /r" , "AVX512_FP16"], ["vcvttsh2usi" , "W:r64, xmm/m16 {sae}" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W1 78 /r" , "AVX512_FP16 X64"], ["vcvtudq2ph" , "W:xmm {kz}, xmm/m128/b32" , "RM-FV" , "EVEX.128.F2.MAP5.W0 7A /r" , "AVX512_FP16-VL"], ["vcvtudq2ph" , "W:xmm {kz}, ymm/m256/b32" , "RM-FV" , "EVEX.256.F2.MAP5.W0 7A /r" , "AVX512_FP16-VL"], ["vcvtudq2ph" , "W:ymm {kz}, zmm/m512/b32 {er}" , "RM-FV" , "EVEX.512.F2.MAP5.W0 7A /r" , "AVX512_FP16"], ["vcvtuqq2ph" , "W:xmm {kz}, xmm/m128/b64" , "RM-FV" , "EVEX.128.F2.MAP5.W1 7A /r" , "AVX512_FP16-VL"], ["vcvtuqq2ph" , "W:xmm {kz}, ymm/m256/b64" , "RM-FV" , "EVEX.256.F2.MAP5.W1 7A /r" , "AVX512_FP16-VL"], ["vcvtuqq2ph" , "W:xmm {kz}, zmm/m512/b64 {er}" , "RM-FV" , "EVEX.512.F2.MAP5.W1 7A /r" , "AVX512_FP16"], ["vcvtusi2sh" , "W:xmm, xmm, r32/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 7B /r" , "AVX512_FP16"], ["vcvtusi2sh" , "W:xmm, xmm, r64/m64 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W1 7B /r" , "AVX512_FP16 X64"], ["vcvtuw2ph" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.F2.MAP5.W0 7D /r" , "AVX512_FP16-VL"], ["vcvtuw2ph" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.F2.MAP5.W0 7D /r" , "AVX512_FP16-VL"], ["vcvtuw2ph" , "W:zmm {kz}, zmm/m512/b16 {er}" , "RM-FV" , "EVEX.512.F2.MAP5.W0 7D /r" , "AVX512_FP16"], ["vcvtw2ph" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.F3.MAP5.W0 7D /r" , "AVX512_FP16-VL"], ["vcvtw2ph" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.F3.MAP5.W0 7D /r" , "AVX512_FP16-VL"], ["vcvtw2ph" , "W:zmm {kz}, zmm/m512/b16 {er}" , "RM-FV" , "EVEX.512.F3.MAP5.W0 7D /r" , "AVX512_FP16"], ["vdivph" , "W:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.NP.MAP5.W0 5E /r" , "AVX512_FP16-VL"], ["vdivph" , "W:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.NP.MAP5.W0 5E /r" , "AVX512_FP16-VL"], ["vdivph" , "W:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.NP.MAP5.W0 5E /r" , "AVX512_FP16"], ["vdivsh" , "W:xmm {kz}, xmm, xmm/m16 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 5E /r" , "AVX512_FP16"], ["vfcmaddcph" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.F2.MAP6.W0 56 /r" , "AVX512_FP16-VL"], ["vfcmaddcph" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.F2.MAP6.W0 56 /r" , "AVX512_FP16-VL"], ["vfcmaddcph" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.F2.MAP6.W0 56 /r" , "AVX512_FP16"], ["vfcmaddcsh" , "X:xmm {kz}, xmm, xmm/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F2.MAP6.W0 57 /r" , "AVX512_FP16-VL"], ["vfcmulcph" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.F2.MAP6.W0 D6 /r" , "AVX512_FP16-VL"], ["vfcmulcph" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.F2.MAP6.W0 D6 /r" , "AVX512_FP16-VL"], ["vfcmulcph" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.F2.MAP6.W0 D6 /r" , "AVX512_FP16"], ["vfcmulcsh" , "X:xmm {kz}, xmm, xmm/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F2.MAP6.W0 D7 /r" , "AVX512_FP16-VL"], ["vfmadd132ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 98 /r" , "AVX512_FP16-VL"], ["vfmadd132ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 98 /r" , "AVX512_FP16-VL"], ["vfmadd132ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 98 /r" , "AVX512_FP16"], ["vfmadd132sh" , "X:xmm {kz}, xmm, xmm/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 99 /r" , "AVX512_FP16"], ["vfmadd213ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 A8 /r" , "AVX512_FP16-VL"], ["vfmadd213ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 A8 /r" , "AVX512_FP16-VL"], ["vfmadd213ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 A8 /r" , "AVX512_FP16"], ["vfmadd213sh" , "X:xmm {kz}, xmm, xmm/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 A9 /r" , "AVX512_FP16"], ["vfmadd231ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 B8 /r" , "AVX512_FP16-VL"], ["vfmadd231ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 B8 /r" , "AVX512_FP16-VL"], ["vfmadd231ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 B8 /r" , "AVX512_FP16"], ["vfmadd231sh" , "X:xmm {kz}, xmm, xmm/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 B9 /r" , "AVX512_FP16"], ["vfmaddcph" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.F3.MAP6.W0 56 /r" , "AVX512_FP16-VL"], ["vfmaddcph" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.F3.MAP6.W0 56 /r" , "AVX512_FP16-VL"], ["vfmaddcph" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.F3.MAP6.W0 56 /r" , "AVX512_FP16"], ["vfmaddcsh" , "X:xmm {kz}, xmm, xmm/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP6.W0 57 /r" , "AVX512_FP16-VL"], ["vfmaddsub132ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 96 /r" , "AVX512_FP16-VL"], ["vfmaddsub132ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 96 /r" , "AVX512_FP16-VL"], ["vfmaddsub132ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 96 /r" , "AVX512_FP16"], ["vfmaddsub213ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 A6 /r" , "AVX512_FP16-VL"], ["vfmaddsub213ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 A6 /r" , "AVX512_FP16-VL"], ["vfmaddsub213ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 A6 /r" , "AVX512_FP16"], ["vfmaddsub231ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 B6 /r" , "AVX512_FP16-VL"], ["vfmaddsub231ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 B6 /r" , "AVX512_FP16-VL"], ["vfmaddsub231ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 B6 /r" , "AVX512_FP16"], ["vfmsub132ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 9A /r" , "AVX512_FP16-VL"], ["vfmsub132ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 9A /r" , "AVX512_FP16-VL"], ["vfmsub132ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 9A /r" , "AVX512_FP16"], ["vfmsub132sh" , "X:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 9B /r" , "AVX512_FP16"], ["vfmsub213ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 AA /r" , "AVX512_FP16-VL"], ["vfmsub213ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 AA /r" , "AVX512_FP16-VL"], ["vfmsub213ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 AA /r" , "AVX512_FP16"], ["vfmsub213sh" , "X:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 AB /r" , "AVX512_FP16"], ["vfmsub231ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 BA /r" , "AVX512_FP16-VL"], ["vfmsub231ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 BA /r" , "AVX512_FP16-VL"], ["vfmsub231ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 BA /r" , "AVX512_FP16"], ["vfmsub231sh" , "X:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 BB /r" , "AVX512_FP16"], ["vfmsubadd132ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 97 /r" , "AVX512_FP16-VL"], ["vfmsubadd132ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 97 /r" , "AVX512_FP16-VL"], ["vfmsubadd132ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 97 /r" , "AVX512_FP16"], ["vfmsubadd213ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 A7 /r" , "AVX512_FP16-VL"], ["vfmsubadd213ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 A7 /r" , "AVX512_FP16-VL"], ["vfmsubadd213ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 A7 /r" , "AVX512_FP16"], ["vfmsubadd231ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 B7 /r" , "AVX512_FP16-VL"], ["vfmsubadd231ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 B7 /r" , "AVX512_FP16-VL"], ["vfmsubadd231ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 B7 /r" , "AVX512_FP16"], ["vfmulcph" , "X:xmm {kz}, xmm, xmm/m128/b32" , "RVM-FV" , "EVEX.128.F3.MAP6.W0 D6 /r" , "AVX512_FP16-VL"], ["vfmulcph" , "X:ymm {kz}, ymm, ymm/m256/b32" , "RVM-FV" , "EVEX.256.F3.MAP6.W0 D6 /r" , "AVX512_FP16-VL"], ["vfmulcph" , "X:zmm {kz}, zmm, zmm/m512/b32 {er}" , "RVM-FV" , "EVEX.512.F3.MAP6.W0 D6 /r" , "AVX512_FP16"], ["vfmulcsh" , "X:xmm {kz}, xmm, xmm/m32 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP6.W0 D7 /r" , "AVX512_FP16-VL"], ["vfnmadd132ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 9C /r" , "AVX512_FP16-VL"], ["vfnmadd132ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 9C /r" , "AVX512_FP16-VL"], ["vfnmadd132ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 9C /r" , "AVX512_FP16"], ["vfnmadd132sh" , "X:xmm {kz}, xmm, xmm/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 9D /r" , "AVX512_FP16"], ["vfnmadd213ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 AC /r" , "AVX512_FP16-VL"], ["vfnmadd213ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 AC /r" , "AVX512_FP16-VL"], ["vfnmadd213ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 AC /r" , "AVX512_FP16"], ["vfnmadd213sh" , "X:xmm {kz}, xmm, xmm/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 AD /r" , "AVX512_FP16"], ["vfnmadd231ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 BC /r" , "AVX512_FP16-VL"], ["vfnmadd231ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 BC /r" , "AVX512_FP16-VL"], ["vfnmadd231ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 BC /r" , "AVX512_FP16"], ["vfnmadd231sh" , "X:xmm {kz}, xmm, xmm/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 BD /r" , "AVX512_FP16"], ["vfnmsub132ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 9E /r" , "AVX512_FP16-VL"], ["vfnmsub132ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 9E /r" , "AVX512_FP16-VL"], ["vfnmsub132ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 9E /r" , "AVX512_FP16"], ["vfnmsub132sh" , "X:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 9F /r" , "AVX512_FP16"], ["vfnmsub213ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 AE /r" , "AVX512_FP16-VL"], ["vfnmsub213ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 AE /r" , "AVX512_FP16-VL"], ["vfnmsub213ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 AE /r" , "AVX512_FP16"], ["vfnmsub213sh" , "X:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 AF /r" , "AVX512_FP16"], ["vfnmsub231ph" , "X:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 BE /r" , "AVX512_FP16-VL"], ["vfnmsub231ph" , "X:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 BE /r" , "AVX512_FP16-VL"], ["vfnmsub231ph" , "X:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 BE /r" , "AVX512_FP16"], ["vfnmsub231sh" , "X:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 BF /r" , "AVX512_FP16"], ["vfpclassph" , "W:k {k}, xmm/m128/b16, ib/ub" , "RMI-FV" , "EVEX.128.NP.0F3A.W0 66 /r ib" , "AVX512_FP16-VL"], ["vfpclassph" , "W:k {k}, ymm/m256/b16, ib/ub" , "RMI-FV" , "EVEX.256.NP.0F3A.W0 66 /r ib" , "AVX512_FP16-VL"], ["vfpclassph" , "W:k {k}, zmm/m512/b16, ib/ub" , "RMI-FV" , "EVEX.512.NP.0F3A.W0 66 /r ib" , "AVX512_FP16"], ["vfpclasssh" , "W:k {k}, xmm[15:0]/m16, ib/ub" , "RMI-T1S" , "EVEX.LIG.NP.0F3A.W0 67 /r ib" , "AVX512_FP16"], ["vgetexpph" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.66.MAP6.W0 42 /r" , "AVX512_FP16-VL"], ["vgetexpph" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.66.MAP6.W0 42 /r" , "AVX512_FP16-VL"], ["vgetexpph" , "W:zmm {kz}, zmm/m512/b16 {sae}" , "RM-FV" , "EVEX.512.66.MAP6.W0 42 /r" , "AVX512_FP16"], ["vgetexpsh" , "W:xmm {kz}, xmm[127:16], xmm[15:0]/m16 {sae}" , "RM-T1S" , "EVEX.LIG.66.MAP6.W0 43 /r" , "AVX512_FP16"], ["vgetmantph" , "W:xmm {kz}, xmm/m128/b16, ib/ub" , "RMI-FV" , "EVEX.128.NP.0F3A.W0 26 /r ib" , "AVX512_FP16-VL"], ["vgetmantph" , "W:ymm {kz}, ymm/m256/b16, ib/ub" , "RMI-FV" , "EVEX.256.NP.0F3A.W0 26 /r ib" , "AVX512_FP16-VL"], ["vgetmantph" , "W:zmm {kz}, zmm/m512/b16, ib/ub {sae}" , "RMI-FV" , "EVEX.512.NP.0F3A.W0 26 /r ib" , "AVX512_FP16"], ["vgetmantsh" , "W:xmm {kz},xmm[127:16],xmm[15:0]/m16,ib/ub {sae}", "RMI-T1S" , "EVEX.LIG.NP.0F3A.W0 27 /r ib" , "AVX512_FP16"], ["vmaxph" , "W:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.NP.MAP5.W0 5F /r" , "AVX512_FP16-VL"], ["vmaxph" , "W:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.NP.MAP5.W0 5F /r" , "AVX512_FP16-VL"], ["vmaxph" , "W:zmm {kz}, zmm, zmm/m512/b16 {sae}" , "RVM-FV" , "EVEX.512.NP.MAP5.W0 5F /r" , "AVX512_FP16"], ["vmaxsh" , "W:xmm {kz}, xmm, xmm[15:0]/m16 {sae}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 5F /r" , "AVX512_FP16"], ["vminph" , "W:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.NP.MAP5.W0 5D /r" , "AVX512_FP16-VL"], ["vminph" , "W:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.NP.MAP5.W0 5D /r" , "AVX512_FP16-VL"], ["vminph" , "W:zmm {kz}, zmm, zmm/m512/b16 {sae}" , "RVM-FV" , "EVEX.512.NP.MAP5.W0 5D /r" , "AVX512_FP16"], ["vminsh" , "W:xmm {kz}, xmm, xmm[15:0]/m16 {sae}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 5D /r" , "AVX512_FP16"], ["vmovsh" , "W:m16, xmm[15:0]" , "MR-T1S" , "EVEX.LIG.F3.MAP5.W0 11 /r" , "AVX512_FP16"], ["vmovsh" , "W:xmm[15:0] {kz}, m16" , "RM-T1S" , "EVEX.LIG.F3.MAP5.W0 10 /r" , "AVX512_FP16"], ["vmovsh" , "W:xmm {kz}, xmm[127:16], xmm[15:0]" , "MVR" , "EVEX.LIG.F3.MAP5.W0 11 /r" , "AVX512_FP16"], ["vmovsh" , "W:xmm {kz}, xmm[127:16], xmm[15:0]" , "RVM" , "EVEX.LIG.F3.MAP5.W0 10 /r" , "AVX512_FP16"], ["vmovw" , "W:r32[15:0]/m16, xmm[15:0]" , "MR-T1S" , "EVEX.128.66.MAP5.WIG 7E /r" , "AVX512_FP16"], ["vmovw" , "W:xmm[15:0] {kz}, r32[15:0]/m16" , "RM-T1S" , "EVEX.128.66.MAP5.WIG 6E /r" , "AVX512_FP16"], ["vmulph" , "W:xmm {kz},~xmm,~xmm/m128/b16" , "RVM-FV" , "EVEX.128.NP.MAP5.W0 59 /r" , "AVX512_FP16-VL"], ["vmulph" , "W:ymm {kz},~ymm,~ymm/m256/b16" , "RVM-FV" , "EVEX.256.NP.MAP5.W0 59 /r" , "AVX512_FP16-VL"], ["vmulph" , "W:zmm {kz},~zmm,~zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.NP.MAP5.W0 59 /r" , "AVX512_FP16"], ["vmulsh" , "W:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 59 /r" , "AVX512_FP16"], ["vrcpph" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.66.MAP6.W0 4C /r" , "AVX512_FP16"], ["vrcpph" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.66.MAP6.W0 4C /r" , "AVX512_FP16"], ["vrcpph" , "W:zmm {kz}, zmm/m512/b16" , "RM-FV" , "EVEX.512.66.MAP6.W0 4C /r" , "AVX512_FP16"], ["vrcpsh" , "W:xmm {kz}, xmm[127:16], xmm[15:0]/m16" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 4D /r" , "AVX512_FP16"], ["vreduceph" , "W:xmm {kz}, xmm/m128/b16, ib/ub" , "RMI-FV" , "EVEX.128.NP.0F3A.W0 56 /r ib" , "AVX512_FP16-VL"], ["vreduceph" , "W:ymm {kz}, ymm/m256/b16, ib/ub" , "RMI-FV" , "EVEX.256.NP.0F3A.W0 56 /r ib" , "AVX512_FP16-VL"], ["vreduceph" , "W:zmm {kz}, zmm/m512/b16, ib/ub {sae}" , "RMI-FV" , "EVEX.512.NP.0F3A.W0 56 /r ib" , "AVX512_FP16"], ["vreducesh" , "W:xmm {kz},xmm[127:16],xmm[15:0]/m16,ib/ub {sae}", "RVMI-T1S", "EVEX.LIG.NP.0F3A.W0 57 /r ib" , "AVX512_FP16"], ["vrndscaleph" , "W:xmm {kz}, xmm/m128/b16, ib/ub" , "RMI-FV" , "EVEX.128.NP.0F3A.W0 08 /r ib" , "AVX512_FP16-VL"], ["vrndscaleph" , "W:ymm {kz}, ymm/m256/b16, ib/ub" , "RMI-FV" , "EVEX.256.NP.0F3A.W0 08 /r ib" , "AVX512_FP16-VL"], ["vrndscaleph" , "W:zmm {kz}, zmm/m512/b16, ib/ub {sae}" , "RMI-FV" , "EVEX.512.NP.0F3A.W0 08 /r ib" , "AVX512_FP16"], ["vrndscalesh" , "W:xmm {kz},xmm[127:16],xmm[15:0]/m16,ib/ub {sae}", "RVMI-T1S", "EVEX.LIG.NP.0F3A.W0 0A /r ib" , "AVX512_FP16"], ["vrsqrtph" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.66.MAP6.W0 4E /r" , "AVX512_FP16-VL"], ["vrsqrtph" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.66.MAP6.W0 4E /r" , "AVX512_FP16-VL"], ["vrsqrtph" , "W:zmm {kz}, zmm/m512/b16" , "RM-FV" , "EVEX.512.66.MAP6.W0 4E /r" , "AVX512_FP16"], ["vrsqrtsh" , "W:xmm {kz}, xmm[127:16], xmm[15:0]/m16" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 4F /r" , "AVX512_FP16"], ["vscalefph" , "W:xmm {kz}, xmm, xmm/m128/b16" , "RVM-FV" , "EVEX.128.66.MAP6.W0 2C /r" , "AVX512_FP16-VL"], ["vscalefph" , "W:ymm {kz}, ymm, ymm/m256/b16" , "RVM-FV" , "EVEX.256.66.MAP6.W0 2C /r" , "AVX512_FP16-VL"], ["vscalefph" , "W:zmm {kz}, zmm, zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.66.MAP6.W0 2C /r" , "AVX512_FP16"], ["vscalefsh" , "W:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.66.MAP6.W0 2D /r" , "AVX512_FP16"], ["vsqrtph" , "W:xmm {kz}, xmm/m128/b16" , "RM-FV" , "EVEX.128.NP.MAP5.W0 51 /r" , "AVX512_FP16-VL"], ["vsqrtph" , "W:ymm {kz}, ymm/m256/b16" , "RM-FV" , "EVEX.256.NP.MAP5.W0 51 /r" , "AVX512_FP16-VL"], ["vsqrtph" , "W:zmm {kz}, zmm/m512/b16 {er}" , "RM-FV" , "EVEX.512.NP.MAP5.W0 51 /r" , "AVX512_FP16"], ["vsqrtsh" , "W:xmm {kz}, xmm[127:16], xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 51 /r" , "AVX512_FP16"], ["vsubph" , "W:xmm {kz},~xmm,~xmm/m128/b16" , "RVM-FV" , "EVEX.128.NP.MAP5.W0 5C /r" , "AVX512_FP16-VL"], ["vsubph" , "W:ymm {kz},~ymm,~ymm/m256/b16" , "RVM-FV" , "EVEX.256.NP.MAP5.W0 5C /r" , "AVX512_FP16-VL"], ["vsubph" , "W:zmm {kz},~zmm,~zmm/m512/b16 {er}" , "RVM-FV" , "EVEX.512.NP.MAP5.W0 5C /r" , "AVX512_FP16"], ["vsubsh" , "W:xmm {kz}, xmm, xmm[15:0]/m16 {er}" , "RVM-T1S" , "EVEX.LIG.F3.MAP5.W0 5C /r" , "AVX512_FP16"], ["vucomish" , "R:xmm[15:0], xmm[15:0]/m16 {sae}" , "RM-T1S" , "EVEX.LIG.NP.MAP5.W0 2E /r" , "AVX512_FP16"], ["ldtilecfg" , "R:m512" , "M" , "VEX.128.0F38.W0 49 /0" , "AMX_TILE X64"], ["sttilecfg" , "W:m512" , "M" , "VEX.128.66.0F38.W0 49 /0" , "AMX_TILE X64"], ["tdpbf16ps" , "X:tmm, tmm, tmm" , "RMV" , "VEX.128.F3.0F38.W0 5C /r" , "AMX_BF16 X64"], ["tdpbssd" , "X:tmm, tmm, tmm" , "RMV" , "VEX.128.F2.0F38.W0 5E /r" , "AMX_INT8 X64"], ["tdpbsud" , "X:tmm, tmm, tmm" , "RMV" , "VEX.128.F3.0F38.W0 5E /r" , "AMX_INT8 X64"], ["tdpbusd" , "X:tmm, tmm, tmm" , "RMV" , "VEX.128.66.0F38.W0 5E /r" , "AMX_INT8 X64"], ["tdpbuud" , "X:tmm, tmm, tmm" , "RMV" , "VEX.128.0F38.W0 5E /r" , "AMX_INT8 X64"], ["tileloadd" , "W:tmm, tmem" , "RM" , "VEX.128.F2.0F38.W0 4B /r" , "AMX_TILE X64"], ["tileloaddt1" , "W:tmm, tmem" , "RM" , "VEX.128.66.0F38.W0 4B /r" , "AMX_TILE X64"], ["tilerelease" , "" , "NONE" , "VEX.128.0F38.W0 49 /0" , "AMX_TILE X64"], ["tilestored" , "W:tmem, tmm" , "MR" , "VEX.128.F3.0F38.W0 4B /r" , "AMX_TILE X64"], ["tilezero" , "W:tmm" , "R" , "VEX.128.F2.0F38.W0 49 /r" , "AMX_TILE X64"] ] } // ${JSON:END} ; }).apply(this, typeof module === "object" && module && module.exports ? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "x86data"]); ================================================ FILE: dev/README.md ================================================ This directory contains scripts etc. used to develop GEF features. These do not affect the operation of GEF. ================================================ FILE: dev/angr/test.c ================================================ #include #include int main(int argc, char* argv[]) { if (argc != 2) return 0; if (memcmp(argv[1], "hogehoge", 8) == 0) { puts("ok"); } return 0; } ================================================ FILE: dev/bpf/bpf_insn.h ================================================ /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ /* eBPF instruction mini library */ #ifndef __BPF_INSN_H #define __BPF_INSN_H struct bpf_insn; /* ArgX, context and stack frame pointer register positions. Note, * Arg1, Arg2, Arg3, etc are used as argument mappings of function * calls in BPF_CALL instruction. */ #define BPF_REG_ARG1 BPF_REG_1 #define BPF_REG_ARG2 BPF_REG_2 #define BPF_REG_ARG3 BPF_REG_3 #define BPF_REG_ARG4 BPF_REG_4 #define BPF_REG_ARG5 BPF_REG_5 #define BPF_REG_CTX BPF_REG_6 #define BPF_REG_FP BPF_REG_10 /* Additional register mappings for converted user programs. */ #define BPF_REG_A BPF_REG_0 #define BPF_REG_X BPF_REG_7 #define BPF_REG_TMP BPF_REG_8 /* BPF program can access up to 512 bytes of stack space. */ #define MAX_BPF_STACK 512 /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ #define BPF_ALU64_REG(OP, DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) #define BPF_ALU32_REG(OP, DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) /* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ #define BPF_ALU64_IMM(OP, DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) #define BPF_ALU32_IMM(OP, DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* Short form of mov, dst_reg = src_reg */ #define BPF_MOV64_REG(DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) #define BPF_MOV32_REG(DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_MOV | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) /* Short form of mov, dst_reg = imm32 */ #define BPF_MOV64_IMM(DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) #define BPF_MOV32_IMM(DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_MOV | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ #define BPF_LD_IMM64(DST, IMM) \ BPF_LD_IMM64_RAW(DST, 0, IMM) #define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ ((struct bpf_insn) { \ .code = BPF_LD | BPF_DW | BPF_IMM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = (__u32) (IMM) }), \ ((struct bpf_insn) { \ .code = 0, /* zero is reserved opcode */ \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = ((__u64) (IMM)) >> 32 }) #ifndef BPF_PSEUDO_MAP_FD # define BPF_PSEUDO_MAP_FD 1 #endif /* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ #define BPF_LD_MAP_FD(DST, MAP_FD) \ BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) /* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ #define BPF_LD_ABS(SIZE, IMM) \ ((struct bpf_insn) { \ .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* Memory load, dst_reg = *(uint *) (src_reg + off16) */ #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Memory store, *(uint *) (dst_reg + off16) = src_reg */ #define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* * Atomic operations: * * BPF_ADD *(uint *) (dst_reg + off16) += src_reg * BPF_AND *(uint *) (dst_reg + off16) &= src_reg * BPF_OR *(uint *) (dst_reg + off16) |= src_reg * BPF_XOR *(uint *) (dst_reg + off16) ^= src_reg * BPF_ADD | BPF_FETCH src_reg = atomic_fetch_add(dst_reg + off16, src_reg); * BPF_AND | BPF_FETCH src_reg = atomic_fetch_and(dst_reg + off16, src_reg); * BPF_OR | BPF_FETCH src_reg = atomic_fetch_or(dst_reg + off16, src_reg); * BPF_XOR | BPF_FETCH src_reg = atomic_fetch_xor(dst_reg + off16, src_reg); * BPF_XCHG src_reg = atomic_xchg(dst_reg + off16, src_reg) * BPF_CMPXCHG r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg) */ #define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = OP }) /* Legacy alias */ #define BPF_STX_XADD(SIZE, DST, SRC, OFF) BPF_ATOMIC_OP(SIZE, BPF_ADD, DST, SRC, OFF) /* Memory store, *(uint *) (dst_reg + off16) = imm32 */ #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ ((struct bpf_insn) { \ .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM }) /* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ #define BPF_JMP_REG(OP, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */ #define BPF_JMP32_REG(OP, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP32 | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM }) /* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */ #define BPF_JMP32_IMM(OP, DST, IMM, OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM }) /* Function call */ #define BPF_EMIT_CALL(FUNC) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_CALL, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = (FUNC) }) /* Raw code statement block */ #define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ ((struct bpf_insn) { \ .code = CODE, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = IMM }) /* Program exit */ #define BPF_EXIT_INSN() \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_EXIT, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = 0 }) #endif ================================================ FILE: dev/bpf/test.c ================================================ #include #include #include #include #include #include #include #include #include #include "bpf_insn.h" void fatal(const char *msg) { perror(msg); exit(1); } int bpf(int cmd, union bpf_attr *attrs) { return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs)); } int map_create(int val_size, int max_entries) { union bpf_attr attr = { .map_type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(int), .value_size = val_size, .max_entries = max_entries }; int mapfd = bpf(BPF_MAP_CREATE, &attr); if (mapfd == -1) fatal("bpf(BPF_MAP_CREATE)"); return mapfd; } int map_update(int mapfd, int key, void *pval) { union bpf_attr attr = { .map_fd = mapfd, .key = (unsigned long)&key, .value = (unsigned long)pval, .flags = BPF_ANY }; int res = bpf(BPF_MAP_UPDATE_ELEM, &attr); if (res == -1) fatal("bpf(BPF_MAP_UPDATE_ELEM)"); return res; } int map_lookup(int mapfd, int key, void *pval) { union bpf_attr attr = { .map_fd = mapfd, .key = (unsigned long)&key, .value = (unsigned long)pval, .flags = BPF_ANY }; return bpf(BPF_MAP_LOOKUP_ELEM, &attr); // -1 if not found } int main() { char verifier_log[0x10000]; /* prepare the bpf map */ unsigned long val; int mapfd = map_create(sizeof(val), 4); val = 0xdeadbeef; map_update(mapfd, 1, &val); val = 0xcafebabe; map_update(mapfd, 2, &val); /* prepare the bpf program */ struct bpf_insn insns[] = { BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 1), // key=1 BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0x1337), // val=0x1337 // arg1: mapfd BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd), // arg2: key pointer BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP), BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -8), // arg3: value pointer BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_2), BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -8), // arg4: flags BPF_MOV64_IMM(BPF_REG_ARG4, 0), // store BPF_EMIT_CALL(BPF_FUNC_map_update_elem), // map_update_elem(mapfd, &k, &v) BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 2), // key=2 BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0x1338), // val=0x1338 // arg1: mapfd BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd), // arg2: key pointer BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP), BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -8), // arg3: value pointer BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_2), BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -8), // arg4: flags BPF_MOV64_IMM(BPF_REG_ARG4, 0), // store BPF_EMIT_CALL(BPF_FUNC_map_update_elem), // map_update_elem(mapfd, &k, &v) // socket filter; Cut off after the 4th bytes from the top BPF_MOV64_IMM(BPF_REG_0, 4), BPF_EXIT_INSN(), }; /* setup as socket filter */ union bpf_attr prog_attr = { .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, .insn_cnt = sizeof(insns) / sizeof(insns[0]), .insns = (unsigned long)insns, .license = (unsigned long)"GPL v2", .log_level = 2, .log_size = sizeof(verifier_log), .log_buf = (unsigned long)verifier_log }; /* load the BPF program */ int progfd = bpf(BPF_PROG_LOAD, &prog_attr); if (progfd == -1) { fatal("bpf(BPF_PROG_LOAD)"); } /* make sockets */ int socks[2]; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) fatal("socketpair"); if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(int))) fatal("setsockopt"); /* use the map */ map_lookup(mapfd, 1, &val); printf("val (before): 0x%lx\n", val); map_lookup(mapfd, 2, &val); printf("val (before): 0x%lx\n", val); /* use the socket */ write(socks[1], "Hello", 5); /* use the map */ map_lookup(mapfd, 1, &val); printf("val (after) : 0x%lx\n", val); map_lookup(mapfd, 2, &val); printf("val (after) : 0x%lx\n", val); /* use the socket */ char buf[0x10] = {}; read(socks[0], buf, 0x10); printf("Received: %s\n", buf); char c[1]; scanf("%c%c", c, c); return 0; } ================================================ FILE: dev/dma-heap/test.c ================================================ #include #include #include #include #include #include #include #define DMA_HEAP_IOCTL_ALLOC 0xc0184800 typedef unsigned long long u64; typedef unsigned int u32; struct dma_heap_allocation_data { u64 len; u32 fd; u32 fd_flags; u64 heap_flags; }; void fatal(const char *msg) { perror(msg); exit(1); } int main(void) { // Open DMA-BUF int dma_fd = open("/dev/dma_heap/system", O_RDWR); if (dma_fd == -1) fatal("/dev/dma_heap/system"); // Allocate DMA-BUF heap int dma_buf_fd = -1; struct dma_heap_allocation_data data; data.len = 0x400000; data.fd_flags = O_RDWR; data.heap_flags = 0; data.fd = 0; if (ioctl(dma_fd, DMA_HEAP_IOCTL_ALLOC, &data) < 0) fatal("DMA_HEAP_IOCTL_ALLOC"); printf("[+] dma_buf_fd: %d\n", dma_buf_fd = data.fd); close(dma_fd); // wait puts("Press enter to mmap/write"); scanf("%*[^\n]"); scanf("%*c"); // Mmap DMA-BUF heap void *dma_buf = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, dma_buf_fd, 0); printf("[+] dma_buf: %p\n", dma_buf); // write to DMA-BUF heap memcpy(dma_buf, "AAAABBBBCCCCDDDD", 16); // wait puts("Press enter to close"); scanf("%*[^\n]"); scanf("%*c"); // close close(dma_buf_fd); return 0; } ================================================ FILE: dev/dtor/test.c ================================================ #include #include #include #include typedef void (*dtor_func) (void *); extern void *__dso_handle __attribute__ ((__visibility__ ("hidden"))); extern int __cxa_thread_atexit_impl (dtor_func, void *, void *); typedef struct { void *val; } A; void A_dtor(void *obj) { } int main(int argc, char* argv[]) { // tls_dtor_list static __thread A b; //__cxa_thread_atexit_impl((dtor_func)A_dtor, &b, __dso_handle); __cxa_thread_atexit_impl((dtor_func)0x41414141, &b, __dso_handle); // __exit_funcs atexit((void (*)(void))0x42424242); on_exit((void (*)(int, void*))0x43434343, NULL); // __quick_exit_funcs at_quick_exit((void (*)(void))0x44444444); sleep(3); return 0; } ================================================ FILE: dev/dtor/test2.c ================================================ // gcc test2.c -lpthread #include #include #include void cleanup_func(void *arg) { printf("called: %s \n",(char *)arg); } void* func(void *arg) { // It's actually a macro, which is stored on the stack, making it difficult to dump. pthread_cleanup_push(cleanup_func, "cleanup_func (1)"); pthread_cleanup_push(cleanup_func, "cleanup_func (2)"); pthread_exit((void*)2); // never reached pthread_cleanup_pop(0); pthread_cleanup_pop(0); } int main(int argc, char* argv[]) { pthread_t tid; void* tret; pthread_create(&tid, NULL, func, (void*) 1); pthread_join(tid, &tret); return 0; } ================================================ FILE: dev/glibc-heap/test.c ================================================ // gcc test.c -lpthread #include #include #include #include #include #define COUNT 0x30 void *func(void* arg) { void* p[COUNT*2] = {}; void* dummy; // allocate for (int i=0; i small bin; unsorted -> large bin) void *q = malloc(0x10000); // free for (int i=COUNT / 3 * 2; i #include #include #include #include #include #include #include #define QUEUE_DEPTH 1 #define BLOCK_SZ 1024 struct file_info { off_t file_sz; struct iovec iovecs[]; /* Referred by readv/writev */ }; /* * Returns the size of the file whose open file descriptor is passed in. * Properly handles regular file and block devices as well. Pretty. * */ off_t get_file_size(int fd) { struct stat st; if(fstat(fd, &st) < 0) { perror("fstat"); return -1; } if (S_ISBLK(st.st_mode)) { unsigned long long bytes; if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { perror("ioctl"); return -1; } return bytes; } else if (S_ISREG(st.st_mode)) return st.st_size; return -1; } /* * Output a string of characters of len length to stdout. * We use buffered output here to be efficient, * since we need to output character-by-character. * */ void output_to_console(char *buf, int len) { while (len--) { fputc(*buf++, stdout); } } /* * Wait for a completion to be available, fetch the data from * the readv operation and print it to the console. * */ int get_completion_and_print(struct io_uring *ring) { struct io_uring_cqe *cqe; int ret = io_uring_wait_cqe(ring, &cqe); if (ret < 0) { perror("io_uring_wait_cqe"); return 1; } if (cqe->res < 0) { fprintf(stderr, "Async readv failed.\n"); return 1; } struct file_info *fi = io_uring_cqe_get_data(cqe); int blocks = (int) fi->file_sz / BLOCK_SZ; if (fi->file_sz % BLOCK_SZ) blocks++; for (int i = 0; i < blocks; i ++) output_to_console(fi->iovecs[i].iov_base, fi->iovecs[i].iov_len); io_uring_cqe_seen(ring, cqe); return 0; } /* * Submit the readv request via liburing * */ int submit_read_request(char *file_path, struct io_uring *ring) { int file_fd = open(file_path, O_RDONLY); if (file_fd < 0) { perror("open"); return 1; } off_t file_sz = get_file_size(file_fd); off_t bytes_remaining = file_sz; off_t offset = 0; int current_block = 0; int blocks = (int) file_sz / BLOCK_SZ; if (file_sz % BLOCK_SZ) blocks++; struct file_info *fi = malloc(sizeof(*fi) + (sizeof(struct iovec) * blocks)); /* * For each block of the file we need to read, we allocate an iovec struct * which is indexed into the iovecs array. This array is passed in as part * of the submission. If you don't understand this, then you need to look * up how the readv() and writev() system calls work. * */ while (bytes_remaining) { off_t bytes_to_read = bytes_remaining; if (bytes_to_read > BLOCK_SZ) bytes_to_read = BLOCK_SZ; offset += bytes_to_read; fi->iovecs[current_block].iov_len = bytes_to_read; void *buf; if (posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ)) { perror("posix_memalign"); return 1; } fi->iovecs[current_block].iov_base = buf; current_block++; bytes_remaining -= bytes_to_read; } fi->file_sz = file_sz; /* Get an SQE */ struct io_uring_sqe *sqe = io_uring_get_sqe(ring); /* Setup a readv operation */ io_uring_prep_readv(sqe, file_fd, fi->iovecs, blocks, 0); /* Set user data */ io_uring_sqe_set_data(sqe, fi); /* Finally, submit the request */ io_uring_submit(ring); return 0; } int main(int argc, char *argv[]) { struct io_uring ring; if (argc < 2) { fprintf(stderr, "Usage: %s [file name] <[file name] ...>\n", argv[0]); return 1; } /* Initialize io_uring */ io_uring_queue_init(QUEUE_DEPTH, &ring, 0); for (int i = 1; i < argc; i++) { int ret = submit_read_request(argv[i], &ring); if (ret) { fprintf(stderr, "Error reading file: %s\n", argv[i]); return 1; } get_completion_and_print(&ring); } /* Call the clean-up function. */ __asm__("int3"); io_uring_queue_exit(&ring); return 0; } ================================================ FILE: dev/iouring/test2.c ================================================ // https://github.com/shuveb/io_uring-by-example // gcc test2.c #include #include #include #include #include #include #include #include #include #include #include /* If your compilation fails because the header file below is missing, * your kernel is probably too old to support io_uring. * */ #include #define QUEUE_DEPTH 1 #define BLOCK_SZ 1024 /* This is x86 specific */ #define read_barrier() __asm__ __volatile__("":::"memory") #define write_barrier() __asm__ __volatile__("":::"memory") struct app_io_sq_ring { unsigned *head; unsigned *tail; unsigned *ring_mask; unsigned *ring_entries; unsigned *flags; unsigned *array; }; struct app_io_cq_ring { unsigned *head; unsigned *tail; unsigned *ring_mask; unsigned *ring_entries; struct io_uring_cqe *cqes; }; struct submitter { int ring_fd; struct app_io_sq_ring sq_ring; struct io_uring_sqe *sqes; struct app_io_cq_ring cq_ring; }; struct file_info { off_t file_sz; struct iovec iovecs[]; /* Referred by readv/writev */ }; /* * This code is written in the days when io_uring-related system calls are not * part of standard C libraries. So, we roll our own system call wrapper * functions. * */ int io_uring_setup(unsigned entries, struct io_uring_params *p) { return (int) syscall(__NR_io_uring_setup, entries, p); } int io_uring_enter(int ring_fd, unsigned int to_submit, unsigned int min_complete, unsigned int flags) { return (int) syscall(__NR_io_uring_enter, ring_fd, to_submit, min_complete, flags, NULL, 0); } /* * Returns the size of the file whose open file descriptor is passed in. * Properly handles regular file and block devices as well. Pretty. * */ off_t get_file_size(int fd) { struct stat st; if(fstat(fd, &st) < 0) { perror("fstat"); return -1; } if (S_ISBLK(st.st_mode)) { unsigned long long bytes; if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { perror("ioctl"); return -1; } return bytes; } else if (S_ISREG(st.st_mode)) return st.st_size; return -1; } /* * io_uring requires a lot of setup which looks pretty hairy, but isn't all * that difficult to understand. Because of all this boilerplate code, * io_uring's author has created liburing, which is relatively easy to use. * However, you should take your time and understand this code. It is always * good to know how it all works underneath. Apart from bragging rights, * it does offer you a certain strange geeky peace. * */ int app_setup_uring(struct submitter *s) { struct app_io_sq_ring *sring = &s->sq_ring; struct app_io_cq_ring *cring = &s->cq_ring; struct io_uring_params p; void *sq_ptr, *cq_ptr; /* * We need to pass in the io_uring_params structure to the io_uring_setup() * call zeroed out. We could set any flags if we need to, but for this * example, we don't. * */ memset(&p, 0, sizeof(p)); s->ring_fd = io_uring_setup(QUEUE_DEPTH, &p); if (s->ring_fd < 0) { perror("io_uring_setup"); return 1; } /* * io_uring communication happens via 2 shared kernel-user space ring buffers, * which can be jointly mapped with a single mmap() call in recent kernels. * While the completion queue is directly manipulated, the submission queue * has an indirection array in between. We map that in as well. * */ int sring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned); int cring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe); /* In kernel version 5.4 and above, it is possible to map the submission and * completion buffers with a single mmap() call. Rather than check for kernel * versions, the recommended way is to just check the features field of the * io_uring_params structure, which is a bit mask. If the * IORING_FEAT_SINGLE_MMAP is set, then we can do away with the second mmap() * call to map the completion ring. * */ if (p.features & IORING_FEAT_SINGLE_MMAP) { if (cring_sz > sring_sz) { sring_sz = cring_sz; } cring_sz = sring_sz; } /* Map in the submission and completion queue ring buffers. * Older kernels only map in the submission queue, though. * */ sq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_SQ_RING); if (sq_ptr == MAP_FAILED) { perror("mmap"); return 1; } if (p.features & IORING_FEAT_SINGLE_MMAP) { cq_ptr = sq_ptr; } else { /* Map in the completion queue ring buffer in older kernels separately */ cq_ptr = mmap(0, cring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_CQ_RING); if (cq_ptr == MAP_FAILED) { perror("mmap"); return 1; } } /* Save useful fields in a global app_io_sq_ring struct for later * easy reference */ sring->head = sq_ptr + p.sq_off.head; sring->tail = sq_ptr + p.sq_off.tail; sring->ring_mask = sq_ptr + p.sq_off.ring_mask; sring->ring_entries = sq_ptr + p.sq_off.ring_entries; sring->flags = sq_ptr + p.sq_off.flags; sring->array = sq_ptr + p.sq_off.array; /* Map in the submission queue entries array */ s->sqes = mmap(0, p.sq_entries * sizeof(struct io_uring_sqe), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_SQES); if (s->sqes == MAP_FAILED) { perror("mmap"); return 1; } /* Save useful fields in a global app_io_cq_ring struct for later * easy reference */ cring->head = cq_ptr + p.cq_off.head; cring->tail = cq_ptr + p.cq_off.tail; cring->ring_mask = cq_ptr + p.cq_off.ring_mask; cring->ring_entries = cq_ptr + p.cq_off.ring_entries; cring->cqes = cq_ptr + p.cq_off.cqes; return 0; } /* * Output a string of characters of len length to stdout. * We use buffered output here to be efficient, * since we need to output character-by-character. * */ void output_to_console(char *buf, int len) { while (len--) { fputc(*buf++, stdout); } } /* * Read from completion queue. * In this function, we read completion events from the completion queue, get * the data buffer that will have the file data and print it to the console. * */ void read_from_cq(struct submitter *s) { struct file_info *fi; struct app_io_cq_ring *cring = &s->cq_ring; struct io_uring_cqe *cqe; unsigned head, reaped = 0; head = *cring->head; do { read_barrier(); /* * Remember, this is a ring buffer. If head == tail, it means that the * buffer is empty. * */ if (head == *cring->tail) break; /* Get the entry */ cqe = &cring->cqes[head & *s->cq_ring.ring_mask]; fi = (struct file_info*) cqe->user_data; if (cqe->res < 0) fprintf(stderr, "Error: %s\n", strerror(abs(cqe->res))); int blocks = (int) fi->file_sz / BLOCK_SZ; if (fi->file_sz % BLOCK_SZ) blocks++; for (int i = 0; i < blocks; i++) output_to_console(fi->iovecs[i].iov_base, fi->iovecs[i].iov_len); head++; } while (1); *cring->head = head; write_barrier(); } /* * Submit to submission queue. * In this function, we submit requests to the submission queue. You can submit * many types of requests. Ours is going to be the readv() request, which we * specify via IORING_OP_READV. * * */ int submit_to_sq(char *file_path, struct submitter *s) { struct file_info *fi; int file_fd = open(file_path, O_RDONLY); if (file_fd < 0 ) { perror("open"); return 1; } struct app_io_sq_ring *sring = &s->sq_ring; unsigned index = 0, current_block = 0, tail = 0, next_tail = 0; off_t file_sz = get_file_size(file_fd); if (file_sz < 0) return 1; off_t bytes_remaining = file_sz; int blocks = (int) file_sz / BLOCK_SZ; if (file_sz % BLOCK_SZ) blocks++; fi = malloc(sizeof(*fi) + sizeof(struct iovec) * blocks); if (!fi) { fprintf(stderr, "Unable to allocate memory\n"); return 1; } fi->file_sz = file_sz; /* * For each block of the file we need to read, we allocate an iovec struct * which is indexed into the iovecs array. This array is passed in as part * of the submission. If you don't understand this, then you need to look * up how the readv() and writev() system calls work. * */ while (bytes_remaining) { off_t bytes_to_read = bytes_remaining; if (bytes_to_read > BLOCK_SZ) bytes_to_read = BLOCK_SZ; fi->iovecs[current_block].iov_len = bytes_to_read; void *buf; if( posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ)) { perror("posix_memalign"); return 1; } fi->iovecs[current_block].iov_base = buf; current_block++; bytes_remaining -= bytes_to_read; } /* Add our submission queue entry to the tail of the SQE ring buffer */ next_tail = tail = *sring->tail; next_tail++; read_barrier(); index = tail & *s->sq_ring.ring_mask; struct io_uring_sqe *sqe = &s->sqes[index]; sqe->fd = file_fd; sqe->flags = 0; sqe->opcode = IORING_OP_READV; sqe->addr = (unsigned long) fi->iovecs; sqe->len = blocks; sqe->off = 0; sqe->user_data = (unsigned long long) fi; sring->array[index] = index; tail = next_tail; /* Update the tail so the kernel can see it. */ if(*sring->tail != tail) { *sring->tail = tail; write_barrier(); } /* * Tell the kernel we have submitted events with the io_uring_enter() system * call. We also pass in the IOURING_ENTER_GETEVENTS flag which causes the * io_uring_enter() call to wait until min_complete events (the 3rd param) * complete. * */ int ret = io_uring_enter(s->ring_fd, 1,1, IORING_ENTER_GETEVENTS); if(ret < 0) { perror("io_uring_enter"); return 1; } return 0; } int main(int argc, char *argv[]) { struct submitter *s; if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } s = malloc(sizeof(*s)); if (!s) { perror("malloc"); return 1; } memset(s, 0, sizeof(*s)); if(app_setup_uring(s)) { fprintf(stderr, "Unable to setup uring!\n"); return 1; } for (int i = 1; i < argc; i++) { if(submit_to_sq(argv[i], s)) { fprintf(stderr, "Error reading file\n"); return 1; } read_from_cq(s); } __asm__("int3"); return 0; } ================================================ FILE: dev/ipcs/test_posix.c ================================================ #include #include #include #include #include #include #include #include #include #include int main(int argc, char* argv[]) { if (argc < 2) { printf("%s s|r|d\n", argv[0]); puts("* POSIX sem/shm can be accessed from under /dev/shm"); puts("* POSIX mq can be accessed from under /dev/mqueue"); exit(1); } if (argv[1][0] == 's') { /* POSIX semaphore */ sem_t* semid = sem_open("/sample", O_CREAT, 0666, 0); printf("semid /sample: %lx\n", (unsigned long)semid); sem_close(semid); /* POSIX message queue */ mqd_t msqid = mq_open("/sample", O_WRONLY | O_CREAT, 0666, NULL); printf("msqid /sample: %d\n", msqid); char buf[] = "AAAA"; mq_send(msqid, (char*)&buf, strlen(buf), 0); mq_close(msqid); /* POSIX shared memory */ int shmid = shm_open("/sample", O_RDWR | O_CREAT, 0666); printf("shmid /sample: %d\n", shmid); close(shmid); } else if (argv[1][0] == 'r') { /* POSIX semaphore */ // do nothing /* POSIX message queue */ mqd_t msqid = mq_open("/sample", O_RDONLY); struct mq_attr attr; mq_getattr(msqid, &attr); char *buf = malloc(attr.mq_msgsize); mq_receive(msqid, (char*)buf, attr.mq_msgsize, NULL); puts("mq /sample: receive"); puts(buf); free(buf); mq_close(msqid); /* POSIX shared memory */ // do nothing } else if (argv[1][0] == 'd') { /* POSIX semaphore */ puts("sem /sample: unlink"); sem_unlink("/sample"); /* POSIX message queue */ puts("mq /sample: unlink"); mq_unlink("/sample"); /* POSIX shared memory */ puts("shm /sample: unlink"); shm_unlink("/sample"); } return 0; } ================================================ FILE: dev/ipcs/test_sysv.c ================================================ #include #include #include #include #include #include #include #define NSEMS 10 union semun { int val; struct semid_ds *buf; unsigned short *array; }; #define MSGTYPE 1 struct msgbuf { long mtype; char mtext[4]; }; int main(int argc, char* argv[]) { if (argc < 3) { printf("%s s|r|d\n", argv[0]); puts("* System-V IPCs can be dumped by `ipcs`"); exit(1); } key_t key = ftok(argv[1], 'S'); if (key == -1) { perror("ftok"); exit(1); } if (argv[2][0] == 's') { /* SystemV semaphore */ int semid = semget(key, NSEMS, IPC_CREAT | 0666); printf("semid: %d\n", semid); union semun arg = { .val = 1 }; semctl(semid, 0, SETVAL, arg); /* SystemV message queue */ int msqid = msgget(key, IPC_CREAT | 0666); printf("msqid: %d\n", msqid); struct msgbuf message = { .mtype = MSGTYPE, .mtext = {'A', 'A', 'A', '\0'} }; msgsnd(msqid, &message, sizeof(message.mtext), IPC_NOWAIT); /* SystemV shared memory */ int shmid = shmget(key, 0x1000, IPC_CREAT | 0666); printf("shmid: %d\n", shmid); } else if (argv[2][0] == 'r') { /* SystemV semaphore */ // do nothing /* SystemV message queue */ int msqid = msgget(key, 0); printf("msqid: %d\n", msqid); struct msgbuf message = { .mtype = MSGTYPE }; msgrcv(msqid, &message, sizeof(message.mtext), MSGTYPE, IPC_NOWAIT); puts(message.mtext); /* SystemV shared memory */ // do nothing } else if (argv[2][0] == 'd') { /* SystemV semaphore */ int semid = semget(key, 0, 0); printf("semid: %d\n", semid); semctl(semid, 0, IPC_RMID); /* SystemV message queue */ int msqid = msgget(key, 0); printf("msqid: %d\n", msqid); msgctl(msqid, IPC_RMID, NULL); /* SystemV shared memory */ int shmid = shmget(key, 0, 0); printf("shmid: %d\n", shmid); shmctl(shmid, IPC_RMID, NULL); } return 0; } ================================================ FILE: dev/js/test.js ================================================ // d8 --allow-natives-syntax test.js // ---- basic objects ---- let obj = { a: 1, b: 2 }; let arr = [1, 2, 3]; let str = "hello"; let num = 42; let bool = true; let date = new Date(); let map = new Map([[1, 'one'], [2, 'two']]); let set = new Set([1, 2, 3]); // other let weakMap = new WeakMap(); let weakSet = new WeakSet(); let tempObj = {}; weakMap.set(tempObj, 'value'); weakSet.add(tempObj); let promise = Promise.resolve(123); let symbol = Symbol("mysymbol"); let bigInt = 1234567890123456789012345678901234567890n; // ---- buffer ---- let buffer = new ArrayBuffer(0x1000); let view = new DataView(buffer); let typedArray = new Uint8Array(buffer); // ---- functions ---- function normalFunc(x) { return x + 1; } let arrowFunc = (x) => x * 2; let closureFunc = (function(y) { return function(x) { return x + y; }; })(10); class MyClass { constructor(value) { this.value = value; } method() { return this.value; } } let classInstance = new MyClass(123); // ---- Wasm Module ---- let wasmCode = new Uint8Array([ 0x00,0x61,0x73,0x6D, // magic "\0asm" 0x01,0x00,0x00,0x00, // version 1 // minimal empty wasm module ]); let wasmModule = new WebAssembly.Module(wasmCode); let wasmInstance = new WebAssembly.Instance(wasmModule); // ---- Proxy ---- let target = { foo: "bar" }; let handler = { get: function(obj, prop) { return prop in obj ? obj[prop] : "default"; } }; let proxy = new Proxy(target, handler); // ---- Generator ---- function* genFunc() { yield 1; yield 2; return 3; } let generator = genFunc(); // ---- AsyncFunction ---- async function asyncFunc() { return 42; } // ---- AsyncGenerator ---- async function* asyncGenFunc() { yield 1; yield 2; } let asyncGenerator = asyncGenFunc(); // ---- Reflect ---- let reflectObj = Reflect; // ---- Intl ---- let intlCollator = new Intl.Collator(); let intlNumberFormat = new Intl.NumberFormat(); let intlDateTimeFormat = new Intl.DateTimeFormat(); // ---- SharedArrayBuffer ---- let sharedArrayBuffer = new SharedArrayBuffer(1024); // ---- Atomics ---- let sharedInt32Array = new Int32Array(sharedArrayBuffer); Atomics.store(sharedInt32Array, 0, 42); // ---- Error ---- let error = new Error("test error"); let typeError = new TypeError("test type error"); // ---- RegExp ---- let re = /abc/; // ---- FinalizationRegistry / WeakRef ---- let registry = new FinalizationRegistry(() => {}); let weakRef = new WeakRef({}); // --------------------------------------------------- // ---- dump ---- function debug(name, obj) { print(`=== ${name} ===`); %DebugPrint(obj); } debug("Object", obj); debug("Array", arr); debug("String", str); debug("Number", num); debug("Boolean", bool); debug("Date", date); debug("Map", map); debug("Set", set); debug("WeakMap", weakMap); debug("WeakSet", weakSet); debug("Promise", promise); debug("Symbol", symbol); debug("BigInt", bigInt); debug("ArrayBuffer", buffer); debug("DataView", view); debug("TypedArray", typedArray); debug("Normal Function", normalFunc); debug("Arrow Function", arrowFunc); debug("Closure Function", closureFunc); debug("Class", MyClass); debug("Class Instance", classInstance); debug("Wasm Module", wasmModule); debug("Wasm Instance", wasmInstance); debug("Proxy", proxy); debug("Generator", generator); debug("AsyncFunction", asyncFunc); debug("AsyncGenerator", asyncGenerator); debug("Reflect", reflectObj); debug("Intl.Collator", intlCollator); debug("Intl.NumberFormat", intlNumberFormat); debug("Intl.DateTimeFormat", intlDateTimeFormat); debug("SharedArrayBuffer", sharedArrayBuffer); debug("Shared Int32Array (Atomics target)", sharedInt32Array); debug("Error", error); debug("TypeError", typeError); debug("RegExp", re); debug("FinalizationRegistry", registry); debug("WeakRef", weakRef); debug("Math", Math); debug("JSON", JSON); while (1) {} ================================================ FILE: dev/partition-alloc-dump/downloader.py ================================================ #!/usr/bin/python3 import sys import os import subprocess import requests import json import functools import bisect @functools.lru_cache def get_chrome_info(): url = "https://chromiumdash.appspot.com/fetch_releases?num=1" r = requests.get(url) j = json.loads(r.text) return j @functools.lru_cache def get_channel_info(channel): j = get_chrome_info() for entry in j: if entry["channel"].lower() == channel and entry["platform"] == "Linux": return entry raise @functools.lru_cache def get_valid_pos(pos): urlbase = "https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64" while pos > 0: print(f"[!] checking {pos}...") url = urlbase + f"%2F{pos}%2FREVISIONS?&alt=media" r = requests.get(url) if "chromium_revision" in r.text: return pos pos -= 1 return pos def download_binary(channel): print("#"*50) print(f"[*] channel: {channel}") # preparing e = get_channel_info(channel) current_version = e["version"] print(f"[*] current_version: {current_version}") pos = e["chromium_main_branch_position"] print(f"[*] position: {pos}") pos = get_valid_pos(pos) print(f"[*] position where snapshot exists: {pos}") # check exists dirname = f"./chrome_{channel}" os.makedirs(dirname, exist_ok=True) if os.path.exists(f"{dirname}/chrome-linux-{pos}.zip"): print(" Already exists, download is skipped") return # download # https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/ urlbase = "https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64" url = urlbase + f"%2F{pos}%2Fchrome-linux.zip?&alt=media" cmd = f"wget -O {dirname}/chrome-linux-{pos}.zip '{url}'" print(" Execute: {:s}".format(cmd)) subprocess.getoutput(cmd) return def print_git_url(channel): e = get_channel_info(channel) current_version = e["version"] pos = get_valid_pos(e["chromium_main_branch_position"]) url_base = "https://source.chromium.org/chromium/chromium/src/+/main:base" dir_base = "allocator/partition_allocator/src/partition_alloc" commit = e["hashes"]["chromium"] print("[*] commit hash: {:s}".format(commit)) print("[*] struct base::PartitionRoot") print(" {:s}/{:s}/partition_root.h;drc={:s}".format(url_base, dir_base, commit)) print("[*] struct base::internal::PartitionBucket:") print(" {:s}/{:s}/partition_bucket.h;drc={:s}".format(url_base, dir_base, commit)) print("[*] struct PartitionSuperPageExtentEntry:") print(" {:s}/{:s}/partition_superpage_extent_entry.h;drc={:s}".format(url_base, dir_base, commit)) print("[*] struct PartitionDirectMapExtent:") print(" {:s}/{:s}/partition_direct_map_extent.h;drc={:s}".format(url_base, dir_base, commit)) print("[*] struct SlotSpanMetadata:") print(" {:s}/{:s}/partition_page.h;drc={:s}".format(url_base, dir_base, commit)) print("[*] v{:s}.x / {:d} / {:s}".format(current_version.split(".")[0], pos, commit)) print() return def memo(): print("#"*50) print("[*] memo") print(" [preparation]") print(" disable apparmor. I added `apparmor=0` to `GRUB_CMDLINE_LINUX` in `/etc/default/grub`, then `update-grub && reboot`") print(" [term1]") print(" cd www && python3 -m http.server 8080") print(" [term2]") print(" unzip -d /tmp chrome_dev/chrome-linux-*.zip && cd /tmp/chrome-linux") print(" rm -rf /tmp/u && sudo -u NON_ROOT_USER_NAME ./chrome --headless=new --disable-gpu " "--remote-debugging-port=1338 --user-data-dir=/tmp/u --enable-logging=stderr http://localhost:8080/inf-loop.html") print(" [term3 (for renderer process)]") print(""" gdb -q -p $(ps -ef | grep -- "--[t]ype=renderer" | awk '{pid=$2; for(i=1;i<=NF;i++) """ """if($i ~ /--renderer-client-id=/) {split($i,a,"="); print a[2], pid}}' | sort -n | head -n 1 | awk '{print $2}')""") print(" [term3 (for browser process)]") print(""" gdb -q -p $(ps -ef | grep "\\./[c]hrome" | grep -v type | grep -v sudo | awk '{print $2}')""") return if __name__ == '__main__': if len(sys.argv) == 1: channels = ["stable", "beta", "dev"] elif sys.argv[1] in ["-h", "--help"]: print("[*] usage") print(" python3 {:s} # download all channels (stable, beta, dev)".format(sys.argv[0])) print(" python3 {:s} stable beta # download specific channel(s)".format(sys.argv[0])) print() channels = [] else: channels = sys.argv[1:] if channels: for chan in channels: if chan in ["stable", "beta", "dev"]: download_binary(chan) print_git_url(chan) else: print("channel is stable, beta, or dev") exit() memo() ================================================ FILE: dev/partition-alloc-dump/www/inf-loop.html ================================================ ================================================ FILE: dev/seccomp/test1.c ================================================ // gcc test1.c -lseccomp #include #include int main() { scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); if (ctx < 0) perror("seccomp_init"); if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0)) perror("seccomp_rule_add"); if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0)) perror("seccomp_rule_add"); if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0)) perror("seccomp_rule_add"); if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0)) perror("seccomp_rule_add"); if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0)) perror("seccomp_rule_add"); if (seccomp_load(ctx)) perror("seccomp_load"); seccomp_release(ctx); return 0; } ================================================ FILE: dev/seccomp/test2.c ================================================ // gcc test2.c #include #include #include #include #include #include #include #include int main() { struct sock_filter filter[] = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS), BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, __X32_SYSCALL_BIT , 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_open, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_read, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_exit, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_exit_group, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), }; struct sock_fprog prog = { .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])), .filter = filter, }; if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) perror("prctl(NO_NEW_PRIVS)"); if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,0, &prog)) perror("seccomp"); return 0; } ================================================ FILE: dev/simple-heap/test.c ================================================ #include #include #include #include #define COUNT 0x10 void *func(int sz) { void* p[COUNT] = {}; void* dummy; // allocate for (int i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #define PORT_TCP4 5001 #define PORT_TCP6 5002 #define PORT_UDP4 5003 #define PORT_UDP6 5004 #define UNIX_SOCK_PATH "/tmp/echo_unix.sock" #define BUFFER_SIZE 1024 char* bytes_to_hex(const unsigned char* bytes, size_t len) { if (bytes == NULL || len == 0) { return NULL; } size_t hex_len = len * 2 + 1; char* hex_str = (char*)malloc(hex_len); if (hex_str == NULL) { perror("malloc failed"); return NULL; } for (size_t i = 0; i < len; ++i) { sprintf(hex_str + (i * 2), "%02X", bytes[i]); } hex_str[hex_len - 1] = '\0'; return hex_str; } void *tcp4_echo_server(void *arg) { int server_fd, client_fd; struct sockaddr_in addr; server_fd = socket(AF_INET, SOCK_STREAM, 0); printf("[DEBUG] tcp4_server socket: fd=%d\n", server_fd); if (server_fd < 0) { perror("tcp4 socket"); exit(1); } addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(PORT_TCP4); if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("tcp4 bind"); exit(1); } if (listen(server_fd, 3) < 0) { perror("tcp4 listen"); exit(1); } while (1) { struct sockaddr_in client_addr; socklen_t addrlen = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addrlen); printf("[DEBUG] tcp4_server accept: fd=%d\n", client_fd); if (client_fd < 0) { perror("tcp4 accept"); continue; } char buffer[BUFFER_SIZE]; int valread; while ((valread = read(client_fd, buffer, BUFFER_SIZE)) > 0) { write(client_fd, buffer, valread); } close(client_fd); printf("[DEBUG] tcp4_server close client: fd=%d\n", client_fd); } return NULL; } void *tcp6_echo_server(void *arg) { int server_fd, client_fd; struct sockaddr_in6 addr; server_fd = socket(AF_INET6, SOCK_STREAM, 0); printf("[DEBUG] tcp6_server socket: fd=%d\n", server_fd); if (server_fd < 0) { perror("tcp6 socket"); exit(1); } addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = htons(PORT_TCP6); if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("tcp6 bind"); exit(1); } if (listen(server_fd, 3) < 0) { perror("tcp6 listen"); exit(1); } while (1) { struct sockaddr_in6 client_addr; socklen_t addrlen = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addrlen); printf("[DEBUG] tcp6_server accept: fd=%d\n", client_fd); if (client_fd < 0) { perror("tcp6 accept"); continue; } char buffer[BUFFER_SIZE]; int valread; while ((valread = read(client_fd, buffer, BUFFER_SIZE)) > 0) { write(client_fd, buffer, valread); } close(client_fd); printf("[DEBUG] tcp6_server close client: fd=%d\n", client_fd); } return NULL; } void *udp4_server(void *arg) { int sockfd; struct sockaddr_in addr; sockfd = socket(AF_INET, SOCK_DGRAM, 0); printf("[DEBUG] udp4_server socket: fd=%d\n", sockfd); if (sockfd < 0) { perror("udp4 socket"); exit(1); } addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(PORT_UDP4); if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("udp4 bind"); exit(1); } while (1) { char buffer[BUFFER_SIZE]; struct sockaddr_in client_addr; socklen_t addrlen = sizeof(client_addr); ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &addrlen); if (n < 0) { perror("udp4 recvfrom"); continue; } // 何もしない printf("[udp4 srever] %s", buffer); } return NULL; } void *udp6_server(void *arg) { int sockfd; struct sockaddr_in6 addr; sockfd = socket(AF_INET6, SOCK_DGRAM, 0); printf("[DEBUG] udp6_server socket: fd=%d\n", sockfd); if (sockfd < 0) { perror("udp6 socket"); exit(1); } addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = htons(PORT_UDP6); if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("udp6 bind"); exit(1); } while (1) { char buffer[BUFFER_SIZE]; struct sockaddr_in6 client_addr; socklen_t addrlen = sizeof(client_addr); ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &addrlen); if (n < 0) { perror("udp6 recvfrom"); continue; } printf("[udp6 server] %s", buffer); } return NULL; } void *unix_echo_server(void *arg) { int server_fd, client_fd; struct sockaddr_un addr; unlink(UNIX_SOCK_PATH); server_fd = socket(AF_UNIX, SOCK_STREAM, 0); printf("[DEBUG] unix_server socket: fd=%d\n", server_fd); if (server_fd < 0) { perror("unix socket"); exit(1); } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, UNIX_SOCK_PATH, sizeof(addr.sun_path)-1); if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("unix bind"); exit(1); } if (listen(server_fd, 3) < 0) { perror("unix listen"); exit(1); } while (1) { client_fd = accept(server_fd, NULL, NULL); printf("[DEBUG] unix_server accept: fd=%d\n", client_fd); if (client_fd < 0) { perror("unix accept"); continue; } char buffer[BUFFER_SIZE]; int valread; while ((valread = read(client_fd, buffer, BUFFER_SIZE)) > 0) { write(client_fd, buffer, valread); } close(client_fd); printf("[DEBUG] unix_server close client: fd=%d\n", client_fd); } return NULL; } void *icmp_server(void *arg) { int sockfd; struct sockaddr_in addr; sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); printf("[DEBUG] icmp_server socket: fd=%d\n", sockfd); if (sockfd < 0) { perror("icmp socket (要root)"); pthread_exit(NULL); } addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("icmp bind"); pthread_exit(NULL); } while (1) { char buffer[BUFFER_SIZE]; struct sockaddr_in from; socklen_t fromlen = sizeof(from); ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&from, &fromlen); if (n < 0) { perror("icmp recvfrom"); continue; } printf("[icmp4 server] %s\n", bytes_to_hex(buffer, n)); fflush(stdout); } return NULL; } void *icmp6_server(void *arg) { int sockfd; struct sockaddr_in6 addr; sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); printf("[DEBUG] icmp6_server socket: fd=%d\n", sockfd); if (sockfd < 0) { perror("icmp6 socket (要root)"); pthread_exit(NULL); } addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = 0; if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("icmp6 bind"); pthread_exit(NULL); } while (1) { char buffer[BUFFER_SIZE]; struct sockaddr_in6 from; socklen_t fromlen = sizeof(from); ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&from, &fromlen); if (n < 0) { perror("icmp6 recvfrom"); continue; } printf("[icmp6 server] %s\n", bytes_to_hex(buffer, n)); fflush(stdout); } return NULL; } void *raw_server(void *arg) { int sockfd; struct sockaddr_in addr; sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); printf("[DEBUG] raw_server socket: fd=%d\n", sockfd); if (sockfd < 0) { perror("raw socket (要root)"); pthread_exit(NULL); } addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("raw bind"); pthread_exit(NULL); } while (1) { char buffer[BUFFER_SIZE]; struct sockaddr_in from; socklen_t fromlen = sizeof(from); ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&from, &fromlen); if (n < 0) { perror("raw recvfrom"); continue; } printf("[raw server] %s", buffer); fflush(stdout); } return NULL; } void *raw6_server(void *arg) { int sockfd; struct sockaddr_in6 addr; sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); printf("[DEBUG] raw6_server socket: fd=%d\n", sockfd); if (sockfd < 0) { perror("raw6 socket (要root)"); pthread_exit(NULL); } addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = 0; if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("raw6 bind"); pthread_exit(NULL); } while (1) { char buffer[BUFFER_SIZE]; struct sockaddr_in6 from; socklen_t fromlen = sizeof(from); ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&from, &fromlen); if (n < 0) { perror("raw6 recvfrom"); continue; } printf("[raw6 server] %s", buffer); fflush(stdout); } return NULL; } void *client_thread(void *arg) { int sock4 = socket(AF_INET, SOCK_STREAM, 0); printf("[DEBUG] client tcp4 socket: fd=%d\n", sock4); if (sock4 < 0) { perror("client tcp4 socket"); exit(1); } struct sockaddr_in addr4; addr4.sin_family = AF_INET; addr4.sin_port = htons(PORT_TCP4); inet_pton(AF_INET, "127.0.0.1", &addr4.sin_addr); if (connect(sock4, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) { perror("client tcp4 connect"); exit(1); } int sock6 = socket(AF_INET6, SOCK_STREAM, 0); printf("[DEBUG] client tcp6 socket: fd=%d\n", sock6); if (sock6 < 0) { perror("client tcp6 socket"); exit(1); } struct sockaddr_in6 addr6; addr6.sin6_family = AF_INET6; addr6.sin6_port = htons(PORT_TCP6); inet_pton(AF_INET6, "::1", &addr6.sin6_addr); if (connect(sock6, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) { perror("client tcp6 connect"); exit(1); } int usock4 = socket(AF_INET, SOCK_DGRAM, 0); printf("[DEBUG] client udp4 socket: fd=%d\n", usock4); if (usock4 < 0) { perror("client udp4 socket"); exit(1); } struct sockaddr_in uaddr4; uaddr4.sin_family = AF_INET; uaddr4.sin_port = htons(PORT_UDP4); inet_pton(AF_INET, "127.0.0.1", &uaddr4.sin_addr); int usock6 = socket(AF_INET6, SOCK_DGRAM, 0); printf("[DEBUG] client udp6 socket: fd=%d\n", usock6); if (usock6 < 0) { perror("client udp6 socket"); exit(1); } struct sockaddr_in6 uaddr6; uaddr6.sin6_family = AF_INET6; uaddr6.sin6_port = htons(PORT_UDP6); inet_pton(AF_INET6, "::1", &uaddr6.sin6_addr); int unix_sock = socket(AF_UNIX, SOCK_STREAM, 0); printf("[DEBUG] client unix socket: fd=%d\n", unix_sock); if (unix_sock < 0) { perror("client unix socket"); exit(1); } struct sockaddr_un unix_addr; memset(&unix_addr, 0, sizeof(unix_addr)); unix_addr.sun_family = AF_UNIX; strncpy(unix_addr.sun_path, UNIX_SOCK_PATH, sizeof(unix_addr.sun_path)-1); while (connect(unix_sock, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) { if (errno == ENOENT) { usleep(100000); continue; } perror("client unix connect"); exit(1); } int icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); printf("[DEBUG] client icmp socket: fd=%d\n", icmp_sock); struct sockaddr_in icmp_addr; icmp_addr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &icmp_addr.sin_addr); int icmp6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); printf("[DEBUG] client icmp6 socket: fd=%d\n", icmp6_sock); struct sockaddr_in6 icmp6_addr; icmp6_addr.sin6_family = AF_INET6; inet_pton(AF_INET6, "::1", &icmp6_addr.sin6_addr); int raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); printf("[DEBUG] client raw socket: fd=%d\n", raw_sock); struct sockaddr_in raw_addr; raw_addr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &raw_addr.sin_addr); int raw6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); printf("[DEBUG] client raw6 socket: fd=%d\n", raw6_sock); struct sockaddr_in6 raw6_addr; raw6_addr.sin6_family = AF_INET6; inet_pton(AF_INET6, "::1", &raw6_addr.sin6_addr); while (1) { char input[BUFFER_SIZE]; sleep(0.1); printf("Input: "); if (!fgets(input, BUFFER_SIZE, stdin)) break; send(sock4, input, strlen(input), 0); char resp4[BUFFER_SIZE] = {0}; int r4 = read(sock4, resp4, BUFFER_SIZE-1); if (r4 > 0) resp4[r4] = '\0'; printf("[TCPv4 echo] %s", resp4); send(sock6, input, strlen(input), 0); char resp6[BUFFER_SIZE] = {0}; int r6 = read(sock6, resp6, BUFFER_SIZE-1); if (r6 > 0) resp6[r6] = '\0'; printf("[TCPv6 echo] %s", resp6); sendto(usock4, input, strlen(input), 0, (struct sockaddr *)&uaddr4, sizeof(uaddr4)); sendto(usock6, input, strlen(input), 0, (struct sockaddr *)&uaddr6, sizeof(uaddr6)); send(unix_sock, input, strlen(input), 0); char unix_resp[BUFFER_SIZE] = {0}; int ur = read(unix_sock, unix_resp, BUFFER_SIZE-1); if (ur > 0) unix_resp[ur] = '\0'; printf("[UNIX echo] %s", unix_resp); if (icmp_sock >= 0) { char icmp_packet[8] = {8, 0, 0, 0, 0, 0, 0, 0}; sendto(icmp_sock, icmp_packet, sizeof(icmp_packet), 0, (struct sockaddr *)&icmp_addr, sizeof(icmp_addr)); } if (icmp6_sock >= 0) { char icmp6_packet[8] = {128, 0, 0, 0, 0, 0, 0, 0}; sendto(icmp6_sock, icmp6_packet, sizeof(icmp6_packet), 0, (struct sockaddr *)&icmp6_addr, sizeof(icmp6_addr)); } if (raw_sock >= 0) { char raw_data[BUFFER_SIZE] = "RAW DATA"; sendto(raw_sock, raw_data, strlen(raw_data), 0, (struct sockaddr *)&raw_addr, sizeof(raw_addr)); } if (raw6_sock >= 0) { char raw6_data[BUFFER_SIZE] = "RAW6 DATA"; sendto(raw6_sock, raw6_data, strlen(raw6_data), 0, (struct sockaddr *)&raw6_addr, sizeof(raw6_addr)); } } close(sock4); close(sock6); close(usock4); close(usock6); close(unix_sock); if (icmp_sock >= 0) close(icmp_sock); if (icmp6_sock >= 0) close(icmp6_sock); if (raw_sock >= 0) close(raw_sock); if (raw6_sock >= 0) close(raw6_sock); return NULL; } int main() { pthread_t threads[10]; pthread_create(&threads[0], NULL, tcp4_echo_server, NULL); pthread_create(&threads[1], NULL, tcp6_echo_server, NULL); pthread_create(&threads[2], NULL, udp4_server, NULL); pthread_create(&threads[3], NULL, udp6_server, NULL); pthread_create(&threads[4], NULL, unix_echo_server, NULL); pthread_create(&threads[5], NULL, icmp_server, NULL); pthread_create(&threads[6], NULL, icmp6_server, NULL); pthread_create(&threads[7], NULL, raw_server, NULL); pthread_create(&threads[8], NULL, raw6_server, NULL); pthread_create(&threads[9], NULL, client_thread, NULL); for (int i = 0; i < 10; ++i) { pthread_join(threads[i], NULL); } return 0; } ================================================ FILE: dev/split/de-split.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import re import sys def reconstruct(): base_dir = os.path.dirname(os.path.abspath(__file__)) save_dir = os.path.join(base_dir, "save") splitted_path = os.path.join(save_dir, "gef-splitted.py") output_path = os.path.join(base_dir, "gef-reconstructed.py") if not os.path.exists(splitted_path): print(f"Error: {splitted_path} not found.") sys.exit(1) with open(splitted_path, "r", encoding="utf-8") as f: lines = f.read().splitlines() out_lines = [] import_re = re.compile(r"^from\s+(lib[a-zA-Z0-9_.]+)\s+import\s+([a-zA-Z0-9_]+)(?:\s+as\s+([a-zA-Z0-9_]+))?") sys_path_re = re.compile(r"^sys\.path\.insert\(0,\s*[\"'].*?save.*?[\"']\)$") in_hash = False i = 0 while i < len(lines): line = lines[i] if sys_path_re.match(line): i += 1 if i < len(lines) and lines[i] == "": # Optional empty line skip if any pass continue m = import_re.match(line) if m: module_path = m.group(1) file_path = os.path.join(save_dir, *module_path.split(".")) + ".py" if os.path.exists(file_path): with open(file_path, "r", encoding="utf-8") as f: file_content = f.read() if module_path.startswith("lib.hash."): if not in_hash: out_lines.append("class Hash:") in_hash = True file_lines = file_content.splitlines() if file_lines and file_lines[0].strip() == "class Hash:": file_lines = file_lines[1:] out_lines.extend(file_lines) else: in_hash = False out_lines.extend(file_content.splitlines()) # adding blank lines to restore what `collapse_blank_lines_between_from_imports` removed if i + 1 < len(lines): next_m = import_re.match(lines[i+1]) if next_m: next_is_hash = next_m.group(1).startswith("lib.hash.") if in_hash and next_is_hash: out_lines.append("") else: out_lines.append("") out_lines.append("") i += 1 continue in_hash = False out_lines.append(line) i += 1 with open(output_path, "w", encoding="utf-8") as f: f.write("\n".join(out_lines) + "\n") print(f"[+] Reconstructed GEF saved to {output_path}") if __name__ == "__main__": reconstruct() ================================================ FILE: dev/split/split.py ================================================ #!/usr/bin/python3 # This file simply splits each class into a separate file for AI code review. # Dependencies will not be resolved, so splitting GEF will not work correctly. import os import re import ast import shutil def get_comment_start(lines, start_lineno): start = start_lineno idx = start_lineno - 2 while idx >= 0: if not lines[idx].lstrip().startswith("#"): break start = idx + 1 idx -= 1 return start def class_ranges_from_file(path): with open(path, "r", encoding="utf-8") as f: src = f.read() tree = ast.parse(src, filename=path) result = {} tops = [n for n in tree.body if hasattr(n, "lineno")] for node in tops: if not isinstance(node, ast.ClassDef): continue if node.decorator_list: start = min(d.lineno for d in node.decorator_list) else: start = node.lineno end = node.end_lineno result[node.name] = (start - 1, end) return result def global_ranges_from_file(path): with open(path, "r", encoding="utf-8") as f: src = f.read() tree = ast.parse(src, filename=path) lines = src.splitlines() result = {} tops = [n for n in tree.body if hasattr(n, "lineno")] for node in tops: name = None if isinstance(node, ast.Assign): if len(node.targets) != 1: continue if not isinstance(node.targets[0], ast.Name): continue name = node.targets[0].id elif isinstance(node, ast.AnnAssign): if not isinstance(node.target, ast.Name): continue name = node.target.id else: continue if ("syscall_defs" not in name) and ("syscall_tbl" not in name) and ("syscall_list" not in name): continue start = get_comment_start(lines, node.lineno) end = node.end_lineno result[name] = (start - 1, end) return result def top_level_function_ranges_from_file(path): with open(path, "r", encoding="utf-8") as f: src = f.read() tree = ast.parse(src, filename=path) lines = src.splitlines() result = {} for node in tree.body: if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): continue if node.decorator_list: start = min(d.lineno for d in node.decorator_list) else: start = node.lineno start = get_comment_start(lines, start) end = node.end_lineno result[node.name] = (start - 1, end) return result def used_decorator_names_from_file(path): with open(path, "r", encoding="utf-8") as f: src = f.read() tree = ast.parse(src, filename=path) used_decorators = set() def add_decorator_name(expr): target = expr if isinstance(target, ast.Call): target = target.func if isinstance(target, ast.Name): used_decorators.add(target.id) return if isinstance(target, ast.Attribute): used_decorators.add(target.attr) return for node in ast.walk(tree): if not isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): continue for deco in node.decorator_list: add_decorator_name(deco) return used_decorators def function_groups_from_file(path): top_level_funcs = top_level_function_ranges_from_file(path) used_decorators = used_decorator_names_from_file(path) decorator_dic = {} rw_dic = {} checker_dic = {} forced_decorators = {"perf", "cperf"} for name, rng in top_level_funcs.items(): if name in used_decorators or name in forced_decorators: decorator_dic[name] = rng continue if re.match(r"^(read|write)_[A-Za-z0-9_]+$", name): rw_dic[name] = rng continue if re.match(r"^is_[A-Za-z0-9_]+$", name) or name in ["kgdb_has_system_registers"]: checker_dic[name] = rng continue return { "decorator": decorator_dic, "rw": rw_dic, "checker": checker_dic, } def hash_groups_from_file(path): with open(path, "r", encoding="utf-8") as f: src = f.read() tree = ast.parse(src, filename=path) lines = src.splitlines() hash_node = None for node in tree.body: if isinstance(node, ast.ClassDef) and node.name == "Hash": hash_node = node break if hash_node is None: return None nested_classes = [] nested_names = set() for node in hash_node.body: if not isinstance(node, ast.ClassDef): continue nested_classes.append(node) nested_names.add(node.name) ranges = {} base_map = {} order = [] for node in nested_classes: if node.decorator_list: start = min(d.lineno for d in node.decorator_list) else: start = node.lineno start = get_comment_start(lines, start) end = node.end_lineno ranges[node.name] = (start - 1, end) order.append(node.name) base_name = None if len(node.bases) > 1: raise ValueError(f"multiple inheritance is not supported: Hash.{node.name}") if node.bases: base = node.bases[0] if isinstance(base, ast.Name) and base.id in nested_names: base_name = base.id elif ( isinstance(base, ast.Attribute) and isinstance(base.value, ast.Name) and base.value.id == "Hash" and base.attr in nested_names ): base_name = base.attr base_map[node.name] = base_name def root_of(name): cur = name seen = set() while base_map[cur] is not None: if cur in seen: raise ValueError(f"cyclic inheritance detected under Hash: {name}") seen.add(cur) cur = base_map[cur] return cur groups = {} for name in order: root = root_of(name) if root not in groups: groups[root] = [] groups[root].append(name) hash_start = min(d.lineno for d in hash_node.decorator_list) if hash_node.decorator_list else hash_node.lineno hash_start = get_comment_start(lines, hash_start) hash_end = hash_node.end_lineno return { "hash_range": (hash_start - 1, hash_end), "groups": groups, "ranges": ranges, "order": order, } def write_hash_groups(path, save_dir, hash_info): with open(path, "r", encoding="utf-8") as f: lines = f.read().splitlines() hash_dir = os.path.join(save_dir, "lib", "hash") if not os.path.exists(hash_dir): os.mkdir(hash_dir) group_imports = [] order_index = {name: idx for idx, name in enumerate(hash_info["order"])} for root_name, class_names in sorted( hash_info["groups"].items(), key=lambda item: order_index[item[0]], ): sorted_names = sorted(class_names, key=lambda name: order_index[name]) chunks = [] for class_name in sorted_names: s, e = hash_info["ranges"][class_name] chunks.extend(lines[s:e]) chunks.append("") if chunks and chunks[-1] == "": chunks.pop() out_lines = ["class Hash:"] out_lines.extend(chunks) file_path = os.path.join(hash_dir, root_name + ".py") with open(file_path, "w", encoding="utf-8") as f: f.write("\n".join(out_lines) + "\n") alias = f"Hash__{root_name}" group_imports.append(f"from lib.hash.{root_name} import Hash as {alias}") return group_imports def collapse_blank_lines_between_from_imports(lines): result = [] i = 0 def is_from_import(line): return line.startswith("from ") and " import " in line while i < len(lines): line = lines[i] if is_from_import(line): result.append(line) j = i + 1 while j < len(lines) and lines[j] == "": j += 1 if j < len(lines) and is_from_import(lines[j]): i = j continue i += 1 continue result.append(line) i += 1 return result def split_gef(path, class_dic, global_dic, function_groups, save_dir): with open(path, "r", encoding="utf-8") as f: lines = f.read().splitlines() hash_info = hash_groups_from_file(path) hash_imports = [] new_gef = [] pos = 0 items = [] for class_name, (s, e) in class_dic.items(): items.append(("class", class_name, s, e)) for global_name, (s, e) in global_dic.items(): items.append(("global", global_name, s, e)) for decorator_name, (s, e) in function_groups["decorator"].items(): items.append(("decorator", decorator_name, s, e)) for rw_name, (s, e) in function_groups["rw"].items(): items.append(("rw", rw_name, s, e)) for checker_name, (s, e) in function_groups["checker"].items(): items.append(("checker", checker_name, s, e)) if hash_info is not None: hash_s, hash_e = hash_info["hash_range"] items = [item for item in items if not (item[0] == "class" and item[1] == "Hash")] items.append(("hash", "Hash", hash_s, hash_e)) hash_imports = write_hash_groups(path, save_dir, hash_info) items.sort(key=lambda x: x[2]) for item_type, name, s, e in items: new_gef.extend(lines[pos:s]) code = lines[s:e] if item_type == "class": if name.endswith("Command"): file_path = os.path.join(save_dir, "lib", "command", name + ".py") new_gef.append(f"from lib.command.{name} import {name}") elif (s + 1) < len(lines) and ("GEF representation of " in lines[s + 1]) and ("architecture." in lines[s + 1]): file_path = os.path.join(save_dir, "lib", "arch", name + ".py") new_gef.append(f"from lib.arch.{name} import {name}") else: file_path = os.path.join(save_dir, "lib", name + ".py") new_gef.append(f"from lib.{name} import {name}") with open(file_path, "w", encoding="utf-8") as f: f.write("\n".join(code)) elif item_type == "global": file_path = os.path.join(save_dir, "lib", "syscall", name + ".py") new_gef.append(f"from lib.syscall.{name} import {name}") with open(file_path, "w", encoding="utf-8") as f: f.write("\n".join(code)) elif item_type == "decorator": file_path = os.path.join(save_dir, "lib", "decorator", name + ".py") new_gef.append(f"from lib.decorator.{name} import {name}") with open(file_path, "w", encoding="utf-8") as f: f.write("\n".join(code)) elif item_type == "rw": file_path = os.path.join(save_dir, "lib", "rw", name + ".py") new_gef.append(f"from lib.rw.{name} import {name}") with open(file_path, "w", encoding="utf-8") as f: f.write("\n".join(code)) elif item_type == "checker": file_path = os.path.join(save_dir, "lib", "checker", name + ".py") new_gef.append(f"from lib.checker.{name} import {name}") with open(file_path, "w", encoding="utf-8") as f: f.write("\n".join(code)) elif item_type == "hash": new_gef.extend(hash_imports) else: raise ValueError(f"unknown item type: {item_type}") pos = e new_gef.extend(lines[pos:]) idx = new_gef.index("import traceback") + 1 new_gef = ( new_gef[:idx] + ['sys.path.insert(0, "' + os.path.join(os.path.dirname(__file__), "save") + '")'] + new_gef[idx:] ) new_gef = collapse_blank_lines_between_from_imports(new_gef) file_name = os.path.join(save_dir, "gef-splitted.py") with open(file_name, "w", encoding="utf-8") as f: f.write("\n".join(new_gef)) return if __name__ == "__main__": base_dir = os.path.dirname(__file__) save_dir = os.path.join(base_dir, "save") if os.path.exists(save_dir): shutil.rmtree(save_dir) if not os.path.exists(save_dir): os.mkdir(save_dir) if not os.path.exists(os.path.join(save_dir, "lib")): os.mkdir(os.path.join(save_dir, "lib")) if not os.path.exists(os.path.join(save_dir, "lib", "command")): os.mkdir(os.path.join(save_dir, "lib", "command")) if not os.path.exists(os.path.join(save_dir, "lib", "arch")): os.mkdir(os.path.join(save_dir, "lib", "arch")) if not os.path.exists(os.path.join(save_dir, "lib", "syscall")): os.mkdir(os.path.join(save_dir, "lib", "syscall")) if not os.path.exists(os.path.join(save_dir, "lib", "hash")): os.mkdir(os.path.join(save_dir, "lib", "hash")) if not os.path.exists(os.path.join(save_dir, "lib", "decorator")): os.mkdir(os.path.join(save_dir, "lib", "decorator")) if not os.path.exists(os.path.join(save_dir, "lib", "rw")): os.mkdir(os.path.join(save_dir, "lib", "rw")) if not os.path.exists(os.path.join(save_dir, "lib", "checker")): os.mkdir(os.path.join(save_dir, "lib", "checker")) gef_path = os.path.normpath(os.path.join(base_dir, "../../gef.py")) class_dic = class_ranges_from_file(gef_path) global_dic = global_ranges_from_file(gef_path) function_groups = function_groups_from_file(gef_path) split_gef(gef_path, class_dic, global_dic, function_groups, save_dir) ================================================ FILE: dev/tls/test.c ================================================ #include _Thread_local unsigned int x = 0xdeadbeef; // __thread unsigned int x = 0xdeadbeef; int main(void) { printf("%#x\n", x); return 0; } ================================================ FILE: dev/tmux/tmux_setup.py ================================================ # How to use: # gef> source /path/to/tmux_setup.py import os import gdb import atexit def main(): # reset panes try: gdb.execute("pi GefTmuxSetupCommand.reset_panes()", to_string=True) except gdb.error: print("GEF is not loaded") return # split """ +--------+--------+--------+ | | code | legend | | | args | regs | | | source +--------+ | | | stack | | +--------+ mem_acc| | | others | | | cmd | | | +--------+--------+--------+ """ panes = {} panes["legend"] = os.popen('tmux split-window -P -F"#{pane_tty}" -h -l 30% -d "cat -"').read().strip() panes["regs"] = panes["legend"] panes["stack"] = os.popen('tmux split-window -P -F"#{pane_tty}" -v -t {bottom-right} -l 66% -d "cat -"').read().strip() panes["mem_access"] = panes["stack"] panes["code"] = os.popen('tmux split-window -P -F"#{pane_tty}" -h -l 50% -d "cat -"').read().strip() panes["args"] = panes["code"] panes["source"] = panes["code"] panes["trace"] = os.popen('tmux split-window -P -F"#{pane_tty}" -v -t {right-of} -l 33% -d "cat -"').read().strip() panes["threads"] = panes["trace"] panes["mem_watch"] = panes["trace"] panes["extra"] = panes["trace"] # set config for section, pane_tty in panes.items(): if pane_tty: gdb.execute(f"gef config context_{section}.redirect {pane_tty}", to_string=True) # set more config gdb.execute(f"gef config context_code.nb_lines 16", to_string=True) gdb.execute(f"gef config context_code.nb_lines_prev 8", to_string=True) gdb.execute(f"gef config context_stack.nb_lines 16", to_string=True) # add atexit gdb.execute("pi atexit.register(GefTmuxSetupCommand.reset_panes)", to_string=True) # clear cache gdb.execute("gef reset-cache", to_string=True) return if __name__ == "__main__": main() ================================================ FILE: dev/update-kmalloc-tracer/check.py ================================================ import argparse import os import subprocess import re class KernelVersion: def __init__(self, version_string): self.version_string = version_string try: major, minor, patch, rc = self.to_version_tuple(version_string) except Exception as err: raise argparse.ArgumentTypeError from err self.major = major self.minor = minor self.patch = patch self.rc = rc self.version_tuple = (major, minor, patch, rc) return def to_version_tuple(self, v): r = re.match(r"(\d+)\.(\d+)-rc(\d+)", v) if r: return int(r.group(1)), int(r.group(2)), 0, int(r.group(3)) r = re.match(r"(\d+)\.(\d+)\.(\d+)", v) if r: return int(r.group(1)), int(r.group(2)), int(r.group(3)), 0 r = re.match(r"(\d+)\.(\d+)", v) if r: return int(r.group(1)), int(r.group(2)), 0, 0 raise def __str__(self): if self.rc > 0: return "{:d}.{:d}-rc{:d}".format(self.major, self.minor, self.rc) elif self.patch == 0: return "{:d}.{:d}".format(self.major, self.minor) else: return "{:d}.{:d}.{:d}".format(self.major, self.minor, self.patch) raise @property def dirname(self): return "linux-{:s}".format(str(self)) @property def filename(self): if self.rc > 0: return "{:s}.tar.gz".format(self.dirname) return "{:s}.tar.xz".format(self.dirname) @property def url(self): if self.rc > 0: return "https://git.kernel.org/torvalds/t/{:s}".format(self.filename) return "https://cdn.kernel.org/pub/linux/kernel/v{:d}.x/{:s}".format(self.major, self.filename) def check_black_list(line): black_list = [ "kmalloc_caches", # variable "random_kmalloc_seed", # variable "kmalloc_dma_caches", # variable "kfree_const", # always calls kfree "kfree_sensitive", # always calls kfree "mempool_kfree", # always calls kfree "mempool_kmalloc", # always calls kmalloc "kmem_cache_alloc_bulk_noprof", # does not return ptr "kmem_cache_alloc_bulk", # does not return ptr "kmalloc_size_roundup", # does not return ptr "__kasan_kmalloc", # does not get ptr "kasan_kmalloc", # does not get ptr ] for f in black_list: f = "EXPORT_SYMBOL({:s})".format(f) if f in line: return True return False def doit(args, version): if not os.path.exists(version.filename): os.system("wget {:s}".format(version.url)) if not os.path.exists(version.dirname): print("[+] Extracting...") os.system("tar xf {:s} '{:s}/mm/'".format(version.filename, version.dirname)) os.chdir(os.path.join(version.dirname, "mm")) ret = subprocess.getoutput("grep -r EXPORT_SYMBOL |egrep 'kmalloc|krealloc|kfree|kmem_cache_alloc'") os.chdir("../..") lines = [] for line in ret.splitlines(): if check_black_list(line): continue line = re.sub(r"\((.+?)\)", lambda x: "(" + '\x1b[1;37m' + x.group(1) + '\x1b[00m' + ")", line) if args.simple: line = line.split(":")[1] lines.append(line) return sorted(set(lines)) def main(): parser = argparse.ArgumentParser() parser.add_argument("version", metavar="VERSION", type=KernelVersion, help="target version") parser.add_argument("version2", metavar="VERSION2", nargs="?", type=KernelVersion, help="target version for diff") parser.add_argument("-s", "--simple", action="store_true", help="omit source filename") args = parser.parse_args() if not args.version2: lines = doit(args, args.version) for line in lines: print(line) else: lines = doit(args, args.version) lines2 = doit(args, args.version2) print("#" * 30 + args.version.dirname) for line in lines: if line in lines2: continue print(line) print("#" * 30 + args.version2.dirname) for line in lines2: if line in lines: continue print(line) if __name__ == "__main__": main() ================================================ FILE: dev/update-kops/check.py ================================================ import argparse import os import subprocess import re import tarfile import difflib class KernelVersion: def __init__(self, version_string): self.version_string = version_string try: major, minor, patch, rc = self.to_version_tuple(version_string) except Exception as err: raise argparse.ArgumentTypeError from err self.major = major self.minor = minor self.patch = patch self.rc = rc self.version_tuple = (major, minor, patch, rc) return def to_version_tuple(self, v): r = re.match(r"(\d+)\.(\d+)-rc(\d+)", v) if r: return int(r.group(1)), int(r.group(2)), 0, int(r.group(3)) r = re.match(r"(\d+)\.(\d+)\.(\d+)", v) if r: return int(r.group(1)), int(r.group(2)), int(r.group(3)), 0 r = re.match(r"(\d+)\.(\d+)", v) if r: return int(r.group(1)), int(r.group(2)), 0, 0 raise def __str__(self): if self.rc > 0: return "{:d}.{:d}-rc{:d}".format(self.major, self.minor, self.rc) elif self.patch == 0: return "{:d}.{:d}".format(self.major, self.minor) else: return "{:d}.{:d}.{:d}".format(self.major, self.minor, self.patch) raise @property def dirname(self): return "linux-{:s}".format(str(self)) @property def filename(self): if self.rc > 0: return "{:s}.tar.gz".format(self.dirname) return "{:s}.tar.xz".format(self.dirname) @property def url(self): if self.rc > 0: return "https://git.kernel.org/torvalds/t/{:s}".format(self.filename) return "https://cdn.kernel.org/pub/linux/kernel/v{:d}.x/{:s}".format(self.major, self.filename) Entries = { "address_space_operations": ["include/linux/fs.h"], "ata_port_operations": ["include/linux/libata.h"], "btf_kind_operations": ["kernel/bpf/btf.c"], "block_device_operations": ["include/linux/blkdev.h"], "clk_ops": ["include/linux/clk-provider.h"], "configfs_item_operations": ["include/linux/configfs.h"], "configfs_group_operations": ["include/linux/configfs.h"], "damon_operations": ["include/linux/damon.h"], "dentry_operations": ["include/linux/dcache.h"], "dev_pm_ops": ["include/linux/pm.h"], "dma_buf_ops": ["include/linux/dma-buf.h"], "export_operations": ["include/linux/exportfs.h"], "file_operations": ["include/linux/fs.h"], "fs_context_operations": ["include/linux/fs_context.h"], "inode_operations": ["include/linux/fs.h"], "kobj_ns_type_operations": ["include/linux/kobject_ns.h"], "media_entity_operations": ["include/media/media-entity.h"], "movable_operations": ["include/linux/migrate.h"], "net_device_ops": ["include/linux/netdevice.h"], "page_ext_operations": ["include/linux/page_ext.h"], "parport_operations": ["include/linux/parport.h"], "pernet_operations": ["include/net/net_namespace.h"], "pipe_buf_operations": ["include/linux/pipe_fs_i.h"], "proc_ns_operations": ["include/linux/proc_ns.h"], "proc_ops": ["include/linux/proc_fs.h"], "regulator_ops": ["include/linux/regulator/driver.h"], "seq_operations": ["include/linux/seq_file.h"], "smp_operations": ["arch/arm/include/asm/smp.h"], "super_operations": ["include/linux/fs.h", "include/linux/fs/super_types.h"], "tty_ldisc_ops": ["include/linux/tty_ldisc.h"], "tty_operations": ["include/linux/tty_driver.h"], "tty_port_operations": ["include/linux/tty_port.h"], "ucsi_operations": ["drivers/usb/typec/ucsi/ucsi.h"], "vm_operations_struct": ["include/linux/mm.h"], } def doit(args, version): if not os.path.exists(version.filename): r = os.system("wget {:s}".format(version.url)) if r != 0: print("Failed to downloard") raise ops = {} for struct_name, filenames in Entries.items(): found = False for filename in filenames: filepath = os.path.join(version.dirname, filename) if not os.path.exists(filepath): print("[+] Extracting... {:s}".format(filepath)) r = os.system("tar xf {:s} '{:s}'".format(version.filename, filepath)) if r != 0: print("Failed to extract") continue content = open(filepath).read() struct_name_ = "struct {:s}".format(struct_name) struct_lines = [] inner = False for line in content.splitlines(): if not inner: if line.startswith(struct_name_) and not line.endswith(";"): inner = True struct_lines.append(line) continue else: struct_lines.append(line) if line.startswith("}"): ops[struct_name] = [filepath, struct_lines] found = True break continue if found: break else: ops[struct_name] = [filepath, ["Not found"]] return ops def main(): parser = argparse.ArgumentParser() parser.add_argument("version", metavar="VERSION", type=KernelVersion, help="target version") parser.add_argument("version2", metavar="VERSION2", nargs="?", type=KernelVersion, help="target version for diff") args = parser.parse_args() if not args.version2: ops = doit(args, args.version) assert len(ops) == len(Entries) for struct_name, [filepath, lines] in ops.items(): print("\x1b[1;37m{:s}:{:s}\x1b[00m".format(filepath, struct_name)) for line in lines: print(line) else: ops1 = doit(args, args.version) ops2 = doit(args, args.version2) assert len(ops1) == len(ops2) == len(Entries) for struct_name in Entries.keys(): print("\x1b[1;37m{:s}\x1b[00m".format(struct_name)) path1, content1 = ops1[struct_name] path2, content2 = ops2[struct_name] for line in difflib.unified_diff(content1, content2, fromfile=path1, tofile=path2): if line[0] == "-": print("\x1b[1;31m{:s}\x1b[00m".format(line)) # Red elif line[0] == "+": print("\x1b[1;32m{:s}\x1b[00m".format(line)) # Green else: print(line) return if __name__ == "__main__": main() ================================================ FILE: dev/update-syscalls/update-syscalls.py ================================================ #!/usr/bin/python import sys import os import subprocess import shutil import hashlib ################################################################################ # init and arguments check def init(): if len(sys.argv) != 3: print("usage: {:s} KERNEL_SRC_DIR GEF_DIR".format(sys.argv[0])) print() print("example: {:s} /tmp/work/linux-6.6-rc7 /tmp/work/gef".format(sys.argv[0])) exit() which("clang-format") global K_DIR, GEF_DIR, GEF_PATH, GEF_TMP_PATH K_DIR = sys.argv[1] GEF_DIR = sys.argv[2] GEF_PATH = os.path.join(sys.argv[2], "gef.py") GEF_TMP_PATH = GEF_PATH + ".tmp" if not os.path.exists(K_DIR): print("[-] Not found {:s}".format(K_DIR)) exit() if not os.path.exists(GEF_PATH): print("[-] Not found {:s}".format(GEF_PATH)) exit() if os.path.exists(GEF_TMP_PATH): print("[-] Found {:s} already.".format(GEF_TMP_PATH)) exit() if os.path.exists("/tmp/a"): print("[-] Found {:s} already.".format("/tmp/a")) exit() if os.path.exists("/tmp/b"): print("[-] Found {:s} already.".format("/tmp/b")) exit() shutil.copyfile(GEF_PATH, GEF_TMP_PATH) return def print_patch_result(): print(titlify("patch result")) h1 = hashlib.sha1(open(GEF_PATH, "rb").read()).hexdigest() h2 = hashlib.sha1(open(GEF_TMP_PATH, "rb").read()).hexdigest() if h1 != h2: print("[+] patched gef.py is saved to {:s}".format(GEF_TMP_PATH)) else: print("[+] No diff") os.unlink(GEF_TMP_PATH) return def cleanup(): if os.path.exists("/tmp/a"): os.unlink("/tmp/a") if os.path.exists("/tmp/b"): os.unlink("/tmp/b") return ################################################################################ # utility def get_terminal_size(): import termios import struct import fcntl try: tty_rows, tty_columns = struct.unpack("hh", fcntl.ioctl(1, termios.TIOCGWINSZ, "1234")) return tty_rows, tty_columns except OSError: return 600, 100 def titlify(text): cols = get_terminal_size()[1] cs = "\033[36m" # cyan ce = "\033[0m" # normal msg = [] if text: nb = (cols - len(text) - 2) // 2 msg.append(cs + "{} ".format("-" * nb) + ce) msg.append(cs + text + ce) msg.append(cs + " {}".format("-" * nb) + ce) else: msg.append(cs + "{}".format("-" * cols) + ce) return "".join(msg) def which(command): x = subprocess.getoutput("which {:s}".format(command)) if not x: print("[-] Not found {:s}".format(command)) exit() return x def str2bytes(x): if isinstance(x, bytes): return x if isinstance(x, str): x = bytes(ord(xx) for xx in x) return x raise def bytes2str(x): if isinstance(x, str): return x if isinstance(x, bytes): x = "".join(chr(xx) for xx in x) return x raise ################################################################################ # utility2 def print_diff(a, b): import difflib for i, line in enumerate(difflib.unified_diff(a, b, fromfile="before", tofile="after")): if i < 2: continue print(line) return def write_back(lines, s, e): gef = open(GEF_TMP_PATH, "rb").read().decode("ascii").splitlines() gef[s + 1:e] = lines open(GEF_TMP_PATH, "wb").write(("\n".join(gef) + "\n").encode()) return ################################################################################ # update syscall interfaces def get_new_defs(header_path): clang = which("clang-format") header = os.path.join(K_DIR, header_path) cmd = "{:s} --style='{{BasedOnStyle: Google, ColumnLimit: 1000}}' {:s}".format(clang, header) syscall_defs = subprocess.getoutput(cmd) syscall_defs = [line for line in syscall_defs.splitlines() if line.startswith("asmlinkage")] return syscall_defs def get_gef_defs(start_kw, end_kw): gef = open(GEF_TMP_PATH, "rb").read().decode("ascii").splitlines() start_kw_pos = gef.index(start_kw) end_kw_pos = gef.index(end_kw, start_kw_pos) return gef[start_kw_pos + 1:end_kw_pos], start_kw_pos, end_kw_pos # replace line and add "!" def replace_lines1(replace_rules, lines): for rule in replace_rules: before, after = rule if before not in lines: continue idx = lines.index(before) lines[idx] = "!" + after return lines # add "#" def replace_lines2(replace_rules, lines): for rule in replace_rules: lines_tmp = [] for line in lines: if line.startswith(rule): line = "#" + line lines_tmp.append(line) lines = lines_tmp[::] return lines def syscall_defs_update(): print(titlify("syscall_defs")) new_syscall_defs = get_new_defs("include/linux/syscalls.h") old_syscall_defs, s, e = get_gef_defs('syscall_defs = """', '"""') # replace some defs replace_rules1 = [ ["asmlinkage long sys_io_submit(aio_context_t, long, struct iocb __user *__user *);", "asmlinkage long sys_io_submit(aio_context_t ctx_id, long nr, struct iocb __user * __user *iocbpp);", ], ["asmlinkage long sys_pselect6(int, fd_set __user *, fd_set __user *, fd_set __user *, struct __kernel_timespec __user *, void __user *);", "asmlinkage long sys_pselect6(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct __kernel_timespec __user *tsp, void __user *sig);", ], ["asmlinkage long sys_pselect6_time32(int, fd_set __user *, fd_set __user *, fd_set __user *, struct old_timespec32 __user *, void __user *);", "asmlinkage long sys_pselect6_time32(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct old_timespec32 __user *tsp, void __user *sig);", ], ["asmlinkage long sys_ppoll(struct pollfd __user *, unsigned int, struct __kernel_timespec __user *, const sigset_t __user *, size_t);", "asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds, struct __kernel_timespec __user *tsp, const sigset_t __user *sigmask, size_t sigsetsize);", ], ["asmlinkage long sys_ppoll_time32(struct pollfd __user *, unsigned int, struct old_timespec32 __user *, const sigset_t __user *, size_t);", "asmlinkage long sys_ppoll_time32(struct pollfd __user *ufds, unsigned int nfds, struct old_timespec32 __user *tsp, const sigset_t __user *sigmask, size_t sigsetsize);", ], ["asmlinkage long sys_rt_sigaction(int, const struct sigaction __user *, struct sigaction __user *, size_t);", "asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act, struct sigaction __user *oact, size_t sigsetsize);", ], ["asmlinkage long sys_socket(int, int, int);", "asmlinkage long sys_socket(int family, int type, int protocol);", ], ["asmlinkage long sys_socketpair(int, int, int, int __user *);", "asmlinkage long sys_socketpair(int family, int type, int protocol, int __user *usockvec);", ], ["asmlinkage long sys_bind(int, struct sockaddr __user *, int);", "asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen);", ], ["asmlinkage long sys_listen(int, int);", "asmlinkage long sys_listen(int fd, int backlog);", ], ["asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *);", "asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen);", ], ["asmlinkage long sys_connect(int, struct sockaddr __user *, int);", "asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen);", ], ["asmlinkage long sys_getsockname(int, struct sockaddr __user *, int __user *);", "asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len);", ], ["asmlinkage long sys_getpeername(int, struct sockaddr __user *, int __user *);", "asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len);", ], ["asmlinkage long sys_sendto(int, void __user *, size_t, unsigned, struct sockaddr __user *, int);", "asmlinkage long sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, struct sockaddr __user *addr, int addr_len);", ], ["asmlinkage long sys_recvfrom(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *);", "asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags, struct sockaddr __user *addr, int __user *addr_len);", ], ["asmlinkage long sys_shutdown(int, int);", "asmlinkage long sys_shutdown(int fd, int how);", ], ["asmlinkage long sys_accept4(int, struct sockaddr __user *, int __user *, int);", "asmlinkage long sys_accept4(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen, int flags);", ], ] replace_rules2 = [ "asmlinkage long sys_clone(", "asmlinkage long sys_sigsuspend(int", "asmlinkage long sys_ni_syscall(", "asmlinkage long sys_fanotify_mark(", ] new_syscall_defs = replace_lines1(replace_rules1, new_syscall_defs) new_syscall_defs = replace_lines2(replace_rules2, new_syscall_defs) print_diff(old_syscall_defs, new_syscall_defs) write_back(new_syscall_defs, s, e) return def syscall_defs_compat_update(): print(titlify("syscall_defs_compat")) new_syscall_defs = get_new_defs("include/linux/compat.h") old_syscall_defs, s, e = get_gef_defs('syscall_defs_compat = """', '"""') replace_rules1 = [ ["asmlinkage long compat_sys_waitid(int, compat_pid_t, struct compat_siginfo __user *, int, struct compat_rusage __user *);", "asmlinkage long compat_sys_waitid(int which, compat_pid_t pid, struct compat_siginfo __user *waitid, int options, struct compat_rusage __user *uru);", ], ["asmlinkage long compat_sys_kexec_load(compat_ulong_t entry, compat_ulong_t nr_segments, struct compat_kexec_segment __user *, compat_ulong_t flags);", "asmlinkage long compat_sys_kexec_load(compat_ulong_t entry, compat_ulong_t nr_segments, struct compat_kexec_segment __user *segments, compat_ulong_t flags);", ], ["asmlinkage long compat_sys_rt_sigaction(int, const struct compat_sigaction __user *, struct compat_sigaction __user *, compat_size_t);", "asmlinkage long compat_sys_rt_sigaction(int sig, const struct compat_sigaction __user *act, struct compat_sigaction __user *oact, compat_size_t sigsetsize);", ], ["asmlinkage long compat_sys_fanotify_mark(int, unsigned int, __u32, __u32, int, const char __user *);", "asmlinkage long compat_sys_fanotify_mark(int fanotify_fd, unsigned int flags, __u32 mask_1, __u32 mask_2, int dfd, const char __user *pathname);", ], ["asmlinkage long compat_sys_io_pgetevents(compat_aio_context_t ctx_id, compat_long_t min_nr, compat_long_t nr, struct io_event __user *events, struct old_timespec32 __user *timeout, const struct __compat_aio_sigset __user *usig);", "asmlinkage long compat_sys_io_pgetevents(compat_aio_context_t ctx_id, compat_long_t min_nr, compat_long_t nr, struct io_event __user *events, struct old_timespec32 __user *timeout, const struct __compat_aio_sigset __user *usig); # codespell:ignore", ], ["asmlinkage long compat_sys_io_pgetevents_time64(compat_aio_context_t ctx_id, compat_long_t min_nr, compat_long_t nr, struct io_event __user *events, struct __kernel_timespec __user *timeout, const struct __compat_aio_sigset __user *usig);", "asmlinkage long compat_sys_io_pgetevents_time64(compat_aio_context_t ctx_id, compat_long_t min_nr, compat_long_t nr, struct io_event __user *events, struct __kernel_timespec __user *timeout, const struct __compat_aio_sigset __user *usig); # codespell:ignore", ], ] replace_rules2 = [ ] new_syscall_defs = replace_lines1(replace_rules1, new_syscall_defs) new_syscall_defs = replace_lines2(replace_rules2, new_syscall_defs) print_diff(old_syscall_defs, new_syscall_defs) write_back(new_syscall_defs, s, e) return ################################################################################ # update syscall table def get_new_tbl(tbl_path): path = os.path.join(K_DIR, tbl_path) print("[+] path:", path) new_tbl = open(path, "rb").read().decode("ascii").expandtabs(8).splitlines() new_tbl = [l for l in new_tbl if l and not l.startswith("#")] return new_tbl def get_new_tbl_by_cmds(cmds): cmds = "; ".join([cmd.lstrip() for cmd in cmds.splitlines() if cmd]) print("[+] cmds:", cmds) result = subprocess.getoutput(cmds) return result.splitlines() def x64_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/x86/entry/syscalls/syscall_64.tbl") old_tbl, s, e = get_gef_defs('x64_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def i386_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/x86/entry/syscalls/syscall_32.tbl") old_tbl, s, e = get_gef_defs('x86_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def arm64_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/arm64/tools/syscall_64.tbl") old_tbl, s, e = get_gef_defs('arm64_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def arm_compat_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/arm64/tools/syscall_32.tbl") old_tbl, s, e = get_gef_defs('arm_compat_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def arm_native_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/arm/tools/syscall.tbl") old_tbl, s, e = get_gef_defs('arm_native_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def mips_o32_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/mips/kernel/syscalls/syscall_o32.tbl") old_tbl, s, e = get_gef_defs('mips_o32_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def mips_n32_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/mips/kernel/syscalls/syscall_n32.tbl") old_tbl, s, e = get_gef_defs('mips_n32_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def mips_n64_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/mips/kernel/syscalls/syscall_n64.tbl") old_tbl, s, e = get_gef_defs('mips_n64_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def ppc_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/powerpc/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('ppc_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def sparc_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/sparc/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('sparc_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def riscv64_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) print("\033[1m" + "[!] same with arm64 \033[0m") return def riscv32_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) print("\033[1m" + "[!] same with arm64 \033[0m") return def s390x_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/s390/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('s390x_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def sh4_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/sh/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('sh4_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def m68k_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/m68k/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('m68k_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def alpha_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/alpha/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('alpha_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def hppa_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/parisc/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('hppa_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def or1k_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) print("\033[1m" + "[!] same with arm64 \033[0m") return def nios2_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) print("\033[1m" + "[!] same with arm64 \033[0m") return def microblaze_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/microblaze/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('microblaze_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def xtensa_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl("arch/xtensa/kernel/syscalls/syscall.tbl") old_tbl, s, e = get_gef_defs('xtensa_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def cris_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) new_tbl = get_new_tbl_by_cmds( r""" cd {:s} awk '/sys_call_table:/,/^$/' arch/cris/arch-v10/kernel/entry.S | grep -o "\.long \w*" | nl -v0 | awk '{print $1" cris "substr($3,5)" "$3}' |column -t """.format(K_DIR) ) old_tbl, s, e = get_gef_defs('cris_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def loongarch_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) print("\033[1m" + "[!] skip {:s}. need fix manually.".format(sys._getframe().f_code.co_name[:-19]) + "\033[0m") return new_tbl = get_new_tbl_by_cmds( r""" cd {:s} gcc -I `pwd`/include/uapi/ -E -D__SYSCALL=SYSCALL arch/loongarch/include/uapi/asm/unistd.h | grep ^SYSCALL | sed -e 's/SYSCALL(//;s/[,)]//g' > /tmp/a grep -oP "__NR\S+\s+\d+$" include/uapi/asm-generic/unistd.h | grep -v __NR_sync_file_range2 > /tmp/b join -2 2 -o 1.1,1.10,2.1,1.2 -e loongarch /tmp/a /tmp/b 2>/dev/null | sed -e 's/\(__NR_\|__NR3264_\)//g' | column -t """.format(K_DIR) ) old_tbl, s, e = get_gef_defs('loongarch_syscall_tbl = """', '"""') print_diff(old_tbl, new_tbl) write_back(new_tbl, s, e) return def arc_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) print("\033[1m" + "[!] same with arm64 \033[0m") return def csky_syscall_tbl_update(): print(titlify(sys._getframe().f_code.co_name[:-7])) print("\033[1m" + "[!] same with arm64 \033[0m") return ################################################################################ # main if __name__ == "__main__": init() syscall_defs_update() syscall_defs_compat_update() x64_syscall_tbl_update() i386_syscall_tbl_update() arm64_syscall_tbl_update() arm_compat_syscall_tbl_update() arm_native_syscall_tbl_update() mips_o32_syscall_tbl_update() mips_n32_syscall_tbl_update() mips_n64_syscall_tbl_update() ppc_syscall_tbl_update() sparc_syscall_tbl_update() riscv64_syscall_tbl_update() riscv32_syscall_tbl_update() s390x_syscall_tbl_update() sh4_syscall_tbl_update() m68k_syscall_tbl_update() alpha_syscall_tbl_update() hppa_syscall_tbl_update() or1k_syscall_tbl_update() nios2_syscall_tbl_update() microblaze_syscall_tbl_update() xtensa_syscall_tbl_update() #cris_syscall_tbl_update() # cris is removed at current linux loongarch_syscall_tbl_update() # skip arc_syscall_tbl_update() csky_syscall_tbl_update() print_patch_result() cleanup() ================================================ FILE: dev/zellij/zellij-init.py ================================================ # GEF Zellij Initializer # # This script configures GEF to redirect its context output to the Zellij panes. # Place it in the same directory as `zellij-wrapper.py`. # This script is automatically called from `zellij-wrapper.py`. # Change `PANE_SECTIONS` to match the layout description in `zellij-wrapper.py`. import os import glob import time import signal import subprocess import gdb import atexit PANE_SECTIONS = { # pane_name: [gef_context_sections] "code_args_source": ["code", "args", "source"], "trace_threads_memwatch_extra": ["trace", "threads", "mem_watch", "extra"], "legend_regs": ["legend", "regs"], "stack_memaccess": ["stack", "mem_access"], } TTY_FILE_PREFIX = "/tmp/gef/zellij-" TTY_FILE_SUFFIX = ".tty" # Timeout for waiting TTY files to appear (seconds) TTY_WAIT_TIMEOUT = 10 TTY_WAIT_INTERVAL = 0.2 def wait_for_tty_files(): """Wait for all pane TTY files to be written and return a dict of {pane_name: tty_path}.""" expected_panes = set(PANE_SECTIONS.keys()) result = {} deadline = time.time() + TTY_WAIT_TIMEOUT while time.time() < deadline: for pane_name in list(expected_panes): tty_file = f"{TTY_FILE_PREFIX}{pane_name}{TTY_FILE_SUFFIX}" if os.path.exists(tty_file): try: with open(tty_file, "r") as f: tty_path = f.read().strip() if tty_path: result[pane_name] = tty_path expected_panes.discard(pane_name) except (IOError, OSError): pass if not expected_panes: return result time.sleep(TTY_WAIT_INTERVAL) if expected_panes: print(f"[zellij_init] Warning: Timed out waiting for TTY files: {expected_panes}") return result def get_redirect_configs(): """Return a list of GEF redirect config keys.""" return [ "context.redirect", "context_args.redirect", "context_code.redirect", "context_extra.redirect", "context_legend.redirect", "context_mem_access.redirect", "context_mem_watch.redirect", "context_regs.redirect", "context_source.redirect", "context_stack.redirect", "context_threads.redirect", "context_trace.redirect", ] def configure_gef_redirects(tty_map): """Set GEF context section redirects based on the TTY map.""" for pane_name, sections in PANE_SECTIONS.items(): tty_path = tty_map.get(pane_name) if not tty_path: continue for section in sections: try: gdb.execute( f"gef config context_{section}.redirect {tty_path}", to_string=True ) except gdb.error as e: print( f"[zellij_init] Warning: Failed to set redirect for {section}: {e}" ) # Additional GEF settings try: gdb.execute("gef config context_code.nb_lines 16", to_string=True) gdb.execute("gef config context_code.nb_lines_prev 8", to_string=True) gdb.execute("gef config context_stack.nb_lines 16", to_string=True) except gdb.error: pass def reset_gef_redirects(): """Clear all GEF context redirect settings.""" for config in get_redirect_configs(): try: gdb.execute('gef config {:s} ""'.format(config), to_string=True) except gdb.error: pass cleanup_state = { "pane_pids": [], "registered": False, } def find_cat_pids_for_ttys(tty_map): """Find PIDs of 'cat' processes running on the given TTYs.""" pids = [] for pane_name, tty_path in tty_map.items(): try: result = subprocess.run( ["ps", "-t", tty_path.replace("/dev/", ""), "-o", "pid,comm", "--no-headers"], capture_output=True, text=True, timeout=3, ) for line in result.stdout.strip().splitlines(): parts = line.strip().split() if len(parts) >= 2 and parts[1] == "cat": pids.append(int(parts[0])) except Exception: pass return pids def reset_panes(): """Reset GEF settings and kill display pane processes. Called on GDB exit.""" reset_gef_redirects() for pid in cleanup_state.get("pane_pids", []): try: os.kill(pid, signal.SIGTERM) except (ProcessLookupError, PermissionError, OSError): pass # Clean up TTY files for f in glob.glob(f"{TTY_FILE_PREFIX}*{TTY_FILE_SUFFIX}"): try: os.remove(f) except OSError: pass try: atexit.unregister(reset_panes) except Exception: pass cleanup_state["registered"] = False def main(): # Check if we're in a Zellij session if not os.environ.get("ZELLIJ"): print("[zellij_init] Error: Not in a Zellij session.") return # Reset any previous pane settings from GEF tmux-setup try: gdb.execute("pi GefTmuxSetupCommand.reset_panes()", to_string=True) except gdb.error: pass print("[zellij_init] Waiting for display panes to initialize...") tty_map = wait_for_tty_files() if not tty_map: print("[zellij_init] Error: No pane TTYs discovered. Setup failed.") return # Find cat PIDs for cleanup cleanup_state["pane_pids"] = find_cat_pids_for_ttys(tty_map) # Configure GEF redirects configure_gef_redirects(tty_map) # Register cleanup on exit atexit.register(reset_panes) cleanup_state["registered"] = True # Clear GEF cache try: gdb.execute("gef reset-cache", to_string=True) except gdb.error: pass # Report success print("[zellij_init] Zellij panes configured successfully.") for pane_name, tty_path in sorted(tty_map.items()): sections = ", ".join(PANE_SECTIONS[pane_name]) print(f" {pane_name}: {tty_path} -> [{sections}]") if __name__ == "__main__": main() ================================================ FILE: dev/zellij/zellij-wrapper.py ================================================ #!/usr/bin/env python3 # GEF Zellij Wrapper # # This wrapper launches Zellij with a GEF-compatible layout and starts GDB # in the command pane. When GDB exits, the Zellij session is closed. # # Usage: # ./zellij-wrapper.py [gdb_args...] # # Examples: # ./zellij-wrapper.py # Just start gdb # ./zellij-wrapper.py ./a.out # gdb ./a.out # ./zellij-wrapper.py -q ./a.out # gdb -q ./a.out # # NOTE: This script should be run OUTSIDE of Zellij. # It will create a new Zellij session with the correct layout. import os import sys import glob import subprocess def generate_layout_kdl(gdb_args, script_path): """Generate a KDL layout file for GEF panes. Layout: +--------+--------+--------+ | | code | legend | | | args | regs | | | source +--------+ | | | stack | | +--------+ mem_acc| | | others | | | cmd | | | +--------+--------+--------+ """ # Build the GDB command with auto-source of the setup script # The setup script (zellij-init.py) will be sourced in GDB to configure redirects init_script = os.path.join(os.path.dirname(script_path), "zellij-init.py") # Build args string for GDB command pane quoted_args = " ".join(f'"{a}"' for a in gdb_args) layout = """ layout { pane split_direction="vertical" { pane size="33%" focus=true { name "command" command "gdb" args "-ex" "source INIT_SCRIPT" GDB_ARGS close_on_exit true } pane size="33%" split_direction="horizontal" { pane size="67%" { name "code/args/source" command "bash" args "-c" "tty > /tmp/gef/zellij-code_args_source.tty; exec cat" close_on_exit true } pane size="33%" { name "trace/thread/mem-watch/extra" command "bash" args "-c" "tty > /tmp/gef/zellij-trace_threads_memwatch_extra.tty; exec cat" close_on_exit true } } pane size="34%" split_direction="horizontal" { pane size="33%" { name "legend/regs" command "bash" args "-c" "tty > /tmp/gef/zellij-legend_regs.tty; exec cat" close_on_exit true } pane size="67%" { name "stack/mem-access" command "bash" args "-c" "tty > /tmp/gef/zellij-stack_memaccess.tty; exec cat" close_on_exit true } } } } """.replace("INIT_SCRIPT", init_script).replace("GDB_ARGS", quoted_args) return layout def cleanup_tty_files(): """Remove any leftover TTY files from a previous session.""" for f in glob.glob("/tmp/gef/zellij-*.tty"): try: os.remove(f) except OSError: pass def main(): # Clean up leftover TTY files cleanup_tty_files() # Generate layout layout_content = generate_layout_kdl(sys.argv[1:], os.path.abspath(__file__)) # Write layout to temporary file layout_file = "/tmp/gef/zellij-wrapper-layout.kdl" with open(layout_file, "w") as f: f.write(layout_content) print(f"[zellij-wrapper] Layout written to {layout_file}") try: # Launch zellij with the layout print("[zellij-wrapper] Starting Zellij with GDB...") subprocess.run(["zellij", "--layout", layout_file], check=False) except KeyboardInterrupt: pass finally: # Clean up cleanup_tty_files() try: os.remove(layout_file) except OSError: pass return 0 if __name__ == "__main__": sys.exit(main()) ================================================ FILE: docs/BUILDING-QEMU-SYSTEM-ENV.md ================================================ ## Building Qemu ```bash apt install libslirp-dev apt build-dep qemu ninja-build wget https://download.qemu.org/qemu-9.2.0.tar.xz tar xf qemu-9.2.0.tar.xz cd qemu-9.2.0 cp -r pc-bios /usr/local/bin ./configure --enable-slirp make -j make install ``` ## buildroot x86-64 ```bash tar xf buildroot-2024.11.1.tar.xz cd buildroot-2024.11.1/ make qemu_x86_64_defconfig make qemu-system-x86_64 \ -M pc \ -m 512M \ -cpu kvm64,+smep,+smap \ -kernel output/images/bzImage \ -drive file=output/images/rootfs.ext2,if=virtio,format=raw \ -append "rootwait root=/dev/vda console=tty1 console=ttyS0 noapic" \ -net nic,model=virtio \ -net user \ -nographic \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s ``` - Notes - Various default build configs for buildroot are in `configs/`. - When you run `make qemu_x86_64_defconfig`, it will be copied to `.config` in the top directory and used. - The build configuration for the Linux kernel is in `board/qemu/*/linux.config`. - Change this if you want to change the configuration. - Or copy it to another location and specify the following in the build configuration (`.config`) of buildroot itself in the top directory. - `BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y` - `BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="/path/to/config"` - The startup command is written in `board/qemu/*/readme.txt`. - Modify it as appropriate, for example adding `-nographic` or removing `-serial`. - If the Qemu download is too slow, abort the build, switch to the mirror site as follows, and make again. ``` vi package/qemu/qemu.mk -QEMU_SITE = http://download.qemu.org +QEMU_SITE = https://mirror.koddos.net/blfs/conglomeration/qemu ``` - If you want to replace the kernel with the latest version, just build the kernel, replace the `bzImage`, and start it. ``` git clone https://github.com/torvalds/linux.git cd linux/ make defconfig make # If you want to cross compile here, # For i386: make defconfig && make ARCH=i386 # For AArch64: make ARCH=arm64 defconfig && make ARCH=arm64 CROSS_COMPILE="aarch64-linux-gnu-" # For ARM: make ARCH=arm defconfig && make ARCH=arm CROSS_COMPILE="arm-linux-gnueabihf-" qemu-system-x86_64 \ -M pc \ -kernel kernel/linux/arch/x86_64/boot/bzImage \ -drive file=buildroot-2024.11.1/output/images/rootfs.ext2,if=ide \ -append "root=/dev/sda console=ttyS0" \ -nographic \ -netdev tap,id=tap0 \ -device e1000,netdev=tap0 \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s # Note 1 # When I updated the kernel, eth0 was no longer recognized for some reason, # so I recommend creating tap0 with the qemu command (-netdev tap,id=tap0 -device e1000,netdev=tap0) and attaching it. # https://unix.stackexchange.com/questions/171874/no-network-interface-in-qemu # Note 2 # For some reason, the new kernel no longer recognizes /dev/vda, # so it is a good idea to change the type to ide and start it as /dev/sda # (virtio: /dev/vda, ide connection: /dev/sda, SD card: /dev/mmcblk0). ``` ## buildroot x86 ```bash tar xf buildroot-2024.11.1.tar.xz cd buildroot-2024.11.1/ make qemu_x86_defconfig make qemu-system-i386 \ -M pc \ -kernel output/images/bzImage \ -drive file=output/images/rootfs.ext2,if=virtio,format=raw \ -append "rootwait root=/dev/vda console=tty1 console=ttyS0" \ -net nic,model=virtio \ -net user \ -nographic \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s ``` ## buildroot ARM ```bash tar xf buildroot-2024.11.1.tar.xz cd buildroot-2024.11.1/ make qemu_arm_vexpress_defconfig make qemu-system-arm \ -M vexpress-a9 \ -m 256 \ -kernel output/images/zImage \ -dtb output/images/vexpress-v2p-ca9.dtb \ -drive file=output/images/rootfs.ext2,if=sd,format=raw \ -append "console=ttyAMA0,115200 rootwait root=/dev/mmcblk0" \ -net nic,model=lan9118 \ -net user \ -nographic \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s ``` - Notes - In the case of ARM (vexpress), there is no configuration in `board/qemu/arm-vexpress/linux.config`. - The following steps are necessary (I think), but I'm not sure what the correct way is. - Build once with buildroot - Copy the generated `output/build/linux-XXX/.config` to another location and modify the contents - Specify `.config` with `BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE`. ## buildroot AArch64 ```bash tar xf buildroot-2024.11.1.tar.xz cd buildroot-2024.11.1/ make qemu_aarch64_virt_defconfig make qemu-system-aarch64 \ -M virt \ -cpu cortex-a53 \ -smp 1 \ -kernel output/images/Image \ -append "rootwait root=/dev/vda console=ttyAMA0" \ -netdev user,id=eth0 \ -device virtio-net-device,netdev=eth0 \ -drive file=output/images/rootfs.ext4,if=none,format=raw,id=hd0 \ -device virtio-blk-device,drive=hd0 \ -nographic \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s ``` ## buildroot mipsel ```bash tar xf buildroot-2024.11.1.tar.xz cd buildroot-2024.11.1/ make qemu_mips32r6el_malta_defconfig make qemu-system-mipsel \ -M malta \ -cpu mips32r6-generic \ -kernel output/images/vmlinux \ -drive file=output/images/rootfs.ext2,format=raw \ -append "rootwait root=/dev/hda" \ -net nic,model=pcnet \ -net user \ -nographic \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s ``` ## buildroot mips ```bash tar xf buildroot-2024.11.1.tar.xz cd buildroot-2024.11.1/ make qemu_mips32r6_malta_defconfig make qemu-system-mips \ -M malta \ -cpu mips32r6-generic \ -kernel output/images/vmlinux \ -drive file=output/images/rootfs.ext2,format=raw \ -append "rootwait root=/dev/hda" \ -net nic,model=pcnet \ -net user \ -nographic \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s ``` ## buildroot mips64el ```bash tar xf buildroot-2024.11.1.tar.xz cd buildroot-2024.11.1/ make qemu_mips64r6el_malta_defconfig make qemu-system-mips64el \ -M malta \ -cpu I6400 \ -kernel output/images/vmlinux \ -drive file=output/images/rootfs.ext2,format=raw \ -append "rootwait root=/dev/hda" \ -nographic \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s ``` ## buildroot mips64 ```bash tar xf buildroot-2024.11.1.tar.xz cd buildroot-2024.11.1/ make qemu_mips64r6_malta_defconfig make qemu-system-mips64el \ -M malta \ -cpu I6400 \ -kernel output/images/vmlinux \ -drive file=output/images/rootfs.ext2,format=raw \ -append "rootwait root=/dev/hda" \ -nographic \ -monitor telnet:127.0.0.1:9999,server,nowait \ -s ``` ## debian x86-64 ```bash wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.9.0-amd64-netinst.iso qemu-img create -f qcow2 disk.qcow2 20G # For some reason, adding -nographic -serial mon:stdio did not work as the output was not displayed on the screen, # so I started it without those options and set it up in a GUI environment. qemu-system-x86_64 \ -enable-kvm \ -m 2G \ -hda disk.qcow2 \ -net nic,model=virtio \ -net user \ -cdrom debian-12.9.0-amd64-netinst.iso \ -boot d # Open disk.qcow2 with 7-zip file manager and extract the following two files: 0.img/boot/initrd.img-6.1.123-amd64 0.img/boot/vmlinuz-6.1.123-amd64 # Run qemu-system-x86_64 \ -enable-kvm \ -m 2G \ -hda disk.qcow2 \ -kernel vmlinuz-6.1.123-1-amd64 \ -append 'root=/dev/sda1 console=ttyS0' \ -initrd initrd.img-6.1.123-1-amd64 \ -net nic,model=virtio \ -net user,hostfwd=tcp:127.0.0.1:2222-:22 \ -nographic \ -serial mon:stdio \ -s ``` ## debian x86 ```bash wget https://cdimage.debian.org/debian-cd/current/i386/iso-cd/debian-12.9.0-i386-netinst.iso qemu-img create -f qcow2 disk.qcow2 20G # For some reason, adding -nographic -serial mon:stdio did not work as the output was not displayed on the screen, # so I started it without those options and set it up in a GUI environment. qemu-system-i386 \ -enable-kvm \ -m 2G \ -hda disk.qcow2 \ -net nic,model=virtio \ -net user \ -cdrom debian-12.9.0-i386-netinst.iso \ -boot d # Open disk.qcow2 with 7-zip file manager and extract the following two files: 0.img/boot/initrd.img-6.1.123-1-686-pae 0.img/boot/vmlinuz-6.1.123-1-686-pae # Run qemu-system-i386 \ -enable-kvm \ -m 2G \ -hda disk.qcow2 \ -kernel vmlinuz-6.1.123-1-686-pae \ -append 'root=/dev/sda1 console=ttyS0' \ -initrd initrd.img-6.1.123-1-686-pae \ -net nic,model=virtio \ -net user,hostfwd=tcp:127.0.0.1:2222-:22 \ -nographic \ -serial mon:stdio \ -s ``` ## debian ARM ```bash wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-armhf/current/images/netboot/initrd.gz wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-armhf/current/images/netboot/vmlinuz qemu-img create -f qcow2 disk.qcow2 20G # Select C for language and your country (e.g. Japan or Japanese, etc.) for others. # Please note that mirror download destinations may not work properly in some cases. # Installation takes about 2 hours. # You may get a grub installation error, but you can ignore it and proceed. # When it's time for the final reboot, use pkill -9 qemu from another terminal to force it to terminate. qemu-system-arm \ -smp 2 \ -M virt \ -cpu cortex-a15 \ -m 1G \ -initrd initrd.gz \ -kernel vmlinuz \ -append "root=/dev/ram console=ttyAMA0" \ -global virtio-blk-device.scsi=off \ -device virtio-scsi-device,id=scsi \ -drive file=disk.qcow2,id=rootimg,cache=unsafe,if=none \ -device scsi-hd,drive=rootimg \ -netdev user,id=unet \ -device virtio-net-device,netdev=unet \ -net user \ -nographic \ -s # Copy kernel and ramdisk apt install nbd-client && modprobe nbd max_part=8 && qemu-nbd --connect=/dev/nbd0 disk.qcow2 mkdir mnt && mount /dev/nbd0p1 mnt && cp mnt/initrd.img-*-armmp-lpae mnt/vmlinuz-*-armmp-lpae . && sync umount /dev/nbd0p1 && nbd-client -d /dev/nbd0 && rmdir mnt && rm -f initrd.gz vmlinuz # Run qemu-system-arm \ -smp 2 \ -M virt \ -cpu cortex-a15 \ -m 1G \ -initrd initrd.img-4.19.0-9-armmp-lpae \ -kernel vmlinuz-4.19.0-9-armmp-lpae \ -append "root=/dev/sda2 console=ttyAMA0" \ -global virtio-blk-device.scsi=off \ -device virtio-scsi-device,id=scsi \ -drive file=disk.qcow2,id=rootimg,cache=unsafe,if=none \ -device scsi-hd,drive=rootimg \ -device virtio-net-device,netdev=net0 \ -netdev user,hostfwd=tcp:127.0.0.1:2222-:22,id=net0 \ -nographic \ -s ``` ## debian AArch64 ```bash wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-arm64/current/images/netboot/debian-installer/arm64/initrd.gz wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-arm64/current/images/netboot/debian-installer/arm64/linux qemu-img create -f qcow2 disk.qcow2 20G # Select C for language and your country (e.g. Japan or Japanese, etc.) for others. # Please note that mirror download destinations may not work properly in some cases. # Installation takes about 2 hours. # You may get a grub installation error, but you can ignore it and proceed. # When it's time for the final reboot, use pkill -9 qemu from another terminal to force it to terminate. qemu-system-aarch64 \ -smp 2 \ -M virt \ -cpu cortex-a57 \ -m 1G \ -initrd initrd.gz \ -kernel linux \ -append "root=/dev/ram console=ttyAMA0" \ -global virtio-blk-device.scsi=off \ -device virtio-scsi-device,id=scsi \ -drive file=disk.qcow2,id=rootimg,cache=unsafe,if=none \ -device scsi-hd,drive=rootimg \ -netdev user,id=unet \ -device virtio-net-device,netdev=unet \ -net user \ -nographic \ -s # Copy kernel and ramdisk apt install nbd-client && modprobe nbd max_part=8 && qemu-nbd --connect=/dev/nbd0 disk.qcow2 mkdir mnt && mount /dev/nbd0p1 mnt && cp mnt/initrd.img-*-arm64 mnt/vmlinuz-*-arm64 . && sync umount /dev/nbd0p1 && nbd-client -d /dev/nbd0 && rmdir mnt && rm -f initrd.gz linux # Run qemu-system-aarch64 -smp 2 \ -M virt \ -cpu cortex-a57 \ -m 1G \ -initrd initrd.img-4.19.0-9-arm64 \ -kernel vmlinuz-4.19.0-9-arm64 \ -append "root=/dev/sda2 console=ttyAMA0" \ -global virtio-blk-device.scsi=off \ -device virtio-scsi-device,id=scsi \ -drive file=disk.qcow2,id=rootimg,cache=unsafe,if=none \ -device scsi-hd,drive=rootimg \ -device virtio-net-device,netdev=net0 \ -netdev user,hostfwd=tcp:127.0.0.1:2222-:22,id=net0 \ -nographic \ -s ``` ## debian mipsel ```bash wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-mipsel/current/images/malta/netboot/initrd.gz wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-mipsel/current/images/malta/netboot/vmlinux-4.19.0-9-4kc-malta qemu-img create -f qcow2 disk.qcow2 20G # Select C for language and your country (e.g. Japan or Japanese, etc.) for others. # Please note that mirror download destinations may not work properly in some cases. # Installation takes about 2 hours. # You may get a grub installation error, but you can ignore it and proceed. # When it's time for the final reboot, use pkill -9 qemu from another terminal to force it to terminate. qemu-system-mipsel \ -M malta \ -m 1G \ -hda ./disk.qcow2 \ -initrd ./initrd.gz \ -kernel ./vmlinux-4.19.0-9-4kc-malta \ -append "nokaslr" \ -nographic # Copy kernel and ramdisk apt install nbd-client && modprobe nbd max_part=8 && qemu-nbd --connect=/dev/nbd0 disk.qcow2 mkdir mnt && mount /dev/nbd0p1 mnt && cp mnt/boot/initrd.img-*-malta . && sync umount /dev/nbd0p1 && nbd-client -d /dev/nbd0 && rmdir mnt && rm -f initrd.gz # Run qemu-system-mipsel \ -M malta \ -m 1G \ -hda ./disk.qcow2 \ -initrd ./initrd.img-4.19.0-9-4kc-malta \ -kernel ./vmlinux-4.19.0-9-4kc-malta \ -append "nokaslr root=/dev/sda1" \ -net nic,model=e1000 \ -net user,hostfwd=tcp:127.0.0.1:2222-:22,id=net0 \ -nographic ``` ```bash # Execute in the guest # fix interface cat > /etc/network/interfaces << EOF auto lo auto enp0s18 iface lo inet loopback iface enp0s18 inet dhcp EOF systemctl restart networking ``` ## debian mips ```bash wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-mips/current/images/malta/netboot/initrd.gz wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-mips/current/images/malta/netboot/vmlinux-4.19.0-9-4kc-malta qemu-img create -f qcow2 disk.qcow2 20G # Select C for language and your country (e.g. Japan or Japanese, etc.) for others. # Please note that mirror download destinations may not work properly in some cases. # Installation takes about 2 hours. # You may get a grub installation error, but you can ignore it and proceed. # When it's time for the final reboot, use pkill -9 qemu from another terminal to force it to terminate. qemu-system-mips \ -M malta \ -m 1G \ -hda ./disk.qcow2 \ -initrd ./initrd.gz \ -kernel ./vmlinux-4.19.0-9-4kc-malta \ -append "nokaslr" \ -nographic # Copy kernel and ramdisk apt install nbd-client && modprobe nbd max_part=8 && qemu-nbd --connect=/dev/nbd0 disk.qcow2 mkdir mnt && mount /dev/nbd0p1 mnt && cp mnt/boot/initrd.img-*-malta . && sync umount /dev/nbd0p1 && nbd-client -d /dev/nbd0 && rmdir mnt && rm -f initrd.gz # Run qemu-system-mips \ -M malta \ -m 1G \ -hda ./disk.qcow2 \ -initrd ./initrd.img-4.19.0-9-4kc-malta \ -kernel ./vmlinux-4.19.0-9-4kc-malta \ -append "nokaslr root=/dev/sda1" \ -net nic,model=e1000 \ -net user,hostfwd=tcp:127.0.0.1:2222-:22,id=net0 \ -nographic ``` ```bash # Execute in the guest # fix interface cat > /etc/network/interfaces << EOF auto lo auto enp0s18 iface lo inet loopback iface enp0s18 inet dhcp EOF systemctl restart networking ``` ## debian mips64el ```bash wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-mips64el/current/images/malta/netboot/initrd.gz wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-mips64el/current/images/malta/netboot/vmlinux-4.19.0-9-5kc-malta qemu-img create -f qcow2 disk.qcow2 20G # Select C for language and your country (e.g. Japan or Japanese, etc.) for others. # Please note that mirror download destinations may not work properly in some cases. # Installation takes about 2 hours. # You may get a grub installation error, but you can ignore it and proceed. # When it's time for the final reboot, use pkill -9 qemu from another terminal to force it to terminate. qemu-system-mips64el \ -M malta \ -cpu MIPS64R2-generic \ -m 1G \ -hda ./disk.qcow2 \ -initrd ./initrd.gz \ -kernel ./vmlinux-4.19.0-9-5kc-malta \ -append "nokaslr" \ -nographic # Copy kernel and ramdisk apt install nbd-client && modprobe nbd max_part=8 && qemu-nbd --connect=/dev/nbd0 disk.qcow2 mkdir mnt && mount /dev/nbd0p1 mnt && cp mnt/boot/initrd.img-*-malta . && sync umount /dev/nbd0p1 && nbd-client -d /dev/nbd0 && rmdir mnt && rm -f initrd.gz # Run qemu-system-mips64el \ -M malta \ -cpu MIPS64R2-generic \ -m 1G \ -hda ./disk.qcow2 \ -initrd ./initrd.img-4.19.0-9-5kc-malta \ -kernel ./vmlinux-4.19.0-9-5kc-malta \ -append "nokaslr root=/dev/sda1" \ -net nic,model=e1000 \ -net user,hostfwd=tcp:127.0.0.1:2222-:22,id=net0 \ -nographic ``` ```bash # Execute in the guest # fix interface cat > /etc/network/interfaces << EOF auto lo auto enp0s18 iface lo inet loopback iface enp0s18 inet dhcp EOF systemctl restart networking ``` ## debian ppc64el ```bash wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-ppc64el/current/images/netboot/debian-installer/ppc64el/initrd.gz wget http://ftp.debian.org/debian/dists/Debian10.4/main/installer-ppc64el/current/images/netboot/debian-installer/ppc64el/vmlinux qemu-img create -f qcow2 disk.qcow2 20G # Select C for language and your country (e.g. Japan or Japanese, etc.) for others. # Please note that mirror download destinations may not work properly in some cases. # Installation takes about 2 hours. # You may get a grub installation error, but you can ignore it and proceed. # When it's time for the final reboot, use pkill -9 qemu from another terminal to force it to terminate. qemu-system-ppc64 \ -M pseries-2.12 \ -cpu power9 \ -smp 2 \ -m 2G \ -hda disk.qcow2 \ -initrd ./initrd.gz \ -kernel ./vmlinux \ -append "nokaslr" \ -net nic,macaddr=52:54:00:fa:ce:12,model=virtio \ -net user \ -nographic \ -nodefaults \ -serial stdio # Open disk.qcow2 with 7-zip file manager and extract the following two files: 1.img/boot/initrd.img-4.19.0-9-powerpc64le 1.img/boot/vmlinux-4.19.0-9-powerpc64le # Delete the kernel and ramdisk used during installation rm -f initrd.gz vmlinux # Run qemu-system-ppc64 \ -M pseries-2.12 \ -cpu power9 \ -smp 2 \ -m 2G \ -hda disk.qcow2 \ -initrd ./initrd.img-4.19.0-9-powerpc64le \ -kernel ./vmlinux-4.19.0-9-powerpc64le \ -append "nokaslr root=/dev/sda2" \ -net nic,macaddr=52:54:00:fa:ce:12,model=virtio \ -net user,hostfwd=tcp:127.0.0.1:2222-:22,id=net0 \ -nographic \ -nodefaults \ -serial stdio ``` ## debian sparc64 ```bash # Versions later than 9.0 will not install properly. wget http://cdimage.debian.org/cdimage/ports/9.0/sparc64/iso-cd/debian-9.0-sparc64-NETINST-1.iso qemu-img create -f qcow2 disk.qcow2 20G # Press Enter to start booting from the CD, select C for language and your country (e.g. Japan or Japanese, etc.) for others. # You will be told that there is no NIC, but this is what you intended so select "no ethernet card" and proceed. # When asked whether to scan another CD/DVD, select No and proceed. # When it comes to entering the mirror, press "Go Back" to skip the installation from the network and proceed. # Installation takes about 2 hours. # You may get a grub installation error, but you can ignore it and proceed. # When it's time for the final reboot, use pkill -9 qemu from another terminal to force it to terminate. qemu-system-sparc64 \ -m 2G \ -hda disk.qcow2 \ -cdrom debian-9.0-sparc64-NETINST-1.iso \ -boot once=d \ -serial stdio \ -nographic \ -nodefaults # Run qemu-system-sparc64 \ -machine sun4u,accel=tcg,usb=off \ -m 2G \ -realtime mlock=off \ -smp 1,sockets=1,cores=1,threads=1 \ -uuid ccd8b5c2-b8e4-4d5e-af19-9322cd8e55bf \ -rtc base=utc \ -no-reboot \ -no-shutdown \ -boot strict=on \ -drive file=disk.qcow2,if=none,id=hd,format=qcow2,cache=none,aio=native \ -device ide-hd,bus=ide.0,unit=0,drive=hd \ -net nic,model=e1000 \ -net user,hostfwd=tcp:127.0.0.1:2222-:22,id=net0 \ -msg timestamp=on \ -serial mon:stdio \ -nographic \ -nodefaults ``` ```bash # Execute in the guest # fix interface cat > /etc/network/interfaces << EOF auto lo auto enp2s0 iface lo inet loopback iface enp2s0 inet dhcp EOF systemctl restart networking # Added blacklist module cat > /etc/modprobe.d/drm-blacklist.conf << EOF # blacklist of DRM modules that do not load on qemu-system-sparc64 sun4u blacklist drm blacklist bochs-drm blacklist ttm EOF # Add the apt repository and key that are missing. echo 'deb http://ftp.ports.debian.org/debian-ports/ unstable main' > /etc/apt/sources.list busybox wget http://ftp.ports.debian.org/debian-ports/pool/main/d/debian-ports-archive-keyring/debian-ports-archive-keyring_2019.11.05_all.deb dpkg -i debian-ports-archive-keyring_2019.11.05_all.deb && rm debian-ports-archive-keyring_2019.11.05_all.deb # Parallel disk writing occurs during apt, causing frequent I/O errors. # As a countermeasure, save the apt cache to tmpfs. # Of course, this will disappear when you restart the computer. mv /var/cache/apt/archives/* /tmp rmdir /var/cache/apt/archives ln -s /tmp /var/cache/apt/archives ``` ## debian s390x ```bash wget https://cdimage.debian.org/pub/debian/dists/stretch/main/installer-s390x/current/images/generic/kernel.debian wget https://cdimage.debian.org/pub/debian/dists/stretch/main/installer-s390x/current/images/generic/initrd.debian qemu-img create -f qcow2 disk.qcow2 20G # You will first be asked for the NIC and IP, so enter the model as virtio, IP as 10.0.2.15, default as 10.0.2.2, and DNS as 10.0.2.3. # Select C for language and your country (e.g. Japan or Japanese, etc.) for others. # Please note that mirror download destinations may not work properly in some cases. # Installation takes about 2 hours. # You may get a grub installation error, but you can ignore it and proceed. # When it's time for the final reboot, use pkill -9 qemu from another terminal to force it to terminate. qemu-system-s390x \ -M s390-ccw-virtio \ -m 2G \ -kernel kernel.debian \ -initrd initrd.debian \ -drive file=disk.qcow2,if=none,format=qcow2,id=hd0 \ -device virtio-blk-ccw,drive=hd0,id=virtio-disk0 \ -net nic,macaddr=52:54:00:fa:ce:18,model=virtio \ -net user \ -nographic # Copy kernel and ramdisk apt install nbd-client && modprobe nbd max_part=8 && qemu-nbd --connect=/dev/nbd0 disk.qcow2 mkdir mnt && mount /dev/nbd0p1 mnt && cp mnt/boot/initrd.img mnt/boot/vmlinuz . && sync umount /dev/nbd0p1 && nbd-client -d /dev/nbd0 && rmdir mnt && rm -f initrd.gz # Run qemu-system-s390x \ -M s390-ccw-virtio \ -m 2G \ -kernel vmlinuz \ -append "root=/dev/vda1" \ -initrd initrd.img \ -drive file=disk.qcow2,if=none,format=qcow2,id=hd0 \ -device virtio-blk-ccw,drive=hd0,id=virtio-disk0 \ -net nic,macaddr=52:54:00:fa:ce:18,model=virtio \ -net user,hostfwd=tcp:127.0.0.1:2222-:22,id=net0 \ -nographic ``` ================================================ FILE: docs/FAQ.md ================================================ # FAQ ## Table of Contents - [About GEF's Files or Directories](#about-gefs-files-or-directories) - [About the Installation](#about-the-installation) - [About the Host Environment](#about-the-host-environment) - [About the Guest (Debugged) Environment](#about-the-guest-debugged-environment) - [About GEF Settings](#about-gef-settings) - [About Commands](#about-commands) - [About the Internal Mechanism](#about-the-internal-mechanism) - [About the Python Interface](#about-the-python-interface) - [About the Development Schedule](#about-the-development-schedule) - [About Reporting, etc.](#about-reporting-etc) - [About Orynth](#about-orynth) # About GEF's Files or Directories ## Where is `gef.py`? By default, GEF (`gef.py`) is placed at `/root/.gef/gef.py`. GEF is primarily a single file (`gef.py`). Optional configuration and helper files may accompany it. ## What is `~/.gef.rc`? This is the GEF configuration file. It is not present by default. Executing the `gef save` command saves the current settings to disk (`~/.gef.rc`). The next time GEF starts, it will automatically load this file and apply the saved settings. This includes the current values of items configurable with `gef config` and user-defined command aliases. ## What is `~/.gdbinit`? This is the command file that GDB will execute when it starts up. By including the following command to load `gef.py`, GEF will be loaded automatically. There are two ways to load GEF from `.gdbinit`; either one works. ``` # New style python sys.path.insert(0, "/root/.gef"); from gef import *; Gef.main() # Old style source /root/.gef/gef.py # Another old style source /root/.gdbinit-gef.py # In the old installer, GEF was located here ``` Notes: - The former (new style) imports GEF using Python. Because a `*.pyc` file is generated and cached, subsequent startups are faster. This is the default setting in recent versions of GEF. - The latter (old style) is the traditional method, where GEF is loaded directly. It takes longer to load each time, but the command is simpler. ## What is `/tmp/gef`? This is the directory where GEF temporarily stores files. Since it is used for caching, deleting it does not cause any issues. It will be created automatically the next time GEF starts. The variable `GEF_TEMP_DIR` is defined in `gef.py` and can be changed if necessary. ## What is `install-uv.sh`? This is the new installer that creates and uses a Python virtual environment (`venv`) with `uv`. It installs the same packages as `install-no-uv.sh` (previously named simply `install.sh`). The key difference is that Python packages are installed into the virtual environment. By default, it installs into `/root/.gef/.venv-gef`. External tools such as `rp++`(`rp-lin`), `seccomp-tools`, etc. are also installed under this directory (`/root/.gef/.venv-gef/bin`). Usage: ``` # Run the following commands as `root` or `sudo` wget -q https://raw.githubusercontent.com/bata24/gef/dev/install-uv.sh -O- | sudo sh ``` ## What is `install-no-uv.sh`? This is the old installer that was used before the `uv`-based installation became available. This installer is not currently recommended, but it still works. Usage: ``` # Run the following commands as `root` or `sudo` # On Ubuntu 23.04 or later, global Python package installation via pip3 is restricted. # Use the --break-system-packages option. wget -q https://raw.githubusercontent.com/bata24/gef/dev/install-no-uv.sh -O- \ | sed -e 's/pip3 install/pip3 install --break-system-packages/g' | sudo sh # For Ubuntu 22.10 or earlier wget -q https://raw.githubusercontent.com/bata24/gef/dev/install-no-uv.sh -O- | sudo sh ``` ## What is `install-minimal.sh`? This is an installer for running GEF in restricted environments where required packages cannot be installed due to various limitations. Most core features work, but commands that depend on extra Python packages or external tools will be unavailable. Usage: ``` # Run the following commands as `root` or `sudo` wget -q https://raw.githubusercontent.com/bata24/gef/dev/install-minimal.sh -O- | sudo sh ``` Notes: - The process is straightforward: download `gef.py`, place it in the appropriate location, and add a line to `.gdbinit` to load it. You can also do the same thing manually. - Because it does not install any Python packages, it does not create a `venv`. ## What is `gef.venv.conf`? This is the path information file required by GEF that is automatically generated when you install GEF using `install-uv.sh`. It is not generated if you use `install-no-uv.sh` or `install-minimal.sh`. Place this file in the same directory as `gef.py`. It contains three pieces of path information: - `GEF_VENV_GEM_HOME` - Sets `GEM_HOME` so Ruby `gem`-installed tools can be discovered and used by GEF. - `GEF_VENV_SYS_PATH` - By default, GEF searches globally installed Python packages. - Prepend this path to `sys.path` so that packages inside the `venv` take precedence. - `GEF_VENV_BIN_PATH` - By default, GEF searches for executables using the `$PATH` environment variable (the actual process is a bit more complicated). - GEF will prepend the specified path to the search directories before searching. # About the Installation ## How many ways are there to install GEF? There are three installers. - `uv`-based install - This is the installation method provided by `install-uv.sh`. - This uses `uv` to install Python packages into an isolated environment. - This is a full installation, so you can use almost all the features that GEF provides. - This is the currently recommended installer for GEF. - Minimal install - This is the installation method provided by `install-minimal.sh`. - Simply download `gef.py` and place it in the appropriate location. - Because this method is very simple, it can also be performed manually. - Normal install - This is the installation method provided by `install-no-uv.sh`. - This uses `python3-pip` to install Python packages globally. - This is a full installation, so you can use almost all the features that GEF provides. - This installer is not currently recommended, but it still works. - This method was used before the `uv`-based installation became available. For an explanation of each installer, see [About GEF's Files or Directories](#about-gefs-files-or-directories). ## Why does GEF need to run as `root`? Some GDB stubs require GEF to gather information and to read or write process memory through `/proc`, which typically needs root privileges. Examples: - Resolving the real PID during remote debugging (e.g., when connecting to `localhost`): - GEF may search across `/proc` to identify the target process and determine details about the remote network endpoint. - Gathering attachment details for a process running in a container (e.g., namespaces, real file paths). - Intel Pin / SDE: Reads memory via `/proc//mem` for performance. - qemu-user etc.: Applies patches at addresses that are not writable. - qemu-system: Reads physical memory via `/proc//mem` for performance. - If this is unavailable, GEF falls back to slower memory access through `gdb`. - qemu-system (ARM32/ARM64): Reads and writes secure-world memory. As a result, running GEF as an unprivileged user is not currently tested. If you prefer not to run GEF as `root`, you have two options: - Install and run it inside a container (e.g., Docker). - Install it as a normal user using the steps below. ## How to change the location of GEF? / How can I run GEF as a non-`root` user? This is NOT officially supported; proceed at your own risk. Future updates may break this workflow. ``` # First, download GEF install script wget -q https://raw.githubusercontent.com/bata24/gef/dev/install-uv.sh -P /tmp # apt-get install as root sudo apt-get update sudo apt-get install # see /tmp/install-uv.sh # 1. replace GDBINIT_PATH and GEF_DIR as you wanted # 2. remove root user check # 3. remove apt-get install vim /tmp/install-uv.sh # install sh /tmp/install-uv.sh # Delete /tmp/gef directory (if it exists) sudo rm -rf /tmp/gef ``` Alternative simple method (no external tools; simple loading): ``` # Download wget -q https://raw.githubusercontent.com/bata24/gef/dev/gef.py -O ~/.gdbinit-gef.py # Add path to .gdbinit echo "source $HOME/.gdbinit-gef.py" >> ~/.gdbinit ``` Notes: - GEF is designed to have as few dependencies as possible. Many commands should work with just `gef.py` without any additional external tools. - If you do not install external tools, the features that will not be available are listed below. ## If I do not install external tools, which commands will no longer be available? The following is a breakdown. It may not be comprehensive. To use these commands fully, you need to manually install the necessary packages and tools. |GEF command/feature|Required apt package|Required python3 package|Required other tools| |:---|:---|:---|:---| |(`gef`)|`gdb` or `gdb-multiarch`|-|-| |`got`|`binutils` (`objdump`, `readelf`)|-|-| |`got --cppfilt`|`binutils` (`c++filt`)|-|-| |`add-symbol-temporary`|`binutils` (`objcopy`)|-|-| |`ksymaddr-remote-apply`|`binutils` (`objcopy`)|-|-| |`kmod -a`|`binutils` (`objcopy`)|-|-| |`ksymaddr-remote --vmlinux-file`|`binutils` (`nm`)|-|-| |`ktypes`|`bpftool`|-|-| |`ktypes-load`|`bpftool`,`gcc`|-|-| |`qemu-device-info`|`binutils` (`nm`)|-|-| |`rp`|-|-|`rp++`| |`rp --kernel`|`binutils` (`nm`), `grep`|-|`rp++`| |`binwalk-memory`|`binwalk`|-|-| |`diffo colordiff`|`colordiff`|-|-| |`diffo git-diff`|`git`|-|-| |`tmux-setup`|`tmux`|-|-| |`ps`|`procps`|-|-| |`proc-info`|`procps`|-|-| |`proc-dump`|`bsdextrautils` (`hexdump`, `column`)|-|-| |`heap dump-image`|`imagemagick`|-|-| |`vdump`|`imagemagick`|-|-| |`sixel-memory`|`imagemagick`|-|-| |`sixel-memory -b`|-|`pillow`, `pyzbar`|-| |`ktask -S`|-|-|`ceccomp` or `seccomp-tools`| |`seccomp-tools`|-|-|`ceccomp` or `seccomp-tools`| |`onegadget`|-|-|`one_gadget`| |Progress Indicator|-|`tqdm`|-| |`angr`|-|`angr`|-| |`asm-list`|-|`capstone`|-| |`capstone-disassemble`|-|`capstone`|-| |`dasm`|-|`capstone`|-| |`i8086` mode|-|`capstone`|-| |`unicorn-emulate`|-|`capstone`, `unicorn`, `setuptools`(python 3.12+)|-| |`heap try-free`|-|`capstone`, `unicorn`, `setuptools`(python 3.12+)|-| |`heap try-malloc`|-|`capstone`, `unicorn`, `setuptools`(python 3.12+)|-| |`heap try-realloc`|-|`capstone`, `unicorn`, `setuptools`(python 3.12+)|-| |`heap try-calloc`|-|`capstone`, `unicorn`, `setuptools`(python 3.12+)|-| |`asm`|-|`keystone-engine`|-| |`base-n-decode`|-|`codext`|-| |`base-n-encode`|-|`codext`|-| |`crc`|-|`crccheck`|-| |`hash`(FSB hash) |-|`gmpy2`|-| |`uefi-ovmf-info`|-|`crccheck`|-| |`filetype-memory`|`file`|`magika`|-| |`ropper`|-|`ropper`|-| |`vmlinux-to-elf-apply`|-|`vmlinux-to-elf`|-| Notes: - To save installation time, the GEF installer does not install `binwalk` by default. This is because it has many package dependencies. - The GEF installer does no longer install `bpftool` when run inside a Docker container because `bpftool` is intended to run in the host. - The GEF installer installs `seccomp-tools` if neither `ceccomp` nor `seccomp-tools` is found. I recommend `ceccomp`, but its build is not simple. Install it manually if needed. - The GEF installer does no longer install `vmlinux-to-elf` because in many cases you can use `ks-apply` instead. ## Is there a lighter version with fewer features? There is no such version. But you can adjust it yourself. 1. Split into classes using `dev/split/split.py` 2. Edit `dev/split/save/gef-splitted.py` and remove unnecessary commands and architectures 3. Recombine with `dev/split/de-split.py` 4. `dev/split/gef-reconstructed.py` is generated # About the Host Environment ## Does GEF work properly on operating systems other than Ubuntu? Yes, it generally works on most standard Linux distributions; however, not every command is validated on every distribution. I have used it on Debian, and some users are running it on Arch Linux. It also seems to be working fine on WSL2 (Ubuntu) so far. However, I have not confirmed that all commands work correctly. ## Will this GEF work as a plugin for `hugsy/gef`? No, it does not work as a plugin. It replaces `hugsy/gef`. Compatibility with `hugsy/gef` has already been lost, as well as with `hugsy/gef-extras`. It should be considered an entirely separate product. Similarly, this GEF cannot be used at the same time as `peda` or `pwndbg`. Make sure you only load one of them. ## GDB will not load GEF. Start GDB as the `root` user or with `sudo`. By default, GEF is installed under `/root/.gef`, so non-`root` sessions will not find it unless you change the install paths or update your `.gdbinit` to point to the actual location. ## GDB still does not load GEF, even as the `root` user. It is likely that your GDB does not support integration with `python3`. This can happen if you are not using the GDB provided by Ubuntu's `apt` package manager. For example: - You built it yourself from source code - You are using a version that someone else prepared for a specific architecture Consider rebuilding GDB from the latest tarball or from the Git repository. ## How to build GDB? - From the latest tarball: - Download latest tarball from https://ftp.gnu.org/gnu/gdb/ ``` tar xf gdb-16.3.tar.xz && cd gdb-16.3 ./configure --enable-targets=all --with-python=/usr/bin/python3 make && make install ``` - From Git: ``` apt install -y libdebuginfod-dev libreadline-dev git clone --depth 1 https://gnu.googlesource.com/binutils-gdb && cd binutils-gdb ./configure --disable-{binutils,ld,gold,gas,sim,gprof,gprofng} --enable-targets=all \ --with-python=/usr/bin/python3 --with-debuginfod --with-system-{zlib,readline} \ --disable-{dependency-tracking,gdbtk,unit-tests} --without-{tcl,tk,x,guile,babeltrace,intel-pt,xx-hash} make && make install ``` ## When debugging with GDB, how can I display the source code of preinstalled libraries and commands? For Ubuntu 22.10 and later, it is recommended to use `debuginfod`. - Enable `debuginfod` (Ubuntu 22.10 and later) ``` export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com" echo "set debuginfod enabled on" >> ~/.gdbinit ``` - If you are unable to use `debuginfod`, please set up the symbols manually. ``` # Not necessary if debuginfod is enabled apt install libc6-dbg echo "set debug-file-directory /usr/lib/debug" >> ~/.gdbinit ``` However, `debuginfod` often does not provide `glibc` source files. Fetch and place the `glibc` sources locally as shown below. - Get the `glibc` source ``` # Ubuntu 24.04 or later sed -i -e 's/^Types: deb$/Types: deb deb-src/g' /etc/apt/sources.list.d/ubuntu.sources # Ubuntu 23.10 or before sed -i -e 's/^# deb-src/deb-src/g' /etc/apt/sources.list # common cd /usr/lib/debug && apt update && apt source libc6 echo "directory /usr/lib/debug/glibc-2.39" >> ~/.gdbinit # You need to adjust the version for your environment. ``` # About the Guest (Debugged) Environment ## What Linux kernel versions does GEF support as guests in qemu-system? Most commands have been validated on kernels from 3.x up to 6.19.x. Newer kernels may work but are not fully validated. However, I have not verified every kernel version. For example, certain symbols in some versions may not be supported by heuristic symbol detection. Also, the structure may differ depending on the build configuration and the compiler used to build the kernel. Therefore, there may be environments where GEF does not work. If you encounter any issues, please report them on the issue page. ## Is there a way to get a prebuilt kernel for each version? I use [https://kernel.ubuntu.com/](https://kernel.ubuntu.com/mainline). Download the desired `linux-image-unsigned-*_amd64.deb` and extract `/boot/vmlinuz-*` from it (e.g., with `dpkg-deb -x`). No filesystem image is provided. Please use one created with `buildroot` or one provided in past CTF challenges. Download the `linux-modules-*_amd64.deb` package for `System.map` and `config`. Another option is [COPR of Fedora](https://download.copr.fedorainfracloud.org/results/%40kernel-vanilla/fedora/fedora-rawhide-x86_64/). ## Will each GEF command be more accurate if I have `vmlinux` with debug symbols? Let's consider debug information and debug symbols separately. - Debug information - Partially yes, partially no. - Some commands (e.g.; `slub-dump`, `buddy-dump`, `ktask`, `kmod`, etc.) use type information, if available, to speed up offset calculation. - However, there are still not many commands that implement this kind of optimization. - Debug symbols - Yes. - If the symbols are already loaded: - The `ksymaddr-remtote` command will automatically use offsets read from the `vmlinux` file (with rebasing). - If the symbols are not loaded yet: - First, load the `vmlinux` symbols using the `file` command. - Note: - GEF internally uses the addresses resolved with `ksymaddr-remote`, and these results are cached. - If `vmlinux` file is already loaded, the results of `ksymaddr-remote` can be replaced with accurate values (with rebasing) and cached. ## Does GEF support i386 16-bit mode (real mode)? Yes, GEF supports real mode experimentally. Use `qemu-system-i386`, and do NOT use `qemu-system-x86_64`. Explicitly specify the i8086 architecture before connecting: `gdb -ex 'set architecture i8086' -ex 'target remote localhost:1234'`. GEF automatically handles transitions between 16-bit real mode and 32-bit protected mode, but expect limitations compared to 32/64-bit workflows. ## Does GEF support ARM Cortex-M? Yes, GEF supports ARM Cortex-M. You can debug microcontrollers, but the memory map cannot be enumerated via GDB; consult vendor documentation or SVD files for layout details. ## Is it possible to debug userland with GEF when using qemu-system? Partially, yes. I think it can be useful when you want to trace before and after a system call. However, of course, I do not recommend continually debugging userland with qemu-system. This is because many commands are restricted for various reasons. Consider setting up `gdbserver` in the guest and connecting from the outside. Note: - If KPTI is enabled, many kernel-related commands cannot be used in userland. This is because most memory access to kernel space is unavailable if KPTI is enabled. ## How do I break in userland when using qemu-system? Use a hardware breakpoint. When execution is stopped in the kernel, first ensure you are in the intended process context. If so, you can set a `break *ADDRESS` as usual. If execution stopped in the kernel context of a different user process than you expected, or in a kernel thread like `swapper/0`, the virtual address of the process you want is not mapped. For this reason, software breakpoints that embed `0xcc` in virtual memory cannot be used in some situations. However, hardware breakpoints can be used without any problems. ## Does GEF support debugging the Android kernel? Yes, it is supported, but not fully. When I tried using `Android Studio`, most commands seemed to work. - I used `Android Studio` on Windows and connected from Linux. - Refer to [docs/SUPPORTED-MODE.md](SUPPORTED-MODE.md) for the commands I used. However, the QEMU in `Android Studio` is based on an older version, 2.12.0, and seems to have compatibility issues with recent GDB versions (16.x and later). Specifically, repeated memory reads may cause QEMU's GDB stub to return incorrect results. This is especially noticeable for commands that perform repeated memory access, such as `ktask` and `kchecksec`. ## Does GEF support debugging Android userland binaries? Yes, it is supported, but not fully. When I tried using `Android Studio`, most commands seemed to work. - I used `Android Studio` on Windows and connected from Linux. - Refer to [docs/SUPPORTED-MODE.md](SUPPORTED-MODE.md) for the commands I used. However, Android does not use glibc (it uses the Bionic C library). Therefore, be aware that all `glibc`-specific commands, such as the `heap` command, cannot be used. ## Does GEF support TEE OSs other than OP-TEE? No, GEF does not support them. # About GEF Settings ## I prefer the AT&T syntax. You can set the AT&T syntax for each session using the `set disassembly-flavor att` command. Alternatively, since the `set disassembly-flavor intel` command is executed in the main function of GEF, you may want to comment it out. However, as GEF is not optimized for AT&T syntax parsing, some commands may not function correctly. If you find a case where it does not work, please report it on the issue page. ## I don't like the color scheme. Customize it using the `theme` command, then run `gef save`. This will save the configuration to `~/.gef.rc`. Another option is to disable colors. Try `gef config gef.disable_color True`. ## I don't want to add `-n` to every command to disable pager. To permanently disable the pager, use the command `gef config gef.always_no_pager True` followed by `gef save`. # About Commands ## Which command should I start with when debugging the kernel? Try `pagewalk` , `ks-apply`, and `kchecksec`. After that, try `slub-dump`, `ktask` and `ksysctl` as well. Other commands are less important, so check them with `gef help` if necessary. ## Is it possible for GEF to re-display the results of a command (for using the `less` pager)? In general, no. If you need to keep output while the `less` pager is active, use `|$cat > /tmp/foo.txt`. Alternatively, set `gef config gef.keep_pager_result True` and then `gef save`. From the next time onwards, temporary files will no longer be deleted. ## Is it possible for GEF to pass the result of a command to a shell command? Yes, you can use the built-in `pipe` command. For example, `pipe elf-info -n | grep .data` or `|pdisas | grep call`. ## `ktask` (or other kernel-related commands) does not work. The kernel you are debugging may have been built with `CONFIG_RANDSTRUCT=y`. In this case, except for a few commands, most will not work correctly. Currently, at least the following commands do not work: - `ktask` - `kmod` - `kbdev` - `kcdev` - `kops` - `kpipe` - `ksysctl` - `kmalloc-tracer` - `kmalloc-allocated-by` - `kfiles` - `kregs` - `ksighands` - `kpcidev` - `knamespaces` - `kipcs` - `kfilesystems` If it does not work properly even though `CONFIG_RANDSTRUCT=n`, GEF may be failing to parse due to a change in `struct task_struct` or similar. If you think there is a problem with GEF, please report it on the issues page. ## The `vmlinux-to-elf-apply` command causes an error when creating an ELF file. Please update `vmlinux-to-elf` to the latest version. If the problem persists, try using the `ks-apply` command. The logic is slightly different, so it might work. If it still does not work, please report it on the issue page. ## If I have a `vmlinux` with debuginfo, how can I use `ks-apply`? The `ks-apply` command is unnecessary. Run `kload `. Below is a summary of each. |Component|How GDB uses it|How GEF uses it| |:---|:---|:---| |`vmlinux` debuginfo|- Loaded via `add-symbol-file`
- Or `kload` (easy wrapper with `kbase`)
- Essential for source-level debugging|- Used by some commands if available
- Can be viewed with `dt`| |`vmlinux` symbols|- Loaded via `add-symbol-file`
- Or `kload` (easy wrapper with `kbase`)
- Provides kernel symbol resolution|- Accessible after `ksymaddr-remote`
- Addresses will be automatically rebased| |Memory-resident `debuginfo`
(if `CONFIG_DEBUG_INFO_BTF=y`)|- Not available by default
- Accessible via `ktypes-load`|- Used by some commands if available after `ktypes-load`
- Can be viewed with `dt`| |Memory-resident `kallsyms`
(if `CONFIG_KALLSYMS=y`)|- Not available by default
- Accessible via `ks-apply`|- Accessible after `ksymaddr-remote`
- Used by various GEF commands internally| ## The kernel-related commands are unstable; sometimes they work fine, sometimes they don't. / The output of `ksymaddr-remote` seems odd. This may be due to the GEF's caching mechanism. GEF resolves kernel symbols using the `ksymaddr-remote` command. This command caches the offsets (needed for parsing) under the `/tmp/gef` directory. At that time, it uses the kernel version string (its hash) as the cache key. Because of this, in a situation like following, you may encounter an issue where the wrong cache is used. - You have built and debug multiple kernels with slightly different build configs (same kernel version) To resolve this, after attaching with GDB, execute either of the following: - `gef reset-cache --hard` (clears the cache) - `ks -rv` (resolves symbols ignoring the cache) - Once symbols are resolved, it remains in effect for the duration of that GDB (GEF) debug session. ## The `got` command does not display PLT addresses. This problem is probably caused by an outdated version of `binutils`. The `got` command uses `objdump` internally to obtain the PLT addresses. However, with certain combinations of `binutils` and `glibc` versions, `objdump` does not display the PLT addresses. The currently known combinations are as follows: - `binutils 2.38` (Ubuntu 22.04 default) + `glibc 2.37 or later` This problem occurs when you try to use a newer `glibc` in an Ubuntu 22.04 environment using `patchelf` etc. The workaround is to build and install a newer version of `binutils` from source code. ## Can I switch to a mode that references physical memory? Yes, when using qemu-system. Switch with `pi enable_phys()` and revert with `pi disable_phys()`. GEF uses this internally; if a command is interrupted mid-execution, you may need to revert manually. ## The `magic` command produces few valid results. This is because libc symbols are not loaded. Unlike kernel symbols, userland symbols do not undergo heuristic detection (with some special exceptions). Therefore, missing symbols may not be detected by the `magic` command. If you are referring to system-wide `glibc`, you can resolve this with the following steps: 1. Install the symbols with `apt install libc6-dbg`. 2. Add `set debug-file-directory /usr/lib/debug` to `~/.gdbinit`. ## The command to get the source (e.g., `ptr-mangle --source`) does not work. Do not use `~` in paths that point to the `gef.py` directory in `.gdbinit`. Python's `inspect` may not expand tildes reliably; use absolute paths instead. I encountered this behavior in Python 3.9.2 on Debian 11. ## When using qemu-user, an error occurs when continuing execution. Is the error something like this? ``` ... dwarf2/dwz.c:188: internal-error: dwarf2_read_dwz_file: Assertion `is_main_thread ()' failed. A problem internal to GDB has been detected, further debugging may prove unreliable. ----- Backtrace ----- ... ``` If so, this is caused by the `continue-for-qemu-user` command. This problem occurs only when the configuration option `continue_for_qemu_user.use_fork` is set to `False`. `continue-for-qemu-user` is a wrapper for the `c`(`continue`) command that allows `Ctrl+C` to be accepted even during `continue` under `qemu-user`. On some architectures, this wrapper may not work properly when running dynamically linked binaries with `qemu-user`. There are two ways to work around this: - Use the `main-break` command to reach `main` once; after that, this error will no longer occur. - Use the `continue` command instead of the `c` command (but `Ctrl+C` will not work). # About the Internal Mechanism ## How does GEF implement kernel analysis-related commands without symbols? Internally, this process consists of several steps: 1. Enumerate memory map information from the page table structure. 2. Detect the `.rodata` area of the kernel from the memory map information. 3. Scan `.rodata` to identify the kernel version. 4. Parse the structure of `kallsyms` in `.rodata` and obtain all "symbol and address" pairs. 5. If global variable symbols are available at this point, use them (= `CONFIG_KALLSYMS_ALL=y`). - If not, GEF disassembles the function that uses the specified global variable. - By parsing the result, GEF obtains the address of the required global variable. - This is implemented in `KernelAddressHeuristicFinder` and `KernelAddressHeuristicFinderUtil` classes. 6. Detect the offset of the structure member, if necessary. - To identify it heuristically, GEF uses facts such as whether a value in memory is an address or whether a structure in memory has a specific layout. - At this time, GEF takes into account the presence or absence of members and changes in their order due to differences in kernel versions. 7. Parse and display the value in memory using all the information detected so far. As you can see, this does not work well if structure members are arranged randomly (`CONFIG_RANDSTRUCT=y`). Also, depending on the assembly output by the compiler, it may not be possible to parse correctly. ## How does GEF achieve the conversion from `page` to `virt` (or `phys`)? GEF achieves this by using the parsed results of the SLUB free list. If you are interested in this question, you probably know how difficult this conversion formula is. As you can see, this conversion (`page <-> virt`) is very difficult. This is because several values are needed to convert `page` to `virt` (or vice versa), two of which are hard to obtain without symbols and type information: - `vmemmap` - `sizeof(struct page)` I concluded that the only way to get these is to calculate them backwards from valid `page` and `virt` pairs. These pairs can be found with a very high probability while parsing the SLUB structure. Therefore, GEF calls the `slub-dump` command internally and temporarily, then calculates these values from the result. This is the reason why the first time the `page2virt` command runs, it takes a long time: it parses the page tables, identifies function symbols, and internally calls `slub-dump` twice. Note: - The `slub-dump` command itself uses the `page` to `virt` conversion function as well, resulting in a circular reference. GEF avoid this problem by adding an option to skip this (`--skip-page2virt`). # About the Python Interface ## Can I access each GEF command or alias instance from `python-interactive`? Yes, you can access them via `GCI` or `GAI`. For example, `pi GCI["vmmap"]` or `pi GAI["us"]`. - `GCI` stands for Gef Command Instances. - `GAI` stands for Gef Alias Instances. ## The class name `KernelAddressHeuristicFinder` is too long. You can access it using `KF`. For example, use `pi KF.get_slab_caches()` instead of `pi KernelAddressHeuristicFinder.get_slab_caches()`. ## Can I revert the output of `python-interactive` back to decimal from hex? Yes, you can do so by executing `pi hexoff()`. ## How can I get the instruction object? You can get the instruction object with `pi get_insn(addr=None)`. There are also similar functions. Here is the list: - `get_insn(addr=None)` - `get_insn_next(addr=None)` - `get_insn_prev(addr=None)` ## Are there any other globally accessible functions that are useful? - Memory access - `write_memory(addr, data)`, `read_memory(addr, length)` - `read_int_from_memory(addr)` # Sizes are estimated - `read_int8_from_memory(addr)` - `read_int16_from_memory(addr)` - `read_int32_from_memory(addr)` - `read_int64_from_memory(addr)` - `read_cstring_from_memory(addr, max_length=None)` - `read_physmem(paddr, size)`, `write_physmem(paddr, data)` - `is_valid_addr(addr)` - `is_single_link_list(addr)`, `is_double_link_list(addr, min_len=0)` - `is_ascii_string(addr)` - Register access - `get_register(regname, use_mbed_exec=False, use_monitor=False)` - Calculating a minor hash function (100+ variants) - `Hash.SHA0`, `Hash.Whirlpool`, etc. - Value manipulation - `String.str2bytes(x)`, `String.bytes2str(x)` - `slicer(data, n)`, `slice_unpack(data, n)` - `p8`, `p16`, `p32`, `p64` - `u8`, `u16`, `u32`, `u64`, `u128` - `byteswap(x, byte_size=None)`, `xor(a, b=None)`, - `ror(val, bits, arch_bits=64)`, `rol(val, bits, arch_bits=64)`, - `align(value, align)`, `align_to_ptrsize(addr)`, `align_to_pagesize(addr)`, - Other - `get_pagesize()`, `get_pagesize_mask_low()`, `get_pagesize_mask_high()` - `get_syscall_table(arch=None, mode=None)` If you want the complete list, run `gef pyobj-list`. ## I want to add a command. How do I get started? Copy and paste the `TemplateCommand` class and edit it as you like. Here are some notes: - Class name - Rename the newly added command class to any name you like. - Make sure it ends with `...Command`. - Inheritance - Make sure you inherit from the `GenericCommand` class. - This is required for the command to be registered. - Important attributes - `_cmdline_`: used to invoke the command. - `_aliases_`: used to create command aliases. - `_category_`, `_syntax_`, `_example_` and `_note_`: used by `gef help`. - `_repeat_`: enables command repetition. - `__init__()` - This method is executed only once, when GEF starts. - There is usually no need to override this method. - Delete it if you do not need to do anything special. - `do_invoke()` - It is important to override this method. - When a command is executed, it starts from this method. - Command arguments - They should be handled with the `argparse` module. - They are managed by the `parse_args` decorator of the `do_invoke()` method. - Command execution conditions - Add decorators to the `do_invoke()` method as needed. - You can check the list of available decorators with `gef pyobj-list`. - Other - Use the `gef_print()` function instead of the `print()` function whenever possible. - The function named `complete()` is reserved. ## `pi current_arch` is always `None`. Use `pi get_current_arch()`. ## Is there an easy way to get an overview of the code? Try splitting it by class with `dev/split/split.py`. `dev/split/save/gef-splitted.py` is broken and doesn't work as a python script, but it is very useful for getting an overview of the code. # About the Development Schedule ## Are there any plans to support kernels for other architectures? There are no plans. ## Are there any plans to support more architectures with qemu-user? Yes. However, it is becoming difficult to find new targets to support. This is because three things are required: 1. Toolchain - `linux-headers`, `binutils`, `gcc`, `glibc` (or `uClibc`) are needed. - A prebuilt tarball is preferred. 2. Qemu-user - It needs an implementation of GDB stub. 3. GDB - It needs `python3` support. # About Reporting, etc. ## After upgrading GEF, it stopped working. The configuration file format may have changed. Try renaming `~/.gef.rc` (for the `root` user this is `/root/.gef.rc`). ## I found a bug. Please feel free to report it on the issue page. I will respond as soon as possible. ## Can you please add this feature? / I don't like a certain feature, so please fix it. I will consider it, so please report it on the issue page. However, this is a personal project, so I have the final decision. I appreciate your understanding. ## What information should I provide when reporting an issue? Please provide a screenshot or a copy of the terminal output when the issue occurred. - Additionally, include the results of the `gef version` and `gef status` commands. - If the issue is related to kernel debugging, please also provide your environment files (such as `run.sh`, `bzImage`, etc.) or information on where they can be obtained. ## Is it okay to fork and modify? Yes, please follow the license terms and preserve copyright and attribution notices. # About Orynth ## Is this project listed on Orynth? Yes. This project is listed on Orynth for discovery: https://www.orynth.dev/projects/bata24-gef-8901 ## What is `B24G`? `B24G` is the market token symbol shown on the Orynth project page. - Using this project does NOT require token. - This is just fan token and no utility. - Orynth markets are optional and do not represent equity/ownership/rights. ## Official token address - Symbol: B24G - Address: cox9QpSKymFq1oQc8Pm1KZpAcK82RMQNvu846WSYory ================================================ FILE: docs/HOW-TO-DEBUG-AARCH64-MTE.md ================================================ ## Build static gdbserver I used debian-arm64 environment. See https://hackmd.io/lDob-hTUTfqIJyj0ahYY3A ``` wget https://ftp.gnu.org/gnu/gdb/gdb-14.1.tar.xz tar xf gdb-14.1.tar.xz && cd gdb-14.1 apt install libgmp-dev libmpfr-dev LDFLAGS=-static ./configure && make scp -P 2222 root@localhost:/root/gdb-14.1/gdbserver/gdbserver . ``` ## Build aarch64 buildroot ``` wget https://buildroot.org/downloads/buildroot-2023.11.tar.xz tar xf buildroot-2023.11.tar.xz cd buildroot-2023.11 cat << EOF >> board/qemu/aarch64-virt/linux.config CONFIG_ARM64_HW_AFDBM=y CONFIG_ARM64_PAN=y CONFIG_AS_HAS_LDAPR=y CONFIG_AS_HAS_LSE_ATOMICS=y CONFIG_ARM64_LSE_ATOMICS=y CONFIG_ARM64_USE_LSE_ATOMICS=y CONFIG_AS_HAS_ARMV8_2=y CONFIG_AS_HAS_SHA3=y CONFIG_ARM64_RAS_EXTN=y CONFIG_ARM64_CNP=y CONFIG_ARM64_PTR_AUTH=y CONFIG_ARM64_PTR_AUTH_KERNEL=y CONFIG_CC_HAS_BRANCH_PROT_PAC_RET=y CONFIG_CC_HAS_SIGN_RETURN_ADDRESS=y CONFIG_AS_HAS_PAC=y CONFIG_AS_HAS_CFI_NEGATE_RA_STATE=y CONFIG_ARM64_AMU_EXTN=y CONFIG_AS_HAS_ARMV8_4=y CONFIG_ARM64_TLB_RANGE=y CONFIG_AS_HAS_ARMV8_5=y CONFIG_ARM64_BTI=y CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI=y CONFIG_ARM64_E0PD=y CONFIG_ARM64_AS_HAS_MTE=y CONFIG_ARM64_MTE=y CONFIG_ARM64_EPAN=y EOF make qemu_aarch64_virt_defconfig make mkdir ~/qemu-aarch64-buildroot cp output/images/{Image,rootfs.ext2} ~/qemu-aarch64-buildroot ``` ## Start qemu-system Add `mte=on`, `-cpu max` and `hostfwd` settings. ``` (host) cd ~/qemu-aarch64-buildroot (host) qemu-system-aarch64 \ -machine virt,mte=on \ -cpu max \ -smp 1 \ -kernel Image \ -append "rootwait root=/dev/vda console=ttyAMA0" \ -netdev user,id=eth0,hostfwd=tcp:127.0.0.1:1234-:1234,hostfwd=tcp:127.0.0.1:1235-:1235 \ -device virtio-net-device,netdev=eth0 \ -drive file=rootfs.ext2,if=none,format=raw,id=hd0 \ -device virtio-blk-device,drive=hd0 \ -nographic ``` ## Send the target binary and gdbserver ``` (host) python -m http.server 8080 (guest) wget http://HOST_IP:8080/main (guest) wget http://HOST_IP:8080/gdbserver (guest) chmod +x main gdbserver ``` ## Start debugging ``` (guest) ./gdbserver 0.0.0.0:1234 ./main (host) gdb-multiarch -q ./main -ex 'target remote :1234' ``` ## Note GDB (aarch64-linux) already supports tagged pointers, treating the top byte as non-address data. In practice, this means tagged-pointer chasing (e.g., `telescope`) is generally usable for userland debugging without manually stripping tags. Here is a sample (see `0x007ffa47e858`). ``` gef> telescope -n $sp 8 $x29+ 0x007ffa47e830|+0x0000|+000: 0x0000007ffa47e8c0 -> 0x0000007ffa47e8d0 -> 0x0000007ffa47e9e0 -> ... 0x007ffa47e838|+0x0008|+001: 0x0000000000401128 -> 0x5280000097ffff52 <- retaddr[1] 0x007ffa47e840|+0x0010|+002: 0x0000007ffa47e850 -> 0x0000007ffa47e8b0 -> 0x0000007ffa47e000 -> ... 0x007ffa47e848|+0x0018|+003: 0x0000007ffa47e870 -> 0x0000000a41414141 ('AAAA\n'?) 0x007ffa47e850|+0x0020|+004: 0x0000007ffa47e8b0 -> 0x0000007ffa47e000 -> 0x0000000000000000 0x007ffa47e858|+0x0028|+005: 0x4100007ffa47e870 -> 0x0000000a41414141 ('AAAA\n'?) <- $x0 0x007ffa47e860|+0x0030|+006: 0x0000000000000000 0x007ffa47e868|+0x0038|+007: 0x0000000000000000 gef> ``` ================================================ FILE: docs/QEMU-USER-SUPPORTED-ARCH.md ================================================ # Qemu-user supported architectures I also list the tools I used in my Ubuntu 25.10 environment. ## x86 - toolchain: `gcc` via apt with `-m32` option. - qemu: `qemu-i386` via apt. - gdb: `gdb-multiarch` via apt. ## x64 - toolchain: `gcc` via apt. - qemu: `qemu-x86_64` via apt. - gdb: `gdb-multiarch` via apt. ## arm (Cortex-A) - toolchain: `gcc-arm-linux-gnueabihf` via apt. - qemu: `qemu-arm` via apt. - gdb: `gdb-multiarch` via apt. ## aarch64 (Cortex-A) - toolchain: `gcc-aarch64-linux-gnu` via apt. - qemu: `qemu-aarch64` via apt. - gdb: `gdb-multiarch` via apt. ## ppc32 - toolchain: `gcc-powerpc-linux-gnu` via apt. - qemu: `qemu-ppc` via apt. - gdb: `gdb-multiarch` via apt. ## ppc64 - toolchain: `gcc-powerpc64{,le}-linux-gnu` via apt. - qemu: `qemu-ppc64{,le}` via apt. - gdb: `gdb-multiarch` via apt. ## mips32 - toolchain: `gcc-mips{,el}-linux-gnu` via apt. - toolchain (Ubuntu 25.10): `mips32{,el}--glibc--stable-2025.08-1` from https://toolchains.bootlin.com/ - qemu: `qemu-mips{,el}` via apt. - gdb: `gdb-multiarch` via apt. ## mips64 - toolchain: `gcc-mips64{,el}-linux-gnuabi64` via apt. - toolchain (Ubuntu 25.10): not found. - qemu: `qemu-mips64{,el}` via apt. - gdb: `gdb-multiarch` via apt. ## mipsn32 - toolchain: `gcc-multilib-mips{,el}-linux-gnu` or `gcc-multilib-mips64{,el}-linux-gnuabi64` via apt with `-mabi=n32` option. - toolchain (Ubuntu 25.10): `mips64{,el}-n32--glibc--stable-2025.08-1` from https://toolchains.bootlin.com/ - qemu: `qemu-mipsn32{,el}` via apt. - gdb: `gdb-multiarch` via apt. ## sparc32 - toolchain: `sparcv8--uclibc--stable-2025.08-1` from https://toolchains.bootlin.com/ - qemu: `qemu-sparc` via apt. - gdb: `gdb-multiarch` via apt. ## sparc32plus - toolchain: `gcc-multilib-sparc64-linux-gnu` via apt with `-m32` option. - qemu: `qemu-sparc32plus` via apt. - gdb: `gdb-multiarch` via apt. ## sparc64 - toolchain: `gcc-sparc64-linux-gnu` via apt. - qemu: `qemu-sparc64` via apt. - gdb: `gdb-multiarch` via apt. ## riscv32 - toolchain: `riscv32-ilp32d--glibc--stable-2025.08-1` from https://toolchains.bootlin.com/ - qemu: `qemu-riscv32` via apt. - gdb: `gdb-multiarch` via apt. ## riscv64 - toolchain: `gcc-riscv64-linux-gnu` via apt. - qemu: `qemu-riscv64` via apt. - gdb: `gdb-multiarch` via apt. ## s390x - toolchain: `gcc-s390x-linux-gnu` via apt. - qemu: `qemu-s390x` via apt. - gdb: `gdb-multiarch` via apt. ## sh4 - toolchain: `gcc-sh4-linux-gnu` via apt. - qemu: `qemu-sh4` via apt. - gdb: `gdb-multiarch` via apt. ## m68k - toolchain: `gcc-m68k-linux-gnu` via apt. - qemu: `qemu-m68k` via apt. - gdb: `gdb-multiarch` via apt. ## alpha - toolchain: `gcc-alpha-linux-gnu` via apt. - qemu: `qemu-alpha` via apt. - gdb: `gdb-multiarch` via apt. ## hppa32 (PA-RISC) - toolchain: `gcc-hppa-linux-gnu` via apt. - qemu: `qemu-hppa` via apt. - gdb: `gdb-multiarch` via apt. ## or1k (OpenRISC 1000) - toolchain: `openrisc--glibc--stable-2025.08-1` from https://toolchains.bootlin.com/ - qemu: `qemu-or1k` via apt. - gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## nios2 - toolchain: `nios2--glibc--bleeding-edge-2024.05-1` from https://toolchains.bootlin.com/ - qemu: `qemu-nios2` via apt. - qemu (Ubuntu 25.04): not found. - gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## microblaze - toolchain: `microblazebe--glibc--stable-2025.08-1` from https://toolchains.bootlin.com/ - qemu: `qemu-microblaze` via apt. - gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## xtensa (lx60) - toolchain: `xtensa-lx60--uclibc--stable-2025.08-1` from https://toolchains.bootlin.com/ - Because the toolchain obtained with apt seems to be broken since the C header is unavailable. - qemu: `qemu-xtensa` via apt. - gdb: `xtensa-lx60--uclibc--bleeding-edge-2023.08-1` from https://toolchains.bootlin.com/ - Because `gdb` built from latest source will not work with `set architecture xtensa`. - But old toolchain (not 2025, but 2023) includes `xtensa-linux-gdb` that works fine. ## cris - lib: [cris-dist_1.64-1_i386.deb](https://www.axis.com/ftp/pub/axis/tools/cris/compiler-kit/cris-dist_1.64-1_i386.deb) - toolchain: [x86_64-gcc-7.3.0-nolibc_cris-linux.tar.xz](https://ftp.iij.ad.jp/pub/linux/kernel/tools/crosstool/files/bin/x86_64/7.3.0/x86_64-gcc-7.3.0-nolibc_cris-linux.tar.xz) from http://ftp.iij.ad.jp/pub/linux/kernel/tools/crosstool/ - `mkdir cris-gcc-7.3.0-glibc && tar xf x86_64-gcc-7.3.0-nolibc_cris-linux.tar.xz -C cris-gcc-7.3.0-glibc --strip-components 1` - `cd cris-gcc-7.3.0-glibc && mkdir rootfs && dpkg-deb -x ../cris-dist_1.64-1_i386.deb rootfs` - `export CRIS=$(pwd)/rootfs/usr/local/cris/cris-axis-linux-gnu` - `cd /lib/x86_64-linux-gnu && ln -s libisl.so.{23,15} && ln -s libmpfr.so.{6,4}` - `cris-linux-gcc -B $CRIS/lib -I $CRIS/sys-include -static ./test.c` - qemu: `qemu-cris` via apt. - It needs `-cpu` option like `qemu-cris -cpu crisv17 -g 1234 ./a.out`. - Could not use `-cpu crisv32` because gdb does not support it. - gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## loongarch64 - toolchain: [x86_64-cross-t ools-loongarch64-binutils_2.45-gcc_15.1.0-glibc_2.42.tar.xz](https://github.com/loongson/build-tools/releases/download/2025.08.08/x86_64-cross-tools-loongarch64-binutils_2.45-gcc_15.1.0-glibc_2.42.tar.xz) - qemu: `qemu-loongarch64` via apt. - gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## arc32 (HS38; ARCv2) - toolchain: `gcc-arc-linux-gnu` via apt. - qemu: https://github.com/foss-for-synopsys-dwc-arc-processors/qemu - Add `#ifndef` and `#endif` like following to `linux-user/syscall.c`. - ``` 341 #ifndef SCHED_ATTR_SIZE_VER0 342 struct sched_attr { 343 uint32_t size; 344 uint32_t sched_policy; 345 uint64_t sched_flags; 346 int32_t sched_nice; 347 uint32_t sched_priority; 348 uint64_t sched_runtime; 349 uint64_t sched_deadline; 350 uint64_t sched_period; 351 uint32_t sched_util_min; 352 uint32_t sched_util_max; 353 }; 354 #endif ``` - `./configure --target-list=arc-linux-user --disable-werror && make && cp build/qemu-arc /usr/local/bin` - It needs `-cpu` option like `qemu-arc -cpu hs5x -g 1234 ./a.out`. - gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## arc32 (HS58; ARCv3) - toolchain: [arc_gnu_2025.09_prebuilt_arc32_glibc_linux_install.tar.bz2](https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases/download/arc-2025.09-release/arc_gnu_2025.09_prebuilt_arc32_glibc_linux_install.tar.xz) - `gdb` is included, but it does not support `python3`. - qemu: https://github.com/foss-for-synopsys-dwc-arc-processors/qemu - Add `#ifndef` and `#endif` like following to `linux-user/syscall.c`. - ``` 341 #ifndef SCHED_ATTR_SIZE_VER0 342 struct sched_attr { 343 uint32_t size; 344 uint32_t sched_policy; 345 uint64_t sched_flags; 346 int32_t sched_nice; 347 uint32_t sched_priority; 348 uint64_t sched_runtime; 349 uint64_t sched_deadline; 350 uint64_t sched_period; 351 uint32_t sched_util_min; 352 uint32_t sched_util_max; 353 }; 354 #endif ``` - `./configure --target-list=arc-linux-user --disable-werror && make && cp build/qemu-arc /usr/local/bin` - It needs `-cpu` option like `qemu-arc -cpu hs5x -g 1234 ./a.out`. - gdb: https://github.com/foss-for-synopsys-dwc-arc-processors/binutils-gdb - `./configure --disable-{binutils,ld,gold,gas,sim,gprof,gprofng} --target=arc64-snps-linux-gnu --with-python=/usr/bin/python3 && make && cp gdb/gdb /usr/local/bin/gdb-arc` ## arc64 (HS68; ARCv3) - toolchain: [arc_gnu_2025.09_prebuilt_arc64_glibc_linux_install.tar.bz2](https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases/download/arc-2025.09-release/arc_gnu_2025.09_prebuilt_arc64_glibc_linux_install.tar.xz) - `gdb` is included, but it does not support `python3`. - qemu: https://github.com/foss-for-synopsys-dwc-arc-processors/qemu - Add `#ifndef` and `#endif` like following to `linux-user/syscall.c`. - ``` 341 #ifndef SCHED_ATTR_SIZE_VER0 342 struct sched_attr { 343 uint32_t size; 344 uint32_t sched_policy; 345 uint64_t sched_flags; 346 int32_t sched_nice; 347 uint32_t sched_priority; 348 uint64_t sched_runtime; 349 uint64_t sched_deadline; 350 uint64_t sched_period; 351 uint32_t sched_util_min; 352 uint32_t sched_util_max; 353 }; 354 #endif ``` - `./configure --target-list=arc64-linux-user --disable-werror && make && cp build/qemu-arc64 /usr/local/bin` - It needs `-cpu` option like `qemu-arc64 -cpu hs6x -g 1234 ./a.out`. - gdb: https://github.com/foss-for-synopsys-dwc-arc-processors/binutils-gdb - `./configure --disable-{binutils,ld,gold,gas,sim,gprof,gprofng} --target=arc64-snps-linux-gnu --with-python=/usr/bin/python3 && make && cp gdb/gdb /usr/local/bin/gdb-arc` ## csky - toolchain: https://github.com/c-sky/toolchain-build - `apt -y install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev` - `git submodule update --init && python3 build-csky-gcc.py csky-gcc --src ./ --triple csky-unknown-linux-gnu --disable-gdb` - `build-gcc-csky-unknown-linux-gnu/Xuantie-800-gcc-linux-5.10.4-glibc-x86_64` is result toolchains. - qemu: https://github.com/XUANTIE-RV/qemu - Add `#ifndef` and `#endif` like following to `linux-user/syscall.c`. - ``` 364 #ifndef SCHED_ATTR_SIZE_VER0 365 struct sched_attr { 366 uint32_t size; 367 uint32_t sched_policy; 368 uint64_t sched_flags; 369 int32_t sched_nice; 370 uint32_t sched_priority; 371 uint64_t sched_runtime; 372 uint64_t sched_deadline; 373 uint64_t sched_period; 374 uint32_t sched_util_min; 375 uint32_t sched_util_max; 376 }; 377 #endif ``` - `./configure --target-list=cskyv1-linux-user,cskyv1eb-linux-user,cskyv2-linux-user,cskyv2eb-linux-user --disable-{bpf,werror} && make && cp build/qemu-cskyv{1,2}{,eb} /usr/local/bin` - It needs `-cpu` option like `qemu-cskyv2 -cpu ck810 -g 1234 ./a.out`. - gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). # Qemu-user UNSUPPORTED architectures These will be added if I find a combination that works. If you find it, please let me know in the issue page. ## bfin: - [x] toolchain: `bfin--uclibc--bleeding-edge-2018.02-1` from https://toolchains.bootlin.com/ - [ ] qemu: https://github.com/vapier/qemu - gdb stub is broken. - [x] gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## hexagon: - [x] toolchain: https://github.com/quic/toolchain_for_hexagon - [x] qemu: `qemu-hexagon` via apt. - [ ] gdb: not found. ## tilegx: - [x] toolchain: https://ftp.riken.jp/Linux/kernel.org/tools/crosstool/files/bin/x86_64/7.3.0/ - [x] lib: http://www.voidrouter.net/archives/211 - [ ] qemu: https://github.com/qemu/qemu/releases/tag/v5.2.0 - the breakpoint is broken. - [x] gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## s390 - [x] toolchain: `gcc-multilib-s390x-linux-gnu` via apt with `-m31` option. - [ ] toolchain (Ubuntu 25.04): not found. - [ ] qemu: not found. - [x] gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## hppa64 - [x] toolchain: `gcc-hppa64-linux` via apt. - [ ] lib: not found. - [ ] qemu: not found. - [ ] gdb: not found. ## loongarch32 - [x] toolchain: [loongson-gnu-toolchain-8.3-x86_64-loongarch32r-linux-gnusf-v2.0.tar.xz](https://gitee.com/loongson-edu/la32r-toolchains/releases/download/v0.0.3/loongson-gnu-toolchain-8.3-x86_64-loongarch32r-linux-gnusf-v2.0.tar.xz) - [ ] qemu: https://gitee.com/loongson-edu/la32r-QEMU - `./configure --target-list=loongarch32-linux-user --disable-werror && make && cp build/qemu-loongarch32 /usr/local/bin` - It says `Architecture rejected` when connecting from gdb. - [x] gdb: build from latest. See [docs/FAQ](FAQ.md#how-to-build-gdb). ## e2k - [ ] toolchain: not found. - [x] qemu: https://github.com/OpenE2K/qemu-e2k - `./configure --target-list=e2k-linux-user --disable-werror && make && cp build/qemu-e2k /usr/local/bin` - [ ] gdb: https://github.com/OpenE2K/binutils-gdb - `git switch gdb-9.1-mcst` - `mkdir build && cd build && ../configure --disable-{binutils,ld,gold,gas,sim,gprof,gprofng,nls,bpf} --target=e2k-linux-gnu --with-python=/usr/bin/python3 && make` - Crash when it executes. It seems that binding python3 failed. ## nds32 - [x] toolchain: https://github.com/VincentZWC/prebuilt-nds32-v3f-toolchain - [ ] qemu: not found. - [ ] gdb: not found. ================================================ FILE: docs/SUPPORTED-MODE.md ================================================ # Supported mode ## Standard debugging - Usage - Run `gdb-multiarch` or `gdb` as `root` user. - e.g., `gdb-multiarch /PATH/TO/BINARY`, then `run [ARGS]`. - Alternatively, `sudo gdb-multiarch /PATH/TO/BINARY`, then `run [ARGS]`. - Supported architectures - Debugger (GDB) host (= Debuggee (ELF) host) - x86 and x64 - Possibly ARM and ARM64 - Notes - The following instructions assume that you are the `root` user. Add `sudo` commands as needed. - The instructions also assume that you use `gdb-multiarch`. Of course, you can use `gdb`. ## Attaching to a running process - Usage - Run `gdb-multiarch /PATH/TO/BINARY -p PID`. - Supported architectures - Debugger (GDB) host (= Debuggee (ELF) host) - x86 and x64 - Possibly ARM and ARM64 ## With Docker - Usage - Attach from outside of Docker using `gdb-multiarch /PATH/TO/BINARY -p PID`. - The `PID` refers to the process ID visible to the host. - Supported architectures - Debugger (GDB) host (= Debuggee (Docker, ELF) host) - x86 and x64 - Possibly ARM and ARM64 - Notes - You can also install and use GEF inside Docker. - However, the `--privileged` option is required when running `docker run` or `docker exec`. ## With Gdbserver - Usage - Start `gdbserver localhost:1234 /PATH/TO/BINARY [ARGS]` to listen on port `0.0.0.0:1234`. - Attach using `gdb-multiarch -ex 'target remote :1234'`. - Supported architectures - Debugger (GDB) host - x86 and x64 - Possibly ARM and ARM64 - Debugger stub (Gdbserver) host (= Debuggee (ELF) host) - x86 and x64 - Possibly ARM and ARM64 ## With Qemu-system - Usage - Start `qemu-system` with the `-s` option to listen on `localhost:1234`. - If you want to change the listening port, use the `-gdb tcp::9876` option. - Attach using `gdb-multiarch -ex 'target remote localhost:1234'`. - Alternatively, use `gdb-multiarch -ex 'set architecture TARGET_ARCH' -ex 'target remote localhost:1234'` (for old versions of QEMU). - Supported architectures - Debugger (GDB) host - x64 - Debugger stub (Qemu-system) host - x64 - Debuggee (Qemu-system guest) - x86, x64, ARM and ARM64 - i8086 (16-bit) is supported experimentally. - Notes - Most commands should work fine unless `CONFIG_RANDSTRUCT=y`. - It works with any version of `qemu-system`, but the latest version is recommended. - It is preferable to run `qemu-system` on `localhost`. - If you run `qemu-system` remotely (another host), you can not handle SecureWorld's memory. - For more information, see [docs/FAQ.md](FAQ.md). ## With Qemu-user - Usage - Start `qemu-user` with the `-g 1234` option to listen on `localhost:1234`. - Attach using `gdb-multiarch /PATH/TO/BINARY -ex 'target remote localhost:1234'`. - Alternatively, use `gdb-multiarch -ex 'set architecture TARGET_ARCH' -ex 'target remote localhost:1234'` (for old versions of QEMU). - Supported architectures - Debugger (GDB) host - x64 - Debugger stub (Qemu-user) host - x64 - Debuggee (ELF) - See [docs/QEMU-USER-SUPPORTED-ARCH.md](QEMU-USER-SUPPORTED-ARCH.md) for details. - Notes - It works with any version of `qemu-user`, but the latest version is recommended. - From QEMU 8.1 onwards, the `info proc mappings` command is supported in `qemu-user`, which significantly speeds up memory map generation. - However, in some architectures (e.g., `x86_64`), this may not be possible, and it will fall back to heuristic detection. - It is preferable to run `qemu-user` on `localhost`. - If you run `qemu-user` remotely (another host), you can not use memory patching. ## With Intel Pin - Usage - Listen using `pin -appdebug -appdebug_server_port 1234 -t obj-intel64/inscount0.so -- /PATH/TO/BINARY`. - Attach using `gdb-multiarch /PATH/TO/BINARY -ex 'target remote localhost:1234'`. - Supported architectures - Debugger (GDB) host - x64 - Debugger stub (Intel Pin) host - x64 - Debuggee (ELF) - x86 and x64 - Note - This runs very slowly and is not recommended. ## With Intel SDE - Usage - Listen using `sde64 -debug -debug-port 1234 -- /PATH/TO/BINARY`. - Attach using `gdb-multiarch /PATH/TO/BINARY -ex 'target remote localhost:1234'`. - Supported architectures - Debugger (GDB) host - x64 - Debugger stub (Intel SDE) host - x64 - Debuggee (ELF) - x86 and x64 - Note - This runs very slowly and is not recommended. ## With Qiling framework - Usage - Run `qltool run -f /PATH/TO/BINARY --rootfs / --gdb :1234`. - Alternatively, write a harness. See [here](https://docs.qiling.io/en/latest/debugger/) for more information. - If the target architecture differs from the host architecture, specify the appropriate `rootfs` directory. - Attach using `gdb-multiarch /PATH/TO/BINARY -ex 'target remote localhost:1234'`. - Supported architectures - Debugger (GDB) host - x64 - Debugger stub (Qiling framework) host - x64 - Debuggee (ELF) - x86, x64, ARM and ARM64 - Notes - When debugging ARM64 binaries, the flag register is not available, so branch taken/not taken detection may be incorrect. - This is experimental support, so some commands may not work. ## With KGDB - Usage - Host OS - Configure two serial ports as named pipes in both the debugger and debuggee virtual machine settings. - Vmware example: - Debugger - Use named pipe: `\\.\pipe\pipe0` (Windows host) / `/tmp/sock0` (Linux host) - Configure as `This end is the client.` and `The other end is a virtual machine.` - Use named pipe: `\\.\pipe\pipe1` (Windows host) / `/tmp/sock1` (Linux host) - configure as `This end is the client.` and `The other end is a virtual machine.` - Debuggee - Use named pipe: `\\.\pipe\pipe0` (Windows host) / `/tmp/sock0` (Linux Host) - Configure as `This end is the server.` and `The other end is an application.` - Use named pipe: `\\.\pipe\pipe1` (Windows host) / `/tmp/sock1` (Linux host) - Configure as `This end is the server.` and `The other end is an application.` - Debuggee - Build the kernel with configurations such as `CONFIG_KGDB=y`. Ubuntu supports this by default. - Edit `/etc/default/grub` and append `kgdbwait kgdboc=ttyS0,115200 console=ttyS1,115200 nokaslr` to the end of `GRUB_CMDLINE_LINUX_DEFAULT`. - Then run `update-grub && reboot`. - See [official documentation](https://www.kernel.org/doc/html/latest/dev-tools/kgdb.html) for more information. - Debugger - Attach using `gdb-multiarch -ex 'target remote /dev/ttyS0'`. - Connect with `screen /dev/ttyS1 115200` for console access. - Supported architectures - Debugger (GDB) host - x64 - Debuggee (debugged kernel) - x64, ARM64 - Notes - You need `gdb` version 12.x or later. - This runs very slowly over a serial interface; without a vmlinux with symbols, debugging is effectively impractical. - The `Ctrl+C` interrupt does not work; instead, use `echo g > /proc/sysrq-trigger` in the console. - Unlike qemu-system, some commands are unsupported in KGDB mode because it is still under development. ## With VMware - Usage - Host OS - Add the following configurations to the `vmx` file. - `debugStub.listen.guest64 = "TRUE"` - `debugStub.listen.guest64.remote = "TRUE"` - `debugStub.hideBreakpoints = "TRUE"` - `debugStub.port.guest64 = "1234"` - See [here](https://communities.vmware.com/t5/VMware-Fusion-Discussions/Using-debugStub-to-debug-a-guest-linux-kernel/td-p/394906). - Start the guest OS normally. - Debugger - Attach using `gdb-multiarch -ex 'target remote :1234'`. - Supported architectures - Debugger (GDB) host - x64 - Debuggee (debugged kernel) - x64 - Notes - It runs faster than KGDB mode, and `Ctrl+C` interrupt works, but it is still slow. - Access to physical memory and control registers is possible thanks to the `monitor` command. ## With rr - Usage - First, run `rr record /PATH/TO/BINARY`. - Then, use `rr replay` for time-travel debugging. - Supported architectures - Debugger (`rr`) host (= Debuggee (ELF) host) - x86 and x64 - Note - This is experimental support, so some commands may not work. ## With Wine - Usage - Run `winedbg --gdb --no-start /PATH/TO/BINARY` and attach using `gdb -ex 'target remote localhost:'`. - It is recommended to use the `--no-start` option because pressing `Ctrl+C` without `--no-start` will terminate `gdb`. - Supported architectures - Debugger (GDB) host - x64 - Debugger stub (`winedbg`) host - x64 - Debuggee (PE) - x86 and x64 - Notes - You must run `winedbg` on `localhost`. - This is experimental support, so some commands may not work. ## With Qemu-system for Android kernel (when using Android Studio) - Usage - Start as `emulator -avd -no-audio -no-snapshot -qemu -s` to listen on `localhost:1234`. - If `-no-audio -no-snapshot` are not necessary, you can remove them. - Available `` values can be obtained with `emulator -list-avds`. - Attach using `gdb-multiarch -ex 'set architecture i386:x86-64' -ex 'target remote localhost:1234'`. - Supported architectures - Debugger (GDB) host - x64 - Debugger stub (Qemu-system) host - x64 - Debuggee (Qemu-system guest) - x64 - Possibly x86, ARM and ARM64 - Notes - This method sometimes fails. - For more information, see [docs/FAQ.md](FAQ.md). - If an issue occurs, try closing `gdb` and reconnecting. - To connect from another machine, specify the IP address. - `gdb-multiarch -ex 'set architecture i386:x86-64' -ex 'target remote :1234'`. ## With Android userland binary (when using Android Studio) - Usage - Setup `adb` settings. - Run `adb root` if necessary. - Use `adb forward tcp:9999 tcp:9999` for port forwarding. - Push the statically built `gdbserver` to the Android device. - `adb push gdbserver-static /data/local/tmp`. - `adb shell chmod +x /data/local/tmp/gdbserver-static`. - Start `gdbserver`. - `adb shell /data/local/tmp/gdbserver-static localhost:9999 /PATH/TO/BINARY`. - Attach using `gdb-multiarch 'target remote localhost:9999'`. - Supported architectures - Debugger (GDB) host - x64 - Debugger stub (Gdbserver) host (= Debuggee (ELF) host) - x64 - Possibly x86, ARM and ARM64 - Notes - To connect from another machine, you can forward the port further. - Run `socat TCP-LISTEN:9998,fork,reuseaddr TCP-CONNECT:localhost:9999` on the `Android Studio` host machine. - Attach using `gdb-multiarch 'target remote :9998'` from another machine. ================================================ FILE: gef.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # ####################################################################################### # GEF - Multi-Architecture GDB Enhanced Features for Exploiters & Reverse-Engineers # * Original: by @_hugsy_ # * Improvement: by @bata_24 # ####################################################################################### # This GEF solely supports GDB compiled with Python3 and running on Linux. # # Supported architectures are the following (some require qemu). # * x86-32 & x86-64 # * arm v5,v6,v7 # * aarch64 (armv8) # * mips & mipsn32 & mips64 # * powerpc & powerpc64 # * sparc & sparc32plus & sparc64 # * riscv32 & riscv64 # * s390x # * sh4 # * m68k # * alpha # * hppa32 (pa-risc) # * or1k (openrisc) # * nios2 # * microblaze # * xtensa # * cris # * loongarch64 # * arc32 (v2, v3) & arc64 # * csky # See README.md for details. # # To start: in gdb, type `source /path/to/gef.py` # ####################################################################################### # GEF is distributed under the MIT License. # # Copyright (c) 2021-2026 bata24 (@bata_24) # # This is a fork of GEF (https://github.com/hugsy/gef). # This software is released under the MIT license. # See https://opensource.org/licenses/MIT # # Copyright (c) 2013-2026 crazy rabbidz # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # ####################################################################################### # Use this command when checking with `ruff`: # $ ruff check gef.py --select B,C4,E,F --ignore B905,E402,E501,E731 # # B905: `zip()` without an explicit `strict=` parameter # -> The strict argument of zip() is from python3.10. Too new to apply. # E402: module level import not at top of file # -> For faster startup, less frequently used modules should be loaded on demand. # E501: line too long (> 79 characters) # -> This rule does not seem to make sense in a modern environment. # E731: do not assign a lambda expression, use a def # -> It can be written more cleanly using lambdas. # ####################################################################################### # Use this command when checking with `vulture`: # $ vulture gef.py --ignore-names="*Command" # ####################################################################################### # Use this command when checking with `codespell`: # $ codespell gef.py # ####################################################################################### # Use this command to measure the time: # $ PYTHONPROFILEIMPORTTIME=1 gdb # print("Loading GEF...") import abc import argparse import builtins import binascii import codecs import collections import configparser import ctypes import datetime import functools import hashlib import itertools import json import os import re import struct import subprocess import sys import tempfile import time import traceback def http_get(url): """Basic HTTP wrapper for GET request.""" import urllib.request try: req = urllib.request.Request(url) req.add_header("Cache-Control", "no-cache, no-store") http = urllib.request.urlopen(req, timeout=5) if http.getcode() != 200: return None return http.read() except Exception: return None def update_gef(argv): """Try to update `gef` to the latest version.""" gef_local = os.path.realpath(argv[0]) hash_gef_local = hashlib.sha512(open(gef_local, "rb").read()).digest() gef_remote = "https://raw.githubusercontent.com/bata24/gef/dev/gef.py" gef_remote_data = http_get(gef_remote) if gef_remote_data is None: print("[-] Failed to get remote gef") return 1 hash_gef_remote = hashlib.sha512(gef_remote_data).digest() if hash_gef_local == hash_gef_remote: print("[-] No update") else: with open(gef_local, "wb") as f: f.write(gef_remote_data) print("[+] Updated") return 0 try: # GEF determines whether it is inside or outside gdb # based on whether it can import the gdb module. import gdb except ImportError: # If out of gdb, the only action allowed is to update gef.py. # No further code will be executed. if len(sys.argv) == 2 and sys.argv[1].lower() in ("--update", "--upgrade", "-u"): ret = update_gef(sys.argv) sys.exit(ret) else: print("[-] gef cannot run as standalone") sys.exit(0) # Very important global variables. __gef_commands__ = [] # gef command classes for registering __gef_command_instances__ = {} # gef command instances GCI = __gef_command_instances__ # short cut for debug # noqa: F841 __gef_alias_instances__ = {} # gef alias instances GAI = __gef_alias_instances__ # short cut for debug # noqa: F841 current_arch = None # keep Architecture instance GEF_RC = os.getenv("GEF_RC") or os.path.join(os.getenv("HOME") or "~", ".gef.rc") GEF_TEMP_DIR = os.path.join(tempfile.gettempdir(), "gef") GEF_FILEPATH = os.path.expanduser(http_get.__code__.co_filename) # the full path of GEF # note: __file__ will no longer be available from gdb 15 def get_current_arch(): # noqa global current_arch return current_arch def perf(f): # noqa """Decorator wrapper to measure performance.""" @functools.wraps(f) def wrapper(*args, **kwargs): import io import line_profiler pr = line_profiler.LineProfiler() pr.add_function(f) pr.enable() ret = f(*args, **kwargs) pr.disable() s = io.StringIO() pr.print_stats(stream=s) print(s.getvalue()) return ret return wrapper def cperf(f): # noqa """Decorator wrapper to measure performance.""" @functools.wraps(f) def wrapper(*args, **kwargs): import cProfile import pstats import io pr = cProfile.Profile() pr.enable() ret = f(*args, **kwargs) pr.disable() s = io.StringIO() #sortby = pstats.SortKey.CUMULATIVE sortby = pstats.SortKey.TIME ps = pstats.Stats(pr, stream=s).sort_stats(sortby) ps.print_stats(20) print(s.getvalue()) return ret return wrapper class DisplayHook: """It enables pretty printing of list, dict, set, and so on. It also displays in hexadecimal by default.""" @staticmethod def pp(o, idt): """Create a string for pretty print recursively.""" def I1(idt): """Create an indent for current level.""" return " " * idt def I2(idt): """Create an indent for next level.""" return " " * (idt + 1) def R(o, idt): return [DisplayHook.pp(x, idt + 1) for x in o] def R1(o, idt): """Return a string of the elements concatenated with commas (for list, tuple, set, ...).""" return ", ".join(R(o, idt)) def R2(o, idt): """Return a list of the elements with indentation and commas (for list, tuple, set, ...).""" return [I2(idt) + x + "," for x in R(o, idt)] def Z(s, e, o, idt): """Return a string of the elements concatenated with commas (for list, tuple, set, ...), taking into account the width of the screen.""" # Create a string without newlines and return it if it's short enough. f = s + R1(o, idt) + e if len(f) < width: return f # Return a string with a newline for each element. f = [s] + R2(o, idt) + [I1(idt) + e] return "\n".join(f) def RD(o, idt): return [DisplayHook.pp(k, idt + 1) + ": " + DisplayHook.pp(v, idt + 1) for k, v in o] def RD1(o, idt): """Return a string of the elements concatenated with commas (for dict, ...).""" return ", ".join(RD(o, idt)) def RD2(o, idt): """Return a list of the elements with indentation and commas (for dict, ...).""" return [I2(idt) + x + "," for x in RD(o, idt)] def ZD(s, e, o, idt): """Return a string of the elements concatenated with commas (for dict, ...), taking into account the width of the screen.""" # Create a string without newlines and return it if it's short enough. f = s + RD1(o, idt) + e if len(f) < width: return f # Return a string with a newline for each element. f = [s] + RD2(o, idt) + [I1(idt) + e] return "\n".join(f) name = type(o).__name__ width = GefUtil.get_terminal_size()[1] + len(I1(idt)) if name in ("int", "long"): return hex(o) if name == "list": return Z("[", "]", o, idt) elif name == "tuple": return Z("(", ")", o, idt) elif name == "set": return Z("{", "}", o, idt) elif name == "dict": return ZD("{", "}", o.items(), idt) elif name == "dict_keys": return Z("dict_keys([", "])", o, idt) elif name == "dict_values": return Z("dict_values([", "])", o, idt) elif name == "dict_items": return ZD("dict_items([", "])", o, idt) elif name == "Zone": return re.sub(r"(zone_start=|zone_end=)(\d+)", lambda x:x.group(1) + hex(int(x.group(2))), str(o)) elif name == "Table": f = "Table(arch={!r}, mode={!r}, name_table={{...}}, nr_table={{\n".format(o.arch, o.mode) f += "\n".join(RD2(o.nr_table.items(), idt)) f += "\n" + I1(idt) + "})" return f elif name == "Kinfo": f = "Kinfo(\n" f += I1(idt + 1) + "text_base=" + DisplayHook.pp(o.text_base, 0) + ", " f += "text_size=" + DisplayHook.pp(o.text_size, 0) + ", " f += "text_end=" + DisplayHook.pp(o.text_end, 0) + ",\n" f += I1(idt + 1) + "ro_base=" + DisplayHook.pp(o.ro_base, 0) + ", " f += "ro_size=" + DisplayHook.pp(o.ro_size, 0) + ", " f += "ro_end=" + DisplayHook.pp(o.ro_end, 0) + ",\n" f += I1(idt + 1) + "rw_base=" + DisplayHook.pp(o.rw_base, 0) + ", " f += "rw_size=" + DisplayHook.pp(o.rw_size, 0) + ", " f += "rw_end=" + DisplayHook.pp(o.rw_end, 0) + ",\n" f += I1(idt + 1) + "rwx=" + DisplayHook.pp(o.rwx, 0) + ", " f += "has_none=" + DisplayHook.pp(o.has_none, 0) + ", " if o.maps is None: f += "maps=" + DisplayHook.pp(o.maps, 0) + ",\n" else: f += "maps=[\n" f += "\n".join(R2(o.maps, idt + 1)) f += "\n" + I1(idt + 1) + "]\n" f += I1(idt) + ")" return f elif name == "Entry": f = "Entry(\n" f += I2(idt) + "nr={:#x},\n".format(o.nr) f += I2(idt) + "name={!r},\n".format(o.name) f += I2(idt) + "ret_regs={!s},\n".format(o.ret_regs) f += I2(idt) + "arg_regs={!s},\n".format(o.arg_regs) f += I2(idt) + "args_full={!s},\n".format(o.args_full) f += I2(idt) + "args={!s},\n".format(o.args) f += I1(idt) + ")" return f return repr(o) @staticmethod def displayhook(o): # noqa """An alternative to the default display function.""" builtins._ = o # noqa if o is None: return out = DisplayHook.pp(o, 0) print(out) return def hexon(): # noqa """Replace the print function that is implicitly called when running "python-interactive 1" etc.""" sys.displayhook = DisplayHook.displayhook # noqa return def hexoff(): # noqa """Revert the print function that is implicitly called when running "python-interactive 1" etc.""" sys.displayhook = sys.__displayhook__ # noqa return class Cache: """Manage the gef cache. The cache has 2 types: "until_next" and "this_session". "until_next": Cached for a very short period of time. Cleared every time an instruction is stepped, etc. "this_session": Cached until gdb exits. Note: each command may have its own cache outside this mechanism. Not all caches are centralized here.""" __gef_caches__ = {"until_next": {}, "this_session": {}} @staticmethod def cache_wrap(life_time, f, skip_None_cache=False): @functools.wraps(f) def wrapper(*args, **kwargs): caches = Cache.__gef_caches__[life_time] fname = f"{f.__module__}:{f.__qualname__}" fcache = caches.setdefault(fname, {}) try: kw = tuple(sorted(kwargs.items())) key = (args, kw) return fcache[key] except KeyError: ret = f(*args, **kwargs) if skip_None_cache is False or ret is not None: fcache[key] = ret return ret except TypeError: return f(*args, **kwargs) return wrapper @staticmethod def cache_until_next(f): return Cache.cache_wrap("until_next", f) @staticmethod # noqa def cache_until_next_skip_None_cache(f): return Cache.cache_wrap("until_next", f, skip_None_cache=True) @staticmethod def cache_this_session(f): return Cache.cache_wrap("this_session", f) @staticmethod def cache_this_session_skip_None_cache(f): return Cache.cache_wrap("this_session", f, skip_None_cache=True) @staticmethod def reset_gef_caches(all=False): """Clear the cache of GEF. By default, it only clears caches of `until_next` type.""" Cache.__gef_caches__["until_next"].clear() if all: Cache.__gef_caches__["this_session"].clear() # gdb cache try: gdb.execute("maintenance flush dcache", to_string=True) except Exception: pass return @staticmethod # noqa def clear_cache_for(f): """Clear the cache of specified function.""" fname = f"{f.__module__}:{f.__qualname__}" Cache.__gef_caches__["until_next"].pop(fname, None) Cache.__gef_caches__["this_session"].pop(fname, None) return class Config: """Manage gef configurations. Most configs are tied to specific commands. They are defined in the form `command_name.config_name`. Internally it is stored as a triple (value, type, description).""" __gef_config__ = {} # keep gef configs __gef_config_orig__ = {} # for debugging @staticmethod @Cache.cache_until_next def get_gef_setting(name): """Read global gef settings. Return None if not found.""" setting = Config.__gef_config__.get(name, None) if setting is None: return None # A valid config can never return None, but False, 0 or "" return setting[0] @staticmethod def set_gef_setting(name, value, _type=None, _desc=None): """Set global gef settings. Raise ValueError if `name` doesn't exist and `type` and `desc` are not provided.""" Cache.reset_gef_caches() if name not in Config.__gef_config__: # create new setting if _type is None or _desc is None: raise ValueError("Setting '{}' is undefined, need to provide type and description".format(name)) Config.__gef_config__[name] = [_type(value), _type, _desc] return # set existing setting func = Config.__gef_config__[name][1] Config.__gef_config__[name][0] = func(value) return def gef_print(x="", less=False, redirect=None, skip_color=False, *args, **kwargs): """Wrapper around print(), with pager, redirecting and coloring.""" if not skip_color: x = HighlightCommand.highlight_text(x) if redirect: with open(redirect, "w") as f: print(x, *args, **kwargs, file=f) return if less: try: less = GefUtil.which("less") except FileNotFoundError: less = False if not less: # if x is blank, print a blank line. print(x, *args, **kwargs) return # use pager but x is blank if not x: return always_no_pager = Config.get_gef_setting("gef.always_no_pager") if always_no_pager: print(x, *args, **kwargs) return pager_min_lines = Config.get_gef_setting("gef.pager_min_lines") if len(x.splitlines()) < pager_min_lines: print(x, *args, **kwargs) return # write to file and less if isinstance(x, bytes): tmp_fd, tmp_path = GefUtil.mkstemp(prefix="gef_print", suffix=".txt") os.fdopen(tmp_fd, "wb").write(x) else: try: # this is faster than converting to bytes and then writing. tmp_fd, tmp_path = GefUtil.mkstemp(prefix="gef_print", suffix=".txt") os.fdopen(tmp_fd, "w").write(x) except Exception: # fallback if a write error occurs # tmp_fd is closed at this point, so delete it and reopen it os.unlink(tmp_path) tmp_fd, tmp_path = GefUtil.mkstemp(prefix="gef_print", suffix=".txt") os.fdopen(tmp_fd, "wb").write(String.str2bytes(x)) # less less_option = Config.get_gef_setting("gef.less_option") os.system("{!r} {:s} {!r}".format(less, less_option, tmp_path)) # cleanup keep_pager_result = Config.get_gef_setting("gef.keep_pager_result") if keep_pager_result: print("result saved at {:s}".format(tmp_path)) else: os.unlink(tmp_path) return class Color: """Used to colorify terminal output.""" # https://en.wikipedia.org/wiki/ANSI_escape_code colors = { "normal" : "\033[0m", "bold" : "\033[1m", "bold_off" : "\033[21m", "highlight" : "\033[2m", "highlight_off" : "\033[22m", "italic" : "\033[3m", "italic_off" : "\033[23m", "underline" : "\033[4m", "underline_off" : "\033[24m", "blink" : "\033[5m", "blink_off" : "\033[25m", "black" : "\033[30m", "red" : "\033[31m", "green" : "\033[32m", "yellow" : "\033[33m", "blue" : "\033[34m", "magenta" : "\033[35m", "cyan" : "\033[36m", "bright_black" : "\033[90m", "bright_red" : "\033[91m", "bright_green" : "\033[92m", "bright_yellow" : "\033[93m", "bright_blue" : "\033[94m", "bright_magenta" : "\033[95m", "bright_cyan" : "\033[96m", "bright_white" : "\033[97m", "coral_red" : "\033[38;2;239;133;125m", "sunshine_yellow" : "\033[38;2;255;237;171m", "ice_green" : "\033[38;2;163;214;204m", "wistaria" : "\033[38;2;141;147;200m", "pink_almond" : "\033[38;2;227;172;174m", "poppy_red" : "\033[38;2;234;85;80m", "cream_yellow" : "\033[38;2;255;243;184m", "turquoise_green" : "\033[38;2;0;148;122m", "blue_lavender" : "\033[38;2;164;168;212m", "rose_dust" : "\033[38;2;230;192;192m", "_red" : "\033[38;2;234;85;80m", "naples_yellow" : "\033[38;2;253;211;92m", "sea_green" : "\033[38;2;0;172;151m", "pannsy" : "\033[38;2;77;67;152m", "white" : "\033[38;2;255;255;255m", "tomato_red" : "\033[38;2;234;85;73m", "topaz" : "\033[38;2;233;188;0m", "peppermint_green" : "\033[38;2;0;172;154m", "violet" : "\033[38;2;90;68;152m", "snow_white" : "\033[38;2;250;253;255m", "vermilion" : "\033[38;2;234;85;58m", "chrome_yellow" : "\033[38;2;252;200;0m", "peacock_green" : "\033[38;2;0;164;151m", "heliotrope" : "\033[38;2;144;121;182m", "pink_white" : "\033[38;2;254;249;251m", "scarlet" : "\033[38;2;234;85;50m", "cream" : "\033[38;2;227;215;163m", "nile_blue" : "\033[38;2;44;180;173m", "deep_royal_purple" : "\033[38;2;71;38;110m", "milky_white" : "\033[38;2;255;255;249m", "carrot_orange" : "\033[38;2;237;109;53m", "straw" : "\033[38;2;236;224;147m", "saxe_blue" : "\033[38;2;65;139;137m", "grape" : "\033[38;2;86;37;110m", "amber_white" : "\033[38;2;255;249;245m", "chinese_red" : "\033[38;2;237;109;70m", "jasmine_yellow" : "\033[38;2;237;222;123m", "slate_green" : "\033[38;2;60;113;112m", "mauve" : "\033[38;2;145;93;163m", "lavender_ice" : "\033[38;2;247;246;251m", "terracotta" : "\033[38;2;189;104;86m", "antique_gold" : "\033[38;2;193;171;5m", "teal_green" : "\033[38;2;0;106;108m", "iris" : "\033[38;2;199;165;204m", "pearl_white" : "\033[38;2;247;246;245m", "cocoa_brown" : "\033[38;2;152;96;94m", "olive" : "\033[38;2;114;100;12m", "aqua_green" : "\033[38;2;136;191;191m", "lilac" : "\033[38;2;209;186;218m", "ivory" : "\033[38;2;248;244;230m", "mahogany" : "\033[38;2;107;63;49m", "olive_drab" : "\033[38;2;102;90;26m", "aquamarine" : "\033[38;2;103;181;183m", "lavender" : "\033[38;2;202;184;217m", "powder_pink" : "\033[38;2;245;236;244m", "chocolate" : "\033[38;2;108;53;36m", "jaune_brillant" : "\033[38;2;255;220;0m", "peacock_blue" : "\033[38;2;0;158;159m", "crocus" : "\033[38;2;183;159;203m", "silver_white" : "\033[38;2;239;239;239m", "marron" : "\033[38;2;106;25;23m", "_yellow" : "\033[38;2;255;220;0m", "turquoise" : "\033[38;2;0;155;159m", "lavender_mauve" : "\033[38;2;166;136;189m", "frosty_gray" : "\033[38;2;232;236;233m", "sepia" : "\033[38;2;98;45;24m", "citrus" : "\033[38;2;237;220;68m", "capri_blue" : "\033[38;2;0;163;167m", "purple" : "\033[38;2;155;114;176m", "silver_pink" : "\033[38;2;238;234;236m", "coffee" : "\033[38;2;123;85;68m", "limelight" : "\033[38;2;255;247;153m", "cambridge_blue" : "\033[38;2;37;183;192m", "royal_purple" : "\033[38;2;127;17;132m", "beige_cameo" : "\033[38;2;238;233;230m", "brown" : "\033[38;2;143;101;82m", "canary_yellow" : "\033[38;2;255;244;98m", "turquoise_blue" : "\033[38;2;0;175;204m", "raisin" : "\033[38;2;107;57;95m", "ecru" : "\033[38;2;238;231;224m", "burnt_sienna" : "\033[38;2;187;85;53m", "mimosa" : "\033[38;2;255;244;98m", "horizon_blue" : "\033[38;2;130;205;221m", "plum" : "\033[38;2;108;36;99m", "pink_beige" : "\033[38;2;237;228;225m", "amber_rose" : "\033[38;2;230;191;178m", "lemon_yellow" : "\033[38;2;255;243;82m", "summer_shower" : "\033[38;2;161;216;226m", "raspberry" : "\033[38;2;132;26;117m", "frosty_white" : "\033[38;2;230;234;230m", "beige_rose" : "\033[38;2;232;211;202m", "melon_yellow" : "\033[38;2;224;222;148m", "_horizon_blue" : "\033[38;2;161;216;230m", "framboise" : "\033[38;2;154;13;124m", "oyster_white" : "\033[38;2;234;232;225m", "salmon_pink" : "\033[38;2;243;166;140m", "chartreuse_yellow" : "\033[38;2;227;229;72m", "cerulean_blue" : "\033[38;2;0;141;183m", "dahlia_purple" : "\033[38;2;165;0;130m", "wisteria_mist" : "\033[38;2;211;214;221m", "sahara" : "\033[38;2;226;150;118m", "lime_yellow" : "\033[38;2;234;238;162m", "duck_blue" : "\033[38;2;0;113;153m", "orchid_purple" : "\033[38;2;175;0;130m", "cloud" : "\033[38;2;212;217;223m", "ash_rose" : "\033[38;2;230;191;171m", "lime_green" : "\033[38;2;230;235;148m", "marine_blue" : "\033[38;2;0;104;136m", "raspberry_red" : "\033[38;2;159;22;106m", "moon_gray" : "\033[38;2;212;217;220m", "shell_pink" : "\033[38;2;251;218;200m", "chartreuse_green" : "\033[38;2;217;227;103m", "madonna_blue" : "\033[38;2;0;96;141m", "orchid" : "\033[38;2;217;170;205m", "china_clay" : "\033[38;2;212;220;211m", "baby_pink" : "\033[38;2;253;237;228m", "lettuce_green" : "\033[38;2;209;222;76m", "egyptian_blue" : "\033[38;2;0;115;168m", "lilla" : "\033[38;2;224;181;211m", "sand_beige" : "\033[38;2;220;214;210m", "nail_pink" : "\033[38;2;252;228;214m", "olive_green" : "\033[38;2;95;101;39m", "baby_blue" : "\033[38;2;187;226;241m", "rose_tendre" : "\033[38;2;230;175;207m", "orchid_mist" : "\033[38;2;211;211;216m", "raw_sienna" : "\033[38;2;225;123;52m", "moss_green" : "\033[38;2;119;126;65m", "sky_blue" : "\033[38;2;160;216;239m", "orchid_pink" : "\033[38;2;218;129;178m", "reed_gray" : "\033[38;2;212;217;214m", "caramel" : "\033[38;2;188;97;30m", "grass_green" : "\033[38;2;123;141;66m", "shadow_blue" : "\033[38;2;113;155;173m", "cyclamen_pink" : "\033[38;2;208;79;151m", "sky_gray" : "\033[38;2;203;208;211m", "sunset" : "\033[38;2;246;180;131m", "spring_green" : "\033[38;2;156;187;28m", "_cyan" : "\033[38;2;0;161;233m", "_magenta" : "\033[38;2;228;0;127m", "lavender_gray" : "\033[38;2;188;186;206m", "cinnamon" : "\033[38;2;190;143;104m", "leaf_green" : "\033[38;2;159;194;77m", "yacht_blue" : "\033[38;2;64;158;204m", "bougainvillaea" : "\033[38;2;230;47;139m", "silver" : "\033[38;2;201;202;202m", "tan" : "\033[38;2;191;120;62m", "white_lily" : "\033[38;2;240;246;218m", "chalk_blue" : "\033[38;2;104;169;207m", "ruby" : "\033[38;2;199;0;103m", "pearl_gray" : "\033[38;2;201;201;196m", "champagne" : "\033[38;2;233;218;203m", "asparagus_green" : "\033[38;2;219;235;196m", "pigeon_blue" : "\033[38;2;136;181;211m", "claret" : "\033[38;2;148;31;87m", "sand_gray" : "\033[38;2;201;201;194m", "peach" : "\033[38;2;251;216;181m", "citron_green" : "\033[38;2;97;142;52m", "smoke_blue" : "\033[38;2;164;193;215m", "azalee" : "\033[38;2;216;52;115m", "marble_gray" : "\033[38;2;192;197;194m", "cafe_au_lait" : "\033[38;2;148;108;69m", "meadow_green" : "\033[38;2;101;171;49m", "frosty_blue" : "\033[38;2;187;219;243m", "cosmos" : "\033[38;2;220;107;154m", "opal_gray" : "\033[38;2;191;190;197m", "orange" : "\033[38;2;238;120;0m", "apple_green" : "\033[38;2;167;210;141m", "bleu_acide" : "\033[38;2;0;110;176m", "lotus_pink" : "\033[38;2;222;130;167m", "french_gray" : "\033[38;2;141;160;182m", "apricot" : "\033[38;2;247;185;119m", "ivy_green" : "\033[38;2;87;138;61m", "cobalt_blue" : "\033[38;2;0;104;183m", "old_orchid" : "\033[38;2;227;173;193m", "mist" : "\033[38;2;180;174;177m", "amber" : "\033[38;2;194;137;75m", "spinach_green" : "\033[38;2;65;112;56m", "sapphire_blue" : "\033[38;2;0;104;183m", "rose_mist" : "\033[38;2;222;190;204m", "ash_blond" : "\033[38;2;181;181;174m", "bronze" : "\033[38;2;172;107;37m", "cactus" : "\033[38;2;56;125;57m", "spectrum_blue" : "\033[38;2;0;117;194m", "rose_dragee" : "\033[38;2;229;193;205m", "fog" : "\033[38;2;171;177;181m", "vanilla" : "\033[38;2;232;197;156m", "sky_green" : "\033[38;2;190;224;194m", "_blue" : "\033[38;2;0;117;194m", "cherry_pink" : "\033[38;2;235;110;160m", "beige_gray" : "\033[38;2;180;173;169m", "cork" : "\033[38;2;196;154;106m", "spearmint" : "\033[38;2;121;192;110m", "zenith_blue" : "\033[38;2;68;150;211m", "opera" : "\033[38;2;233;83;136m", "silver_gray" : "\033[38;2;175;175;176m", "burnt_umber" : "\033[38;2;111;84;54m", "mint_green" : "\033[38;2;137;201;151m", "heavenly_blue" : "\033[38;2;104;164;217m", "rose_red" : "\033[38;2;234;97;142m", "storm_gray" : "\033[38;2;170;170;176m", "raw_umber" : "\033[38;2;134;102;41m", "parrot_green" : "\033[38;2;55;163;74m", "orchid_gray" : "\033[38;2;188;199;215m", "old_lilac" : "\033[38;2;176;119;140m", "green_fog" : "\033[38;2;171;177;173m", "flesh" : "\033[38;2;250;208;158m", "summer_green" : "\033[38;2;0;153;68m", "powder_blue" : "\033[38;2;188;205;219m", "cocoa" : "\033[38;2;110;74;85m", "ash_gray" : "\033[38;2;159;160;158m", "golden_yellow" : "\033[38;2;246;174;84m", "opal_green" : "\033[38;2;190;224;206m", "light_blue" : "\033[38;2;178;203;228m", "wine_red" : "\033[38;2;179;62;92m", "rose_gray" : "\033[38;2;157;142;135m", "mandarin_orange" : "\033[38;2;243;152;29m", "spray_green" : "\033[38;2;164;213;189m", "_baby_blue" : "\033[38;2;162;194;230m", "garnet" : "\033[38;2;148;35;67m", "elephant_skin" : "\033[38;2;159;159;152m", "marigold" : "\033[38;2;243;152;0m", "bottle_green" : "\033[38;2;0;77;37m", "day_dream" : "\033[38;2;163;185;224m", "cochineal_red" : "\033[38;2;200;44;85m", "battleship_gray" : "\033[38;2;137;137;137m", "ecru_beige" : "\033[38;2;246;229;204m", "cobalt_green" : "\033[38;2;60;179;122m", "salvia_blue" : "\033[38;2;148;173;218m", "strawberry" : "\033[38;2;231;53;98m", "stone_gray" : "\033[38;2;137;136;128m", "oyster" : "\033[38;2;234;225;207m", "evergreen" : "\033[38;2;0;152;79m", "hyacinth_blue" : "\033[38;2;122;153;207m", "ruby_red" : "\033[38;2;231;53;98m", "moss_gray" : "\033[38;2;126;131;127m", "ochre" : "\033[38;2;186;139;64m", "malachite_green" : "\033[38;2;0;152;84m", "hyacinth" : "\033[38;2;108;155;210m", "carmine" : "\033[38;2;215;0;53m", "dove_gray" : "\033[38;2;125;123;131m", "khaki" : "\033[38;2;197;160;90m", "_green" : "\033[38;2;0;169;96m", "midnight_blue" : "\033[38;2;0;30;67m", "signal_red" : "\033[38;2;232;56;61m", "gray" : "\033[38;2;125;125;125m", "buff" : "\033[38;2;202;172;113m", "emerald_green" : "\033[38;2;0;169;104m", "navy_blue" : "\033[38;2;32;47;85m", "burgundy" : "\033[38;2;108;39;53m", "steel_gray" : "\033[38;2;115;109;113m", "saffron_yellow" : "\033[38;2;250;197;89m", "forest_green" : "\033[38;2;40;140;102m", "prussian_blue" : "\033[38;2;25;47;96m", "bordeaux" : "\033[38;2;108;39;45m", "ivy_gray" : "\033[38;2;102;108;103m", "pumpkin" : "\033[38;2;229;163;35m", "viridian" : "\033[38;2;0;136;90m", "iron_blue" : "\033[38;2;25;47;96m", "camellia" : "\033[38;2;218;83;110m", "slate_gray" : "\033[38;2;98;96;99m", "yellow_ocher" : "\033[38;2;196;151;47m", "holly_green" : "\033[38;2;0;105;72m", "indigo" : "\033[38;2;4;60;120m", "rose" : "\033[38;2;233;84;100m", "graphite" : "\033[38;2;89;78;82m", "blond" : "\033[38;2;242;213;138m", "billiard_green" : "\033[38;2;0;92;66m", "ink_blue" : "\033[38;2;0;63;142m", "rose_pink" : "\033[38;2;241;156;167m", "charcoal_gray" : "\033[38;2;78;69;74m", "beige" : "\033[38;2;238;220;179m", "chrome_green" : "\033[38;2;0;83;63m", "oriental_blue" : "\033[38;2;38;73;157m", "pink" : "\033[38;2;245;178;178m", "taupe" : "\033[38;2;80;73;70m", "biscuit" : "\033[38;2;234;215;164m", "antique_green" : "\033[38;2;84;145;127m", "ultramarine_blue" : "\033[38;2;71;83;162m", "flamingo_pink" : "\033[38;2;245;178;172m", "lamp_black" : "\033[38;2;36;20;14m", "leghorn" : "\033[38;2;255;233;169m", "water_green" : "\033[38;2;165;201;193m", "ultramarine" : "\033[38;2;67;77;162m", "old_rose" : "\033[38;2;226;147;153m", "_black" : "\033[38;2;0;0;0m", "__black" : "\033[38;2;0;0;0m", "aliceblue" : "\033[38;2;240;248;255m", "darkcyan" : "\033[38;2;0;139;139m", "lightyellow" : "\033[38;2;255;255;224m", "coral" : "\033[38;2;255;127;80m", "dimgray" : "\033[38;2;105;105;105m", "_lavender" : "\033[38;2;230;230;250m", "teal" : "\033[38;2;0;128;128m", "lightgoldenrodyellow" : "\033[38;2;250;250;210m", "tomato" : "\033[38;2;255;99;71m", "_gray" : "\033[38;2;128;128;128m", "lightsteelblue" : "\033[38;2;176;196;222m", "darkslategray" : "\033[38;2;47;79;79m", "lemonchiffon" : "\033[38;2;255;250;205m", "orangered" : "\033[38;2;255;69;0m", "darkgray" : "\033[38;2;169;169;169m", "lightslategray" : "\033[38;2;119;136;153m", "darkgreen" : "\033[38;2;0;100;0m", "wheat" : "\033[38;2;245;222;179m", "__red" : "\033[38;2;255;0;0m", "_silver" : "\033[38;2;192;192;192m", "slategray" : "\033[38;2;112;128;144m", "__green" : "\033[38;2;0;128;0m", "burlywood" : "\033[38;2;222;184;135m", "crimson" : "\033[38;2;220;20;60m", "lightgray" : "\033[38;2;211;211;211m", "steelblue" : "\033[38;2;70;130;180m", "forestgreen" : "\033[38;2;34;139;34m", "_tan" : "\033[38;2;210;180;140m", "mediumvioletred" : "\033[38;2;199;21;133m", "gainsboro" : "\033[38;2;220;220;220m", "royalblue" : "\033[38;2;65;105;225m", "seagreen" : "\033[38;2;46;139;87m", "_khaki" : "\033[38;2;240;230;140m", "deeppink" : "\033[38;2;255;20;147m", "whitesmoke" : "\033[38;2;245;245;245m", "midnightblue" : "\033[38;2;25;25;112m", "mediumseagreen" : "\033[38;2;60;179;113m", "__yellow" : "\033[38;2;255;255;0m", "hotpink" : "\033[38;2;255;105;180m", "__white" : "\033[38;2;255;255;255m", "navy" : "\033[38;2;0;0;128m", "mediumaquamarine" : "\033[38;2;102;205;170m", "gold" : "\033[38;2;255;215;0m", "palevioletred" : "\033[38;2;219;112;147m", "snow" : "\033[38;2;255;250;250m", "darkblue" : "\033[38;2;0;0;139m", "darkseagreen" : "\033[38;2;143;188;143m", "_orange" : "\033[38;2;255;165;0m", "_pink" : "\033[38;2;255;192;203m", "ghostwhite" : "\033[38;2;248;248;255m", "mediumblue" : "\033[38;2;0;0;205m", "_aquamarine" : "\033[38;2;127;255;212m", "sandybrown" : "\033[38;2;244;164;96m", "lightpink" : "\033[38;2;255;182;193m", "floralwhite" : "\033[38;2;255;250;240m", "__blue" : "\033[38;2;0;0;255m", "palegreen" : "\033[38;2;152;251;152m", "darkorange" : "\033[38;2;255;140;0m", "thistle" : "\033[38;2;216;191;216m", "linen" : "\033[38;2;250;240;230m", "dodgerblue" : "\033[38;2;30;144;255m", "lightgreen" : "\033[38;2;144;238;144m", "goldenrod" : "\033[38;2;218;165;32m", "__magenta" : "\033[38;2;255;0;255m", "antiquewhite" : "\033[38;2;250;235;215m", "cornflowerblue" : "\033[38;2;100;149;237m", "springgreen" : "\033[38;2;0;255;127m", "peru" : "\033[38;2;205;133;63m", "fuchsia" : "\033[38;2;255;0;255m", "papayawhip" : "\033[38;2;255;239;213m", "deepskyblue" : "\033[38;2;0;191;255m", "mediumspringgreen" : "\033[38;2;0;250;154m", "darkgoldenrod" : "\033[38;2;184;134;11m", "_violet" : "\033[38;2;238;130;238m", "blanchedalmond" : "\033[38;2;255;235;205m", "lightskyblue" : "\033[38;2;135;206;250m", "lawngreen" : "\033[38;2;124;252;0m", "_chocolate" : "\033[38;2;210;105;30m", "_plum" : "\033[38;2;221;160;221m", "bisque" : "\033[38;2;255;228;196m", "skyblue" : "\033[38;2;135;206;235m", "chartreuse" : "\033[38;2;127;255;0m", "sienna" : "\033[38;2;160;82;45m", "_orchid" : "\033[38;2;218;112;214m", "moccasin" : "\033[38;2;255;228;181m", "lightblue" : "\033[38;2;173;216;230m", "greenyellow" : "\033[38;2;173;255;47m", "saddlebrown" : "\033[38;2;139;69;19m", "mediumorchid" : "\033[38;2;186;85;211m", "navajowhite" : "\033[38;2;255;222;173m", "powderblue" : "\033[38;2;176;224;230m", "lime" : "\033[38;2;0;255;0m", "maroon" : "\033[38;2;128;0;0m", "darkorchid" : "\033[38;2;153;50;204m", "peachpuff" : "\033[38;2;255;218;185m", "paleturquoise" : "\033[38;2;175;238;238m", "limegreen" : "\033[38;2;50;205;50m", "darkred" : "\033[38;2;139;0;0m", "darkviolet" : "\033[38;2;148;0;211m", "mistyrose" : "\033[38;2;255;228;225m", "lightcyan" : "\033[38;2;224;255;255m", "yellowgreen" : "\033[38;2;154;205;50m", "_brown" : "\033[38;2;165;42;42m", "darkmagenta" : "\033[38;2;139;0;139m", "lavenderblush" : "\033[38;2;255;240;245m", "__cyan" : "\033[38;2;0;255;255m", "darkolivegreen" : "\033[38;2;85;107;47m", "firebrick" : "\033[38;2;178;34;34m", "_purple" : "\033[38;2;128;0;128m", "seashell" : "\033[38;2;255;245;238m", "aqua" : "\033[38;2;0;255;255m", "olivedrab" : "\033[38;2;107;142;35m", "indianred" : "\033[38;2;205;92;92m", "_indigo" : "\033[38;2;75;0;130m", "oldlace" : "\033[38;2;253;245;230m", "_turquoise" : "\033[38;2;64;224;208m", "_olive" : "\033[38;2;128;128;0m", "rosybrown" : "\033[38;2;188;143;143m", "darkslateblue" : "\033[38;2;72;61;139m", "_ivory" : "\033[38;2;255;255;240m", "mediumturquoise" : "\033[38;2;72;209;204m", "darkkhaki" : "\033[38;2;189;183;107m", "darksalmon" : "\033[38;2;233;150;122m", "blueviolet" : "\033[38;2;138;43;226m", "honeydew" : "\033[38;2;240;255;240m", "darkturquoise" : "\033[38;2;0;206;209m", "palegoldenrod" : "\033[38;2;238;232;170m", "lightcoral" : "\033[38;2;240;128;128m", "mediumpurple" : "\033[38;2;147;112;219m", "mintcream" : "\033[38;2;245;255;250m", "lightseagreen" : "\033[38;2;32;178;170m", "cornsilk" : "\033[38;2;255;248;220m", "salmon" : "\033[38;2;250;128;114m", "slateblue" : "\033[38;2;106;90;205m", "azure" : "\033[38;2;240;255;255m", "cadetblue" : "\033[38;2;95;158;160m", "_beige" : "\033[38;2;245;245;220m", "lightsalmon" : "\033[38;2;255;160;122m", "mediumslateblue" : "\033[38;2;123;104;238m", } @staticmethod def boldify(msg): return Color.colorify(msg, "bold") @staticmethod def grayify(msg): return Color.colorify(msg, "bright_black") @staticmethod def redify(msg): return Color.colorify(msg, "red") @staticmethod def greenify(msg): return Color.colorify(msg, "green") @staticmethod def yellowify(msg): return Color.colorify(msg, "yellow") @staticmethod def blueify(msg): return Color.colorify(msg, "blue") @staticmethod def cyanify(msg): return Color.colorify(msg, "cyan") NO_COLOR = None @staticmethod def disable_color(): if Config.get_gef_setting("gef.disable_color") is True: return True if Color.NO_COLOR is None: if os.environ.get("NO_COLOR", None): Color.NO_COLOR = True else: Color.NO_COLOR = False if Color.NO_COLOR: return True return False @staticmethod def colorify(text, attrs): """Color text according to the given attributes.""" if Color.disable_color(): return str(text) colors = Color.colors escapes = [colors[attr] for attr in attrs.split() if attr in colors] if len(escapes) == 0: return str(text) out = "".join(escapes) + str(text) + colors["normal"] return out @staticmethod def colorify_hex(value, attrs): text = "{:#x}".format(value) return Color.colorify(text, attrs) @staticmethod def remove_color(text): return re.sub(r"\x1B\[([0-9]{1,2}(;[0-9]{1,3})*)?m", "", text) class Address: """GEF representation of memory addresses.""" def __init__(self, addr): if isinstance(addr, gdb.Value): addr = to_unsigned_long(addr) self.value = addr return @property def section(self): if hasattr(self, "cached_section"): return self.cached_section self.cached_section = ProcessMap.process_lookup_address(self.value) return self.cached_section @property def info(self): if hasattr(self, "cached_info"): return self.cached_info self.cached_info = ProcessMap.file_lookup_address(self.value) return self.cached_info @property def valid(self): return is_valid_addr(self.value) def __repr__(self): return '<{:s}.{:s} object at {:#x}, addr={:#x}, section={}, info={}, valid={}>'.format( self.__module__, self.__class__.__name__, id(self), self.value, bool(self.section), bool(self.info), self.valid, ) def __str__(self): value = AddressUtil.format_address(self.value) if not self.valid: return value line_color = "" if self.is_in_stack_segment(): line_color = Config.get_gef_setting("theme.address_stack") elif self.is_in_heap_segment(): line_color = Config.get_gef_setting("theme.address_heap") elif self.is_in_text_segment(): line_color = Config.get_gef_setting("theme.address_code") elif self.is_in_writable(): line_color = Config.get_gef_setting("theme.address_writable") elif self.is_in_readonly(): line_color = Config.get_gef_setting("theme.address_readonly") elif self.is_valid_but_none(): line_color = Config.get_gef_setting("theme.address_valid_but_none") if self.is_rwx(): line_color += " " + Config.get_gef_setting("theme.address_rwx") return Color.colorify(value, line_color) def long_fmt(self): value = AddressUtil.format_address(self.value, long_fmt=True) if not self.valid: return value line_color = "" if self.is_in_stack_segment(): line_color = Config.get_gef_setting("theme.address_stack") elif self.is_in_heap_segment(): line_color = Config.get_gef_setting("theme.address_heap") elif self.is_in_text_segment(): line_color = Config.get_gef_setting("theme.address_code") elif self.is_in_writable(): line_color = Config.get_gef_setting("theme.address_writable") elif self.is_in_readonly(): line_color = Config.get_gef_setting("theme.address_readonly") elif self.is_valid_but_none(): line_color = Config.get_gef_setting("theme.address_valid_but_none") if self.is_rwx(): line_color += " " + Config.get_gef_setting("theme.address_rwx") return Color.colorify(value, line_color) def is_rwx(self): if self.section is None: return False r = hasattr(self.section, "is_readable") and self.section.is_readable() w = hasattr(self.section, "is_writable") and self.section.is_writable() x = hasattr(self.section, "is_executable") and self.section.is_executable() return r and w and x def is_in_stack_segment(self): if self.section is None: return False return hasattr(self.section, "path") and self.section.path.startswith("[stack]") def is_in_heap_segment(self): if self.section is None: return False return hasattr(self.section, "path") and self.section.path.startswith("[heap]") def is_in_text_segment(self): if self.section is None: return False a = hasattr(self.info, "name") and isinstance(self.info.name, str) and ".text" in self.info.name e = hasattr(self.section, "is_executable") and self.section.is_executable() return a or e def is_in_writable(self): if self.section is None: return False w = hasattr(self.section, "is_writable") and self.section.is_writable() return w def is_in_readonly(self): if self.section is None: return False r = hasattr(self.section, "is_readable") and self.section.is_readable() w = hasattr(self.section, "is_writable") and self.section.is_writable() x = hasattr(self.section, "is_executable") and self.section.is_executable() return r and (not w) and (not x) def is_valid_but_none(self): if self.section is None: return False r = hasattr(self.section, "is_readable") and self.section.is_readable() x = hasattr(self.section, "is_executable") and self.section.is_executable() w = hasattr(self.section, "is_writable") and self.section.is_writable() return (not r) and (not w) and (not x) def dereference(self): # Even if the valid flag is not set, it still dereferences. # This is because the valid flag is not set during kernel debugging. value = AddressUtil.align_address(self.value) derefed = AddressUtil.dereference(value) if derefed is None: return None return int(derefed) class AddressUtil: """A collection of utility functions that operate on addresses.""" @staticmethod @Cache.cache_this_session def ptr_width(): """Determine whether the environment is 32-bit or 64-bit.""" void = GefUtil.cached_lookup_type("void") if void: return void.pointer().sizeof uintptr_t = GefUtil.cached_lookup_type("uintptr_t") if uintptr_t: return uintptr_t.sizeof raise EnvironmentError("GEF is running under an unsupported mode") @staticmethod @Cache.cache_this_session def get_memory_alignment(in_bits=False): """Try to determine the size of a pointer on this system.""" if is_x86_16(): if current_arch.A20: return 2 if not in_bits else 21 else: return 2 if not in_bits else 20 if is_32bit(): return 4 if not in_bits else 32 elif is_64bit(): return 8 if not in_bits else 64 raise EnvironmentError("GEF is running under an unsupported mode") @staticmethod def is_msb_on(addr): """Return whether provided address MSB is on.""" align = AddressUtil.get_memory_alignment() if align == 8: return bool(addr & 0x8000_0000_0000_0000) elif align == 4: return bool(addr & 0x8000_0000) elif align == 2: return bool(addr & 0x8000) raise EnvironmentError("GEF is running under an unsupported mode") @staticmethod def get_format_address_width(memalign_size=None): """Return the width for compactly displaying the pointer.""" if not is_alive(): return 16 + 2 # '0xAAABBBBCCCCDDDD' # assume 64bit if is_32bit() or memalign_size == 4: return 8 + 2 # '0xAAAABBBB' if not is_in_kernel(): return 12 + 2 # '0x7fffAAAABBBB' return 16 + 2 # '0xAAABBBBCCCCDDDD' @staticmethod def format_address(addr, memalign_size=None, long_fmt=False): """Format the address according to its size.""" # if qemu-xxx(32bit arch) runs on x86-64 machine, memalign_size does not match # AddressUtil.get_memory_alignment(), so use the value forcibly if memalign_size is not None if memalign_size is None: memalign_size = AddressUtil.get_memory_alignment() if isinstance(addr, str): addr = int(addr, 16) addr = AddressUtil.align_address(addr, memalign_size) if memalign_size == 4: return "{:#010x}".format(addr) elif memalign_size == 2: return "{:#06x}".format(addr) elif memalign_size == 2.5: if current_arch.A20: return "{:#08x}".format(addr) else: return "{:#07x}".format(addr) if long_fmt: return "{:#018x}".format(addr) if is_in_kernel(): return "{:#018x}".format(addr) return "{:#014x}".format(addr) @staticmethod def align_address(addr, memalign_size=None): """Align the provided address to the process's native length. e.g., 0x1_2345_6789 -> 0x2345_6789 (for 32-bit arch)""" # if qemu-xxx(32bit arch) runs on x86-64 machine, memalign_size does not match # AddressUtil.get_memory_alignment(), so use the value forcibly if memalign_size is not None if memalign_size is None: memalign_size = AddressUtil.get_memory_alignment() if memalign_size == 8: return addr & 0xffff_ffff_ffff_ffff elif memalign_size == 4: return addr & 0xffff_ffff elif memalign_size == 2: return addr & 0xffff elif memalign_size == 2.5: if current_arch.A20: return addr & 0x1f_ffff else: return addr & 0x0f_ffff raise EnvironmentError("GEF is running under an unsupported mode") @staticmethod @Cache.cache_until_next def get_vmem_end(): return 1 << AddressUtil.get_memory_alignment(in_bits=True) @staticmethod @Cache.cache_until_next def get_vmem_end_mask(): return AddressUtil.get_vmem_end() - 1 @staticmethod def parse_address(addr): """Parse an address and return it as an Integer.""" try: return int(addr, 0) except ValueError: pass # on some unsupported architectures (e.g., tricore), gdb.parse_and_eval may cause a crash if current_arch is None: raise ValueError # Don't enclose it in a try-catch. This is because it is used with argparse, # and is intended to raise an exception if parsing fails. return to_unsigned_long(gdb.parse_and_eval(addr)) @staticmethod def parse_string_range(s): """Parse an address range (e.g., 0x400000-0x401000)""" addrs = s.split("-") return [int(x, 16) for x in addrs] @staticmethod @Cache.cache_until_next def dereference(addr): """GEF wrapper for gdb dereference function.""" def use_stdtype(): if is_32bit(): return "uint32_t" elif is_64bit(): return "uint64_t" return "uint16_t" def use_default_type(): if is_32bit(): return "unsigned int" elif is_64bit(): return "unsigned long" return "unsigned short" def use_golang_type(): if is_32bit(): return "uint32" elif is_64bit(): return "uint64" return "uint16" def use_rust_type(): if is_32bit(): return "u32" elif is_64bit(): return "u64" return "u16" ulong_t = GefUtil.cached_lookup_type(use_stdtype()) if not ulong_t: ulong_t = GefUtil.cached_lookup_type(use_default_type()) if not ulong_t: ulong_t = GefUtil.cached_lookup_type(use_golang_type()) if not ulong_t: ulong_t = GefUtil.cached_lookup_type(use_rust_type()) if not ulong_t: return None try: unsigned_long_type = ulong_t.pointer() except (AttributeError, gdb.error): return None try: res = gdb.Value(addr).cast(unsigned_long_type).dereference() # GDB does lazy fetch by default so we need to force access to the value res.fetch_lazy() return res except (gdb.MemoryError, gdb.error, ValueError, TypeError): return None @staticmethod @Cache.cache_this_session def get_recursive_dereference_blacklist(): """Return the blacklist of addresses (for caching purposes after eval()).""" blacklist = eval(Config.get_gef_setting("dereference.blacklist")) for range_list in blacklist: assert isinstance(range_list, list) assert len(range_list) == 2 assert isinstance(range_list[0], int) assert isinstance(range_list[1], int) return blacklist @staticmethod @Cache.cache_until_next def recursive_dereference(addr, phys=False): """Create dereference array.""" if not is_alive(): return [addr], None recursion = Config.get_gef_setting("dereference.max_recursion") blacklist = AddressUtil.get_recursive_dereference_blacklist() addr_list = [] error = None while recursion > 0: # check loop if addr in addr_list: if addr == 0 and len(addr_list) == 1: # the case that address 0x0 is valid and first element is 0x0 (i.e. telescope 0x0). # but no error because it is generally unnecessary information. addr_list.append(addr) # use [0, 0] instead of [0] else: error = "[loop detected]" break # not loop addr_list.append(addr) # check blacklist if any(bstart <= addr < bend for bstart, bend in blacklist): error = "[blacklist detected]" break # check non-address if len(addr_list) > 1 and addr < 0x100: break # goto next if phys and len(addr_list) == 1: mem = read_physmem(addr, current_arch.ptrsize) unpack = u32 if current_arch.ptrsize == 4 else u64 addr = unpack(mem) else: try: addr = read_int_from_memory(addr) except gdb.MemoryError: break recursion -= 1 return addr_list, error @staticmethod @Cache.cache_until_next def recursive_dereference_to_string(value, skip_idx=0, phys=False, quiet=False): """Create string from dereference array.""" string_color = Config.get_gef_setting("theme.dereference_string") nb_max_string_length = Config.get_gef_setting("context.nb_max_string_length") recursion = Config.get_gef_setting("dereference.max_recursion") # dereference addrs, error = AddressUtil.recursive_dereference(value, phys=phys) # if addrs has one element and it is address with an error (e.g., address_A -> [loop detected]), # don't skip the element even if skip_idx=1 if skip_idx == 1: if len(addrs) == 1: if error == "[loop detected]": skip_idx = 0 # add "..." if error is None: if addrs[-1] > 0x100 and is_valid_addr(addrs[-1]) and recursion > 1: error = "..." # replace to string if valid def to_ascii(v): if not isinstance(v, int) or v < 0: return "" s = "" while v & 0xff: # \0 if chr(v & 0xff) in String.STRING_PRINTABLE: s += chr(v & 0xff) else: return "" v >>= 8 return s last_elem = None if error is None and not quiet: s = to_ascii(addrs[-1]) if len(s) < 2: pass elif 2 <= len(s) < current_arch.ptrsize: fa = AddressUtil.format_address(addrs[-1], long_fmt=True) last_elem = "{:s} ({:s}?)".format(fa, Color.colorify(repr(s), string_color)) addrs = addrs[:-1] else: # len(s) == current_arch.ptrsize if len(addrs) >= 2 and is_valid_addr(addrs[-2]): # read more string s = read_cstring_from_memory(addrs[-2], nb_max_string_length) if s: fa = AddressUtil.format_address(addrs[-1], long_fmt=True) last_elem = "{:s} {:s}".format(fa, Color.colorify(repr(s), string_color)) addrs = addrs[:-1] else: # Ignore when the string that do not end with a null character pass else: # fallback fa = AddressUtil.format_address(addrs[-1], long_fmt=True) last_elem = "{:s} ({:s}?)".format(fa, Color.colorify(repr(s), string_color)) addrs = addrs[:-1] # others msg = [] for addr in addrs[skip_idx:]: address = ProcessMap.lookup_address(addr) if quiet: msg.append(address.long_fmt()) else: msg.append(address.long_fmt() + Symbol.get_symbol_string(addr)) if error: msg.append(error) elif last_elem: msg.append(last_elem) return " -> ".join(msg) class Permission: """GEF representation of Linux permission.""" NONE = 0 READ = 1 WRITE = 2 EXECUTE = 4 ALL = READ | WRITE | EXECUTE def __init__(self, **kwargs): self.value = kwargs.get("value", 0) return def __repr__(self): return '<{:s}.{:s} object at {:#x}, perm="{}">'.format( self.__module__, self.__class__.__name__, id(self), str(self), ) def __or__(self, value): return self.value | value def __and__(self, value): return self.value & value def __xor__(self, value): return self.value ^ value def __eq__(self, value): return self.value == value def __ne__(self, value): return self.value != value def __str__(self): perm_str = "" perm_str += "r" if self & Permission.READ else "-" perm_str += "w" if self & Permission.WRITE else "-" perm_str += "x" if self & Permission.EXECUTE else "-" return perm_str def match(self, perm_str): if not isinstance(perm_str, str): return False if len(perm_str) != 3: return False if perm_str[0] not in "rR-_?": return False if perm_str[1] not in "wR-_?": return False if perm_str[2] not in "xX-_?": return False if perm_str[0] in "rR" and not bool(self.value & Permission.READ): return False if perm_str[0] in "-_" and bool(self.value & Permission.READ): return False if perm_str[1] in "wW" and not bool(self.value & Permission.WRITE): return False if perm_str[1] in "-_" and bool(self.value & Permission.WRITE): return False if perm_str[2] in "xX" and not bool(self.value & Permission.EXECUTE): return False if perm_str[2] in "-_" and bool(self.value & Permission.EXECUTE): return False return True @staticmethod def from_process_maps(perm_str): perm = Permission() if perm_str[0] == "r": perm.value += Permission.READ if perm_str[1] == "w": perm.value += Permission.WRITE if perm_str[2] == "x": perm.value += Permission.EXECUTE return perm class Section: """GEF representation of process memory sections.""" def __init__(self, *args, **kwargs): self.page_start = kwargs.get("page_start") self.page_end = kwargs.get("page_end") self.offset = kwargs.get("offset", 0) self.permission = kwargs.get("permission") self.inode = kwargs.get("inode", None) self.path = kwargs.get("path", "") return def __repr__(self): return '<{:s}.{:s} object at {:#x}, page_start={:#x}, page_end={:#x}, perm="{}", path="{:s}">'.format( self.__module__, self.__class__.__name__, id(self), self.page_start, self.page_end, self.permission, self.path, ) def is_readable(self): v = self.permission.value return v and bool(v & Permission.READ) def is_writable(self): v = self.permission.value return v and bool(v & Permission.WRITE) def is_executable(self): v = self.permission.value return v and bool(v & Permission.EXECUTE) @property def size(self): if self.page_end is None or self.page_start is None: return -1 return self.page_end - self.page_start class Elf: """Basic ELF parsing.""" # e_ident[EI_MAG0:EI_MAG3] ELF_MAGIC = 0x7f454c46 # e_ident[EI_CLASS] ELF_CLASS_NONE = 0 ELF_32_BITS = 1 ELF_64_BITS = 2 # e_ident[EI_DATA] ELF_DATA_NONE = 0 LITTLE_ENDIAN = 1 BIG_ENDIAN = 2 # e_ident[EI_OSABI] OSABI_SYSTEMV = 0 # UNIX System V ABI OSABI_HPUX = 1 # Hewlett-Packard HP-UX OSABI_NETBSD = 2 # NetBSD OSABI_LINUX = 3 # GNU Linux OSABI_HURD = 4 # GNU Hurd OSABI_86OPEN = 5 # 86Open Common IA32 ABI OSABI_SOLARIS = 6 # Sun Solaris OSABI_AIX = 7 # IBM AIX OSABI_IRIX = 8 # SGI Irix OSABI_FREEBSD = 9 # FreeBSD OSABI_TRU64 = 10 # Compaq TRU64 UNIX OSABI_MODESTO = 11 # Novell Modesto OSABI_OPENBSD = 12 # OpenBSD OSABI_OPENVMS = 13 # OpenVMS OSABI_NSK = 14 # Hewlett-Packard Non-Stop Kernel OSABI_AROS = 15 # Amiga Research OS OSABI_FENIXOS = 16 # The FenixOS highly scalable multi-core OS OSABI_CLOUDABI = 17 # Nuxi CloudABI OSABI_OPENVOS = 18 # Stratus Technologies OpenVOS OSABI_ARM_AEABI = 64 # ARM EABI OSABI_ARM = 97 # ARM OSABI_STANDALONE = 255 # Standalone (embedded) application # e_type ET_NONE = 0 ET_REL = 1 ET_EXEC = 2 ET_DYN = 3 ET_CORE = 4 # e_machine EM_NONE = 0 # No machine EM_M32 = 1 # AT&T WE 32100 EM_SPARC = 2 # SUN SPARC EM_386 = 3 # Intel 80386 EM_68K = 4 # Motorola m68k family EM_88K = 5 # Motorola m88k family EM_IAMCU = 6 # Intel MCU EM_860 = 7 # Intel 80860 EM_MIPS = 8 # MIPS R3000 big-endian EM_S370 = 9 # IBM System/370 Processor EM_MIPS_RS3_LE = 10 # MIPS RS3000 Little-endian # 11-14 # Reserved for future use EM_PARISC = 15 # Hewlett-Packard PA-RISC # 16 # Reserved for future use EM_VPP500 = 17 # Fujitsu VPP500 EM_SPARC32PLUS = 18 # Enhanced instruction set SPARC EM_960 = 19 # Intel 80960 EM_PPC = 20 # PowerPC EM_PPC64 = 21 # 64-bit PowerPC EM_S390 = 22 # IBM System/390 Processor EM_SPU = 23 # IBM SPU/SPC # 24-35 # Reserved for future use EM_V800 = 36 # NEC V800 EM_FR20 = 37 # Fujitsu FR20 EM_RH32 = 38 # TRW RH-32 EM_RCE = 39 # Motorola RCE EM_ARM = 40 # ARM 32-bit architecture (AARCH32) EM_ALPHA = 41 # Digital Alpha EM_SH = 42 # Hitachi SH EM_SPARCV9 = 43 # SPARC Version 9 EM_TRICORE = 44 # Siemens TriCore embedded processor EM_ARC = 45 # Argonaut RISC Core, Argonaut Technologies Inc. EM_H8_300 = 46 # Hitachi H8/300 EM_H8_300H = 47 # Hitachi H8/300H EM_H8S = 48 # Hitachi H8S EM_H8_500 = 49 # Hitachi H8/500 EM_IA_64 = 50 # Intel IA-64 processor architecture EM_MIPS_X = 51 # Stanford MIPS-X EM_COLDFIRE = 52 # Motorola ColdFire EM_68HC12 = 53 # Motorola M68HC12 EM_MMA = 54 # Fujitsu MMA Multimedia Accelerator EM_PCP = 55 # Siemens PCP EM_NCPU = 56 # Sony nCPU embedded RISC processor EM_NDR1 = 57 # Denso NDR1 microprocessor EM_STARCORE = 58 # Motorola Star*Core processor EM_ME16 = 59 # Toyota ME16 processor EM_ST100 = 60 # STMicroelectronics ST100 processor EM_TINYJ = 61 # Advanced Logic Corp. TinyJ embedded processor family EM_X86_64 = 62 # AMD x86-64 architecture EM_PDSP = 63 # Sony DSP Processor EM_PDP10 = 64 # Digital Equipment Corp. PDP-10 EM_PDP11 = 65 # Digital Equipment Corp. PDP-11 EM_FX66 = 66 # Siemens FX66 microcontroller EM_ST9PLUS = 67 # STMicroelectronics ST9+ 8/16 bit microcontroller EM_ST7 = 68 # STMicroelectronics ST7 8-bit microcontroller EM_68HC16 = 69 # Motorola MC68HC16 Microcontroller EM_68HC11 = 70 # Motorola MC68HC11 Microcontroller EM_68HC08 = 71 # Motorola MC68HC08 Microcontroller EM_68HC05 = 72 # Motorola MC68HC05 Microcontroller EM_SVX = 73 # Silicon Graphics SVx EM_ST19 = 74 # STMicroelectronics ST19 8-bit microcontroller EM_VAX = 75 # Digital VAX EM_CRIS = 76 # Axis Communications 32-bit embedded processor EM_JAVELIN = 77 # Infineon Technologies 32-bit embedded processor EM_FIREPATH = 78 # Element 14 64-bit DSP Processor EM_ZSP = 79 # LSI Logic 16-bit DSP Processor EM_MMIX = 80 # Donald Knuth's educational 64-bit processor EM_HUANY = 81 # Harvard University machine-independent object files EM_PRISM = 82 # SiTera Prism EM_AVR = 83 # Atmel AVR 8-bit microcontroller EM_FR30 = 84 # Fujitsu FR30 EM_D10V = 85 # Mitsubishi D10V EM_D30V = 86 # Mitsubishi D30V EM_V850 = 87 # NEC v850 EM_M32R = 88 # Mitsubishi M32R EM_MN10300 = 89 # Matsushita MN10300 EM_MN10200 = 90 # Matsushita MN10200 EM_PJ = 91 # picoJava EM_OPENRISC = 92 # OpenRISC 32-bit embedded processor EM_ARC_COMPACT = 93 # ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) EM_XTENSA = 94 # Tensilica Xtensa Architecture EM_VIDEOCORE = 95 # Alphamosaic VideoCore processor EM_TMM_GPP = 96 # Thompson Multimedia General Purpose Processor EM_NS32K = 97 # National Semiconductor 32000 series EM_TPC = 98 # Tenor Network TPC processor EM_SNP1K = 99 # Trebia SNP 1000 processor EM_ST200 = 100 # STMicroelectronics ST200 microcontroller EM_IP2K = 101 # Ubicom IP2xxx microcontroller family EM_MAX = 102 # MAX Processor EM_CR = 103 # National Semiconductor CompactRISC microprocessor EM_F2MC16 = 104 # Fujitsu F2MC16 EM_MSP430 = 105 # Texas Instruments embedded microcontroller msp430 EM_BLACKFIN = 106 # Analog Devices Blackfin (DSP) processor EM_SE_C33 = 107 # S1C33 Family of Seiko Epson processors EM_SEP = 108 # Sharp embedded microprocessor EM_ARCA = 109 # Arca RISC Microprocessor EM_UNICORE = 110 # Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University EM_EXCESS = 111 # eXcess: 16/32/64-bit configurable embedded CPU EM_DXP = 112 # Icera Semiconductor Inc. Deep Execution Processor EM_ALTERA_NIOS2 = 113 # Altera Nios II soft-core processor EM_CRX = 114 # National Semiconductor CompactRISC CRX microprocessor EM_XGATE = 115 # Motorola XGATE embedded processor EM_C166 = 116 # Infineon C16x/XC16x processor EM_M16C = 117 # Renesas M16C series microprocessors EM_DSPIC30F = 118 # Microchip Technology dsPIC30F Digital Signal Controller EM_CE = 119 # Freescale Communication Engine RISC core EM_M32C = 120 # Renesas M32C series microprocessors # 121-130 # Reserved for future use EM_TSK3000 = 131 # Altium TSK3000 core EM_RS08 = 132 # Freescale RS08 embedded processor EM_SHARC = 133 # Analog Devices SHARC family of 32-bit DSP processors EM_ECOG2 = 134 # Cyan Technology eCOG2 microprocessor EM_SCORE7 = 135 # Sunplus S+core7 RISC processor EM_DSP24 = 136 # New Japan Radio (NJR) 24-bit DSP Processor EM_VIDEOCORE3 = 137 # Broadcom VideoCore III processor EM_LATTICEMICO32 = 138 # RISC processor for Lattice FPGA architecture EM_SE_C17 = 139 # Seiko Epson C17 family EM_TI_C6000 = 140 # The Texas Instruments TMS320C6000 DSP family EM_TI_C2000 = 141 # The Texas Instruments TMS320C2000 DSP family EM_TI_C5500 = 142 # The Texas Instruments TMS320C55x DSP family EM_TI_ARP32 = 143 # Texas Instruments Application Specific RISC Processor, 32bit fetch EM_TI_PRU = 144 # Texas Instruments Programmable Realtime Unit # 145-159 # Reserved for future use EM_MMDSP_PLUS = 160 # STMicroelectronics 64bit VLIW Data Signal Processor EM_CYPRESS_M8C = 161 # Cypress M8C microprocessor EM_R32C = 162 # Renesas R32C series microprocessors EM_TRIMEDIA = 163 # NXP Semiconductors TriMedia architecture family EM_QDSP6 = 164 # QUALCOMM DSP6 Processor EM_8051 = 165 # Intel 8051 and variants EM_STXP7X = 166 # STMicroelectronics STxP7x family of configurable and extensible RISC processors EM_NDS32 = 167 # Andes Technology compact code size embedded RISC processor family EM_ECOG1 = 168 # Cyan Technology eCOG1X family EM_ECOG1X = 168 # Cyan Technology eCOG1X family EM_MAXQ30 = 169 # Dallas Semiconductor MAXQ30 Core Micro-controllers EM_XIMO16 = 170 # New Japan Radio (NJR) 16-bit DSP Processor EM_MANIK = 171 # M2000 Reconfigurable RISC Microprocessor EM_CRAYNV2 = 172 # Cray Inc. NV2 vector architecture EM_RX = 173 # Renesas RX family EM_METAG = 174 # Imagination Technologies META processor architecture EM_MCST_ELBRUS = 175 # MCST Elbrus general purpose hardware architecture EM_ECOG16 = 176 # Cyan Technology eCOG16 family EM_CR16 = 177 # National Semiconductor CompactRISC CR16 16-bit microprocessor EM_ETPU = 178 # Freescale Extended Time Processing Unit EM_SLE9X = 179 # Infineon Technologies SLE9X core EM_L10M = 180 # Intel L10M EM_K10M = 181 # Intel K10M # 182 # Reserved for future Intel use EM_AARCH64 = 183 # ARM 64-bit architecture (AARCH64) # 184 # Reserved for future ARM use EM_AVR32 = 185 # Atmel Corporation 32-bit microprocessor family EM_STM8 = 186 # STMicroeletronics STM8 8-bit microcontroller EM_TILE64 = 187 # Tilera TILE64 multicore architecture family EM_TILEPRO = 188 # Tilera TILEPro multicore architecture family EM_MICROBLAZE = 189 # Xilinx MicroBlaze 32-bit RISC soft processor core EM_CUDA = 190 # NVIDIA CUDA architecture EM_TILEGX = 191 # Tilera TILE-Gx multicore architecture family EM_CLOUDSHIELD = 192 # CloudShield architecture family EM_COREA_1ST = 193 # KIPO-KAIST Core-A 1st generation processor family EM_COREA_2ND = 194 # KIPO-KAIST Core-A 2nd generation processor family EM_ARCV2 = 195 # Synopsys ARCompact V2 # codespell:ignore EM_OPEN8 = 196 # Open8 8-bit RISC soft processor core EM_RL78 = 197 # Renesas RL78 family EM_VIDEOCORE5 = 198 # Broadcom VideoCore V processor EM_78KOR = 199 # Renesas 78KOR family EM_56800EX = 200 # Freescale 56800EX Digital Signal Controller (DSC) EM_BA1 = 201 # Beyond BA1 CPU architecture EM_BA2 = 202 # Beyond BA2 CPU architecture EM_XCORE = 203 # XMOS xCORE processor family EM_MCHP_PIC = 204 # Microchip 8-bit PIC(r) family EM_INTELGT = 205 # Intel Graphics Technology EM_INTEL206 = 206 # Reserved by Intel EM_INTEL207 = 207 # Reserved by Intel EM_INTEL208 = 208 # Reserved by Intel EM_INTEL209 = 209 # Reserved by Intel EM_KM32 = 210 # KM211 KM32 32-bit processor EM_KMX32 = 211 # KM211 KMX32 32-bit processor EM_KMX16 = 212 # KM211 KMX16 16-bit processor EM_KMX8 = 213 # KM211 KMX8 8-bit processor EM_KVARC = 214 # KM211 KVARC processor EM_CDP = 215 # Paneve CDP architecture family EM_COGE = 216 # Cognitive Smart Memory Processor EM_COOL = 217 # Bluechip Systems CoolEngine EM_NORC = 218 # Nanoradio Optimized RISC EM_CSR_KALIMBA = 219 # CSR Kalimba architecture family EM_Z80 = 220 # Zilog Z80 EM_VISIUM = 221 # Controls and Data Services VISIUMcore processor EM_FT32 = 222 # FTDI Chip FT32 high performance 32-bit RISC architecture EM_MOXIE = 223 # Moxie processor family EM_AMDGPU = 224 # AMD GPU architecture # 225-242 # Reserved EM_RISCV = 243 # RISC-V EM_LANAI = 244 # Lanai 32-bit processor EM_CEVA = 245 # CEVA Processor Architecture Family EM_CEVA_X2 = 246 # CEVA X2 Processor Family EM_BPF = 247 # Linux BPF - in-kernel virtual machine EM_GRAPHCORE_IPU = 248 # Graphcore Intelligent Processing Unit EM_IMG1 = 249 # Imagination Technologies EM_NFP = 250 # Netronome Flow Processor EM_VE = 251 # NEC Vector Engine EM_CSKY = 252 # C-SKY processor family EM_ARC_COMPACT3_64 = 253 # Synopsys ARCv2.3 64-bit # codespell:ignore EM_MCS6502 = 254 # MOS Technology MCS 6502 processor EM_ARC_COMPACT3 = 255 # Synopsys ARCv2.3 32-bit # codespell:ignore EM_KVX = 256 # Kalray VLIW core of the MPPA processor family EM_65816 = 257 # WDC 65816/65C816 EM_LOONGARCH = 258 # LoongArch EM_KF32 = 259 # ChipON KungFu32 EM_U16_U8CORE = 260 # LAPIS nX-U16/U8 EM_TACHYUM = 261 # Tachyum EM_56800EF = 262 # NXP 56800EF Digital Signal Controller (DSC) EM_AVR_UNOFFICIAL = 0x1057 # AVR (unofficial) EM_MSP430_UNOFFICIAL = 0x1059 # MSP430 (unofficial) EM_EPIPHANY_UNOFFICIAL = 0x1223 # Adapteva Epiphany (unofficial) EM_AVR32_UNOFFICIAL = 0x18ad # Atmel AVR32 (unofficial) EM_MT_UNOFFICIAL = 0x2530 # Morpho MT (unofficial) EM_FR30_UNOFFICIAL = 0x3330 # FR30 (unofficial) EM_OPENRISC_OLD = 0x3426 # OpenRISC (obsolete) EM_WEBASSEMBLY = 0x4157 # Web Assembly binaries (unofficial) EM_C166_UNOFFICIAL = 0x4688 # Infineon C166 (unofficial) EM_S12Z = 0x4DEF # Freescale S12Z EM_FRV_UNOFFICIAL = 0x5441 # Cygnus FR-V (unofficial) EM_DLX_UNOFFICIAL = 0x5aa5 # DLX (unofficial) EM_D10V_UNOFFICIAL = 0x7650 # Cygnus D10V (unofficial) EM_D30V_UNOFFICIAL = 0x7676 # Cygnus D30V (unofficial) EM_IP2K_UNOFFICIAL = 0x8217 # Ubicom IP2xxx (unofficial) EM_OPENRISC_OLD2 = 0x8472 # OpenRISC (obsolete) EM_PPC_UNOFFICIAL = 0x9025 # Cygnus PowerPC (unofficial) EM_ALPHA_UNOFFICIAL = 0x9026 # Digital Alpha (unofficial) EM_M32R_UNOFFICIAL = 0x9041 # Cygnus M32R (unofficial) EM_V850_UNOFFICIAL = 0x9080 # Cygnus V859 (unofficial) EM_S390_OLD = 0xa390 # IBM S/390 (obsolete) EM_XTENSA_UNOFFICIAL = 0xabc7 # Old Xtensa (unofficial) EM_XSTORMY_UNOFFICIAL = 0xad45 # xstormy16 (unofficial) EM_MICROBLAZE_UNOFFICIAL = 0xbaab # Old MicroBlaze (unofficial) EM_MN10300_UNOFFICIAL = 0xbeef # Cygnus MN10300 (unofficial) EM_MN10200_UNOFFICIAL = 0xdead # Cygnus MN10200 (unofficial) EM_MEP_UNOFFICIAL = 0xf00d # Toshiba MeP (unofficial) EM_M32C_UNOFFICIAL = 0xfeb0 # Renesas M32C (unofficial) EM_IQ2000_UNOFFICIAL = 0xfeba # Vitesse IQ2000 (unofficial) EM_NIOS_UNOFFICIAL = 0xfebb # NIOS (unofficial) EM_MOXIE_UNOFFICIAL = 0xfeed # Moxie (unofficial) # e_version EV_NONE = 0 EV_CURRENT = 1 # default values e_magic = ELF_MAGIC e_class = ELF_64_BITS e_endianness = LITTLE_ENDIAN e_eiversion = None e_osabi = None e_abiversion = None e_pad = None # noqa: F841 e_type = ET_EXEC e_machine = EM_X86_64 e_version = None e_entry = 0x00 e_phoff = None e_shoff = None e_flags = None e_ehsize = None e_phentsize = None e_phnum = None e_shentsize = None e_shnum = None e_shstrndx = None @staticmethod @Cache.cache_until_next def get_elf(filepath=None): """Return an Elf object with caching.""" try: elf = Elf(filepath) except Exception: elf = None return elf def __init__(self, elf=None, file_offset=0): """Instantiate an ELF object.""" if elf is None: elf = Path.get_filepath() if elf is None: self.e_magic = None err("Could not find the path") return if isinstance(elf, str): if not os.access(elf, os.R_OK): err("'{:s}' not found/readable".format(elf)) self.e_magic = None return self.fd = open(elf, "rb") self.addr = None self.seek_init_offset = file_offset self.pos = 0 self.seek(self.pos) self.filename = elf elif isinstance(elf, int): self.fd = None self.addr = elf self.pos = 0 self.filename = None else: raise # off 0x0 self.e_magic, self.e_class, self.e_endianness, self.e_eiversion = struct.unpack(">IBBB", self.read(7)) # adjust endianness in bin reading endian = "<" if self.e_endianness == Elf.LITTLE_ENDIAN else ">" # off 0x7 self.e_osabi, self.e_abiversion = struct.unpack("{}BB".format(endian), self.read(2)) # off 0x9 self.e_pad = self.read(7) # noqa # off 0x10 self.e_type, self.e_machine, self.e_version = struct.unpack("{}HHI".format(endian), self.read(8)) # off 0x18 if self.e_class == Elf.ELF_64_BITS: self.e_entry, self.e_phoff, self.e_shoff = struct.unpack("{}QQQ".format(endian), self.read(24)) else: self.e_entry, self.e_phoff, self.e_shoff = struct.unpack("{}III".format(endian), self.read(12)) self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum = struct.unpack("{}IHHH".format(endian), self.read(10)) self.e_shentsize, self.e_shnum, self.e_shstrndx = struct.unpack("{}HHH".format(endian), self.read(6)) # phdr self.phdrs = [] for i in range(self.e_phnum): phdr = Elf.Phdr(self, self.e_phoff + self.e_phentsize * i) self.phdrs.append(phdr) # shdr self.shdrs = [] for i in range(self.e_shnum): try: shdr = Elf.Shdr(self, self.e_shoff + self.e_shentsize * i) self.shdrs.append(shdr) except gdb.MemoryError: # Perspective failure. Probably it occurs when parsing ELF loaded into memory. # Even if the ELF is loaded, the section header is not loaded. Therefore, it is ignored. self.shdrs = [] break else: # multiple SHT_NULLs are treated as abnormal if sum([x.sh_type == Elf.Shdr.SHT_NULL for x in self.shdrs]) > 1: self.shdrs = [] if self.fd is not None: self.fd.close() self.fd = None # It will be unusable after initialization. self.read = None self.seek = None return def __repr__(self): if not hasattr(self, "filename"): msg = '<{:s}.{:s} object at {:#x}, invalid>'.format( self.__module__, self.__class__.__name__, id(self), ) elif self.filename: msg = '<{:s}.{:s} object at {:#x}, filename="{:s}">'.format( self.__module__, self.__class__.__name__, id(self), self.filename, ) else: msg = "<{:s}.{:s} object at {:#x}, address={:#x}>".format( self.__module__, self.__class__.__name__, id(self), self.addr, ) return msg def read(self, size): if self.fd is not None: v = self.fd.read(size) elif self.addr is not None: v = read_memory(self.addr + self.pos, size) else: raise self.pos += size return v def seek(self, off): if self.fd is not None: self.fd.seek(self.seek_init_offset + off, 0) elif self.addr is not None: self.pos = off else: raise return def is_valid(self): return self.e_magic == Elf.ELF_MAGIC def get_bits(self): if self.e_class == Elf.ELF_64_BITS: return 64 if self.e_class == Elf.ELF_32_BITS: return 32 raise def get_phdr(self, p_type): for phdr in self.phdrs: if phdr.p_type == p_type: return phdr return None def get_shdr(self, name): for shdr in self.shdrs: if shdr.sh_name == name: return shdr return None def read_phdr(self, p_type): phdr = self.get_phdr(p_type) if phdr is None: return None if self.filename: fd = open(self.filename, "rb") fd.seek(phdr.p_offset) data = fd.read(phdr.p_filesz) fd.close() return data if self.addr: read_addr = phdr.p_vaddr if self.is_pie(): read_addr += self.addr data = read_memory(read_addr, phdr.p_memsz) return data return None def read_shdr(self, name): shdr = self.get_shdr(name) if shdr is None: return None if self.filename: fd = open(self.filename, "rb") fd.seek(shdr.sh_offset) data = fd.read(shdr.sh_size) fd.close() return data if self.addr: if shdr.sh_addr > 0: read_addr = shdr.sh_addr else: # e.g., .comment section of vdso """ [ #] Name Type Address Offset Size EntSiz Flags Link Info Align ... [13] .altinstr_replacement PROGBITS 0x106f 0x106f 0x3c 0x0 AX 0x0 0x0 0x1 [14] .comment PROGBITS 0x0 0x10ab 0x25 0x1 MS 0x0 0x0 0x1 [15] .shstrtab STRTAB 0x0 0x10d0 0x9e 0x0 0x0 0x0 0x1 """ read_addr = shdr.sh_offset if self.is_pie(): read_addr += self.addr data = read_memory(read_addr, shdr.sh_size) return data return None def is_static(self): # note: static-pie has no PT_INTERP return not bool(self.get_phdr(Elf.Phdr.PT_INTERP)) def has_dynamic(self): # note: static-pie has PT_DYNAMIC return bool(self.get_phdr(Elf.Phdr.PT_DYNAMIC)) def is_stripped(self): return not bool(self.get_shdr(".symtab")) def has_debuginfo(self): return bool(self.get_shdr(".debug_info")) def has_canary_heuristic(self): try: objdump_command = GefUtil.which(Config.get_gef_setting("gef.objdump_command")) except FileNotFoundError: return None # it means unknown # heuristic search if self.e_machine in (Elf.EM_X86_64, Elf.EM_386): if self.e_machine == Elf.EM_X86_64: kw = b"%fs:0x28" else: # 32-bit kw = b"%gs:0x14" proc = subprocess.Popen( [objdump_command, "-d", self.filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) for _ in range(0x10000): if kw in proc.stdout.readline(): proc.kill() return True proc.kill() return False return None # it means unknown def is_pie(self): return self.e_type == Elf.ET_DYN def is_nx(self): phdr = self.get_phdr(Elf.Phdr.PT_GNU_STACK) if phdr: return not bool(phdr.p_flags & Elf.Phdr.PF_X) return False def is_relro(self): # both partial and full have PT_GNU_RELRO return bool(self.get_phdr(Elf.Phdr.PT_GNU_RELRO)) def get_dynamic_data(self): # find dynamic phdr = self.get_phdr(Elf.Phdr.PT_DYNAMIC) if phdr is None: return None # read dynamic fd = open(self.filename, "rb") fd.seek(phdr.p_offset) data = fd.read(phdr.p_filesz) fd.close() # unpack data = slice_unpack(data, self.get_bits() // 8) return data def is_full_relro(self): data = self.get_dynamic_data() if data is None: return False # check for tag, value in slicer(data, 2): if tag == 0x18: # DT_BIND_NOW return True if tag == 0x1e: # DT_FLAGS return bool(value & 0x08) # DF_BIND_NOW return False def has_rpath(self): data = self.get_dynamic_data() if data is None: return False for tag, _ in slicer(data, 2): if tag == 0xf: # DT_RPATH return True return False def has_runpath(self): data = self.get_dynamic_data() if data is None: return False for tag, _ in slicer(data, 2): if tag == 0x1d: # DT_RUNPATH return True return False def has_pac_heuristic(self): pac_ops = [ b"paciasp", b"pacia", b"pacibsp", b"pacib", b"pacda", b"pacdb", b"pacga", b"autiasp", b"autia", b"autibsp", b"autib", b"autda", b"autdb", b"retaa", b"retab", b"braa", b"brab", b"blraa", b"blrab", b"eretaa", b"eretab", b"ldraa", b"ldrab" ] try: objdump_command = GefUtil.which(Config.get_gef_setting("gef.objdump_command")) except FileNotFoundError: return None # it means unknown proc = subprocess.Popen( [objdump_command, "-d", self.filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) for _ in range(0x10000): line = proc.stdout.readline().strip() if not line: continue if line.split()[-1] in pac_ops: proc.kill() return True proc.kill() return False def checksec(self): """Check the following security properties of the ELF binary. Canary, NX, PIE, RELRO, Fortify, Static, Symbol, Debuginfo, CET, RPATH/RUNPATH, and Clang CFI/SafeStack.""" def exists_sym(dynstr, strtab, keywords): if dynstr: for kw in keywords: if kw in dynstr: return True if strtab: for kw in keywords: if kw in strtab: return True return False def get_features_from_note(note): note = note[0x10:] # skip header while note: pr_type, note = u32(note[:4]), note[4:] pr_datasz, note = u32(note[:4]), note[4:] pr_data, note = note[:pr_datasz], note[pr_datasz:] if pr_type == 0xc000_0002: # GNU_PROPERTY_X86_FEATURE_1_AND if pr_datasz == 4: return u32(pr_data) pr_padding = 0 while (pr_datasz + pr_padding) % current_arch.ptrsize: pr_padding += 1 note = note[pr_padding:] return 0 dynstr = self.read_shdr(".dynstr") if dynstr: dynstr = dynstr.split(b"\0") strtab = self.read_shdr(".strtab") if strtab: strtab = strtab.split(b"\0") sec = {} # Static sec["Static"] = self.is_static() # Symbol sec["Symbol"] = not self.is_stripped() # Debuginfo sec["Debuginfo"] = self.has_debuginfo() # Canary if self.is_static() and self.is_stripped(): sec["Canary"] = self.has_canary_heuristic() else: keywords = [ b"__stack_chk_fail", b"__stack_chk_fail_local", b"__stack_chk_guard", # for non-x86 b"__intel_security_cookie", # for intel compiler ] sec["Canary"] = exists_sym(dynstr, strtab, keywords) # NX sec["NX"] = self.is_nx() # PIE sec["PIE"] = self.is_pie() # RELRO sec["Partial RELRO"] = self.is_relro() sec["Full RELRO"] = sec["Partial RELRO"] and self.is_full_relro() # Fortify if self.is_static() and self.is_stripped(): sec["Fortify"] = None # it means unknown else: keywords = [ b"__memcpy_chk", b"__memmove_chk", b"__mempcpy_chk", b"__memset_chk", b"__printf_chk", b"__fprintf_chk", b"__dprintf_chk", b"__sprintf_chk", b"__asprintf_chk", b"__snprintf_chk", b"__wprintf_chk", b"__fwprintf_chk", b"__swprintf_chk", b"__obstack_printf_chk", b"__vprintf_chk", b"__vfprintf_chk", b"__vdprintf_chk", b"__vsprintf_chk", b"__vasprintf_chk", b"__vsnprintf_chk", b"__vwprintf_chk", b"__vfwprintf_chk", b"__vswprintf_chk", b"__obstack_vprintf_chk", b"__syslog_chk", b"__vsyslog_chk", ] sec["Fortify"] = exists_sym(dynstr, strtab, keywords) # CET flags via Ehdr if self.e_machine in (Elf.EM_X86_64, Elf.EM_386): sec["CET IBT flag"] = False sec["CET SHSTK flag"] = False note_gnu_property = self.read_phdr(Elf.Phdr.PT_GNU_PROPERTY) if note_gnu_property: features = get_features_from_note(note_gnu_property) sec["CET IBT flag"] = features & 1 # GNU_PROPERTY_X86_FEATURE_1_IBT sec["CET SHSTK flag"] = features & 2 # GNU_PROPERTY_X86_FEATURE_1_SHSTK # PAC if self.e_machine == Elf.EM_AARCH64: sec["PAC"] = self.has_pac_heuristic() # RPATH sec["RPATH"] = self.has_rpath() # RUNPATH sec["RUNPATH"] = self.has_runpath() # Clang CFI (detected only when `-fno-sanitize-trap=all`) if self.is_static() and self.is_stripped(): sec["Clang CFI"] = None else: sec["Clang CFI"] = exists_sym(dynstr, strtab, ["__ubsan_handle_cfi_"]) # Clang SafeStack if self.is_static() and self.is_stripped(): sec["Clang SafeStack"] = None else: sec["Clang SafeStack"] = exists_sym(dynstr, strtab, ["__safestack_init"]) return sec class Phdr: # p_type PT_NULL = 0 PT_LOAD = 1 PT_DYNAMIC = 2 PT_INTERP = 3 PT_NOTE = 4 PT_SHLIB = 5 PT_PHDR = 6 PT_TLS = 7 #PT_LOOS = 0x6000_0000 PT_GNU_EH_FRAME = 0x6474_e550 PT_GNU_STACK = 0x6474_e551 PT_GNU_RELRO = 0x6474_e552 PT_GNU_PROPERTY = 0x6474_e553 PT_GNU_SFRAME = 0x6474_e554 #PT_GNU_MBIND_LO = 0x6474_e555 #PT_GNU_MBIND_HI = 0x6474_f554 #PT_LOSUNW = 0x6fff_fffa PT_SUNWBSS = 0x6fff_fffa PT_SUNWSTACK = 0x6fff_fffb #PT_HISUNW = 0x6fff_ffff #PT_HIOS = 0x6fff_ffff #PT_LOPROC = 0x7000_0000 #PT_HIPROC = 0x7fff_ffff PT_AARCH64_ARCHEXT = 0x7000_0000 # noqa: F841 PT_AARCH64_MEMTAG_MTE = 0x7000_0002 # noqa: F841 PT_ARM_ARCHEXT = 0x7000_0000 # noqa: F841 PT_ARM_EXIDX = 0x7000_0001 # noqa: F841 PT_HP_TLS = 0x6000_0000 # noqa: F841 PT_HP_CORE_NONE = 0x6000_0001 # noqa: F841 PT_HP_CORE_VERSION = 0x6000_0002 # noqa: F841 PT_HP_CORE_KERNEL = 0x6000_0003 # noqa: F841 PT_HP_CORE_COMM = 0x6000_0004 # noqa: F841 PT_HP_CORE_PROC = 0x6000_0005 # noqa: F841 PT_HP_CORE_LOADABLE = 0x6000_0006 # noqa: F841 PT_HP_CORE_STACK = 0x6000_0007 # noqa: F841 PT_HP_CORE_SHM = 0x6000_0008 # noqa: F841 PT_HP_CORE_MMF = 0x6000_0009 # noqa: F841 PT_HP_PARALLEL = 0x6000_0010 # noqa: F841 PT_HP_FASTBIND = 0x6000_0011 # noqa: F841 PT_HP_OPT_ANNOT = 0x6000_0012 # noqa: F841 PT_HP_HSL_ANNOT = 0x6000_0013 # noqa: F841 PT_HP_STACK = 0x6000_0014 # noqa: F841 PT_HP_CORE_UTSNAME = 0x6000_0015 # noqa: F841 PT_IA_64_HP_OPT_ANOT = 0x6000_0012 # noqa: F841 PT_IA_64_HP_HSL_ANOT = 0x6000_0013 # noqa: F841 PT_IA_64_HP_STACK = 0x6000_0014 # noqa: F841 PT_IA_64_ARCHEXT = 0x7000_0000 # noqa: F841 PT_IA_64_UNWIND = 0x7000_0001 # noqa: F841 PT_MIPS_REGINFO = 0x7000_0000 # noqa: F841 PT_MIPS_RTPROC = 0x7000_0001 # noqa: F841 PT_MIPS_OPTIONS = 0x7000_0002 # noqa: F841 PT_MIPS_ABIFLAGS = 0x7000_0003 # noqa: F841 PT_PARISC_ARCHEXT = 0x7000_0000 # noqa: F841 PT_PARISC_UNWIND = 0x7000_0001 # noqa: F841 PT_PARISC_WEAKORDER = 0x7000_0002 # noqa: F841 PT_RISCV_ATTRIBUTES = 0x7000_0003 # noqa: F841 PT_S390_PGSTE = 0x7000_0000 # noqa: F841 # p_flags PF_X = 1 PF_W = 2 PF_R = 4 PF_ARM_SB = 0x1000_0000 # noqa: F841 PF_ARM_PI = 0x2000_0000 # noqa: F841 PF_ARM_ABS = 0x4000_0000 # noqa: F841 PF_HP_CODE = 0x0004_0000 # noqa: F841 PF_HP_MODIFY = 0x0008_0000 # noqa: F841 PF_HP_PAGE_SIZE = 0x0010_0000 # noqa: F841 PF_HP_FAR_SHARED = 0x0020_0000 # noqa: F841 PF_HP_NEAR_SHARED = 0x0040_0000 # noqa: F841 PF_HP_LAZYSWAP = 0x0080_0000 # noqa: F841 PF_HP_CODE_DEPR = 0x0100_0000 # noqa: F841 PF_HP_MODIFY_DEPR = 0x0200_0000 # noqa: F841 PF_HP_LAZYSWAP_DEPR = 0x0400_0000 # noqa: F841 PF_HP_SBP = 0x0800_0000 # noqa: F841 PF_IA_64_NORECOV = 0x8000_0000 # noqa: F841 PF_OVERRAY = 0x0800_0000 # noqa: F841 PF_PARISC_SBP = 0x0800_0000 # noqa: F841 p_type = None p_flags = None p_offset = None p_vaddr = None p_paddr = None p_filesz = None p_memsz = None p_align = None def __init__(self, elf, off): if elf is None: return None elf.seek(off) endian = "<" if elf.e_endianness == Elf.LITTLE_ENDIAN else ">" if elf.e_class == Elf.ELF_64_BITS: self.p_type, self.p_flags, self.p_offset = struct.unpack("{}IIQ".format(endian), elf.read(16)) self.p_vaddr, self.p_paddr = struct.unpack("{}QQ".format(endian), elf.read(16)) self.p_filesz, self.p_memsz, self.p_align = struct.unpack("{}QQQ".format(endian), elf.read(24)) else: self.p_type, self.p_offset = struct.unpack("{}II".format(endian), elf.read(8)) self.p_vaddr, self.p_paddr = struct.unpack("{}II".format(endian), elf.read(8)) self.p_filesz, self.p_memsz, = struct.unpack("{}II".format(endian), elf.read(8)) self.p_flags, self.p_align = struct.unpack("{}II".format(endian), elf.read(8)) def __repr__(self): for e in dir(self): if e.startswith("PT_"): if self.p_type == getattr(self, e): return "<{:s}.{:s} object at {:#x}, p_type={:s}>".format( self.__module__, self.__class__.__name__, id(self), e, ) return "<{:s}.{:s} object at {:#x}, p_type={:#x}>".format( self.__module__, self.__class__.__name__, id(self), self.p_type, ) class Shdr: # sh_type SHT_NULL = 0 SHT_PROGBITS = 1 SHT_SYMTAB = 2 SHT_STRTAB = 3 SHT_RELA = 4 SHT_HASH = 5 SHT_DYNAMIC = 6 SHT_NOTE = 7 SHT_NOBITS = 8 SHT_REL = 9 SHT_SHLIB = 10 SHT_DYNSYM = 11 SHT_INIT_ARRAY = 14 SHT_FINI_ARRAY = 15 SHT_PREINIT_ARRAY = 16 SHT_GROUP = 17 SHT_SYMTAB_SHNDX = 18 SHT_RELR = 19 #SHT_LOOS = 0x6000_0000 SHT_ANDROID_REL = 0x6000_0001 SHT_ANDROID_RELA = 0x6000_0002 SHT_HP_OVLBITS = 0x6000_0000 # noqa: F841 SHT_HP_DLKM = 0x6000_0001 # noqa: F841 SHT_HP_COMDAT = 0x6000_0002 # noqa: F841 SHT_HP_OBJDICT = 0x6000_0003 # noqa: F841 SHT_HP_ANNOT = 0x6000_0004 # noqa: F841 SHT_IA_64_VMS_TRACE = 0x6000_0000 # noqa: F841 SHT_IA_64_VMS_TIE_SIGNATURES = 0x6000_0001 # noqa: F841 SHT_IA_64_VMS_DEBUG = 0x6000_0002 # noqa: F841 SHT_IA_64_VMS_DEBUG_STR = 0x6000_0003 # noqa: F841 SHT_IA_64_VMS_LINKAGES = 0x6000_0004 # noqa: F841 SHT_IA_64_VMS_SYMBOL_VECTOR = 0x6000_0005 # noqa: F841 SHT_IA_64_VMS_FIXUP = 0x6000_0006 # noqa: F841 SHT_IA_64_VMS_DISPLAY_NAME_INFO = 0x6000_0007 # noqa: F841 SHT_GNU_INCREMENTAL_INPUTS = 0x6fff_4700 SHT_LLVM_ODRTAB = 0x6fff_4c00 SHT_LLVM_LINKER_OPTIONS = 0x6fff_4c01 SHT_LLVM_CALL_GRAPH_PROFILE = 0x6fff_4c02 SHT_LLVM_ADDRSIG = 0x6fff_4c03 SHT_LLVM_DEPENDENT_LIBRARIES = 0x6fff_4c04 SHT_LLVM_SYMPART = 0x6fff_4c05 SHT_LLVM_PART_EHDR = 0x6fff_4c06 SHT_LLVM_PART_PHDR = 0x6fff_4c07 SHT_LLVM_BB_ADDR_MAP_V0 = 0x6fff_4c08 SHT_LLVM_CALL_GRAPH_PROFILE = 0x6fff_4c09 SHT_LLVM_BB_ADDR_MAP = 0x6fff_4c0a SHT_LLVM_OFFLOADING = 0x6fff_4c0b SHT_LLVM_LTO = 0x6fff_4c0c SHT_ANDROID_RELR = 0x6fff_ff00 SHT_GNU_ATTRIBUTES = 0x6fff_fff5 SHT_GNU_HASH = 0x6fff_fff6 SHT_GNU_LIBLIST = 0x6fff_fff7 SHT_CHECKSUM = 0x6fff_fff8 #SHT_LOSUNW = 0x6fff_fffa SHT_SUNW_move = 0x6fff_fffa SHT_SUNW_COMDAT = 0x6fff_fffb SHT_SUNW_syminfo = 0x6fff_fffc SHT_GNU_verdef = 0x6fff_fffd SHT_GNU_verneed = 0x6fff_fffe SHT_GNU_versym = 0x6fff_ffff #SHT_HISUNW = 0x6fff_ffff #SHT_HIOS = 0x6fff_ffff #SHT_LOPROC = 0x7000_0000 SHT_AARCH64_ATTRIBUTES = 0x7000_0003 # noqa: F841 SHT_AARCH64_AUTH_RELR = 0x7000_0004 # noqa: F841 SHT_AARCH64_MEMTAG_GLOBALS_STATIC = 0x7000_0007 # noqa: F841 SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC = 0x7000_0008 # noqa: F841 SHT_ALPHA_DEBUG = 0x7000_0001 # noqa: F841 SHT_ALPHA_REGINFO = 0x7000_0002 # noqa: F841 SHT_ARC_ATTRIBUTES = 0x7000_0001 # noqa: F841 SHT_ARM_EXIDX = 0x7000_0001 # noqa: F841 SHT_ARM_PREEMPTMAP = 0x7000_0002 # noqa: F841 SHT_ARM_ATTRIBUTES = 0x7000_0003 # noqa: F841 SHT_ARM_DEBUGOVERLAY = 0x7000_0004 # noqa: F841 SHT_ARM_OVERLAYSECTION = 0x7000_0005 # noqa: F841 SHT_C6000_UNWIND = 0x7000_0001 # noqa: F841 SHT_C6000_PREEMPTMAP = 0x7000_0002 # noqa: F841 SHT_C6000_ATTRIBUTES = 0x7000_0003 # noqa: F841 SHT_CSKY_ATTRIBUTES = 0x7000_0001 # noqa: F841 SHT_IA_64_EXT = 0x7000_0000 # noqa: F841 SHT_IA_64_UNWIND = 0x7000_0001 # noqa: F841 SHT_IA_64_LOPSREG = 0x7800_0000 # noqa: F841 SHT_IA_64_HIPSREG = 0x78ff_ffff # noqa: F841 SHT_IA_64_PRIORITY_INIT = 0x7900_0000 # noqa: F841 SHT_MIPS_LIBLIST = 0x7000_0000 # noqa: F841 SHT_MIPS_MSYM = 0x7000_0001 # noqa: F841 SHT_MIPS_CONFLICT = 0x7000_0002 # noqa: F841 SHT_MIPS_GPTAB = 0x7000_0003 # noqa: F841 SHT_MIPS_UCODE = 0x7000_0004 # noqa: F841 SHT_MIPS_DEBUG = 0x7000_0005 # noqa: F841 SHT_MIPS_REGINFO = 0x7000_0006 # noqa: F841 SHT_MIPS_PACKAGE = 0x7000_0007 # noqa: F841 SHT_MIPS_PACKSYM = 0x7000_0008 # noqa: F841 SHT_MIPS_RELD = 0x7000_0009 # noqa: F841 SHT_MIPS_IFACE = 0x7000_000b # noqa: F841 SHT_MIPS_CONTENT = 0x7000_000c # noqa: F841 SHT_MIPS_OPTIONS = 0x7000_000d # noqa: F841 SHT_MIPS_SHDR = 0x7000_0010 # noqa: F841 SHT_MIPS_FDESC = 0x7000_0011 # noqa: F841 SHT_MIPS_EXTSYM = 0x7000_0012 # noqa: F841 SHT_MIPS_DENSE = 0x7000_0013 # noqa: F841 SHT_MIPS_PDESC = 0x7000_0014 # noqa: F841 SHT_MIPS_LOCSYM = 0x7000_0015 # noqa: F841 SHT_MIPS_AUXSYM = 0x7000_0016 # noqa: F841 SHT_MIPS_OPTSYM = 0x7000_0017 # noqa: F841 SHT_MIPS_LOCSTR = 0x7000_0018 # noqa: F841 SHT_MIPS_LINE = 0x7000_0019 # noqa: F841 SHT_MIPS_RFDESC = 0x7000_001a # noqa: F841 SHT_MIPS_DELTASYM = 0x7000_001b # noqa: F841 SHT_MIPS_DELTAINST = 0x7000_001c # noqa: F841 SHT_MIPS_DELTACLASS = 0x7000_001d # noqa: F841 SHT_MIPS_DWARF = 0x7000_001e # noqa: F841 SHT_MIPS_DELTADECL = 0x7000_001f # noqa: F841 SHT_MIPS_SYMBOL_LIB = 0x7000_0020 # noqa: F841 SHT_MIPS_EVENTS = 0x7000_0021 # noqa: F841 SHT_MIPS_TRANSLATE = 0x7000_0022 # noqa: F841 SHT_MIPS_PIXIE = 0x7000_0023 # noqa: F841 SHT_MIPS_XLATE = 0x7000_0024 # noqa: F841 SHT_MIPS_XLATE_DEBUG = 0x7000_0025 # noqa: F841 SHT_MIPS_WHIRL = 0x7000_0026 # noqa: F841 SHT_MIPS_EH_REGION = 0x7000_0027 # noqa: F841 SHT_MIPS_XLATE_OLD = 0x7000_0028 # noqa: F841 SHT_MIPS_PDR_EXCEPTION = 0x7000_0029 # noqa: F841 SHT_MIPS_ABIFLAGS = 0x7000_002a # noqa: F841 SHT_MIPS_XHASH = 0x7000_002b # noqa: F841 SHT_MSP430_ATTRIBUTES = 0x7000_0003 # noqa: F841 SHT_MSP430_SEC_FLAGS = 0x7000_0005 # noqa: F841 SHT_MSP430_SYM_ALIASES = 0x7000_0006 # noqa: F841 SHT_NFP_MECONFIG = 0x7000_0001 # noqa: F841 SHT_NFP_INITREG = 0x7000_0002 # noqa: F841 SHT_PARISC_EXT = 0x7000_0000 # noqa: F841 SHT_PARISC_UNWIND = 0x7000_0001 # noqa: F841 SHT_PARISC_DOC = 0x7000_0002 # noqa: F841 SHT_PARISC_ANNOT = 0x7000_0003 # noqa: F841 SHT_PARISC_DLKM = 0x7000_0004 # noqa: F841 SHT_PARISC_SYMEXTN = 0x7000_0008 # noqa: F841 SHT_PARISC_STUBS = 0x7000_0009 # noqa: F841 SHT_RISCV_ATTRIBUTES = 0x7000_0003 # noqa: F841 SHT_V850_SCOMMON = 0x7000_0000 # noqa: F841 SHT_V850_TCOMMON = 0x7000_0001 # noqa: F841 SHT_V850_ZCOMMON = 0x7000_0002 # noqa: F841 SHT_X86_64_UNWIND = 0x7000_0001 # noqa: F841 SHT_TI_ICODE = 0x7F00_0000 # noqa: F841 SHT_TI_XREF = 0x7F00_0001 # noqa: F841 SHT_TI_HANDLER = 0x7F00_0002 # noqa: F841 SHT_TI_INITINFO = 0x7F00_0003 # noqa: F841 SHT_TI_PHATTRS = 0x7F00_0004 # noqa: F841 SHT_ORDERED = 0x7fff_ffff # noqa: F841 #SHT_HIPROC = 0x7fff_ffff #SHT_LOUSER = 0x8000_0000 SHT_NFP_UDEBUG = 0x8000_0000 # noqa: F841 SHT_RENESAS_IOP = 0x8000_0000 # noqa: F841 #SHT_HIUSER = 0x8fff_ffff SHT_RENESAS_INFO = 0xa000_0000 # noqa: F841 # sh_flags SHF_WRITE = 1 SHF_ALLOC = 2 SHF_EXECINSTR = 4 SHF_MERGE = 0x10 SHF_STRINGS = 0x20 SHF_INFO_LINK = 0x40 SHF_LINK_ORDER = 0x80 SHF_OS_NONCONFORMING = 0x100 SHF_GROUP = 0x200 SHF_TLS = 0x400 SHF_COMPRESSED = 0x800 SHF_RELA_LIVEPATCH = 0x0010_0000 # noqa: F841 SHF_RO_AFTER_INIT = 0x0020_0000 # noqa: F841 SHF_ORDERED = 0x4000_0000 # noqa: F841 SHF_EXCLUDE = 0x8000_0000 SHF_MIPS_NODUPES = 0x0100_0000 # noqa: F841 SHF_MIPS_NAMES = 0x0200_0000 # noqa: F841 SHF_MIPS_LOCAL = 0x0400_0000 # noqa: F841 SHF_MIPS_NOSTRIP = 0x0800_0000 # noqa: F841 SHF_MIPS_GPREL = 0x1000_0000 # noqa: F841 SHF_MIPS_MERGE = 0x2000_0000 # noqa: F841 SHF_MIPS_ADDR = 0x4000_0000 # noqa: F841 SHF_MIPS_STRING = 0x8000_0000 # noqa: F841 SHF_PARISC_SHORT = 0x2000_0000 # noqa: F841 SHF_PARISC_HUGE = 0x4000_0000 # noqa: F841 SHF_PARISC_SBP = 0x8000_0000 # noqa: F841 SHF_ALPHA_GPREL = 0x1000_0000 # noqa: F841 SHF_IA_64_SHORT = 0x1000_0000 # noqa: F841 sh_name = None sh_type = None sh_flags = None sh_addr = None sh_offset = None sh_size = None sh_link = None sh_info = None sh_addralign = None sh_entsize = None def __init__(self, elf, off): if elf is None: return None elf.seek(off) endian = "<" if elf.e_endianness == Elf.LITTLE_ENDIAN else ">" if elf.e_class == Elf.ELF_64_BITS: self.sh_name, self.sh_type, self.sh_flags = struct.unpack("{}IIQ".format(endian), elf.read(16)) self.sh_addr, self.sh_offset = struct.unpack("{}QQ".format(endian), elf.read(16)) self.sh_size, self.sh_link, self.sh_info = struct.unpack("{}QII".format(endian), elf.read(16)) self.sh_addralign, self.sh_entsize = struct.unpack("{}QQ".format(endian), elf.read(16)) else: self.sh_name, self.sh_type, self.sh_flags = struct.unpack("{}III".format(endian), elf.read(12)) self.sh_addr, self.sh_offset = struct.unpack("{}II".format(endian), elf.read(8)) self.sh_size, self.sh_link, self.sh_info = struct.unpack("{}III".format(endian), elf.read(12)) self.sh_addralign, self.sh_entsize = struct.unpack("{}II".format(endian), elf.read(8)) # name stroff = elf.e_shoff + elf.e_shentsize * elf.e_shstrndx if elf.e_class == Elf.ELF_64_BITS: elf.seek(stroff + 16 + 8) offset = struct.unpack("{}Q".format(endian), elf.read(8))[0] else: elf.seek(stroff + 12 + 4) offset = struct.unpack("{}I".format(endian), elf.read(4))[0] elf.seek(offset + self.sh_name) self.sh_name = "" while True: c = ord(elf.read(1)) if c == 0: break self.sh_name += chr(c) return def __repr__(self): return '<{:s}.{:s} object at {:#x}, sh_name="{:s}">'.format( self.__module__, self.__class__.__name__, id(self), self.sh_name, ) class Instruction: """GEF representation of a CPU instruction.""" RE_SPLIT_LAST_OPERAND_X86_64 = re.compile(r"(.*?)\s+(#.+)$") RE_SPLIT_LAST_OPERAND_ARM64 = re.compile(r"//.+$") RE_SPLIT_LAST_OPERAND_ARM32 = re.compile(r";.+$") RE_SPLIT_LAST_OPERAND_MICROBLAZE = re.compile(r"//.+$") RE_SPLIT_LAST_OPERAND_LOONGARCH64 = re.compile(r"(# .*)$") RE_SPLIT_ELEM = re.compile(r"([*%\[\](): ]|(?)") RE_IS_DIGIT_COMMENT = re.compile(r"#?-?(0x[0-9a-f]+|\d+)") RE_SPLIT_SYMBOL = re.compile(r"(.*?)<(.+)>(.*)$") RE_SPLIT_SYMBOL_OFFSET = re.compile(r"(.+)\+(\d+)$") def __init__(self, address, mnemo, operands, opcodes): # example: # address: 0x55555555a7d0 # mnemo: "lea" # operands: "rcx, [rip+0x11ee5] # 0x55555556c69a" # -> ["rcx", "[rip+0x11ee5] # 0x55555556c69a"] # opcodes: b'H\x8d\r\xe5\x1e\x01\x00' self.address = address self.mnemonic = mnemo # merge symbol includes ","; e.g., <... , ...> operands = [x.strip() for x in operands.split(",")] if len(operands) > 1: operands, o = operands[:-1], operands[-1] while (o.count("<") - o.count("operator<<") * 2) != (o.count(">") - o.count("operator>>") * 2): # The split location is incorrect, so fix it. # This is because there are cases where symbols contain `,`, such as C++ binaries. if len(operands) > 1: operands, oo = operands[:-1], operands[-1] o = oo + ", " + o else: o = operands[0] + ", " + o operands = [] break operands += [o] self.operands = operands self.opcodes = opcodes self.size = len(opcodes) return @property def location(self): if hasattr(self, "__location"): return self.__location # evaluate when needed self.__location = Symbol.get_symbol_string(self.address, nosymbol_string=" ") return self.__location @property def is_branch(self): """Return whether it is a branch instruction. Cache the results.""" if hasattr(self, "__is_branch"): return self.__is_branch if current_arch.is_syscall(self): self.__is_branch = True elif current_arch.is_call(self): self.__is_branch = True elif current_arch.is_jump(self): self.__is_branch = True elif current_arch.is_ret(self): self.__is_branch = True elif current_arch.is_conditional_branch(self): self.__is_branch = True else: self.__is_branch = False return self.__is_branch def get_color(self, highlight, config_name): """A wrapper to easily retrieve color-related configurations.""" if highlight: return Config.get_gef_setting(config_name + "_highlight") else: return Config.get_gef_setting(config_name) def get_string_if_valid_addr(self, operands): """If the last operand is an address and is valid, read and return the string.""" if not operands: return None last_operand = operands[-1] if " " in last_operand: last_operand = last_operand.split()[-1] if len(last_operand) < 10: # len("0xXXXXXXXX") or len("0xXXXXXXXXXXXXXXXX") return None try: v = int(last_operand, 0) except ValueError: return None if not is_valid_addr(v): return None s = read_cstring_from_memory(v) return s def split_last_operands(self, operands): """Separate `operands` into real operands and comment for each architecture.""" if len(operands) == 0: return [], "" comment = "" last_operand = operands[-1] if is_x86_64(): r = self.RE_SPLIT_LAST_OPERAND_X86_64.match(last_operand) # r"(.*?)\s+(#.+)$" if r: last_operand = r.group(1) comment = r.group(2) operands = operands[:-1] + [last_operand] elif is_arm64(): r = self.RE_SPLIT_LAST_OPERAND_ARM64.match(last_operand) # r"//.+$" if r: comment = last_operand operands = operands[:-1] elif is_arm32() or is_arm32_cortex_m(): r = self.RE_SPLIT_LAST_OPERAND_ARM32.match(last_operand) # r";.+$" if r: comment = last_operand operands = operands[:-1] elif is_microblaze(): r = self.RE_SPLIT_LAST_OPERAND_MICROBLAZE.match(last_operand) # r"//.+$" if r: comment = last_operand operands = operands[:-1] elif is_loongarch64(): r = self.RE_SPLIT_LAST_OPERAND_LOONGARCH64.match(last_operand) # r"(# .*)$" if r: comment = r.group(1) operands = operands[:-1] return operands, comment def colored_operands_text(self, highlight, operands): """Parse the operands, color each element, and return it as a string.""" color_operands_normal = self.get_color(highlight, "theme.disassemble_operands_normal") color_operands_const = self.get_color(highlight, "theme.disassemble_operands_const") color_operands_symbol = self.get_color(highlight, "theme.disassemble_operands_symbol") # extract -> coloring -> join colored_operands = [] for o1 in operands: colored_o1 = [] # split by *, [, ], (, ), %, :, space, non-first +, - (without #, @, %), <...> for o2 in self.RE_SPLIT_ELEM.split(o1): # r"([*%\[\](): ]|(?)" o2 = o2.strip() if o2 == "": continue if o2[0] == "<": colored_o1.append(self.hexlify_symbol_offset(o2)) colored_o1.append(" ") elif o2 in ["-", "+", "*"]: colored_o1.append(Color.colorify(o2, color_operands_symbol)) colored_o1.append(" ") elif o2 in [":", "%"]: if colored_o1 and colored_o1[-1] == " ": colored_o1 = colored_o1[:-1] colored_o1.append(Color.colorify(o2, color_operands_symbol)) elif o2 in ["[", "("]: colored_o1.append(Color.colorify(o2, color_operands_symbol)) elif o2 in ["]", ")"]: if colored_o1 and colored_o1[-1] == " ": colored_o1 = colored_o1[:-1] colored_o1.append(Color.colorify(o2, color_operands_symbol)) elif self.RE_IS_DIGIT_COMMENT.match(o2): # r"#?-?(0x[0-9a-f]+|\d+)" colored_o1.append(Color.colorify(o2, color_operands_const)) colored_o1.append(" ") else: colored_o1.append(Color.colorify(o2, color_operands_normal)) colored_o1.append(" ") colored_operands.append("".join(colored_o1).strip()) operands_text = Color.colorify(", ", color_operands_symbol).join(colored_operands) return operands_text def hexlify_symbol_offset(self, x): """i.e., -> """ r1 = self.RE_SPLIT_SYMBOL.match(x) # r"(.*?)<(.+)>(.*)$" if not r1: return x r2 = self.RE_SPLIT_SYMBOL_OFFSET.match(r1.group(2)) # r"(.+)\+(\d+)$" if r2: sym_x = "{}+{:#x}".format(self.smartify_text(r2.group(1)), int(r2.group(2))) else: sym_x = self.smartify_text(r1.group(2)) return "{:s}<{:s}>{:s}".format(r1.group(1), sym_x, r1.group(3)) def get_opcodes_hex(self, opcodes_len): opcodes_hex = "".join("{:02x}".format(b) for b in self.opcodes) # e.g., "488d0de51e0100" # e.g., len=4: opcodes:01020304 -> 01020304 # e.g., len=4: opcodes:0102030405 -> 010203.. if opcodes_len < len(self.opcodes): opcodes_hex = opcodes_hex[:opcodes_len * 2 - 2] + ".." opcodes_hex = "{:{:d}}".format(opcodes_hex, opcodes_len * 2) return opcodes_hex def colored_text(self, opcodes_len=0, highlight=False, disable_color=False): """Color the entire instruction, format it as a string and return it.""" if opcodes_len == 0: return str(self) enable_color = not disable_color # format address address_text = hex(self.address) # format location location_text = self.smartify_text(self.location) if enable_color: color_address = self.get_color(highlight, "theme.disassemble_address") address_text = Color.colorify(address_text, color_address) location_text = Color.colorify(location_text, color_address) # format opcode opcodes_hex = self.get_opcodes_hex(opcodes_len) if enable_color: color_opcode = self.get_color(highlight, "theme.disassemble_opcode") opcodes_hex = Color.colorify(opcodes_hex, color_opcode) # format mnemonic mnemonic_text = "{:6s}".format(self.mnemonic) if enable_color: if self.is_branch: color_mnemonic = self.get_color(highlight, "theme.disassemble_mnemonic_branch") else: color_mnemonic = self.get_color(highlight, "theme.disassemble_mnemonic_normal") mnemonic_text = Color.colorify(mnemonic_text, color_mnemonic) # split last operand by ;, #, // operands_array, comment = self.split_last_operands(self.operands[::]) comment = self.hexlify_symbol_offset(comment) # format operands if enable_color: operands_text = self.colored_operands_text(highlight, operands_array) else: operands_text = ", ".join(operands_array) # the case that gdb does not append symbol but symbol exists if self.is_branch: if "<" not in operands_text and "<" not in comment: if self.operands and self.operands[-1]: addr = ContextCodeCommand.get_branch_addr(self) # Not using += is intentional; useless comments are discarded comment = Symbol.get_symbol_string(addr).lstrip() # add strings if not self.is_branch: if not comment: s = self.get_string_if_valid_addr(operands_array) else: s = self.get_string_if_valid_addr([comment]) if s: if enable_color: string_color = Config.get_gef_setting("theme.dereference_string") if len(s) < current_arch.ptrsize: comment += " ({:s}?)".format(Color.colorify(repr(s), string_color)) else: comment += " ({:s})".format(Color.colorify(repr(s), string_color)) else: if len(s) < current_arch.ptrsize: comment += " ({:s}?)".format(repr(s)) else: comment += " ({:s})".format(repr(s)) # formatting out = "{:s} {:s} {:s} {:s} {:s} {:s}".format( address_text, opcodes_hex, location_text, mnemonic_text, operands_text, comment, ).rstrip() return out def __repr__(self): return '<{:s}.{:s} object at {:#x}, asm="{:s}">'.format( self.__module__, self.__class__.__name__, id(self), str(self), ) def __str__(self): location = self.smartify_text(self.location) operands = self.smartify_text(", ".join(self.operands)) return "{:#10x} {:20s} {:6s} {:s}".format(self.address, location, self.mnemonic, operands) def is_valid(self): return "(bad)" not in self.mnemonic @staticmethod def smartify_text(text): """Simplify and shorten C++ function/type names for improved readability.""" smart_cpp_function_name = Config.get_gef_setting("context.smart_cpp_function_name") if not smart_cpp_function_name: return text if text is None: return text if len(text) == 0: return text text = re.sub(r"\bstd::__1::", "", text) old_text = text[::] while True: text = re.sub(r"\([^(]+?\)", "__MARKER_GEF__", text) if text == old_text: break old_text = text[::] text = re.sub("__MARKER_GEF__", "(...)", text) m = re.match(r"^(\s*\<)(.*)(\>\s*)$", text) if m: text_0, text, text_end = m.group(1), m.group(2), m.group(3) else: text_0, text, text_end = "", text, "" while True: text = re.sub(r"\<[^<]+?\>", "__MARKER_GEF__", text) if text == old_text: break old_text = text[::] text = re.sub("__MARKER_GEF__", "<...>", text) if text_0: text = text_0 + text if text_end: text = text + text_end return text class GenericType: def __init__(self, addr): self.__addr = addr self.char_t = GefUtil.cached_lookup_type("unsigned char") self.int_t = GefUtil.cached_lookup_type("unsigned int") self.long_t = GefUtil.cached_lookup_type("unsigned long") self.size_t = GefUtil.cached_lookup_type("size_t") if not self.size_t: ptr_type = "unsigned long" if current_arch.ptrsize == 8 else "unsigned int" self.size_t = GefUtil.cached_lookup_type(ptr_type) return @property def addr(self): return self.__addr @property @abc.abstractmethod def sizeof(self): pass # helper methods def __getitem__(self, item): return getattr(self, item) def get_size_t(self, addr): return AddressUtil.dereference(addr).cast(self.size_t) def get_size_t_pointer(self, addr): size_t_pointer = self.size_t.pointer() return AddressUtil.dereference(addr).cast(size_t_pointer) def get_size_t_array(self, addr, length): size_t_array = self.size_t.array(length) return AddressUtil.dereference(addr).cast(size_t_array) def get_int_t(self, addr): return AddressUtil.dereference(addr).cast(self.int_t) def get_int_t_array(self, addr, length): int_t_array = self.int_t.array(length) return AddressUtil.dereference(addr).cast(int_t_array) def get_char_t_pointer(self, addr): char_t_pointer = self.char_t.pointer() return AddressUtil.dereference(addr).cast(char_t_pointer) def get_char_t_array(self, addr, length): char_t_array = self.char_t.array(length) return AddressUtil.dereference(addr).cast(char_t_array) def get_long_t(self, addr): return AddressUtil.dereference(addr).cast(self.long_t) class GlibcHeap: """Manage glibc heap-specific settings.""" class HeapInfo(GenericType): """GEF representation of heap_info.""" @staticmethod def MALLOC_ALIGNMENT(): if is_64bit(): return 0x10 elif (is_x86_32() or is_riscv32() or is_ppc32()) and get_libc_version() >= (2, 26): return 0x10 else: return 0x8 @staticmethod def MIN_SIZE(): if is_64bit(): return 0x20 else: return 0x10 def __init__(self, addr): super().__init__(addr) self.MALLOC_ALIGN_MASK = GlibcHeap.HeapInfo.MALLOC_ALIGNMENT() - 1 return # struct offsets @property def addrof_ar_ptr(self): return self.addr @property def addrof_prev(self): return self.addrof_ar_ptr + self.char_t.pointer().sizeof @property def addrof_size(self): return self.addrof_prev + self.char_t.pointer().sizeof @property def addrof_mprotect_size(self): return self.addrof_size + self.size_t.sizeof @property def addrof_pagesize(self): if get_libc_version() >= (2, 35): return self.addrof_mprotect_size + self.size_t.sizeof else: return None @property def addrof_pad(self): if get_libc_version() >= (2, 35): return self.addrof_pagesize + self.size_t.sizeof else: return self.addrof_mprotect_size + self.size_t.sizeof @property def sizeof(self): if get_libc_version() >= (2, 35): end = self.addrof_pad + (-3 * self.size_t.sizeof) & self.MALLOC_ALIGN_MASK else: end = self.addrof_pad + (-6 * self.size_t.sizeof) & self.MALLOC_ALIGN_MASK return end - self.addr # struct members @property def ar_ptr(self): return self.get_char_t_pointer(self.addrof_ar_ptr) @property def prev(self): return self.get_char_t_pointer(self.addrof_prev) @property def size(self): return self.get_size_t(self.addrof_size) @property def mprotect_size(self): return self.get_size_t(self.addrof_mprotect_size) @property def pagesize(self): if get_libc_version() >= (2, 35): return self.get_size_t(self.addrof_pagesize) else: return None @property def pad(self): if get_libc_version() >= (2, 35): length = (-3 * self.size_t.sizeof) & self.MALLOC_ALIGN_MASK else: length = (-6 * self.size_t.sizeof) & self.MALLOC_ALIGN_MASK return self.get_char_t_array(self.addrof_pad, length) class MallocPar(GenericType): """GEF representation of malloc_par.""" # struct offsets @property def addrof_trim_threshold(self): return self.addr @property def addrof_top_pad(self): return self.addrof_trim_threshold + self.long_t.sizeof @property def addrof_mmap_threshold(self): return self.addrof_top_pad + self.size_t.sizeof @property def addrof_arena_test(self): return self.addrof_mmap_threshold + self.size_t.sizeof @property def addrof_arena_max(self): return self.addrof_arena_test + self.size_t.sizeof @property def addrof_thp_mode(self): if get_libc_version() >= (2, 43): return self.addrof_arena_max + self.size_t.sizeof else: return None @property def addrof_thp_pagesize(self): if get_libc_version() >= (2, 43): return self.addrof_thp_mode + self.size_t.sizeof elif get_libc_version() >= (2, 35): return self.addrof_arena_max + self.size_t.sizeof else: return None @property def addrof_hp_pagesize(self): if get_libc_version() >= (2, 35): return self.addrof_thp_pagesize + self.size_t.sizeof else: return None @property def addrof_hp_flags(self): if get_libc_version() >= (2, 35): return self.addrof_hp_pagesize + self.size_t.sizeof else: return None @property def addrof_n_mmaps(self): if get_libc_version() >= (2, 35): return self.addrof_hp_flags + self.int_t.sizeof else: return self.addrof_arena_max + self.size_t.sizeof @property def addrof_n_mmaps_max(self): return self.addrof_n_mmaps + self.int_t.sizeof @property def addrof_max_n_mmaps(self): return self.addrof_n_mmaps_max + self.int_t.sizeof @property def addrof_no_dyn_threshold(self): return self.addrof_max_n_mmaps + self.int_t.sizeof @property def addrof_pagesize(self): if get_libc_version() >= (2, 15): return None else: return self.addrof_max_n_mmaps + self.int_t.sizeof @property def addrof_mmapped_mem(self): if get_libc_version() >= (2, 15): return align_to_ptrsize(self.addrof_no_dyn_threshold + self.int_t.sizeof) else: return align_to_ptrsize(self.addrof_pagesize + self.int_t.sizeof) @property def addrof_max_mmapped_mem(self): return self.addrof_mmapped_mem + self.size_t.sizeof @property def addrof_max_total_mem(self): if get_libc_version() >= (2, 24): return None else: return self.addrof_mmapped_mem + self.size_t.sizeof @property def addrof_sbrk_base(self): if get_libc_version() >= (2, 24): return self.addrof_max_mmapped_mem + self.size_t.sizeof else: return self.addrof_max_total_mem + self.size_t.sizeof @property def addrof_tcache_bins(self): if get_libc_version() >= (2, 26): return self.addrof_sbrk_base + self.char_t.pointer().sizeof else: return None @property def addrof_tcache_max_bytes(self): if get_libc_version() >= (2, 26): return self.addrof_tcache_bins + self.size_t.sizeof else: return None @property def addrof_tcache_count(self): if get_libc_version() >= (2, 26): return self.addrof_tcache_max_bytes + self.size_t.sizeof else: return None @property def addrof_tcache_unsorted_limit(self): if get_libc_version() >= (2, 26): return self.addrof_tcache_count + self.size_t.sizeof else: return None @property def sizeof(self): if get_libc_version() >= (2, 26): end = self.addrof_tcache_unsorted_limit + self.size_t.sizeof else: end = self.addrof_sbrk_base + self.char_t.pointer().sizeof return end - self.addr # struct members @property def trim_threshold(self): return self.get_long_t(self.addrof_trim_threshold) @property def top_pad(self): return self.get_size_t(self.addrof_top_pad) @property def mmap_threshold(self): return self.get_size_t(self.addrof_mmap_threshold) @property def arena_test(self): return self.get_size_t(self.addrof_arena_test) @property def arena_max(self): return self.get_size_t(self.addrof_arena_max) @property def thp_pagesize(self): if get_libc_version() >= (2, 35): return self.get_size_t(self.addrof_thp_pagesize) else: return None @property def hp_pagesize(self): if get_libc_version() >= (2, 35): return self.get_size_t(self.addrof_hp_pagesize) else: return None @property def hp_flags(self): if get_libc_version() >= (2, 35): return self.get_int_t(self.addrof_hp_flags) else: return None @property def n_mmaps(self): return self.get_int_t(self.addrof_n_mmaps) @property def n_mmaps_max(self): return self.get_int_t(self.addrof_n_mmaps_max) @property def max_n_mmaps(self): return self.get_int_t(self.addrof_max_n_mmaps) @property def no_dyn_threshold(self): return self.get_int_t(self.addrof_no_dyn_threshold) @property def pagesize(self): if get_libc_version() >= (2, 15): return None else: return self.get_int_t(self.addrof_pagesize) @property def mmapped_mem(self): return self.get_size_t(self.addrof_mmapped_mem) @property def max_mmapped_mem(self): return self.get_size_t(self.addrof_max_mmapped_mem) @property def max_total_mem(self): if get_libc_version() >= (2, 24): return None else: return self.get_size_t(self.addrof_max_total_mem) @property def sbrk_base(self): return self.get_char_t_pointer(self.addrof_sbrk_base) @property def tcache_bins(self): if get_libc_version() >= (2, 26): return self.get_size_t(self.addrof_tcache_bins) else: return None @property def tcache_max_bytes(self): if get_libc_version() >= (2, 26): return self.get_size_t(self.addrof_tcache_max_bytes) else: return None @property # noqa def tcache_count(self): if get_libc_version() >= (2, 26): return self.get_size_t(self.addrof_tcache_count) else: return None @property def tcache_unsorted_limit(self): if get_libc_version() >= (2, 26): return self.get_size_t(self.addrof_tcache_unsorted_limit) else: return None @staticmethod @Cache.cache_until_next def search_for_mp_(): """search for mp_ from main_arena, then return addr.""" main_arena_ptr = GlibcHeap.search_for_main_arena_from_tls() if main_arena_ptr is None: return None main_arena = read_int_from_memory(main_arena_ptr) heap_base = HeapbaseCommand.heap_base() if heap_base is None: return None offsetof_sbrk_base = GlibcHeap.MallocPar(0).addrof_sbrk_base current = main_arena - GlibcHeap.MallocPar(0).sizeof for _ in range(0, 500): try: x = read_int_from_memory(current) except gdb.MemoryError: return None if x == heap_base: mp_ = current - offsetof_sbrk_base return mp_ current -= current_arch.ptrsize return None class MallocStateStruct(GenericType): """GEF representation of malloc_state.""" def __init__(self, addr): super().__init__(addr) if get_libc_version() >= (2, 43): self.num_fastbins = 0 elif (is_x86_32() or is_riscv32() or is_ppc32()) and get_libc_version() >= (2, 26): # MALLOC_ALIGNMENT is changed from libc 2.26. # for x86_32, MALLOC_ALIGNMENT = 16, so NFASTBINS = 11. self.num_fastbins = 11 else: self.num_fastbins = 10 NBINS = 128 BINMAPSHIFT = 5 BITSPERMAP = 1 << BINMAPSHIFT BINMAPSIZE = NBINS // BITSPERMAP self.num_bins = NBINS * 2 - 2 self.num_binmap = BINMAPSIZE return # struct offsets @property def addrof_mutex(self): return self.addr @property def addrof_flags(self): return self.addrof_mutex + self.int_t.sizeof @property def addrof_have_fastchunks(self): if get_libc_version() >= (2, 43): return None elif get_libc_version() >= (2, 27): return self.addrof_flags + self.int_t.sizeof else: return None @property def addrof_fastbins(self): if get_libc_version() >= (2, 43): return None elif get_libc_version() >= (2, 27): fastbin_offset = align(self.int_t.sizeof * 3, self.size_t.sizeof) else: fastbin_offset = self.int_t.sizeof * 2 return self.addr + fastbin_offset @property def addrof_top(self): if get_libc_version() >= (2, 43): return self.addrof_flags + self.int_t.sizeof else: return self.addrof_fastbins + self.size_t.sizeof * self.num_fastbins @property def addrof_last_remainder(self): return self.addrof_top + self.size_t.sizeof @property def addrof_bins(self): return self.addrof_last_remainder + self.size_t.sizeof @property def addrof_binmap(self): return self.addrof_bins + self.size_t.sizeof * self.num_bins @property def addrof_next(self): return self.addrof_binmap + self.int_t.sizeof * self.num_binmap @property def addrof_next_free(self): if get_libc_version() >= (2, 19): return self.addrof_next + self.size_t.sizeof else: # Before glibc 2.19, the presence of next_free depends on the environment. # However, it appears to be more likely absent, so None is returned. return None @property def addrof_attached_threads(self): if get_libc_version() >= (2, 23): return self.addrof_next_free + self.size_t.sizeof else: return None @property def addrof_system_mem(self): if get_libc_version() >= (2, 23): return self.addrof_attached_threads + self.size_t.sizeof elif get_libc_version() >= (2, 19): return self.addrof_next_free + self.size_t.sizeof else: return self.addrof_next + self.size_t.sizeof @property def addrof_max_system_mem(self): return self.addrof_system_mem + self.size_t.sizeof @property def sizeof(self): return self.addrof_max_system_mem + self.size_t.sizeof - self.addr # struct members @property def mutex(self): return self.get_int_t(self.addrof_mutex) @property def flags(self): return self.get_int_t(self.addrof_flags) @property def have_fastchunks(self): if get_libc_version() >= (2, 43): return None elif get_libc_version() >= (2, 27): return self.get_int_t(self.addrof_have_fastchunks) else: return None @property def fastbinsY(self): if get_libc_version() >= (2, 43): return None else: return self.get_size_t_array(self.addrof_fastbins, self.num_fastbins) @property def top(self): return self.get_size_t_pointer(self.addrof_top) @property def last_remainder(self): return self.get_size_t_pointer(self.addrof_last_remainder) @property def bins(self): return self.get_size_t_array(self.addrof_bins, self.num_bins) @property def binmap(self): return self.get_int_t_array(self.addrof_binmap, self.num_binmap) @property def next(self): return self.get_size_t_pointer(self.addrof_next) @property def next_free(self): if get_libc_version() >= (2, 19): return self.get_size_t_pointer(self.addrof_next_free) else: return None @property def attached_threads(self): if get_libc_version() >= (2, 23): return self.get_size_t(self.addrof_attached_threads) else: return None @property def system_mem(self): return self.get_size_t(self.addrof_system_mem) @property def max_system_mem(self): return self.get_size_t(self.addrof_max_system_mem) @staticmethod @Cache.cache_until_next def search_for_main_arena_from_tls(): """search for main arena from TLS, then return &addr.""" """ [x64] 0x7ffff7f986f8|+0x0038|007: 0x0000555555559010 -> 0x0000000000000000 0x7ffff7f98700|+0x0040|008: 0x0000000000000000 0x7ffff7f98708|+0x0048|009: 0x00007ffff7e19c80 -> 0x0000000000000000 0x7ffff7f98710|+0x0050|010: 0x0000000000000000 0x7ffff7f98718|+0x0058|011: 0x0000000000000000 0x7ffff7f98720|+0x0060|012: 0x0000000000000000 0x7ffff7f98728|+0x0068|013: 0x0000000000000000 0x7ffff7f98730|+0x0070|014: 0x0000000000000000 0x7ffff7f98738|+0x0078|015: 0x0000000000000000 -- TLS -- 0x7ffff7f98740|+0x0000|000: 0x00007ffff7f98740 -> [loop detected] 0x7ffff7f98748|+0x0008|001: 0x00007ffff7f99160 -> 0x0000000000000001 0x7ffff7f98750|+0x0010|002: 0x00007ffff7f98740 -> [loop detected] [x86] 0xf7fbf4d0|+0x00d0|052: 0x5655a010 -> 0x00000000 0xf7fbf4d4|+0x00d4|053: 0x00000000 0xf7fbf4d8|+0x00d8|054: 0xf7e2a7c0 -> 0x00000000 0xf7fbf4dc|+0x00dc|055: 0x00000000 0xf7fbf4e0|+0x00e0|056: 0x00000000 0xf7fbf4e4|+0x00e4|057: 0x00000000 0xf7fbf4e8|+0x00e8|058: 0x00000000 0xf7fbf4ec|+0x00ec|059: 0x00000000 0xf7fbf4f0|+0x00f0|060: 0x00000000 0xf7fbf4f4|+0x00f4|061: 0x00000000 0xf7fbf4f8|+0x00f8|062: 0x00000000 0xf7fbf4fc|+0x00fc|063: 0x00000000 -- TLS -- 0xf7fbf500|+0x0100|064: 0xf7fbf500 -> [loop detected] 0xf7fbf504|+0x0104|065: 0xf7fbfa88 -> 0x00000001 0xf7fbf508|+0x0108|066: 0xf7fbf500 -> [loop detected] [ARM] -- TLS -- 0x0007d580|+0x0000|000: 0x0007a3c8 <_dl_static_dtv+0x8> -> 0x00000000 0x0007d584|+0x0004|001: 0x00000000 0x0007d588|+0x0008|002: 0x00079fa0 <_nl_global_locale> -> ... 0x0007d58c|+0x000c|003: 0x00079fa0 <_nl_global_locale> -> ... 0x0007d590|+0x0010|004: 0x00079fa4 <_nl_global_locale+0x4> -> ... 0x0007d594|+0x0014|005: 0x00079fb0 <_nl_global_locale+0x10> -> ... 0x0007d598|+0x0018|006: 0x00000000 0x0007d59c|+0x001c|007: 0x00079660 -> 0x00000000 0x0007d5a0|+0x0020|008: 0x0007d908 -> 0x00000000 0x0007d5a4|+0x0024|009: 0x00000000 0x0007d5a8|+0x0028|010: 0x00000000 [ARM64] -- TLS -- 0x0000004997c0|+0x0000|000: 0x0000000000493078 <_dl_static_dtv+0x10> -> 0x0000000000000000 0x0000004997c8|+0x0008|001: 0x0000000000000000 0x0000004997d0|+0x0010|002: 0x0000000000492838 <_nl_global_locale> -> ... 0x0000004997d8|+0x0018|003: 0x0000000000492840 <_nl_global_locale+0x8> -> ... 0x0000004997e0|+0x0020|004: 0x0000000000492838 <_nl_global_locale> -> ... 0x0000004997e8|+0x0028|005: 0x0000000000492858 <_nl_global_locale+0x20> -> ... 0x0000004997f0|+0x0030|006: 0x0000000000000000 0x0000004997f8|+0x0038|007: 0x0000000000491678 -> 0x0000000000000000 0x000000499800|+0x0040|008: 0x0000000000499b90 -> 0x0000000000000000 0x000000499808|+0x0048|009: 0x0000000000000000 0x000000499810|+0x0050|010: 0x0000000000000000 0x000000499818|+0x0058|011: 0x000000000045e780 <_nl_C_LC_CTYPE_class+0x100> -> 0x0002000200020002 0x000000499820|+0x0060|012: 0x000000000045de80 <_nl_C_LC_CTYPE_toupper+0x200> -> 0x0000000100000000 0x000000499828|+0x0068|013: 0x000000000045d880 <_nl_C_LC_CTYPE_tolower+0x200> -> 0x0000000100000000 0x000000499830|+0x0070|014: 0x0000000000000000 0x000000499838|+0x0078|015: 0x0000000000000000 """ orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() threads = gdb.selected_inferior().threads() main_thread = [th for th in threads if th.num == 1][0] main_thread.switch() tls = current_arch.get_tls() if tls is None: return None direction = TlsCommand.get_direction() for i in range(1, 500): addr = tls + (current_arch.ptrsize * i) * direction if is_m68k(): addr += 2 if not is_valid_addr(addr): break candidate_arena_addr = read_int_from_memory(addr) if not is_valid_addr(candidate_arena_addr): continue candidate_arena = GlibcHeap.MallocStateStruct(candidate_arena_addr) if candidate_arena.system_mem < get_pagesize(): continue # Statically built binaries have an unaligned system_mem, # so alignment should not be used to determine the validity of system_mem. top = candidate_arena.top if not is_valid_addr(top): continue next_addr = to_unsigned_long(candidate_arena.next) while True: if not is_valid_addr(next_addr): break if candidate_arena_addr == next_addr: orig_thread.switch() # revert thread orig_frame.select() return addr next_addr = to_unsigned_long(GlibcHeap.MallocStateStruct(next_addr).next) # not found orig_thread.switch() # revert thread orig_frame.select() return None @staticmethod @Cache.cache_this_session_skip_None_cache def search_for_main_arena(): """Search for the address of main_arena using multiple strategies and caches the result.""" if is_arm64(): # For some reason, native gdb (at least v10.1) on ARM64 has a bug where evaluating main_arena # destroys tcache symbols. See issues #95 # Once you evaluate it, it will work without any problems after that. # This is a temporary workaround. try: gdb.execute("p (void*) &tcache", to_string=True) except gdb.error: pass # plan 1 (directly) try: return AddressUtil.parse_address("(void*) &main_arena") except gdb.error: pass # plan 2 (from __malloc_hook) if get_libc_version() < (2, 34): try: malloc_hook_addr = AddressUtil.parse_address("(void*) &__malloc_hook") if is_x86(): return align(malloc_hook_addr + current_arch.ptrsize, 0x20) elif is_arm64(): mstate_size = GlibcHeap.MallocStateStruct(0).sizeof return malloc_hook_addr - current_arch.ptrsize * 2 - mstate_size elif is_arm32(): mstate_size = GlibcHeap.MallocStateStruct(0).sizeof return malloc_hook_addr - current_arch.ptrsize - mstate_size else: raise except gdb.error: pass # plan 3 (from TLS) ptr = GlibcHeap.search_for_main_arena_from_tls() if ptr: return read_int_from_memory(ptr) raise OSError("Cannot find main_arena for {}".format(current_arch.arch)) # It must be a static method because it is also used to calculate the Heapbase. @staticmethod def search_for_tcache_from_tls(arena_addr): if get_libc_version() < (2, 26): return None if not current_arch.tls_supported: return None """ [2.42; x86_64 main-heap] gef> tls ... 0x7af8dd82b700|+0x0040|+008: 0x000055555555b010 <----- here 0x7af8dd82b708|+0x0048|+009: 0x0000000000000000 0x7af8dd82b710|+0x0050|+010: 0x00007af8d4034ac0 0x7af8dd82b718|+0x0058|+011: 0x0000000000000000 0x7af8dd82b720|+0x0060|+012: 0x0000000000000000 0x7af8dd82b728|+0x0068|+013: 0x0000000000000000 0x7af8dd82b730|+0x0070|+014: 0x0000000000000000 0x7af8dd82b738|+0x0078|+015: 0x0000000000000000 ----- TLS [2.42.9000; x86_64 main-heap] gef> tls ... 0x7d630b7a16e0|+0x0020|+004: 0x00005eafad721d20 <----- here 0x7d630b7a16e8|+0x0028|+005: 0x00007d630b995740 0x7d630b7a16f0|+0x0030|+006: 0x0000000000000000 0x7d630b7a16f8|+0x0038|+007: 0x0000000000000000 0x7d630b7a1700|+0x0040|+008: 0x0000000000000000 0x7d630b7a1708|+0x0048|+009: 0x0000000000000000 0x7d630b7a1710|+0x0050|+010: 0x00007d630b98dac0 0x7d630b7a1718|+0x0058|+011: 0x0000000000000000 0x7d630b7a1720|+0x0060|+012: 0x0000000000000000 0x7d630b7a1728|+0x0068|+013: 0x0000000000000000 0x7d630b7a1730|+0x0070|+014: 0x0000000000000000 0x7d630b7a1738|+0x0078|+015: 0x0000000000000000 ----- TLS """ def get_all_tls(): orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() threads = gdb.selected_inferior().threads() threads = sorted(threads, key=lambda th: th.num) tls_list = [] if not threads: return None for thread in threads: try: thread.switch() except gdb.error: continue tls = current_arch.get_tls() tls_list.append(tls) orig_thread.switch() # revert thread orig_frame.select() return tls_list def get_suitable_tls_addr(arena_addr): tls_list = get_all_tls() if not tls_list: return None for i in range(1, 500): for direction in [1, -1]: for tls in tls_list: tls_addr_i = tls + (current_arch.ptrsize * i) * direction if not is_valid_addr(tls_addr_i): continue x = read_int_from_memory(tls_addr_i) if is_valid_addr(x) and x == arena_addr: return tls_addr_i return None def get_tcache_perthread_struct_size(): TCACHE_MAX_BINS = GlibcHeap.GlibcArena.TCACHE_MAX_BINS() if get_libc_version() < (2, 30): tcache_perthread_struct_size_real = TCACHE_MAX_BINS * (1 + current_arch.ptrsize) else: tcache_perthread_struct_size_real = TCACHE_MAX_BINS * (2 + current_arch.ptrsize) for _k, v in GlibcHeap.get_binsize_table()["tcache"].items(): if v.get("size", 0) > tcache_perthread_struct_size_real: return v["size"] return None def search_tcache_perthread_struct(arena, tls): tcache_perthread_struct_size = get_tcache_perthread_struct_size() if tcache_perthread_struct_size is None: return None for i in range(1, 20): # "20" has no special meaning for direction in [1, -1]: tls_addr_i = tls + (current_arch.ptrsize * i) * direction if not is_valid_addr(tls_addr_i): continue x = read_int_from_memory(tls_addr_i) if not is_valid_addr(x): continue chunk = GlibcHeap.GlibcChunk(arena, x) try: if chunk.size == tcache_perthread_struct_size: return x except gdb.MemoryError: continue return None tls = get_suitable_tls_addr(arena_addr) if tls is None: return None return search_tcache_perthread_struct(arena_addr, tls) class GlibcArena: """Glibc arena class.""" @staticmethod def TCACHE_SMALL_BINS(): if get_libc_version() < (2, 42): return None else: return 0x40 @staticmethod def TCACHE_LARGE_BINS(): if get_libc_version() < (2, 42): return None else: return 12 @staticmethod @Cache.cache_this_session def TCACHE_MAX_BINS(): if get_libc_version() < (2, 42): return 0x40 else: return GlibcHeap.GlibcArena.TCACHE_SMALL_BINS() + GlibcHeap.GlibcArena.TCACHE_LARGE_BINS() @staticmethod @Cache.cache_this_session def TCACHE_FILL_COUNT(): v = Config.get_gef_setting("heap.tcache_max_count") if v != -1: return v if get_libc_version() < (2, 43): return 7 else: return 16 def __init__(self, arena_addr=None): # Manually calling a command like `call malloc(0x10)` alters the heap's internal structure. # However, the call command does not notify the `memory_changed` event. # Therefore, the GEF cache is not cleared, resulting in wrong output. # gef> bs # gef> call malloc(0x10) # gef> bs <-- wrong result # This is probably a bug in GDB. The solution is to clear the GEF cache. Cache.reset_gef_caches() # get address if arena_addr is None: self.__addr = GlibcHeap.search_for_main_arena() self.__is_main_arena = True else: self.__addr = arena_addr self.__is_main_arena = bool(arena_addr == GlibcHeap.search_for_main_arena()) # get type try: arena = gdb.parse_and_eval("*{:#x}".format(self.__addr)) malloc_state_t = GefUtil.cached_lookup_type("struct malloc_state") self.__arena = arena.cast(malloc_state_t) self.__size = malloc_state_t.sizeof except RuntimeError: self.__arena = GlibcHeap.MallocStateStruct(self.__addr) self.__size = self.__arena.sizeof # This structure (GlibcArena) is created every time you run a heap-related command. # Therefore, it is safe to cache the current value. # The `visual-heap` command evaluates `top` and `last_remainder` many times. # These are expensive because the value is actually retrieved via `__getattr__`. # Caching will improve speed, so it'll cache it here. self.top = int(self.top) self.last_remainder = int(self.last_remainder) return def __getitem__(self, item): return self.__arena[item] def __getattr__(self, item): try: return self.__arena[item] except RuntimeError: raise AttributeError from None def __int__(self): return self.__addr @property def is_main_arena(self): return self.__is_main_arena @property def addr(self): return self.__addr @property def name(self): if self.is_main_arena: return "main_arena" else: return "*{:#x}".format(self.__addr) @property def sizeof(self): # arena aligned_size if current_arch.ptrsize == 4: aligned_size = (self.__size + 7) & ~0b111 else: aligned_size = (self.__size + 15) & ~0b1111 return aligned_size @property def heap_base(self): if self.is_main_arena: return HeapbaseCommand.heap_base() else: return self.addr + self.sizeof @property def tcache(self): return self.addrof_tcachebins_base() @property def tcache_perthread_struct(self): return self.addrof_tcachebins_base() @Cache.cache_until_next def addrof_tcachebins_base(self, force_heuristic=False): if self.heap_base is None: return None def tcache_from_symbol(): if force_heuristic: return None # tcache is per-thread, so the address obtained by a symbol is depending on the current thread. # so we get all tcaches from all threads and take the address closest to self.heap_base. orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() if not orig_thread: # orig_thread may be None if under winedbg return None tcache = None for thread in gdb.selected_inferior().threads(): thread.switch() # change thread try: tcache_candidate = AddressUtil.parse_address("(void*) tcache") if tcache_candidate <= self.heap_base: continue if tcache is None or tcache > tcache_candidate: tcache = tcache_candidate except gdb.error: tcache_candidate = None break orig_thread.switch() # revert thread orig_frame.select() return tcache def tcache_from_heuristic_offset(): # In 2.42 and later, tcache_perthread_struct is not necessarily the first chunk, # so this detection method does not work. if get_libc_version() >= (2, 42): return None # There is no problem if you allocate a small chunk (in the size range of tcache) # at the beginning after executing the binary. Here is an example. # In a 64-bit environment, the first 8 bytes are zeros """ [2.42; x64 main-heap] gef> pi GlibcHeap.get_arena(0).heap_base 0x555555559000 gef> telescope -n 0x555555559000 3 0x555555559000|+0x0000|+000: 0x0000000000000000 <----- here 0x555555559008|+0x0008|+001: 0x0000000000000301 0x555555559010|+0x0010|+002: 0x0007000700070000 gef> [2.42; x64 non-main-heap] gef> pi GlibcHeap.get_arena(1).heap_base 0x7fffe80008d0 gef> telescope -n 0x7fffe80008d0 3 0x7fffe80008d0|+0x0000|+000: 0x0000000000000000 <----- here 0x7fffe80008d8|+0x0008|+001: 0x0000000000000305 0x7fffe80008e0|+0x0010|+002: 0x0007000700070000 gef> [2.42; x64 static; main-heap] gef> pi GlibcHeap.get_arena(0).heap_base 0x4e5d40 gef> telescope -n 0x4e5d40 3 0x0000004e5d40|+0x0000|+000: 0x0000000000000000 <----- here 0x0000004e5d48|+0x0008|+001: 0x0000000000000301 0x0000004e5d50|+0x0010|+002: 0x0007000700070000 gef> """ # On some 32-bit architectures, the first 8 bytes of main-heap are zeros. """ [2.42; x86 main-heap] gef> pi GlibcHeap.get_arena(0).heap_base 0x5655a000 gef> telescope -n 0x5655a000 6 0x5655a000|+0x0000|+000: 0x00000000 <----- here 0x5655a004|+0x0004|+001: 0x00000000 <----- here 0x5655a008|+0x0008|+002: 0x00000000 0x5655a00c|+0x000c|+003: 0x000001d1 0x5655a010|+0x0010|+004: 0x00000007 0x5655a014|+0x0014|+005: 0x00070007 gef> [2.42; x86 non-main-heap] gef> pi GlibcHeap.get_arena(1).heap_base 0xf6a00478 gef> telescope -n 0xf6a00478 4 0xf6a00478|+0x0000|+000: 0x00000000 0xf6a0047c|+0x0004|+001: 0x000001d5 0xf6a00480|+0x0008|+002: 0x00000007 0xf6a00484|+0x000c|+003: 0x00070007 gef> [2.42; x86 static; main-heap] gef> pi GlibcHeap.get_arena(0).heap_base 0x810c880 gef> telescope -n 0x810c880 6 0x0810c880|+0x0000|+000: 0x00000000 <----- here 0x0810c884|+0x0004|+001: 0x00000000 <----- here 0x0810c888|+0x0008|+002: 0x00000000 0x0810c88c|+0x000c|+003: 0x000001d1 0x0810c890|+0x0010|+004: 0x00000007 0x0810c894|+0x0014|+005: 0x00070007 gef> """ # On most 32-bit architectures, the first 8 bytes are non-zeros """ [2.42; arm32 main-heap] gef> pi GlibcHeap.get_arena(0).heap_base 0x421000 gef> telescope -n 0x421000 4 0x00421000|+0x0000|+000: 0x00000000 0x00421004|+0x0004|+001: 0x000001d1 0x00421008|+0x0008|+002: 0x00000007 0x0042100c|+0x000c|+003: 0x00070007 gef> [2.42; arm32 non-main-heap] gef> pi GlibcHeap.get_arena(1).heap_base 0x41b00470 gef> telescope -n 0x41b00470 4 0x41b00470|+0x0000|+000: 0x00000000 0x41b00474|+0x0004|+001: 0x000001d5 0x41b00478|+0x0008|+002: 0x00000007 0x41b0047c|+0x000c|+003: 0x00070007 gef> [2.42; arm32 static; main-heap] gef> pi GlibcHeap.get_arena(0).heap_base 0x84880 gef> telescope -n 0x84880 4 0x00084880|+0x0000|+000: 0x00000000 0x00084884|+0x0004|+001: 0x000001d1 0x00084888|+0x0008|+002: 0x00000007 0x0008488c|+0x000c|+003: 0x00070007 gef> """ first_8 = read_memory(self.heap_base, 8) if first_8 == b"\0\0\0\0\0\0\0\0": return self.heap_base + 0x10 else: return self.heap_base + 0x8 # strict way (from symbol) tcache = tcache_from_symbol() if is_valid_addr(tcache): return tcache # heuristic way 1 tcache = tcache_from_heuristic_offset() if tcache: return tcache # heuristic way 2 tcache = GlibcHeap.search_for_tcache_from_tls(self.addr) if tcache: return tcache return None def addrof_tcachebins_i_count(self, i): """return &tcache_perthread_struct.counts[i] or return &tcache_perthread_struct.num_slots[i]""" tcache_perthread_struct = self.tcache_perthread_struct if tcache_perthread_struct is None: return None """ [2.26~2.29] # define TCACHE_MAX_BINS 64 typedef struct tcache_perthread_struct { char counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct; [2.30~2.41] # define TCACHE_MAX_BINS 64 typedef struct tcache_perthread_struct { uint16_t counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct; [2.42~] # define TCACHE_SMALL_BINS 64 # define TCACHE_LARGE_BINS 12 # define TCACHE_MAX_BINS (TCACHE_SMALL_BINS + TCACHE_LARGE_BINS) typedef struct tcache_perthread_struct { uint16_t num_slots[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct; """ if get_libc_version() < (2, 30): offset = i else: offset = i * 2 return tcache_perthread_struct + offset def tcachebins_i_count(self, i): """return tcache_perthread_struct.counts[i]""" counts_i_addr = self.addrof_tcachebins_i_count(i) if counts_i_addr is None: return None if get_libc_version() < (2, 30): count = read_int8_from_memory(counts_i_addr) else: count = read_int16_from_memory(counts_i_addr) if get_libc_version() >= (2, 42): # num_slot -> count count = self.TCACHE_FILL_COUNT() - count return count def addrof_tcachebins_i(self, i): """return &tcache_perthread_struct.entries[i]""" tcache_perthread_struct = self.tcache_perthread_struct if tcache_perthread_struct is None: return None if get_libc_version() < (2, 30): sizeof_counts = self.TCACHE_MAX_BINS() else: sizeof_counts = self.TCACHE_MAX_BINS() * 2 return tcache_perthread_struct + sizeof_counts + current_arch.ptrsize * i def addrof_fastbins_i(self, i): if hasattr(self.__arena, "addrof_fastbins"): fastbins_addr = self.__arena.addrof_fastbins else: fastbins_type = [x for x in self.__arena.type.fields() if x.name == "fastbinsY"][0] fastbins_addr = self.__addr + fastbins_type.bitpos // 8 return fastbins_addr + i * current_arch.ptrsize def addrof_top(self): if hasattr(self.__arena, "addrof_top"): top_addr = self.__arena.addrof_top else: top_type = [x for x in self.__arena.type.fields() if x.name == "top"][0] top_addr = self.__addr + top_type.bitpos // 8 return top_addr def addrof_last_remainder(self): if hasattr(self.__arena, "addrof_last_remainder"): last_remainder_addr = self.__arena.addrof_last_remainder else: last_remainder_type = [x for x in self.__arena.type.fields() if x.name == "last_remainder"][0] last_remainder_addr = self.__addr + last_remainder_type.bitpos // 8 return last_remainder_addr def addrof_bins_i(self, i): if hasattr(self.__arena, "addrof_bins"): bins_addr = self.__arena.addrof_bins else: bins_type = [x for x in self.__arena.type.fields() if x.name == "bins"][0] bins_addr = self.__addr + bins_type.bitpos // 8 return bins_addr + i * current_arch.ptrsize * 2 def addrof_next(self): if hasattr(self.__arena, "addrof_next"): next_addr = self.__arena.addrof_next else: next_type = [x for x in self.__arena.type.fields() if x.name == "next"][0] next_addr = self.__addr + next_type.bitpos // 8 return next_addr def addrof_next_free(self): if hasattr(self.__arena, "addrof_next_free"): next_free_addr = self.__arena.addrof_next_free else: next_free_type = [x for x in self.__arena.type.fields() if x.name == "next_free"][0] next_free_addr = self.__addr + next_free_type.bitpos // 8 return next_free_addr def addrof_system_mem(self): if hasattr(self.__arena, "addrof_system_mem"): system_mem_addr = self.__arena.addrof_system_mem else: system_mem_type = [x for x in self.__arena.type.fields() if x.name == "system_mem"][0] system_mem_addr = self.__addr + system_mem_type.bitpos // 8 return system_mem_addr def get_tcachebins_i(self, i): """Return head chunk in tcache[i].""" tcache_i_head = self.addrof_tcachebins_i(i) if not tcache_i_head: return None addr = AddressUtil.dereference(tcache_i_head) if not addr: return None return GlibcHeap.GlibcChunk(self, int(addr)) def get_fastbins_i(self, i): """Return head chunk in fastbinsY[i].""" addr = int(self.fastbinsY[i]) if addr == 0: return None return GlibcHeap.GlibcChunk(self, addr + 2 * current_arch.ptrsize) def get_bins_i(self, i): """Return the forward and backward pointers for the specified bin index.""" idx = i * 2 fd = int(self.bins[idx]) bw = int(self.bins[idx + 1]) return fd, bw def get_next(self): """Return the next arena object if available; otherwise returns None.""" try: addr_next = int(self.next) if addr_next == 0: return None if addr_next == GlibcHeap.get_main_arena().addr: return None next_arena = GlibcHeap.GlibcArena(addr_next) str(next_arena) # check memory error return next_arena except gdb.error: return None def __str__(self): """Return a formatted string representation of the arena and its key attributes.""" arena = Color.colorify("Arena", Config.get_gef_setting("theme.heap_arena_label")) if self.heap_base is None: heap_base = "uninitialized" else: heap_base = ProcessMap.lookup_address(self.heap_base) arena_addr = ProcessMap.lookup_address(self.__addr) top = ProcessMap.lookup_address(self.top) if is_valid_addr(self.last_remainder): last_remainder = ProcessMap.lookup_address(self.last_remainder) else: last_remainder = hex(self.last_remainder) next = ProcessMap.lookup_address(self.next) system_mem = int(self.system_mem) try: if is_valid_addr(self.next_free): next_free = ProcessMap.lookup_address(self.next_free) else: next_free = hex(self.next_free) fmt = "{:s}(addr={!s}, heap_base={!s}, top={!s}, last_remainder={!s}, " fmt += "next={!s}, next_free={!s}, system_mem={:#x})" args = (arena, arena_addr, heap_base, top, last_remainder, next, next_free, system_mem) except (gdb.error, TypeError): fmt = "{:s}(addr={!s}, heap_base={!s}, top={!s}, last_remainder={!s}, " fmt += "next={!s}, system_mem={:#x})" args = (arena, arena_addr, heap_base, top, last_remainder, next, system_mem) return fmt.format(*args) def get_tcache_list(self): """Return a dictionary mapping tcache bin indices to lists of chunk addresses, handling loops and corruption.""" if get_libc_version() < (2, 26): return {} if self.heap_base is None: return {} chunks_all = {} for i in range(self.TCACHE_MAX_BINS()): # head check try: chunk = self.get_tcachebins_i(i) except gdb.MemoryError: chunks_all[i] = ["Corrupted"] continue # parse list chunks = [] while chunk is not None: if chunk.address in chunks: chunks.append(chunk.address) chunks.append("Loop detected") break # loop detected chunks.append(chunk.address) next_chunk = chunk.get_fwd_ptr(True) if next_chunk is None: chunks.append("Corrupted") break # invalid if next_chunk == 0: break # valid end chunk = GlibcHeap.GlibcChunk(self, next_chunk) chunks_all[i] = chunks return chunks_all def get_fastbins_list(self): """Return a dictionary of fastbin indices mapped to lists of chunk addresses, handling loops and corruption.""" if get_libc_version() >= (2, 43): return {} def fastbin_index(sz): return (sz >> 4) - 2 if SIZE_SZ == 8 else (sz >> 3) - 2 SIZE_SZ = current_arch.ptrsize MAX_FAST_SIZE = (80 * SIZE_SZ // 4) NFASTBINS = fastbin_index(MAX_FAST_SIZE) - 1 chunks_all = {} for i in range(NFASTBINS): # head check try: chunk = self.get_fastbins_i(i) except gdb.MemoryError: chunks_all[i] = ["Corrupted"] continue # parse list chunks = [] while chunk is not None: if chunk.address in chunks: chunks.append(chunk.address) chunks.append("Loop detected") break # loop detected chunks.append(chunk.address) next_chunk = chunk.get_fwd_ptr(True) if next_chunk is None: chunks.append("Corrupted") break # invalid if next_chunk == 0: break # valid end chunk = GlibcHeap.GlibcChunk(self, next_chunk, from_base=True) chunks_all[i] = chunks return chunks_all def get_bins_list(self, index): """Return a list of chunk addresses in the specified bin, handling loops and corruption.""" try: fw, bk = self.get_bins_i(index) except gdb.MemoryError: return ["Corrupted"] if bk == 0x00 and fw == 0x00: return ["Corrupted"] head = self.addrof_bins_i(index) - current_arch.ptrsize * 2 if fw == head: return [] # no entry corrupted = False chunks_bk = [] # first: process backward while bk != head: chunk = GlibcHeap.GlibcChunk(self, bk, from_base=True) if chunk.chunk_base_address in chunks_bk: chunks_bk.append(chunk.chunk_base_address) chunks_bk.append("Loop detected") corrupted = True break chunks_bk.append(chunk.chunk_base_address) bk = chunk.bck if bk is None: chunks_bk.append("Corrupted") corrupted = True break chunks = chunks_bk[::-1] if corrupted: # second: process forward chunks_fw = [] while fw != head: chunk = GlibcHeap.GlibcChunk(self, fw, from_base=True) if chunk.chunk_base_address in chunks: break # meet backward's list if chunk.chunk_base_address in chunks_fw: break # loop chunks_fw.append(chunk.chunk_base_address) fw = chunk.fwd if fw is None: break # corrupted chunks = chunks_fw + chunks return chunks def get_unsortedbin_list(self): """Return a dictionary containing the list of chunks in the unsorted bin.""" chunks_all = {} chunks_all[0] = self.get_bins_list(0) return chunks_all def get_smallbins_list(self): """Return a dictionary mapping small bin indices to lists of chunk addresses.""" chunks_all = {} for i in range(1, 63): chunks_all[i] = self.get_bins_list(i) return chunks_all def get_largebins_list(self): """Return a dictionary mapping large bin indices to lists of chunk addresses.""" chunks_all = {} for i in range(63, 126): chunks_all[i] = self.get_bins_list(i) return chunks_all def reset_cache(self): """Make some caches. - cached_XXX_list - cached_XXX_addr_list - bins_dict_for_address - bins_dict_for_base_address """ # cached_XXX_list = {bin_idx1: [chunk, chunk, ...], bin_idx2: [chunk, chunk, ...]} self.cached_tcache_list = self.get_tcache_list() self.cached_fastbins_list = self.get_fastbins_list() self.cached_unsortedbin_list = self.get_unsortedbin_list() self.cached_smallbins_list = self.get_smallbins_list() self.cached_largebins_list = self.get_largebins_list() def int_filter(a): return {x for x in a if isinstance(x, int)} # cacheed_XXX_addr_list = {chunk, chunk, ...} self.cached_tcache_addr_list = int_filter(set().union(*self.cached_tcache_list.values())) self.cached_fastbins_addr_list = int_filter(set().union(*self.cached_fastbins_list.values())) self.cached_unsortedbin_addr_list = int_filter(self.cached_unsortedbin_list[0]) self.cached_smallbins_addr_list = int_filter(set().union(*self.cached_smallbins_list.values())) self.cached_largebins_addr_list = int_filter(set().union(*self.cached_largebins_list.values())) # dict[address] = ["bins info1", "bins info2", ...] self.bins_dict_for_address = {} for tcache_idx, tcache_list in self.cached_tcache_list.items(): for address in tcache_list: if not isinstance(address, int): continue pos = ",".join([str(i + 1) for i, x in enumerate(tcache_list) if x == address]) if "size" in GlibcHeap.get_binsize_table()["tcache"][tcache_idx]: sz = GlibcHeap.get_binsize_table()["tcache"][tcache_idx]["size"] m = "tcache[idx={:d},sz={:#x}][{:s}/{:d}]".format(tcache_idx, sz, pos, len(tcache_list)) else: sz_min = GlibcHeap.get_binsize_table()["tcache"][tcache_idx]["size_min"] sz_max = GlibcHeap.get_binsize_table()["tcache"][tcache_idx]["size_max"] m = "tcache[idx={:d},sz={:#x}-{:#x}][{:s}/{:d}]".format( tcache_idx, sz_min, sz_max, pos, len(tcache_list), ) new_list = self.bins_dict_for_address.get(address, []) + [m] self.bins_dict_for_address[address] = new_list for fastbin_idx, fastbin_list in self.cached_fastbins_list.items(): for address in set(fastbin_list): if not isinstance(address, int): continue pos = ",".join([str(i + 1) for i, x in enumerate(fastbin_list) if x == address]) sz = GlibcHeap.get_binsize_table()["fastbins"][fastbin_idx]["size"] m = "fastbins[idx={:d},sz={:#x}][{:s}/{:d}]".format(fastbin_idx, sz, pos, len(fastbin_list)) new_list = self.bins_dict_for_address.get(address, []) + [m] self.bins_dict_for_address[address] = new_list # dict[base_address] = ["bins info1", "bins info2", ...] self.bins_dict_for_base_address = {} for _, unsortedbin_list in self.cached_unsortedbin_list.items(): for base_address in unsortedbin_list: if not isinstance(base_address, int): continue pos = ",".join([str(i + 1) for i, x in enumerate(unsortedbin_list) if x == base_address]) m = "unsortedbins[{:s}/{:d}]".format(pos, len(unsortedbin_list)) new_list = self.bins_dict_for_base_address.get(base_address, []) + [m] self.bins_dict_for_base_address[base_address] = new_list for smallbin_idx, smallbin_list in self.cached_smallbins_list.items(): for base_address in smallbin_list: if not isinstance(base_address, int): continue pos = ",".join([str(i + 1) for i, x in enumerate(smallbin_list) if x == base_address]) sz = GlibcHeap.get_binsize_table()["small_bins"][smallbin_idx]["size"] m = "smallbins[idx={:d},sz={:#x}][{:s}/{:d}]".format(smallbin_idx, sz, pos, len(smallbin_list)) new_list = self.bins_dict_for_base_address.get(base_address, []) + [m] self.bins_dict_for_base_address[base_address] = new_list for largebin_idx, largebin_list in self.cached_largebins_list.items(): for base_address in largebin_list: if not isinstance(base_address, int): continue pos = ",".join([str(i + 1) for i, x in enumerate(largebin_list) if x == base_address]) sz_min = GlibcHeap.get_binsize_table()["large_bins"][largebin_idx]["size_min"] sz_max = GlibcHeap.get_binsize_table()["large_bins"][largebin_idx]["size_max"] m = "largebins[idx={:d},sz={:#x}-{:#x}][{:s}/{:d}]".format( largebin_idx, sz_min, sz_max, pos, len(largebin_list), ) new_list = self.bins_dict_for_base_address.get(base_address, []) + [m] self.bins_dict_for_base_address[base_address] = new_list return def is_chunk_in_tcache(self, chunk): if not hasattr(self, "cached_tcache_addr_list"): self.reset_cache() return chunk.address in self.cached_tcache_addr_list def is_chunk_in_fastbins(self, chunk): if not hasattr(self, "cached_fastbins_addr_list"): self.reset_cache() return chunk.address in self.cached_fastbins_addr_list def is_chunk_in_unsortedbin(self, chunk): if not hasattr(self, "cached_unsortedbin_addr_list"): self.reset_cache() return chunk.chunk_base_address in self.cached_unsortedbin_addr_list def is_chunk_in_smallbins(self, chunk): if not hasattr(self, "cached_smallbins_addr_list"): self.reset_cache() return chunk.chunk_base_address in self.cached_smallbins_addr_list def is_chunk_in_largebins(self, chunk): if not hasattr(self, "cached_largebins_addr_list"): self.reset_cache() return chunk.chunk_base_address in self.cached_largebins_addr_list def is_chunk_in_freelists(self, chunk): if self.is_chunk_in_tcache(chunk): return True if self.is_chunk_in_fastbins(chunk): return True if self.is_chunk_in_unsortedbin(chunk): return True if self.is_chunk_in_smallbins(chunk): return True if self.is_chunk_in_largebins(chunk): return True return False def get_bins_info(self, address_or_chunk, skip_top=False): """Return a list of bin information for the given address or chunk, optionally including the "top" marker.""" if isinstance(address_or_chunk, GlibcHeap.GlibcChunk): address = address_or_chunk.address base_address = address_or_chunk.chunk_base_address elif isinstance(address_or_chunk, int): address = address_or_chunk base_address = address_or_chunk info = [] if not hasattr(self, "bins_dict_for_address"): self.reset_cache() info.extend(self.bins_dict_for_address.get(address, [])) if not hasattr(self, "bins_dict_for_base_address"): self.reset_cache() info.extend(self.bins_dict_for_base_address.get(base_address, [])) if not skip_top: if base_address == self.top: info.append("top") return info @staticmethod def get_arena(address=None): """Return the arena object for the given address or arena index, or None if not found.""" if address is None or is_valid_addr(address): try: arena = GlibcHeap.GlibcArena(address) str(arena) # check memory error return arena except (OSError, AttributeError, gdb.MemoryError, RuntimeError): err("Failed to get the arena, heap commands may not work properly") return None # interpret `address` as the number, not the address of arena. arena_number = address # main_arena arena = GlibcHeap.get_main_arena() if arena is None: return None arenas = [] while arena: arenas.append(arena) arena = arena.get_next() if arena_number >= len(arenas): err("Failed to get the arena, heap commands may not work properly") return None return arenas[arena_number] @staticmethod def get_main_arena(): return GlibcHeap.get_arena(None) @staticmethod def get_all_arenas(): arenas = [] arena = GlibcHeap.get_main_arena() while arena: arenas.append(arena) arena = arena.get_next() return arenas class GlibcChunk: """Glibc chunk class.""" def __init__(self, arena, addr, from_base=False): self.arena = arena self.ptrsize = current_arch.ptrsize if from_base: self.chunk_base_address = addr self.address = addr + 2 * self.ptrsize else: self.chunk_base_address = AddressUtil.align_address(addr - 2 * self.ptrsize) self.address = addr self.size_addr = AddressUtil.align_address(self.address - self.ptrsize) self.prev_size_addr = self.chunk_base_address return def get_chunk_size(self): return read_int_from_memory(self.size_addr) & (~0x07) @property def size(self): return self.get_chunk_size() def get_usable_size(self): cursz = self.get_chunk_size() if cursz == 0: return cursz if self.has_m_bit(): return cursz - 2 * self.ptrsize return cursz - self.ptrsize def get_prev_chunk_size(self): return read_int_from_memory(self.prev_size_addr) def get_next_chunk(self): try: addr = self.address + self.get_chunk_size() return GlibcHeap.GlibcChunk(self.arena, addr) except gdb.MemoryError: return None # if freed functions def get_fwd_ptr(self, sll): try: # Not a single-linked-list (sll) or no Safe-Linking support yet if not sll or get_libc_version() < (2, 32): return read_int_from_memory(self.address) # Unmask ("reveal") the Safe-Linking pointer else: return read_int_from_memory(self.address) ^ (self.address >> 12) except gdb.MemoryError: return None @property def fwd(self): return self.get_fwd_ptr(False) fd = fwd # for compat def get_bkw_ptr(self): try: return read_int_from_memory(self.address + self.ptrsize) except gdb.MemoryError: return None @property def bck(self): return self.get_bkw_ptr() bk = bck # for compat def get_fd_nextsize_ptr(self): return read_int_from_memory(self.address + self.ptrsize * 2) @property def fd_nextsize(self): return self.get_fd_nextsize_ptr() def get_bk_nextsize_ptr(self): return read_int_from_memory(self.address + self.ptrsize * 3) @property def bk_nextsize(self): return self.get_bk_nextsize_ptr() # endif freed functions def has_p_bit(self): return read_int_from_memory(self.size_addr) & 0x01 def has_m_bit(self): return read_int_from_memory(self.size_addr) & 0x02 def has_n_bit(self): return read_int_from_memory(self.size_addr) & 0x04 def is_used(self): # Check if the current block is used by: # - checking the M bit is true # - or checking that next chunk PREV_INUSE flag is true if self.has_m_bit(): return True next_chunk = self.get_next_chunk() try: return True if next_chunk.has_p_bit() else False except gdb.MemoryError as e: # top? if (next_chunk.chunk_base_address & 0xfff) == 0: if is_valid_addr(next_chunk.chunk_base_address - 1): return False raise gdb.MemoryError from e def is_real_used(self): # Even if a chunk has been freed, if it is in tcache or fastbin, # the PREV_IN_USE bit of the chunk directly below it will be set. # This bit will be ignored and we will determine whether it is on the free list. if self.arena.top == self.chunk_base_address: return False return not self.arena.is_chunk_in_freelists(self) def is_top(self): return self.arena.top == self.chunk_base_address def str_chunk_size_flag(self): msg = [] if self.has_p_bit(): msg.append(" PREV_INUSE flag: {}".format(Color.greenify("On"))) else: msg.append(" PREV_INUSE flag: {}".format(Color.redify("Off"))) if self.has_m_bit(): msg.append(" IS_MMAPPED flag: {}".format(Color.greenify("On"))) else: msg.append(" IS_MMAPPED flag: {}".format(Color.redify("Off"))) if self.has_n_bit(): msg.append(" NON_MAIN_ARENA flag: {}".format(Color.greenify("On"))) else: msg.append(" NON_MAIN_ARENA flag: {}".format(Color.redify("Off"))) return "\n".join(msg) def _str_sizes(self): msg = [] failed = False try: msg.append(" Chunk size: {:#x}".format(self.get_chunk_size())) msg.append(" Usable size: {:#x}".format(self.get_usable_size())) except gdb.MemoryError: msg.append(" Chunk size: Cannot read at {:#x} (Corrupted?)".format(self.size_addr)) failed = True if self.has_p_bit(): msg.append(" Previous chunk size: ??? (PREV_INUSE flag: On)") else: try: msg.append(" Previous chunk size: {:#x}".format(self.get_prev_chunk_size())) except gdb.MemoryError: msg.append(" Previous chunk size: Cannot read at {:#x} (Corrupted?)".format(self.chunk_base_address)) failed = True if not failed: msg.append(self.str_chunk_size_flag()) return "\n".join(msg) def _str_pointers(self): fwd = self.address bkw = self.address + self.ptrsize msg = [] try: msg.append(" Forward pointer: {:#x}".format(self.get_fwd_ptr(False))) except gdb.MemoryError: msg.append(" Forward pointer: {:#x} (Corrupted?)".format(fwd)) try: msg.append(" Backward pointer: {:#x}".format(self.get_bkw_ptr())) except gdb.MemoryError: msg.append(" Backward pointer: {:#x} (Corrupted?)".format(bkw)) return "\n".join(msg) def str_as_alloced(self): return self._str_sizes() def str_as_freed(self): return "{}\n\n{}".format(self._str_sizes(), self._str_pointers()) def flags_as_string(self): flags = [] if self.has_p_bit(): flags.append(Color.colorify( "PREV_INUSE", Config.get_gef_setting("theme.heap_chunk_flag_prev_inuse"), )) if self.has_m_bit(): flags.append(Color.colorify( "IS_MMAPPED", Config.get_gef_setting("theme.heap_chunk_flag_is_mmapped"), )) if self.has_n_bit(): flags.append(Color.colorify( "NON_MAIN_ARENA", Config.get_gef_setting("theme.heap_chunk_flag_non_main_arena"), )) return "|".join(flags) def __str__(self): """Return a formatted string representation of the chunk and its key attributes, including color and symbol information.""" def get_sym(addr): a = ProcessMap.lookup_address(addr) b = Symbol.get_symbol_string(addr) return a, b def get_sym_chunk(addr): a = ProcessMap.lookup_address(addr) b1 = Color.colorify_hex(addr, Config.get_gef_setting("theme.heap_chunk_address_freed")) b2 = Color.colorify_hex(addr, Config.get_gef_setting("theme.heap_chunk_address_used")) c = Symbol.get_symbol_string(addr) return a, (b1, b2), c def get_err(addrs, sll=False): for a in addrs: if not a.valid: if sll and a.value == 0: # single link-list && 0: ok continue return " [{:s}]".format(Color.colorify( "Corrupted", Config.get_gef_setting("theme.heap_corrupted_msg"), )) return "" chunk_c = Color.colorify("Chunk", Config.get_gef_setting("theme.heap_chunk_label")) size_c = Color.colorify_hex(self.get_chunk_size(), Config.get_gef_setting("theme.heap_chunk_size")) base, (base_c_f, base_c_u), base_sym = get_sym_chunk(self.chunk_base_address) addr, (addr_c_f, addr_c_u), addr_sym = get_sym_chunk(self.address) flags = self.flags_as_string() # large bins if self.arena.is_chunk_in_largebins(self): fd, fd_sym = get_sym(self.fd) bk, bk_sym = get_sym(self.bk) err = get_err([fd, bk]) if is_valid_addr(self.fd_nextsize) or is_valid_addr(self.bk_nextsize): # largebin and valid (fd|bk)_nextsize fdn, fdn_sym = get_sym(self.fd_nextsize) bkn, bkn_sym = get_sym(self.bk_nextsize) fmt = "{:s}(base={:s}{:s}, addr={:s}{:s}, size={:s}, flags={:s}, fd={!s}{:s}, bk={!s}{:s}, " fmt += "fd_nextsize={!s}{:s}, bk_nextsize={!s}{:s})" msg = fmt.format( chunk_c, base_c_f, base_sym, addr_c_f, addr_sym, size_c, flags, fd, fd_sym, bk, bk_sym, fdn, fdn_sym, bkn, bkn_sym, err, ) else: msg = "{:s}(base={:s}{:s}, addr={:s}{:s}, size={:s}, flags={:s}, fd={!s}{:s}, bk={!s}{:s}{:s})".format( chunk_c, base_c_f, base_sym, addr_c_f, addr_sym, size_c, flags, fd, fd_sym, bk, bk_sym, err, ) # small bins / unsorted bin elif self.arena.is_chunk_in_smallbins(self) or self.arena.is_chunk_in_unsortedbin(self): fd, fd_sym = get_sym(self.fd) bk, bk_sym = get_sym(self.bk) err = get_err([fd, bk]) msg = "{:s}(base={:s}{:s}, addr={:s}{:s}, size={:s}, flags={:s}, fd={!s}{:s}, bk={!s}{:s}{:s})".format( chunk_c, base_c_f, base_sym, addr_c_f, addr_sym, size_c, flags, fd, fd_sym, bk, bk_sym, err, ) # tcache / fastbins elif self.arena.is_chunk_in_fastbins(self) or self.arena.is_chunk_in_tcache(self): if get_libc_version() < (2, 32): fd, fd_sym = get_sym(self.get_fwd_ptr(sll=False)) err = get_err([fd], sll=True) msg = "{:s}(base={:s}{:s}. addr={:s}{:s}, size={:s}, flags={:s}, fd={!s}{:s}{:s})".format( chunk_c, base_c_f, base_sym, addr_c_f, addr_sym, size_c, flags, fd, fd_sym, err, ) else: fd, fd_sym = get_sym(self.get_fwd_ptr(sll=False)) decoded_fd, decoded_fd_sym = get_sym(self.get_fwd_ptr(sll=True)) err = get_err([decoded_fd], sll=True) msg = "{:s}(base={:s}{:s}, addr={:s}{:s}, size={:s}, flags={:s}, fd={!s}{:s}(={!s}{:s}){:s})".format( chunk_c, base_c_f, base_sym, addr_c_f, addr_sym, size_c, flags, fd, fd_sym, decoded_fd, decoded_fd_sym, err, ) # top elif self.arena.top == self.chunk_base_address: msg = "{:s}(base={:s}{:s}, addr={:s}{:s}, size={:s}, flags={:s})".format( chunk_c, base_c_f, base_sym, addr_c_f, addr_sym, size_c, flags, ) # used chunk else: msg = "{:s}(base={:s}{:s}, addr={:s}{:s}, size={:s}, flags={:s})".format( chunk_c, base_c_u, base_sym, addr_c_u, addr_sym, size_c, flags, ) return msg def psprint(self): """Return a detailed, multi-line string representation of the chunk, showing both its summary and allocation state.""" msg = [] msg.append(str(self)) if self.is_used(): msg.append(self.str_as_alloced()) else: msg.append(self.str_as_freed()) return "\n".join(msg) @staticmethod @Cache.cache_this_session def get_binsize_table(): """Return a dictionary containing size information for tcache, fastbins, unsorted bin, small bins, and large bins, based on architecture and libc version.""" table = { "tcache": {}, "fastbins": {}, "unsorted_bin": {}, "small_bins": {}, "large_bins": {}, } MIN_SIZE = GlibcHeap.HeapInfo.MIN_SIZE() MALLOC_ALIGNMENT = GlibcHeap.HeapInfo.MALLOC_ALIGNMENT() # tcache for i in range(64): # MALLOC_ALIGNMENT is changed from libc 2.26. # for x86_32, tcache 0x8 align is no longer used. # but for ARM32, or maybe other arch, still 0x8 align is used. table["tcache"][i] = {"size": MIN_SIZE + MALLOC_ALIGNMENT * i} if get_libc_version() >= (2, 42): if is_64bit(): table["tcache"][64] = {"size_min": 0x420, "size_max": 0x800} table["tcache"][65] = {"size_min": 0x800, "size_max": 0x1000} table["tcache"][66] = {"size_min": 0x1000, "size_max": 0x2000} table["tcache"][67] = {"size_min": 0x2000, "size_max": 0x4000} table["tcache"][68] = {"size_min": 0x4000, "size_max": 0x8000} table["tcache"][69] = {"size_min": 0x8000, "size_max": 0x10000} table["tcache"][70] = {"size_min": 0x10000, "size_max": 0x20000} table["tcache"][71] = {"size_min": 0x20000, "size_max": 0x40000} table["tcache"][72] = {"size_min": 0x40000, "size_max": 0x80000} table["tcache"][73] = {"size_min": 0x80000, "size_max": 0x100000} table["tcache"][74] = {"size_min": 0x100000, "size_max": 0x200000} table["tcache"][75] = {"size_min": 0x200000, "size_max": 0x400000} elif is_x86_32() or is_riscv32() or is_ppc32(): table["tcache"][64] = {"size_min": 0x410, "size_max": 0x800} table["tcache"][65] = {"size_min": 0x800, "size_max": 0x1000} table["tcache"][66] = {"size_min": 0x1000, "size_max": 0x2000} table["tcache"][67] = {"size_min": 0x2000, "size_max": 0x4000} table["tcache"][68] = {"size_min": 0x4000, "size_max": 0x8000} table["tcache"][69] = {"size_min": 0x8000, "size_max": 0x10000} table["tcache"][70] = {"size_min": 0x10000, "size_max": 0x20000} table["tcache"][71] = {"size_min": 0x20000, "size_max": 0x40000} table["tcache"][72] = {"size_min": 0x40000, "size_max": 0x80000} table["tcache"][73] = {"size_min": 0x80000, "size_max": 0x100000} table["tcache"][74] = {"size_min": 0x100000, "size_max": 0x200000} table["tcache"][75] = {"size_min": 0x200000, "size_max": 0x400000} else: # arm32, m68k, sh4, etc table["tcache"][64] = {"size_min": 0x210, "size_max": 0x400} table["tcache"][65] = {"size_min": 0x400, "size_max": 0x800} table["tcache"][66] = {"size_min": 0x800, "size_max": 0x1000} table["tcache"][67] = {"size_min": 0x1000, "size_max": 0x2000} table["tcache"][68] = {"size_min": 0x2000, "size_max": 0x4000} table["tcache"][69] = {"size_min": 0x4000, "size_max": 0x8000} table["tcache"][70] = {"size_min": 0x8000, "size_max": 0x10000} table["tcache"][71] = {"size_min": 0x10000, "size_max": 0x20000} table["tcache"][72] = {"size_min": 0x20000, "size_max": 0x40000} table["tcache"][73] = {"size_min": 0x40000, "size_max": 0x80000} table["tcache"][74] = {"size_min": 0x80000, "size_max": 0x100000} table["tcache"][75] = {"size_min": 0x100000, "size_max": 0x200000} # fastbins if is_64bit(): for i in range(7): size = MIN_SIZE + i * 0x10 table["fastbins"][i] = {"size": size} elif (is_x86_32() or is_riscv32() or is_ppc32()) and get_libc_version() >= (2, 26): # MALLOC_ALIGNMENT is changed from libc 2.26. # for x86_32, fastbin exists every 8 bytes, but only used every 16 bytes. table["fastbins"][0] = {"size": 0x10} table["fastbins"][2] = {"size": 0x20} table["fastbins"][4] = {"size": 0x30} table["fastbins"][6] = {"size": 0x40} else: for i in range(7): size = MIN_SIZE + i * 8 table["fastbins"][i] = {"size": size} # unsorted bins table["unsorted_bin"][0] = {} # smallbins for i in range(1, 63): if is_64bit() or (is_x86_32() and get_libc_version() >= (2, 26)): size = MIN_SIZE + (i - 1) * 0x10 else: size = MIN_SIZE + (i - 1) * 0x8 table["small_bins"][i] = {"size": size} # largebins if is_64bit(): table["large_bins"][63] = {"size_min": 0x400, "size_max": 0x440} table["large_bins"][64] = {"size_min": 0x440, "size_max": 0x480} table["large_bins"][65] = {"size_min": 0x480, "size_max": 0x4c0} table["large_bins"][66] = {"size_min": 0x4c0, "size_max": 0x500} table["large_bins"][67] = {"size_min": 0x500, "size_max": 0x540} table["large_bins"][68] = {"size_min": 0x540, "size_max": 0x580} table["large_bins"][69] = {"size_min": 0x580, "size_max": 0x5c0} table["large_bins"][70] = {"size_min": 0x5c0, "size_max": 0x600} table["large_bins"][71] = {"size_min": 0x600, "size_max": 0x640} table["large_bins"][72] = {"size_min": 0x640, "size_max": 0x680} table["large_bins"][73] = {"size_min": 0x680, "size_max": 0x6c0} table["large_bins"][74] = {"size_min": 0x6c0, "size_max": 0x700} table["large_bins"][75] = {"size_min": 0x700, "size_max": 0x740} table["large_bins"][76] = {"size_min": 0x740, "size_max": 0x780} table["large_bins"][77] = {"size_min": 0x780, "size_max": 0x7c0} table["large_bins"][78] = {"size_min": 0x7c0, "size_max": 0x800} table["large_bins"][79] = {"size_min": 0x800, "size_max": 0x840} table["large_bins"][80] = {"size_min": 0x840, "size_max": 0x880} table["large_bins"][81] = {"size_min": 0x880, "size_max": 0x8c0} table["large_bins"][82] = {"size_min": 0x8c0, "size_max": 0x900} table["large_bins"][83] = {"size_min": 0x900, "size_max": 0x940} table["large_bins"][84] = {"size_min": 0x940, "size_max": 0x980} table["large_bins"][85] = {"size_min": 0x980, "size_max": 0x9c0} table["large_bins"][86] = {"size_min": 0x9c0, "size_max": 0xa00} table["large_bins"][87] = {"size_min": 0xa00, "size_max": 0xa40} table["large_bins"][88] = {"size_min": 0xa40, "size_max": 0xa80} table["large_bins"][89] = {"size_min": 0xa80, "size_max": 0xac0} table["large_bins"][90] = {"size_min": 0xac0, "size_max": 0xb00} table["large_bins"][91] = {"size_min": 0xb00, "size_max": 0xb40} table["large_bins"][92] = {"size_min": 0xb40, "size_max": 0xb80} table["large_bins"][93] = {"size_min": 0xb80, "size_max": 0xbc0} table["large_bins"][94] = {"size_min": 0xbc0, "size_max": 0xc00} table["large_bins"][95] = {"size_min": 0xc00, "size_max": 0xc40} table["large_bins"][96] = {"size_min": 0xc40, "size_max": 0xe00} elif is_x86_32() and get_libc_version() >= (2, 26): table["large_bins"][63] = {"size_min": 0x3f0, "size_max": 0x400} table["large_bins"][64] = {"size_min": 0x400, "size_max": 0x440} table["large_bins"][65] = {"size_min": 0x440, "size_max": 0x480} table["large_bins"][66] = {"size_min": 0x480, "size_max": 0x4c0} table["large_bins"][67] = {"size_min": 0x4c0, "size_max": 0x500} table["large_bins"][68] = {"size_min": 0x500, "size_max": 0x540} table["large_bins"][69] = {"size_min": 0x540, "size_max": 0x580} table["large_bins"][70] = {"size_min": 0x580, "size_max": 0x5c0} table["large_bins"][71] = {"size_min": 0x5c0, "size_max": 0x600} table["large_bins"][72] = {"size_min": 0x600, "size_max": 0x640} table["large_bins"][73] = {"size_min": 0x640, "size_max": 0x680} table["large_bins"][74] = {"size_min": 0x680, "size_max": 0x6c0} table["large_bins"][75] = {"size_min": 0x6c0, "size_max": 0x700} table["large_bins"][76] = {"size_min": 0x700, "size_max": 0x740} table["large_bins"][77] = {"size_min": 0x740, "size_max": 0x780} table["large_bins"][78] = {"size_min": 0x780, "size_max": 0x7c0} table["large_bins"][79] = {"size_min": 0x7c0, "size_max": 0x800} table["large_bins"][80] = {"size_min": 0x800, "size_max": 0x840} table["large_bins"][81] = {"size_min": 0x840, "size_max": 0x880} table["large_bins"][82] = {"size_min": 0x880, "size_max": 0x8c0} table["large_bins"][83] = {"size_min": 0x8c0, "size_max": 0x900} table["large_bins"][84] = {"size_min": 0x900, "size_max": 0x940} table["large_bins"][85] = {"size_min": 0x940, "size_max": 0x980} table["large_bins"][86] = {"size_min": 0x980, "size_max": 0x9c0} table["large_bins"][87] = {"size_min": 0x9c0, "size_max": 0xa00} table["large_bins"][88] = {"size_min": 0xa00, "size_max": 0xa40} table["large_bins"][89] = {"size_min": 0xa40, "size_max": 0xa80} table["large_bins"][90] = {"size_min": 0xa80, "size_max": 0xac0} table["large_bins"][91] = {"size_min": 0xac0, "size_max": 0xb00} table["large_bins"][92] = {"size_min": 0xb00, "size_max": 0xb40} table["large_bins"][93] = {"size_min": 0xb40, "size_max": 0xb80} # table["large_bins"][94] is unused table["large_bins"][95] = {"size_min": 0xb80, "size_max": 0xc00} table["large_bins"][96] = {"size_min": 0xc00, "size_max": 0xe00} else: table["large_bins"][63] = {"size_min": 0x200, "size_max": 0x240} table["large_bins"][64] = {"size_min": 0x240, "size_max": 0x280} table["large_bins"][65] = {"size_min": 0x280, "size_max": 0x2c0} table["large_bins"][66] = {"size_min": 0x2c0, "size_max": 0x300} table["large_bins"][67] = {"size_min": 0x300, "size_max": 0x340} table["large_bins"][68] = {"size_min": 0x340, "size_max": 0x380} table["large_bins"][69] = {"size_min": 0x380, "size_max": 0x3c0} table["large_bins"][70] = {"size_min": 0x3c0, "size_max": 0x400} table["large_bins"][71] = {"size_min": 0x400, "size_max": 0x440} table["large_bins"][72] = {"size_min": 0x440, "size_max": 0x480} table["large_bins"][73] = {"size_min": 0x480, "size_max": 0x4c0} table["large_bins"][74] = {"size_min": 0x4c0, "size_max": 0x500} table["large_bins"][75] = {"size_min": 0x500, "size_max": 0x540} table["large_bins"][76] = {"size_min": 0x540, "size_max": 0x580} table["large_bins"][77] = {"size_min": 0x580, "size_max": 0x5c0} table["large_bins"][78] = {"size_min": 0x5c0, "size_max": 0x600} table["large_bins"][79] = {"size_min": 0x600, "size_max": 0x640} table["large_bins"][80] = {"size_min": 0x640, "size_max": 0x680} table["large_bins"][81] = {"size_min": 0x680, "size_max": 0x6c0} table["large_bins"][82] = {"size_min": 0x6c0, "size_max": 0x700} table["large_bins"][83] = {"size_min": 0x700, "size_max": 0x740} table["large_bins"][84] = {"size_min": 0x740, "size_max": 0x780} table["large_bins"][85] = {"size_min": 0x780, "size_max": 0x7c0} table["large_bins"][86] = {"size_min": 0x7c0, "size_max": 0x800} table["large_bins"][87] = {"size_min": 0x800, "size_max": 0x840} table["large_bins"][88] = {"size_min": 0x840, "size_max": 0x880} table["large_bins"][89] = {"size_min": 0x880, "size_max": 0x8c0} table["large_bins"][90] = {"size_min": 0x8c0, "size_max": 0x900} table["large_bins"][91] = {"size_min": 0x900, "size_max": 0x940} table["large_bins"][92] = {"size_min": 0x940, "size_max": 0x980} table["large_bins"][93] = {"size_min": 0x980, "size_max": 0x9c0} table["large_bins"][94] = {"size_min": 0x9c0, "size_max": 0xa00} table["large_bins"][95] = {"size_min": 0xa00, "size_max": 0xc00} table["large_bins"][96] = {"size_min": 0xc00, "size_max": 0xe00} table["large_bins"][97] = {"size_min": 0xe00, "size_max": 0x1000} table["large_bins"][98] = {"size_min": 0x1000, "size_max": 0x1200} table["large_bins"][99] = {"size_min": 0x1200, "size_max": 0x1400} table["large_bins"][100] = {"size_min": 0x1400, "size_max": 0x1600} table["large_bins"][101] = {"size_min": 0x1600, "size_max": 0x1800} table["large_bins"][102] = {"size_min": 0x1800, "size_max": 0x1a00} table["large_bins"][103] = {"size_min": 0x1a00, "size_max": 0x1c00} table["large_bins"][104] = {"size_min": 0x1c00, "size_max": 0x1e00} table["large_bins"][105] = {"size_min": 0x1e00, "size_max": 0x2000} table["large_bins"][106] = {"size_min": 0x2000, "size_max": 0x2200} table["large_bins"][107] = {"size_min": 0x2200, "size_max": 0x2400} table["large_bins"][108] = {"size_min": 0x2400, "size_max": 0x2600} table["large_bins"][109] = {"size_min": 0x2600, "size_max": 0x2800} table["large_bins"][110] = {"size_min": 0x2800, "size_max": 0x2a00} table["large_bins"][111] = {"size_min": 0x2a00, "size_max": 0x3000} table["large_bins"][112] = {"size_min": 0x3000, "size_max": 0x4000} table["large_bins"][113] = {"size_min": 0x4000, "size_max": 0x5000} table["large_bins"][114] = {"size_min": 0x5000, "size_max": 0x6000} table["large_bins"][115] = {"size_min": 0x6000, "size_max": 0x7000} table["large_bins"][116] = {"size_min": 0x7000, "size_max": 0x8000} table["large_bins"][117] = {"size_min": 0x8000, "size_max": 0x9000} table["large_bins"][118] = {"size_min": 0x9000, "size_max": 0xa000} table["large_bins"][119] = {"size_min": 0xa000, "size_max": 0x10000} table["large_bins"][120] = {"size_min": 0x10000, "size_max": 0x18000} table["large_bins"][121] = {"size_min": 0x18000, "size_max": 0x20000} table["large_bins"][122] = {"size_min": 0x20000, "size_max": 0x28000} table["large_bins"][123] = {"size_min": 0x28000, "size_max": 0x40000} table["large_bins"][124] = {"size_min": 0x40000, "size_max": 0x80000} table["large_bins"][125] = {"size_min": 0x80000, "size_max": 0x0} table["large_bins"][126] = {"size_min": 0x0, "size_max": 0x0} # maybe unused return table # for convenience H = HeapInfo # noqa M = MallocPar # noqa A = GlibcArena # noqa C = GlibcChunk # noqa # for convenience GH = GlibcHeap # noqa @Cache.cache_this_session def get_libc_version(verbose=False): """Detect and return the glibc version as a tuple, using cache, configuration, process maps, or system fallback.""" RE_LIBC_PATH = re.compile(r"libc6?[-_](\d+)\.(\d+)\.so") RE_GLIBC_VERSION = re.compile(rb"glibc (\d+)\.(\d+)") def get_libc_version_from_path(): Cache.reset_gef_caches() # get_process_maps may be caching old information sections = ProcessMap.get_process_maps() for section in sections: r = RE_LIBC_PATH.search(section.path) if r: return tuple(int(x) for x in r.groups()) if "libc" not in section.path: continue if is_container_attach(): real_libc_path = Path.append_proc_root(section.path) if not os.path.exists(real_libc_path): continue data = open(real_libc_path, "rb").read() elif is_remote_debug(): if is_qemu_user(): data = None for maps in ProcessMap.get_process_maps(outer=True): if os.path.basename(maps.path) != os.path.basename(section.path): continue if maps.size != section.size: continue real_libc_path = maps.path data = open(real_libc_path, "rb").read() break else: data = Path.read_remote_file(section.path) if not data: continue else: if not os.path.exists(section.path): continue data = open(section.path, "rb").read() r = RE_GLIBC_VERSION.search(data) if r: return tuple(int(x) for x in r.groups()) return None def get_system_libc_version(): res = GefUtil.gef_execute_external(["cat", "/proc/self/maps"], as_list=True) libc_targets = ("libc-2.", "libc.so.6") for line in res: if not any(kw in line for kw in libc_targets): continue path = line.split()[-1] if not os.path.exists(path): continue data = open(path, "rb").read() r = RE_GLIBC_VERSION.search(data) if r: return tuple(int(x) for x in r.groups()) return None # use manual settings libc_assume_version = eval(Config.get_gef_setting("libc.assume_version")) if libc_assume_version != (): if verbose: info("Use libc.assume_version") return libc_assume_version # resolve from maps information libc_version = get_libc_version_from_path() if libc_version is not None: if verbose: info("Resolve from maps") return libc_version # resolve from system libc if not is_container_attach(): libc_version = get_system_libc_version() if libc_version is not None: if verbose: info("Resolve from system libc") return libc_version err("The libc version could not be determined.") err("Please specify it with the following command: `gef config libc.assume_version (2,39)`") raise def titlify(text, color=None, msg_color=None, horizontal_line="-"): """Print a centered title.""" cols = GefUtil.get_terminal_size()[1] if color is None: color = Config.get_gef_setting("theme.default_title_line") if msg_color is None: msg_color = Config.get_gef_setting("theme.default_title_message") msg = [] if text: nb = (cols - len(text) - 2) // 2 msg.append(Color.colorify("{} ".format(horizontal_line * nb), color)) msg.append(Color.colorify(text, msg_color)) msg.append(Color.colorify(" {}".format(horizontal_line * nb), color)) else: msg.append(Color.colorify("{}".format(horizontal_line * cols), color)) return "".join(msg) def err(msg, redirect=""): """The wrapper of gef_print for error level message.""" gef_print("{} {}".format(Color.colorify("[!]", "bold red"), msg), redirect=redirect) return def warn(msg, redirect=""): """The wrapper of gef_print for warning level message.""" gef_print("{} {}".format(Color.colorify("[*]", "bold yellow"), msg), redirect=redirect) return def ok(msg, redirect=""): """The wrapper of gef_print for ok level message.""" gef_print("{} {}".format(Color.colorify("[+]", "bold green"), msg), redirect=redirect) return def info(msg, redirect=""): """The wrapper of gef_print for information level message.""" gef_print("{} {}".format(Color.colorify("[+]", "bold blue"), msg), redirect=redirect) return class String: """A collection of utility functions that are related to strings.""" STRING_ASCII_LOWERCASE = "abcdefghijklmnopqrstuvwxyz" STRING_ASCII_UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" STRING_ASCII_LETTERS = STRING_ASCII_LOWERCASE + STRING_ASCII_UPPERCASE STRING_DIGITS = "0123456789" STRING_HEXDIGITS = "0123456789abcdefABCDEF" STRING_PUNCTUATION = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" STRING_WHITESPACE = " \t\n\r\x0b\x0c" STRING_PRINTABLE = STRING_DIGITS + STRING_ASCII_LETTERS + STRING_PUNCTUATION + STRING_WHITESPACE MORSE_CODE_DICT = { b".-" : b"A", b"-..." : b"B", b"-.-." : b"C", b"-.." : b"D", b"." : b"E", b"..-." : b"F", b"--." : b"G", b"...." : b"H", b".." : b"I", b".---" : b"J", b"-.-" : b"K", b".-.." : b"L", b"--" : b"M", b"-." : b"N", b"---" : b"O", b".--." : b"P", b"--.-" : b"Q", b".-." : b"R", b"..." : b"S", b"-" : b"T", b"..-" : b"U", b"...-" : b"V", b".--" : b"W", b"-..-" : b"X", b"-.--" : b"Y", b"--.." : b"Z", b".----" : b"1", b"..---" : b"2", b"...--" : b"3", b"....-" : b"4", b"....." : b"5", b"-...." : b"6", b"--..." : b"7", b"---.." : b"8", b"----." : b"9", b"-----" : b"0", b"--..--" : b",", b".-.-.-" : b".", b"..--.." : b"?", b"-..-." : b"/", b"-....-" : b"-", b"-.--." : b"(", b"-.--.-" : b")", } @staticmethod def str2bytes(x): """Helper function for str -> bytes.""" if isinstance(x, bytes): return x if isinstance(x, str): try: return bytes(ord(xx) for xx in x) except ValueError: # If str is UTF-8 multi-byte string, raise an error. # e.g., `pi String.str2bytes(b"\xc5\x82".decode("utf-8"))` # In that case, you should simply encode it as UTF-8. return x.encode("utf-8") raise @staticmethod def bytes2str(x): """Helper function for bytes -> str.""" if isinstance(x, str): return x if isinstance(x, bytes): return "".join(chr(xx) for xx in x) raise @staticmethod # noqa def bits2bytes(a, endian="big"): """Helper function for bits -> bytes.""" if isinstance(a, str): a = String.str2bytes(a) if isinstance(a, bytes): a = a.replace(b"0", b"\x00") a = a.replace(b"1", b"\x01") if not isinstance(a, list): a = list(a) assert set(a) <= {0x0, 0x1} out = [] if endian == "little": s = i = 0 for x in a: s += x << i i += 1 if i == 8: out.append(s) s = i = 0 if i > 0: out.append(s) else: s = i = 0 for x in a: s += s + x i += 1 if i == 8: out.append(s) s = i = 0 if i > 0: s = s << (8 - i) out.append(s) return bytes(out) @staticmethod # noqa def bytes2bits(a, endian="big"): """Helper function for bytes -> bits.""" if isinstance(a, str): a = String.str2bytes(a) out = [] if endian == "little": for x in a: for i in range(8): b = (x >> i) & 1 out.append(b) else: for x in a: for i in range(8): b = (x >> (7 - i)) & 1 out.append(b) return out @staticmethod def morse_decode(a): """Decode a bytes or string sequence from Morse code to text.""" if isinstance(a, str): a = String.str2bytes(a) decoded = b"" for elem in a.split(): decoded += String.MORSE_CODE_DICT.get(elem, elem) return decoded @staticmethod def morse_encode(a): """Encode a bytes or string sequence from text to Morse code.""" if isinstance(a, str): a = String.str2bytes(a) MORSE_CODE_REVERSE_DICT = {v: k for k, v in String.MORSE_CODE_DICT.items()} encoded = b"" for elem in a: elem = bytes([elem]) encoded += MORSE_CODE_REVERSE_DICT.get(elem.upper(), elem) encoded += b" " return encoded[:-1] @staticmethod def is_hex(pattern): """Return whether provided string is a hexadecimal value.""" if not pattern.startswith("0x") and not pattern.startswith("0X"): return False return len(pattern) % 2 == 0 and all(c in String.STRING_HEXDIGITS for c in pattern[2:]) def slicer(data, n): """Helper function for slice.""" return [data[i:i + n] for i in range(0, len(data), n)] def slice_unpack(data, n): """Helper function for slice then unpack.""" if n in [1, 2, 4, 8]: length = len(data) // n fmt = "{:s}{:d}{:s}".format(Endian.endian_str(), length, {1: "B", 2: "H", 4: "I", 8: "Q"}[n]) return struct.unpack(fmt, data) elif n == 16: return [u128(data[i:i + 16]) for i in range(0, len(data), 16)] else: raise def align(value, align): """Align the value to the given size. e.g., 0xdeadbeef with align 8 -> 0xdeadbef0""" return value + ((align - (value % align)) % align) def align_to_ptrsize(addr): """Align the address to the ptrsize.""" return align(addr, current_arch.ptrsize) def align_to_pagesize(addr): """Align the address to the pagesize.""" return align(addr, get_pagesize()) def byteswap(x, byte_size=None): """Helper function for byte swap.""" byte_size = byte_size or current_arch.ptrsize bit_size = byte_size * 8 s = 0 for i in range(0, bit_size, 8): s += ((x >> i) & 0xff) << (bit_size - (i + 8)) return s def xor(a, b=None): """Helper function for xor. xor("AAA", "BBB") -> "\x03\x03\x03" xor(b"AAA", b"BBB") -> b"\x03\x03\x03" xor(["AAA", "BBB", "\x03\x03\x03"]) -> "\0\0\0" """ def _xor(a, b): if len(a) < len(b): a, b = b, a if isinstance(a, str) and isinstance(b, str): return ''.join([chr(ord(c1) ^ ord(c2)) for (c1, c2) in zip(a, itertools.cycle(b))]) elif isinstance(a, bytes) and isinstance(b, bytes): return bytes([c1 ^ c2 for (c1, c2) in zip(a, itertools.cycle(b))]) elif isinstance(a, bytearray) and isinstance(b, bytearray): return bytearray([c1 ^ c2 for (c1, c2) in zip(a, itertools.cycle(b))]) raise if b is None: if hasattr(a, "__iter__"): return functools.reduce(_xor, a) raise return _xor(a, b) def ror(val, bits, arch_bits=64): """Helper function for rotate right.""" new_val = (val >> bits) | (val << (arch_bits - bits)) mask = (1 << arch_bits) - 1 return new_val & mask def rol(val, bits, arch_bits=64): """Helper function for rotate left.""" new_val = (val << bits) | (val >> (arch_bits - bits)) mask = (1 << arch_bits) - 1 return new_val & mask def hexdump(source, length=0x10, separator=".", color=True, show_symbol=True, base=0x00, unit=1): """Return the hexdump of `src` argument.""" style = { "nonprintable": "yellow", "printable": "white", "00": "bright_black", "0a": "blue", "ff": "green", } def style_byte(b, color=True): sbyte = "{:02x}".format(b) if not color or Config.get_gef_setting("highlight.regex"): return sbyte if sbyte in style: st = style[sbyte] elif chr(b) in String.STRING_PRINTABLE: st = style["printable"] else: st = style["nonprintable"] return Color.colorify(sbyte, st) align = AddressUtil.get_format_address_width() tmp = [] max_sym_width = 0 for i in range(0, len(source), length): addr = base + i if show_symbol: sym = Symbol.get_symbol_string(addr) else: sym = "" if len(sym) > max_sym_width: max_sym_width = len(sym) chunk = bytearray(source[i : i + length]) if unit == 1: padlen = (0x10 - len(chunk)) * 3 else: padlen = (0x10 - len(chunk)) // unit * (unit * 2 + 3) if len(chunk) % unit: padlen += ((unit - len(chunk) % unit) * 2) hexa = [style_byte(b, color=color) for b in chunk] if unit > 1: hexa = ["0x" + "".join(x[::-1]) for x in slicer(hexa, unit)] if unit == 1: hexa[min(len(hexa), 8) - 1] += " " # double the blank at the 8th byte hexa = " ".join(hexa) padded_hexa = hexa + " " * padlen text = "".join([chr(b) if 0x20 <= b < 0x7f else separator for b in chunk]) text_padlen = 0x10 - len(text) padded_text = text + " " * text_padlen tmp.append([addr, sym, hexa, padded_hexa, padded_text]) result = [] for addr, sym, _, data, text in tmp: result.append("{:#0{:d}x}:{:<{:d}} {:s} | {:s} |".format( addr, align, sym, max_sym_width, data, text, )) return "\n".join(result) class Symbol: """A collection of utility functions that are related to symbols.""" # `info symbol` called from gdb_get_location is heavy processing. # Moreover, AddressUtil.recursive_dereference causes each address to be resolved every time. # Cache.cache_until_next is ineffective due to frequent resets (each time the `stepi` runs). # Fortunately, symbol information rarely changes. # The cache is retained until explicitly cleared. @staticmethod @Cache.cache_this_session def gdb_get_location(address): """e.g., 0xffffffff9f6bd2a0 -> ('commit_creds', 0)""" if address is None: return None # Do not use gdb.format_address available from gdb 13.x, # because symbols added with add-symbol-temporary may not be recognized. # slow path uses `info symbol` command name = None sym = gdb.execute("info symbol {:#x}".format(address), to_string=True) if sym.startswith("No symbol matches"): return None i = sym.find(" in section ") sym = sym[:i].split() if len(sym) >= 3 and sym[-1].isdigit(): # e.g., ptmalloc_init.part + 1 in section .text of /lib/x86_64-linux-gnu/libc.so.6 name = " ".join(sym[:-2]) offset = int(sym[-1]) else: # e.g., ptmalloc_init.part in section .text of /lib/x86_64-linux-gnu/libc.so.6 name = " ".join(sym) offset = 0 return name, offset @staticmethod @Cache.cache_this_session def get_symbol_string(addr, nosymbol_string=""): """e.g., 0xffffffff9f6bd2a1 -> ' '. Be careful to include leading spaces.""" try: if isinstance(addr, str): addr = Color.remove_color(addr) addr = int(addr, 16) ret = Symbol.gdb_get_location(addr) if ret is None: return nosymbol_string except (ValueError, gdb.error): return nosymbol_string sym_name, sym_offset = ret[0], ret[1] if addr - sym_offset == 0: return nosymbol_string sym_name = Instruction.smartify_text(sym_name) if sym_offset == 0: return " <{}>".format(sym_name) else: return " <{}+{:#x}>".format(sym_name, sym_offset) @staticmethod def get_ksymaddr(sym): """e.g., 'commit_creds' -> 0xffffffff9f6bd2a0""" try: res = gdb.execute("ksymaddr-remote --quiet --no-pager --exact {:s}".format(sym), to_string=True) return int(res.split()[0], 16) except (gdb.error, IndexError, ValueError): return None @staticmethod def get_ksymaddr_multiple(sym): """e.g., 'set_is_seen' -> [0xffffffffba146db0,0xffffffffba6d84e0,0xffffffffba6dd170]""" out = [] try: ret = gdb.execute("ksymaddr-remote --quiet --no-pager --exact {:s}".format(sym), to_string=True) for line in ret.splitlines(): addr = int(line.split()[0], 16) out.append(addr) return out except (gdb.error, IndexError, ValueError): return None @staticmethod def get_ksymaddr_symbol(addr): """e.g., 0xffffffff9f6bd2a0 -> 'commit_creds'""" try: res = gdb.execute("ksymaddr-remote --quiet --no-pager {:#x}".format(addr), to_string=True) res = res.splitlines()[-1] return res.split()[2] except (gdb.error, IndexError): return None @staticmethod def get_symbol_by_monitor(symbol): if not is_kdb(): return None res = gdb.execute("monitor {:s}".format(symbol), to_string=True) r = re.search(symbol + r" = 0x(\S+)", res) if not r: return None v = int(r.group(1), 16) if not is_valid_addr(v): return None return v class ModuleLoader: def load_capstone(f): """Decorator wrapper to load capstone.""" @functools.wraps(f) def wrapper(*args, **kwargs): try: capstone = __import__("capstone") if capstone.cs_version()[0] == 6: LOONGARCH64.capstone_support = True ALPHA.capstone_support = True HPPA.capstone_support = True HPPA64.capstone_support = True return f(*args, **kwargs) except ImportError as err: msg = "Missing `capstone` package for Python, try installing with `pip install capstone`" raise ImportWarning(msg) from err return wrapper def load_unicorn(f): """Decorator wrapper to load unicorn.""" @functools.wraps(f) def wrapper(*args, **kwargs): try: __import__("unicorn") if is_ppc32(): # unicorn does not support ppc64 try: __import__("unicorn.ppc_const") except ImportError: pass if is_riscv32() or is_riscv64(): try: __import__("unicorn.riscv_const") except ImportError: pass if is_s390x(): try: __import__("unicorn.s390x_const") except ImportError: pass return f(*args, **kwargs) except ImportError as err: msg = "Missing `unicorn` package for Python, try installing with `pip install unicorn`" raise ImportWarning(msg) from err return wrapper def load_keystone(f): """Decorator wrapper to load keystone.""" @functools.wraps(f) def wrapper(*args, **kwargs): try: __import__("keystone") return f(*args, **kwargs) except ImportError as err: msg = "Missing `keystone-engine` package for Python, try installing with `pip install keystone-engine`" raise ImportWarning(msg) from err return wrapper def load_ropper(f): """Decorator wrapper to load ropper.""" @functools.wraps(f) def wrapper(*args, **kwargs): try: __import__("ropper") return f(*args, **kwargs) except ImportError as err: msg = "Missing `ropper` package for Python, try installing with `pip install ropper`" raise ImportWarning(msg) from err return wrapper def load_binwalk(f): """Decorator wrapper to load binwalk.""" @functools.wraps(f) def wrapper(*args, **kwargs): try: __import__("binwalk") return f(*args, **kwargs) except ImportError as err: msg = "Missing `binwalk` package for Python, try installing with `apt install binwalk`" raise ImportWarning(msg) from err return wrapper def load_crccheck(f): """Decorator wrapper to load crccheck.""" @functools.wraps(f) def wrapper(*args, **kwargs): try: __import__("crccheck") return f(*args, **kwargs) except ImportError as err: msg = "Missing `crccheck` package for Python, try installing with `pip install crccheck`" raise ImportWarning(msg) from err return wrapper def load_codext(f): """Decorator wrapper to load codext.""" @functools.wraps(f) def wrapper(*args, **kwargs): try: __import__("codext") return f(*args, **kwargs) except ImportError as err: msg = "Missing `codext` package for Python, try installing with `pip install codext`" raise ImportWarning(msg) from err return wrapper def load_angr(f): """Decorator wrapper to load angr.""" @functools.wraps(f) def wrapper(*args, **kwargs): try: # Angr seems to import readline internally, so tab completion breaks after loading. # Therefore, disable readline temporarily. readline = sys.modules.get("readline", None) sys.modules["readline"] = None __import__("angr") sys.modules["readline"] = readline return f(*args, **kwargs) except ImportError as err: msg = "Missing `angr` package for Python, try installing with `pip install angr`" raise ImportWarning(msg) from err return wrapper class Disasm: """A collection of utility functions that makes disassemble.""" __gef_prev_arch__ = None # previous valid result of gdb.selected_frame().architecture() @staticmethod def gdb_disassemble(start_pc, nb_insn=None, end_pc=None): """Disassemble instructions from `start_pc` (Integer). Return an iterator of Instruction objects. This is the backend used by Disasm.gef_disassemble by default.""" if start_pc is None: return None try: arch = gdb.selected_frame().architecture() Disasm.__gef_prev_arch__ = arch except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). # At this time arch cannot be resolved, but if it was successful before, it will be used. if Disasm.__gef_prev_arch__ is None: raise arch = Disasm.__gef_prev_arch__ kwargs = {} if nb_insn is not None: kwargs["count"] = nb_insn if end_pc is not None: kwargs["end_pc"] = end_pc insn_list = list(arch.disassemble(start_pc, **kwargs)) if not insn_list: return None is_thumb = (is_arm32() or is_arm32_cortex_m()) def eff_addr(a): return a - 1 if is_thumb and (a & 1) else a def get_mnemo_operands(insn): asm = insn["asm"].rstrip().split(None, 1) if len(asm) > 1: mnemo = asm[0] operands = asm[1].replace("\t", " ") else: mnemo, operands = asm[0], "" return mnemo, operands try: # fast path: read each instruction at once # calc size base = None end = None for insn in insn_list: ea = eff_addr(insn["addr"]) if base is None or ea < base: base = ea e = ea + insn["length"] if end is None or e > end: end = e blob = read_memory(base, end - base) for insn in insn_list: address = insn["addr"] mnemo, operands = get_mnemo_operands(insn) ea = eff_addr(address) off = ea - base opcodes = blob[off: off + insn["length"]] yield Instruction(address, mnemo, operands, opcodes) except gdb.MemoryError: # slow path: read each instruction sequentially for insn in insn_list: address = insn["addr"] mnemo, operands = get_mnemo_operands(insn) if is_thumb and (address & 1): opcodes = read_memory(address - 1, insn["length"]) else: opcodes = read_memory(address, insn["length"]) yield Instruction(address, mnemo, operands, opcodes) return None @staticmethod def gdb_get_nth_previous_instruction_address(addr, n): """Return the address (Integer) of the `n`-th instruction before `addr`.""" if addr is None: return None # fixed-length ABI if current_arch.instruction_length: return max(0, addr - n * current_arch.instruction_length) # variable-length ABI cur_insn_addr = get_insn(addr).address # we try to find a good set of previous instructions by "guessing" disassembling backwards # the 15 comes from the longest instruction valid size for i in range(15 * n, 0, -1): try: insns = list(Disasm.gdb_disassemble(addr - i, end_pc=cur_insn_addr)) except gdb.MemoryError: # this is because we can hit an unmapped page trying to read backward break # 1. check that the disassembled instructions list size can satisfy if len(insns) < n + 1: # we expect the current instruction plus the n before it continue # If the list of instructions is longer than what we need, then we # could get lucky and already have more than what we need, so slice down insns = insns[-n - 1:] # 2. check that the sequence ends with the current address if insns[-1].address != cur_insn_addr: continue # 3. check all instructions are valid if all(insn.is_valid() for insn in insns): return insns[0].address return None @staticmethod @ModuleLoader.load_capstone def capstone_get_nth_previous_instruction_address(addr, n, cs=None): """Return the address (Integer) of the `n`-th instruction before `addr`.""" if addr is None: return None if cs is None: cs = sys.modules["capstone"].Cs(*UnicornKeystoneCapstone.get_capstone_arch()) # fixed-length ABI if current_arch.instruction_length: return max(0, addr - n * current_arch.instruction_length) # variable-length ABI # we try to find a good set of previous instructions by "guessing" disassembling backwards # the 15 comes from the longest instruction valid size for i in range(15 * n, 0, -1): try: code = read_memory(addr - i, i) except gdb.MemoryError: continue insns = list(cs.disasm(code, addr - i)) # 1. check that the disassembled instructions list size can satisfy if len(insns) < n: continue insns = insns[-n:] # 2. check that the sequence ends with the current address if insns[-1].address + insns[-1].size != addr: continue return insns[0].address return None @staticmethod @ModuleLoader.load_capstone def capstone_disassemble(location, nb_insn, **kwargs): """Disassemble `nb_insn` instructions after `addr` and `nb_prev` before `addr` using the capstone disassembler. Return an iterator of Instruction objects. This is the backend used by Disasm.gef_disassemble if specified in the config. It is also called directly from some commands such as Disasm.capstone_disassemble.""" def cs_insn_to_gef_insn(cs_insn): return Instruction(cs_insn.address, cs_insn.mnemonic, cs_insn.op_str, cs_insn.bytes) capstone = sys.modules["capstone"] arch, mode = UnicornKeystoneCapstone.get_capstone_arch( arch=kwargs.get("arch", None), mode=kwargs.get("mode", None), endian=kwargs.get("endian", None), ) try: cs = capstone.Cs(arch, mode) cs.detail = True # noqa except capstone.CsError: err("CsError") return # fix location by nb_prev nb_prev = kwargs.get("nb_prev", 0) if nb_prev > 0: location_tmp = Disasm.capstone_get_nth_previous_instruction_address( location, nb_prev, capstone.Cs(arch, mode), ) if location_tmp is not None: location = location_tmp nb_insn += nb_prev # split reading by page_size read_addr = location read_size = get_pagesize() - (location & get_pagesize_mask_low()) # fix for arm thumb2 mode if (is_arm32() or is_arm32_cortex_m()) and read_addr & 1: read_addr -= 1 read_size += 1 skip = kwargs.get("skip", 0) arch_inst_length = current_arch.instruction_length or 1 used_bytes = 0 code_remain = bytes.fromhex(kwargs.get("code", "")) dont_read = "code" in kwargs # A loop to read the required memory until the specified length is reached while True: if not dont_read and len(code_remain) < get_pagesize(): # not enough code to disassemble, so read the memory to pool try: read_data = read_memory(read_addr, read_size) except gdb.MemoryError: err("Memory read error at {:#x}-{:#x}".format(read_addr, read_addr + read_size)) return code_remain += read_data # cs.disasm will terminate disassembling if an invalid instruction is detected. # This is a loop to display "(bad)" and increment PC and reinterpret code. while True: # disasm for insn in cs.disasm(code_remain, location): used_bytes += len(insn.bytes) if skip: skip -= 1 continue yield cs_insn_to_gef_insn(insn) nb_insn -= 1 if nb_insn == 0: return # success (disassembled something) if used_bytes > 0: break # failure (maybe the code is invalid) yield Instruction(location, "(bad)", "", code_remain[:arch_inst_length]) nb_insn -= 1 if nb_insn == 0: return location += arch_inst_length code_remain = code_remain[arch_inst_length:] # go away only the size used code_remain = code_remain[used_bytes:] # There may be instructions placed across page boundaries. location += used_bytes used_bytes = 0 read_addr += read_size # 1st loop is the offset size. 2nd~ loops are the page size. read_size = get_pagesize() return @staticmethod def gef_disassemble(addr, nb_insn, nb_prev=0): """Disassemble `nb_insn` instructions after `addr` and `nb_prev` before `addr`. Return an iterator of Instruction objects. Use Disasm.gdb_disassemble or Disasm.capstone_disassemble according to the settings.""" if Config.get_gef_setting("context_code.use_capstone"): get_nth_prev_address = Disasm.capstone_get_nth_previous_instruction_address get_insns = Disasm.capstone_disassemble else: get_nth_prev_address = Disasm.gdb_get_nth_previous_instruction_address get_insns = Disasm.gdb_disassemble if nb_prev: for i in range(nb_prev): nb_prev_addr = get_nth_prev_address(addr, nb_prev - i) if not nb_prev_addr: continue for insn in get_insns(nb_prev_addr, nb_prev): if insn.address == addr: break yield insn break nb_insn = max(1, nb_insn) for insn in get_insns(addr, nb_insn): yield insn return None @staticmethod def gef_instruction_n(addr, n): """Return the `n`-th instruction after `addr` as an Instruction object.""" try: return list(Disasm.gef_disassemble(addr, n + 1))[n] except IndexError: return None def get_insn(addr=None): """Return the current instruction as an Instruction object.""" if addr is None: if not is_alive(): return None addr = current_arch.pc return Disasm.gef_instruction_n(addr, 0) def get_insn_next(addr=None): """Return the next instruction as an Instruction object.""" if addr is None: if not is_alive(): return None addr = current_arch.pc return Disasm.gef_instruction_n(addr, 1) def get_insn_prev(addr=None): """Return the prev instruction as an Instruction object.""" if addr is None: if not is_alive(): return None addr = current_arch.pc try: gen = Disasm.gef_disassemble(addr, 0, nb_prev=2) gen.__next__() return gen.__next__() except (gdb.error, StopIteration): return None class Checksec: """Manage checksec related functions.""" @staticmethod @Cache.cache_until_next def get_cet_status_old_interface(): # https://lore.kernel.org/lkml/1531342544.15351.37.camel@intel.com/ sp = current_arch.sp mem = {} # backup for i in range(3): # *addr = SHSTK/IBT status # *(addr + 1) = SHSTK base address # *(addr + 2) = SHSTK size addr = sp + current_arch.ptrsize * i mem[addr] = read_memory(addr, current_arch.ptrsize) res = gdb.execute("call-syscall arch_prctl 0x3001 {:#x}".format(sp), to_string=True) # ARCH_CET_STATUS output_line = res.splitlines()[-1] ret = int(output_line.split()[2], 0) sp_value = read_int_from_memory(sp) # revert for addr, data in mem.items(): write_memory(addr, data) # check ret if ret != 0: return None return sp_value @staticmethod @Cache.cache_until_next def get_cet_status_new_interface(): # https://www.kernel.org/doc/html/next/arch/x86/shstk.html sp = current_arch.sp mem = {} # backup for i in range(1): # *addr = SHSTK status addr = sp + current_arch.ptrsize * i mem[addr] = read_memory(addr, current_arch.ptrsize) res = gdb.execute("call-syscall arch_prctl 0x5005 {:#x}".format(sp), to_string=True) # ARCH_SHSTK_STATUS output_line = res.splitlines()[-1] ret = int(output_line.split()[2], 0) sp_value = read_int_from_memory(sp) # revert for addr, data in mem.items(): write_memory(addr, data) if ret != 0: return None return sp_value @staticmethod @Cache.cache_until_next def get_cet_status_via_procfs(): # https://www.kernel.org/doc/html/next/arch/x86/shstk.html dic = {} if is_remote_debug(): if Pid.get_pid(remote=True): remote_status = "/proc/{:d}/status".format(Pid.get_pid(remote=True)) data = Path.read_remote_file(remote_status, as_byte=True) # qemu-user is failed here, it is ok if not data: return None else: if Pid.get_pid(): local_status = "/proc/{:d}/status".format(Pid.get_pid()) data = open(local_status, "rb").read() if not data: return None if b"x86_Thread_features:" not in data: return False # unsupported dic["shstk"] = b"x86_Thread_features: shstk" in data dic["shstk lock"] = b"x86_Thread_features_locked: shstk" in data return dic @staticmethod def get_mte_status(): auxv = Auxv.get_auxiliary_values() HWCAP2_MTE = 1 << 18 if auxv and "AT_HWCAP2" in auxv and (auxv["AT_HWCAP2"] & HWCAP2_MTE) == 0: return None # Unsupported res = gdb.execute("call-syscall prctl 0x38 0 0 0 0", to_string=True) # PR_GET_TAGGED_ADDR_CTRL output_line = res.splitlines()[-1] ret = int(output_line.split()[2], 0) pQ = lambda a: struct.pack("" class Architecture: """Generic metaclass for the architecture supported by GEF.""" __metaclass__ = abc.ABCMeta # base properties @property @abc.abstractmethod def arch(self): pass @property @abc.abstractmethod def mode(self): pass @property @abc.abstractmethod def load_condition(self): pass # register properties @property @abc.abstractmethod def all_registers(self): pass @property @abc.abstractmethod def alias_registers(self): pass @property @abc.abstractmethod def special_registers(self): pass @property @abc.abstractmethod def flag_register(self): pass @property @abc.abstractmethod def flags_table(self): pass @property @abc.abstractmethod def return_register(self): pass @property @abc.abstractmethod def function_parameters(self): pass @property @abc.abstractmethod def syscall_register(self): pass @property @abc.abstractmethod def syscall_parameters(self): pass # architecture properties @property @abc.abstractmethod def bit_length(self): pass @property @abc.abstractmethod def endianness(self): pass @property @abc.abstractmethod def instruction_length(self): pass @property @abc.abstractmethod def has_delay_slot(self): pass @property @abc.abstractmethod def has_syscall_delay_slot(self): pass @property @abc.abstractmethod def has_ret_delay_slot(self): pass @property @abc.abstractmethod def stack_grow_down(self): pass @property @abc.abstractmethod def tls_supported(self): pass # module properties @property @abc.abstractmethod def keystone_support(self): pass @property @abc.abstractmethod def capstone_support(self): pass @property @abc.abstractmethod def unicorn_support(self): pass # instruction properties @property @abc.abstractmethod def nop_insn(self): pass @property @abc.abstractmethod def infloop_insn(self): pass @property @abc.abstractmethod def trap_insn(self): pass @property @abc.abstractmethod def ret_insn(self): pass @property @abc.abstractmethod def syscall_insn(self): pass # instruction methods @abc.abstractmethod def is_syscall(self, insn): pass @abc.abstractmethod def is_call(self, insn): pass @abc.abstractmethod def is_jump(self, insn): pass @abc.abstractmethod def is_ret(self, insn): pass @abc.abstractmethod def is_conditional_branch(self, insn): pass @abc.abstractmethod def is_branch_taken(self, insn): pass # register methods @abc.abstractmethod def flag_register_to_human(self, val=None): pass @abc.abstractmethod def get_ra(self, insn, frame): pass @abc.abstractmethod def get_tls(self): pass @abc.abstractmethod def decode_cookie(self, value, cookie): pass @abc.abstractmethod def encode_cookie(self, value, cookie): pass @property def pc(self): return get_register("$pc") @property def sp(self): return get_register("$sp") @property def ptrsize(self): return AddressUtil.get_memory_alignment() def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: i -= len(self.function_parameters) sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val def get_aliased_registers(self): # use cache if hasattr(self, "aliased_registers"): return self.aliased_registers # {"$zero":"$zero/$x0", ...} self.aliased_registers = {} for reg in self.all_registers: if self.alias_registers and reg in self.alias_registers: reg_str = "{:s}/{:s}".format(reg, self.alias_registers[reg]) else: reg_str = reg self.aliased_registers[reg] = reg_str return self.aliased_registers def get_aliased_registers_name_max(self): # use cache if hasattr(self, "aliased_registers_max_len"): return self.aliased_registers_max_len # max(len("$zero/$x0"), ...) maxlen = max([len(v) for v in self.get_aliased_registers().values() if v != self.flag_register]) self.aliased_registers_max_len = maxlen return self.aliased_registers_max_len def get_registers_name_max(self): # use cache if hasattr(self, "registers_max_len"): return self.registers_max_len # max(len("$x0"), ...) maxlen = max([len(v) for v in self.all_registers if v != self.flag_register]) self.registers_max_len = maxlen return self.registers_max_len @staticmethod def flags_to_human(reg_value, value_table): """Return a human readable string showing the flag states.""" flags = [] for i in value_table: if reg_value & (1 << i): flag_str = Color.boldify(value_table[i].upper()) else: flag_str = value_table[i].lower() flags.append(flag_str) return "{:#x} [{}]".format(reg_value, " ".join(flags)) class RISCV(Architecture): """GEF representation of RISCV-32 architecture.""" arch = "RISCV" mode = "32" load_condition = [ # Elf.EM_RISCV cannot determine whether it is 32-bit or 64-bit, so it should not be used. "RISCV", "RISCV32", "RISCV:RV32", ] # https://msyksphinz-self.github.io/riscv-isadoc/html/index.html all_registers = [ "$zero", "$ra", "$sp", "$gp", "$tp", "$t0", "$t1", "$t2", "$fp", "$s1", "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$s8", "$s9", "$s10", "$s11", "$t3", "$t4", "$t5", "$t6", "$pc", ] alias_registers = { "$zero": "$x0", "$ra": "$x1", "$sp": "$x2", "$gp": "$x3", "$tp": "$x4", "$t0": "$x5", "$t1": "$x6", "$t2": "$x7", "$fp": "$x8/$s0", "$s1": "$x9", "$a0": "$x10", "$a1": "$x11", "$a2": "$x12", "$a3": "$x13", "$a4": "$x14", "$a5": "$x15", "$a6": "$x16", "$a7": "$x17", "$s2": "$x18", "$s3": "$x19", "$s4": "$x20", "$s5": "$x21", "$s6": "$x22", "$s7": "$x23", "$s8": "$x24", "$s9": "$x25", "$s10": "$x26", "$s11": "$x27", "$t3": "$x28", "$t4": "$x29", "$t5": "$x30", "$t6": "$x31", } flag_register = None # RISC-V has no flags register return_register = "$a0" function_parameters = ["$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7"] syscall_register = "$a7" syscall_parameters = ["$a0", "$a1", "$a2", "$a3", "$a4", "$a5"] bit_length = 32 endianness = "little" instruction_length = None # variable length has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = False capstone_support = True unicorn_support = True nop_insn = b"\x13\x00\x00\x00" # nop infloop_insn = b"\x6f\x00\x00\x00" # j self trap_insn = b"\x73\x00\x10\x00" # ebreak ret_insn = b"\x67\x80\x00\x00" # ret syscall_insn = b"\x73\x00\x00\x00" # ecall def is_syscall(self, insn): return insn.mnemonic in ["ecall"] def is_call(self, insn): if self.is_ret(insn): return False if insn.mnemonic in ["jal", "jalr", "c.jal", "c.jalr"]: return True return False def is_jump(self, insn): if self.is_conditional_branch(insn): return True if insn.mnemonic in ["c.j", "c.jr", "j", "jr"]: return True return False def is_ret(self, insn): mnemo = insn.mnemonic if mnemo == "ret": # gdb interpret "jalr zero, ra, 0" as "ret" return True if mnemo == "c.jalr": return insn.operands[0] == "ra" return False def is_conditional_branch(self, insn): branch_mnemos = [ "beq", "bne", "blt", "bge", "bltu", "bgeu", "c.beqz", "c.bnez", ] return insn.mnemonic in branch_mnemos def is_branch_taken(self, insn): def long_to_twos_complement(v): """Convert a python long value to its two's complement.""" if is_32bit(): if v & 0x8000_0000: return v - 0x1_0000_0000 elif is_64bit(): if v & 0x8000_0000_0000_0000: return v - 0x1_0000_0000_0000_0000 else: raise OSError("RISC-V: ELF file is not ELF32 or ELF64. This is not currently supported") return v mnemo = insn.mnemonic if mnemo.startswith("c."): mnemo = mnemo[2:] condition = mnemo[1:] if condition.endswith("z"): # r2 is the zero register if we are comparing to 0 rs1 = get_register(insn.operands[0]) rs2 = get_register("$zero") condition = condition[:-1] elif len(insn.operands) > 2: # r2 is populated with the second operand rs1 = get_register(insn.operands[0]) rs2 = get_register(insn.operands[1]) else: raise OSError("RISC-V: Failed to get rs1 and rs2 for instruction: `{}`".format(insn)) # If the conditional operation is not unsigned, convert the python long into # its two's complement if not condition.endswith("u"): rs2 = long_to_twos_complement(rs2) rs1 = long_to_twos_complement(rs1) else: condition = condition[:-1] if condition == "eq": if rs1 == rs2: taken, reason = True, "{}={}".format(rs1, rs2) else: taken, reason = False, "{}!={}".format(rs1, rs2) elif condition == "ne": if rs1 != rs2: taken, reason = True, "{}!={}".format(rs1, rs2) else: taken, reason = False, "{}={}".format(rs1, rs2) elif condition == "lt": if rs1 < rs2: taken, reason = True, "{}<{}".format(rs1, rs2) else: taken, reason = False, "{}>={}".format(rs1, rs2) elif condition == "ge": if rs1 >= rs2: taken, reason = True, "{}>={}".format(rs1, rs2) else: taken, reason = False, "{}<{}".format(rs1, rs2) else: raise OSError("RISC-V: Conditional instruction `{}` not supported yet".format(insn)) return taken, reason def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$ra") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$tp") def decode_cookie(self, value, cookie): return value def encode_cookie(self, value, cookie): return value class RISCV64(RISCV): """GEF representation of RISCV-64 architecture.""" arch = "RISCV" mode = "64" load_condition = [ # Elf.EM_RISCV cannot determine whether it is 32-bit or 64-bit, so it should not be used. "RISCV64", "RISCV:RV64", ] bit_length = 64 class ARM(Architecture): """GEF representation of ARM-32 architecture.""" arch = "ARM" load_condition = [ Elf.EM_ARM, "ARM", "ARM_ANY", "ARMV2", "ARMV2A", "ARMV3", "ARMV4", "ARMV4T", "ARMV5", "ARMV5T", "ARMV5TE", "ARMV5TEJ", "ARMV6", "ARMV6K", "ARMV6KZ", "ARMV6T2", "ARMV7", ] cached_is_cortex_m = None def is_cortex_m(self): if self.cached_is_cortex_m in (True, False): return self.cached_is_cortex_m if self.cached_is_cortex_m is None: # is_alive and get_register are not yet defined here and cannot be used. try: gdb.execute("info registers cpsr", to_string=True) self.cached_is_cortex_m = False return self.cached_is_cortex_m except gdb.error: pass try: gdb.execute("info registers xpsr", to_string=True) self.cached_is_cortex_m = True return self.cached_is_cortex_m except gdb.error: pass # default is Cortex-A self.cached_is_cortex_m = False return self.cached_is_cortex_m @property def all_registers(self): if self.is_cortex_m(): return [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$sp", "$lr", "$pc", "$xpsr", "$msp", "$psp", "$primask", "$basepri", "$faultmask", "$control", ] else: return [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$sp", "$lr", "$pc", "$cpsr", ] @property def flag_register(self): if self.is_cortex_m(): return "$xpsr" else: return "$cpsr" @property def thumb_bit(self): if self.is_cortex_m(): return 24 else: return 5 @property def flags_table(self): if self.is_cortex_m(): return { 31: "negative", 30: "zero", 29: "carry", 28: "overflow", self.thumb_bit: "thumb", } else: return { 31: "negative", 30: "zero", 29: "carry", 28: "overflow", 7: "interrupt", 6: "fast", self.thumb_bit: "thumb", } alias_registers = { "$r11": "$fp", "$r12": "$ip", "$sp": "$r13", "$lr": "$r14", "$pc": "$r15", } return_register = "$r0" function_parameters = ["$r0", "$r1", "$r2", "$r3"] syscall_register = "$r7" syscall_parameters = ["$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6"] def is_thumb(self): """Determine if the machine is currently in THUMB mode.""" if not is_alive(): return False cpsr = get_register(self.flag_register) if cpsr is None: return False return bool(cpsr & (1 << self.thumb_bit)) @property def mode(self): if self.is_thumb(): return "THUMB" else: return "ARM" bit_length = 32 endianness = "little / big" @property def instruction_length(self): # Thumb instructions have variable-length (2 or 4-byte) if self.is_thumb(): return None # variable length else: return 4 has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = True capstone_support = True unicorn_support = True # http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0041c/Caccegih.html @property def nop_insn(self): if self.is_thumb(): return b"\x00\xbf" # nop else: return b"\x01\x10\xa0\xe1" # mov r1, r1 @property def infloop_insn(self): if self.is_thumb(): return b"\xfe\xe7" # b #0 else: return b"\xfe\xff\xff\xea" # b #0 @property def trap_insn(self): if self.is_thumb(): return b"\x00\xbe" # bkpt #0 else: return b"\x70\x00\x20\xe1" # bkpt #0 @property def ret_insn(self): if self.is_thumb(): return b"\xf7\x46" # mov pc, lr else: return b"\x0e\xf0\xa0\xe1" # mov pc, lr @property def syscall_insn(self): if self.is_thumb(): return b"\x00\xdf" # svc 0x0 else: return b"\x00\x00\x00\xef" # svc 0x0 @property def pc(self): pc = get_register("$pc") if self.is_thumb(): pc += 1 return pc def is_syscall(self, insn): return insn.mnemonic in ["svc", "swi"] def is_call(self, insn): conditions = [ "", "eq", "ne", "lt", "le", "gt", "ge", "vs", "vc", "mi", "pl", "hi", "ls", "cs", "cc", "hs", "lo", "al", ] mnemo = insn.mnemonic for cc in conditions: if mnemo in [f"bl{cc}", f"bl{cc}.n", f"bl{cc}.w", f"blx{cc}", f"blx{cc}.n", f"blx{cc}.w"]: return True return False def is_jump(self, insn): if self.is_ret(insn): return False if self.is_conditional_branch(insn): return True mnemo = insn.mnemonic if mnemo in ["b", "b.n", "b.w", "bx", "bx.n", "bx.w"]: return True if mnemo in ["mov", "mov.n", "mov.w", "ldr", "ldr.n", "ldr.w", "add", "add.n", "add.w"]: return insn.operands[0] == "pc" return False def is_ret(self, insn): load_mnemos = [ "pop", "ldm", "ldmea", "ldmed", "ldmfa", "ldmfd", "ldmia", "ldmib", "ldmda", "ldmdb", "ldm.n", "ldmea.n", "ldmed.n", "ldmfa.n", "ldmfd.n", "ldmia.n", "ldmib.n", "ldmda.n", "ldmdb.n", "ldm.w", "ldmea.w", "ldmed.w", "ldmfa.w", "ldmfd.w", "ldmia.w", "ldmib.w", "ldmda.w", "ldmdb.w", ] mnemo = insn.mnemonic if mnemo in load_mnemos: return "pc}" in "".join(insn.operands) if mnemo in ["b", "b.n", "b.w", "bx", "bx.n", "bx.w"]: return insn.operands[0] == "lr" if mnemo in ["mov", "mov.n", "mov.w"]: return insn.operands[:2] == ["pc", "lr"] if mnemo == "add" and len(insn.operands) >= 3: return insn.operands[:2] == ["pc", "lr"] and int(insn.operands[2].lstrip("#"), 0) == 0 if mnemo == "rfe": return True return False def is_conditional_branch(self, insn): conditions = [ "eq", "ne", "lt", "le", "gt", "ge", "vs", "vc", "mi", "pl", "hi", "ls", "cs", "cc", "hs", "lo", "al", ] for cc in conditions: if insn.mnemonic in [f"b{cc}", f"b{cc}.n", f"b{cc}.w", f"bx{cc}", f"bx{cc}.n", f"bx{cc}.w"]: return True if insn.mnemonic in ["cbnz", "cbz", "tbnz", "tbz"]: return True return False def is_branch_taken(self, insn): mnemo, operands = insn.mnemonic, insn.operands # ref: http://www.davespace.co.uk/arm/introduction-to-arm/conditional.html flags = {self.flags_table[k]: k for k in self.flags_table} val = get_register(self.flag_register) taken, reason = False, "" if val is not None: zero = bool(val & (1 << flags["zero"])) negative = bool(val & (1 << flags["negative"])) overflow = bool(val & (1 << flags["overflow"])) carry = bool(val & (1 << flags["carry"])) else: zero = False negative = False overflow = False carry = False if mnemo in ["cbnz", "cbz", "tbnz", "tbz"]: reg = operands[0] op = get_register(reg) if mnemo == "cbnz": if op != 0: taken, reason = True, "{}!=0".format(reg) else: taken, reason = False, "{}==0".format(reg) elif mnemo == "cbz": if op == 0: taken, reason = True, "{}==0".format(reg) else: taken, reason = False, "{}!=0".format(reg) elif mnemo == "tbnz": # operands[1] has a #, then the number i = int(operands[1].lstrip("#"), 0) if (op & (1 << i)) != 0: taken, reason = True, "{}&1<<{}!=0".format(reg, i) else: taken, reason = False, "{}&1<<{}==0".format(reg, i) elif mnemo == "tbz": # operands[1] has a #, then the number i = int(operands[1].lstrip("#"), 0) if (op & (1 << i)) == 0: taken, reason = True, "{}&1<<{}==0".format(reg, i) else: taken, reason = False, "{}&1<<{}!=0".format(reg, i) elif mnemo.endswith(("eq", "eq.n", "eq.w")): taken, reason = zero, "Z" elif mnemo.endswith(("ne", "ne.n", "ne.w")): taken, reason = not zero, "!Z" elif mnemo.endswith(("lt", "lt.n", "lt.w")): taken, reason = negative != overflow, "N!=V" elif mnemo.endswith(("le", "le.n", "le.w")): taken, reason = zero or negative != overflow, "Z || N!=V" elif mnemo.endswith(("gt", "gt.n", "gt.w")): taken, reason = not zero and negative == overflow, "!Z && N==V" elif mnemo.endswith(("ge", "ge.n", "ge.w")): taken, reason = negative == overflow, "N==V" elif mnemo.endswith(("vs", "vs.n", "vs.w")): taken, reason = overflow, "V" elif mnemo.endswith(("vc", "vc.n", "vc.w")): taken, reason = not overflow, "!V" elif mnemo.endswith(("mi", "mi.n", "mi.w")): taken, reason = negative, "N" elif mnemo.endswith(("pl", "pl.n", "pl.w")): taken, reason = not negative, "N==0" elif mnemo.endswith(("hi", "hi.n", "hi.w")): taken, reason = carry and not zero, "C && !Z" elif mnemo.endswith(("ls", "ls.n", "ls.w")): taken, reason = not carry or zero, "!C || Z" elif mnemo.endswith(("cs", "cs.n", "cs.w")) or mnemo.endswith(("hs", "hs.n", "hs.w")): taken, reason = carry, "C" elif mnemo.endswith(("cc", "cc.n", "cc.w")) or mnemo.endswith(("lo", "lo.n", "lo.w")): taken, reason = not carry, "!C" return taken, reason __mode_dic = { # encoding: [mode, PL] 0b10000: ["User", 0], 0b10001: ["FIQ", 1], 0b10010: ["IRQ", 1], 0b10011: ["Supervisor", 1], 0b10110: ["Monitor", 1], 0b10111: ["Abort", 1], 0b11010: ["Hypervisor", 2], 0b11011: ["Undefined", 1], 0b11111: ["System", 1], } def flag_register_to_human(self, val=None): # http://www.botskool.com/user-pages/tutorials/electronics/arm-7-tutorial-part-1 if val is None: reg = self.flag_register val = get_register(reg) & 0xffff_ffff if self.is_cortex_m(): return Architecture.flags_to_human(val, self.flags_table) key = val & 0b11111 CurrentMode, CurrentPL = self.__mode_dic[key] if not is_support_secure_world(): mode = " [Mode={:s}({:#07b},PL{:d})]".format(CurrentMode, key, CurrentPL) else: scr = get_register("$SCR") if scr is None: mode = " [Mode={:s}({:#07b},PL{:d})]".format(CurrentMode, key, CurrentPL) else: secure_state = ["Secure", "Non-Secure"][scr & 1] mode = " [Mode={:s}({:#07b},PL{:d}),{:s}]".format(CurrentMode, key, CurrentPL, secure_state) return Architecture.flags_to_human(val, self.flags_table) + mode def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): if insn.mnemonic == "pop": # If it's a pop, we have to peek into the stack. ra_addr = current_arch.sp + (len(insn.operands) - 1) * AddressUtil.get_memory_alignment() ra = read_int32_from_memory(ra_addr) elif insn.mnemonic.startswith("ldm"): # GDB seems to disassemble ldm* instructions as pop. # This branch may never be hit, but is kept just in case. ra = frame.older().pc() if frame.older() else None else: # 'bx lr' or 'add pc, lr, #0' ra = get_register("$lr") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): if is_in_kernel() or is_in_secure(): return None tls = get_register("$TPIDRURO") # qemu-user + gdb-multiarch if tls is not None: return tls if is_rr(): # unsupported ExecAsm when rr return None if self.is_thumb(): codes = [b"\x1d\xee", b"\x70\x2f"] # mrc p15, #0, r2, c13, c0, #3 else: codes = [b"\x70\x2f\x1d\xee"] # mrc p15, #0, r2, c13, c0, #3 ret = ExecAsm(codes).exec_code() return ret["reg"]["$r2"] def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie class AARCH64(ARM): """GEF representation of ARM-64 architecture.""" arch = "ARM64" mode = "ARM" load_condition = [ Elf.EM_AARCH64, "AARCH64", "ARM64", "ARMV8", "ARMV8-A", "ARMV9", "ARMV9-A", ] all_registers = [ "$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7", "$x8", "$x9", "$x10", "$x11", "$x12", "$x13", "$x14", "$x15", "$x16", "$x17", "$x18", "$x19", "$x20", "$x21", "$x22", "$x23", "$x24", "$x25", "$x26", "$x27", "$x28", "$x29", "$x30", "$sp", "$pc", "$cpsr", "$fpsr", "$fpcr", ] alias_registers = { "$x16": "$ip0", "$x17": "$ip1", "$x29": "$fp", "$x30": "$lr", } flag_register = "$cpsr" flags_table = { 31: "negative", 30: "zero", 29: "carry", 28: "overflow", 7: "interrupt", 6: "fast", } return_register = "$x0" function_parameters = ["$x0", "$x1", "$x2", "$x3", "$x4", "$x5", "$x6", "$x7"] syscall_register = "$x8" syscall_parameters = ["$x0", "$x1", "$x2", "$x3", "$x4", "$x5"] bit_length = 64 endianness = "little" instruction_length = 4 nop_insn = b"\x1f\x20\x03\xd5" # nop infloop_insn = b"\x00\x00\x00\x14" # b #0 trap_insn = b"\x00\x00\x20\xd4" # brk #0 ret_insn = b"\xc0\x03\x5f\xd6" # ret syscall_insn = b"\x01\x00\x00\xd4" # svc #0x0 @property def pc(self): return get_register("$pc") def is_syscall(self, insn): try: return insn.mnemonic == "svc" and int(insn.operands[0].lstrip("#"), 0) == 0 except Exception: return False def is_call(self, insn): return insn.mnemonic in ["bl", "blr"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["b", "br"] def is_ret(self, insn): return insn.mnemonic in ["ret", "eret"] def is_conditional_branch(self, insn): mnemo = insn.mnemonic # https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf # sect. 5.1.1 if mnemo in ["cbnz", "cbz", "tbnz", "tbz"]: return True if mnemo.startswith("b."): return True return False # is_branch_taken is the same as ARM def flag_register_to_human(self, val=None): # http://events.linuxfoundation.org/sites/events/files/slides/KoreaLinuxForum-2014.pdf if val is None: reg = self.flag_register val = get_register(reg) & 0xffff_ffff if not is_support_secure_world(): mode = " [EL={:d},SP={:d}]".format((val >> 2) & 0b11, val & 0b11) else: scr = get_register("$SCR_EL3") if scr is None: mode = " [EL={:d},SP={:d}]".format((val >> 2) & 0b11, val & 0b11) else: secure_state = ["Secure", "Non-Secure"][scr & 1] mode = " [EL={:d},SP={:d},{:s}]".format((val >> 2) & 0b11, val & 0b11, secure_state) return Architecture.flags_to_human(val, self.flags_table) + mode def get_ra(self, insn, frame): try: if insn.mnemonic == "ret": reg = insn.operands[0] if insn.operands else "lr" return get_register(reg) if frame and frame.older(): return frame.older().pc() except gdb.error: pass return None def get_tls(self): if is_in_kernel() or is_in_secure(): return None tls = get_register("$TPIDR_EL0") # qemu-user + gdb-multiarch if tls is not None: return tls tls = get_register("$tpidr") # native gdb 14.0 if tls is not None: return tls if is_rr(): # unsupported ExecAsm when rr return None codes = [b"\x40\xd0\x3b\xd5"] # mrs x0, tpidr_el0 ret = ExecAsm(codes).exec_code() return ret["reg"]["$x0"] class X86(Architecture): """GEF representation of x86-32 architecture.""" arch = "X86" mode = "32" load_condition = [ Elf.EM_386, "X86", "I386", "I386:INTEL", ] general_registers = ["$eax", "$ebx", "$ecx", "$edx", "$esp", "$ebp", "$esi", "$edi", "$eip", "$eflags"] special_registers = ["$cs", "$ss", "$ds", "$es", "$fs", "$gs"] virtual_registers = ["$fs_base", "$gs_base"] flag_register = "$eflags" all_registers = general_registers + special_registers alias_registers = {} flags_table = { 21: "ident", #20: "virtual_interrupt_pending", #19: "virtual_interrupt", 18: "align", 17: "vx86", 16: "resume", #15: N/A 14: "nested", #12-13: "iopl", 11: "overflow", 10: "direction", 9: "interrupt", 8: "trap", 7: "sign", 6: "zero", #5: N/A 4: "adjust", #3: N/A 2: "parity", #1: N/A 0: "carry", } return_register = "$eax" function_parameters = ["$esp"] # but unused because x86 uses stack syscall_register = "$eax" syscall_parameters = ["$ebx", "$ecx", "$edx", "$esi", "$edi", "$ebp"] bit_length = 32 endianness = "little" instruction_length = None # variable length has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = True capstone_support = True unicorn_support = True nop_insn = b"\x90" # nop infloop_insn = b"\xeb\xfe" # jmp 0 trap_insn = b"\xcc" # int3 ret_insn = b"\xc3" # ret syscall_insn = b"\xcd\x80" # int 0x80 def flag_register_to_human(self, val=None): if val is None: reg = self.flag_register val = get_register(reg) & 0xffff_ffff mode = " [Ring={:d}]".format(get_register("$cs") & 0b11) return Architecture.flags_to_human(val, self.flags_table) + mode def is_syscall(self, insn): if insn.mnemonic in ["sysenter", "syscall"]: return True try: return insn.mnemonic == "int" and int(insn.operands[0].lstrip("$"), 0) == 0x80 except Exception: return False def is_call(self, insn): return insn.mnemonic == "call" def is_jump(self, insn): return insn.mnemonic == "jmp" or self.is_conditional_branch(insn) def is_ret(self, insn): if insn.mnemonic in ["ret", "retf"]: return True if insn.mnemonic in ["sysret"]: return True if insn.mnemonic in ["iret", "iretd", "iretw"]: return True return False def is_conditional_branch(self, insn): branch_mnemos = [ "ja", "jnbe", "jae", "jnb", "jnc", "jb", "jc", "jnae", "jbe", "jna", "jcxz", "jecxz", "jrcxz", "je", "jz", "jg", "jnle", "jge", "jnl", "jl", "jnge", "jle", "jng", "jne", "jnz", "jno", "jnp", "jpo", "jns", "jo", "jp", "jpe", "js", ] return insn.mnemonic in branch_mnemos def is_branch_taken(self, insn): mnemo = insn.mnemonic # all kudos to fG! (https://github.com/gdbinit/Gdbinit/blob/master/gdbinit#L1654) flags = {self.flags_table[k]: k for k in self.flags_table} val = get_register(self.flag_register) taken, reason = False, "" zero = bool(val & (1 << flags["zero"])) sign = bool(val & (1 << flags["sign"])) overflow = bool(val & (1 << flags["overflow"])) carry = bool(val & (1 << flags["carry"])) parity = bool(val & (1 << flags["parity"])) if mnemo in ["ja", "jnbe"]: taken, reason = not carry and not zero, "!C && !Z" elif mnemo in ["jae", "jnb", "jnc"]: taken, reason = not carry, "!C" elif mnemo in ["jb", "jc", "jnae"]: taken, reason = carry, "C" elif mnemo in ["jbe", "jna"]: taken, reason = carry or zero, "C || Z" elif mnemo == "jcxz": cx = get_register("$cx") taken, reason = cx == 0, "!$CX" elif mnemo == "jecxz": ecx = get_register("$ecx") taken, reason = ecx == 0, "!$ECX" elif mnemo == "jrcxz": rcx = get_register("$rcx") taken, reason = rcx == 0, "!$RCX" elif mnemo in ["je", "jz"]: taken, reason = zero, "Z" elif mnemo in ["jne", "jnz"]: taken, reason = not zero, "!Z" elif mnemo in ["jg", "jnle"]: taken, reason = not zero and sign == overflow, "!Z && S==O" elif mnemo in ["jge", "jnl"]: taken, reason = sign == overflow, "S==O" elif mnemo in ["jl", "jnge"]: taken, reason = sign != overflow, "S!=O" elif mnemo in ["jle", "jng"]: taken, reason = zero or sign != overflow, "Z || S!=O" elif mnemo in ["jo"]: taken, reason = overflow, "O" elif mnemo in ["jno"]: taken, reason = not overflow, "!O" elif mnemo in ["jpe", "jp"]: taken, reason = parity, "P" elif mnemo in ["jnp", "jpo"]: taken, reason = not parity, "!P" elif mnemo in ["js"]: taken, reason = sign, "S" elif mnemo in ["jns"]: taken, reason = not sign, "!S" return taken, reason def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): if insn.mnemonic == "ret": ra = to_unsigned_long(AddressUtil.dereference(current_arch.sp)) elif insn.mnemonic == "retf": # eip, cs ra = to_unsigned_long(AddressUtil.dereference(current_arch.sp)) elif insn.mnemonic == "sysret": # ecx ra = get_register("$ecx") elif insn.mnemonic in ["iret", "iretd", "iretw"]: # eip, cs, eflags, esp, ss reg = to_unsigned_long(AddressUtil.dereference(current_arch.sp)) eflags = AddressUtil.dereference(current_arch.sp + current_arch.ptrsize * 2) if eflags & (1 << 17): # eflags.vm seg = AddressUtil.dereference(current_arch.sp + current_arch.ptrsize) & 0xffff return ((seg << 4) + reg) & (0x1f_ffff if X86_16.A20 else 0x0f_ffff) return reg elif frame.older(): ra = frame.older().pc() except (gdb.error, AttributeError): pass return ra def get_tls(self): if is_in_kernel(): return None return self.get_gs() def decode_cookie(self, value, cookie): return ror(value, 9, 32) ^ cookie def encode_cookie(self, value, cookie): return rol(value ^ cookie, 9, 32) def get_fs(self): # fastest path fs = get_register("$fs_base") if fs is not None: return fs if is_rr(): # unsupported ptrace and ExecAsm when rr return None # fast path if not is_remote_debug() and not is_in_kernel() and not is_qiling(): PTRACE_ARCH_PRCTL = 30 ARCH_GET_FS = 0x1003 pid, lwpid, tid = gdb.selected_thread().ptid ppvoid = ctypes.POINTER(ctypes.c_void_p) value = ppvoid(ctypes.c_void_p()) value.contents.value = 0 libc = ctypes.CDLL("libc.so.6") ret = libc.ptrace(PTRACE_ARCH_PRCTL, lwpid, value, ARCH_GET_FS) if ret == 0: # success return value.contents.value or 0 # slow path if not is_kvm_enabled() and not is_qiling(): codes = [b"\x64\xa1\x00\x00\x00\x00"] # mov eax, dword ptr fs:[0x0] ret = ExecAsm(codes).exec_code() return ret["reg"]["$eax"] return None def get_gs(self): # fastest path gs = get_register("$gs_base") if gs is not None: return gs if is_rr(): # unsupported ptrace and ExecAsm when rr return None # fast path if not is_remote_debug() and not is_in_kernel() and not is_qiling(): PTRACE_ARCH_PRCTL = 30 ARCH_GET_GS = 0x1004 pid, lwpid, tid = gdb.selected_thread().ptid ppvoid = ctypes.POINTER(ctypes.c_void_p) value = ppvoid(ctypes.c_void_p()) value.contents.value = 0 libc = ctypes.CDLL("libc.so.6") ret = libc.ptrace(PTRACE_ARCH_PRCTL, lwpid, value, ARCH_GET_GS) if ret == 0: # success return value.contents.value or 0 # slow path if not is_kvm_enabled() and not is_qiling(): codes = [b"\x65\xa1\x00\x00\x00\x00"] # mov eax, dword ptr gs:[0x0] ret = ExecAsm(codes).exec_code() return ret["reg"]["$eax"] return None def get_ith_parameter(self, i, in_func=True): if in_func: i += 1 # Account for RA being at the top of the stack sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val def read28(self, addr): codes = [ b"\x8b\x00", # mov eax, dword ptr [eax] b"\x8b\x1b", # mov ebx, dword ptr [ebx] b"\x8b\x09", # mov ecx, dword ptr [ecx] b"\x8b\x12", # mov edx, dword ptr [edx] b"\x8b\x24\x24", # mov esp, dword ptr [esp] # Rewriting EBP triggers an error message, which is noisy, so it is skipped. #b"\x8b\x6d\x00", # mov ebp, dword ptr [ebp] b"\x8b\x36", # mov esi, dword ptr [esi] b"\x8b\x3f", # mov edi, dword ptr [edi] ] regs = [ "$eax", "$ebx", "$ecx", "$edx", "$esp", "$esi", "$edi", ] regs = {reg: addr + i * current_arch.ptrsize for i, reg in enumerate(regs)} ret = ExecAsm(codes, regs=regs, step=len(codes)).exec_code() values = [ret["reg"][reg] for reg in regs] return b"".join([p32(v) for v in values]) class X86_64(X86): """GEF representation of x86-64 architecture.""" arch = "X86" mode = "64" load_condition = [ Elf.EM_X86_64, "X64", "AMD64", "X86_64", "X86-64", "I386:X86-64", "I386:X86-64:INTEL", ] general_registers = [ "$rax", "$rbx", "$rcx", "$rdx", "$rsp", "$rbp", "$rsi", "$rdi", "$rip", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$eflags", ] all_registers = general_registers + X86.special_registers alias_registers = {} return_register = "$rax" function_parameters = ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9"] syscall_register = "$rax" syscall_parameters = ["$rdi", "$rsi", "$rdx", "$r10", "$r8", "$r9"] bit_length = 64 syscall_insn = b"\x0f\x05" # syscall def is_syscall(self, insn): return insn.mnemonic in ["sysenter", "syscall"] def is_ret(self, insn): if insn.mnemonic in ["ret", "retf"]: return True if insn.mnemonic in ["sysret", "sysretd", "sysretq"]: return True if insn.mnemonic in ["iret", "iretd", "iretq", "iretw"]: return True return False def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): if insn.mnemonic == "ret": ra = to_unsigned_long(AddressUtil.dereference(current_arch.sp)) elif insn.mnemonic == "retf": # rip, cs ra = to_unsigned_long(AddressUtil.dereference(current_arch.sp)) elif insn.mnemonic in ["sysret", "sysretd", "sysretq"]: # rcx ra = get_register("$rcx") elif insn.mnemonic in ["iret", "iretd", "iretq", "iretw"]: # rip, cs, rflags, rsp, ss ra = to_unsigned_long(AddressUtil.dereference(current_arch.sp)) elif frame.older(): ra = frame.older().pc() except (gdb.error, AttributeError): pass return ra def get_tls(self): if is_in_kernel(): return None return self.get_fs() def decode_cookie(self, value, cookie): return ror(value, 17, 64) ^ cookie def encode_cookie(self, value, cookie): return rol(value ^ cookie, 17, 64) def get_fs(self): # fastest path fs = get_register("$fs_base") if fs is not None: return fs if is_rr(): # unsupported ptrace and ExecAsm when rr return None # fast path if not is_remote_debug() and not is_in_kernel() and not is_qiling(): PTRACE_ARCH_PRCTL = 30 ARCH_GET_FS = 0x1003 _pid, lwpid, _tid = gdb.selected_thread().ptid ppvoid = ctypes.POINTER(ctypes.c_void_p) value = ppvoid(ctypes.c_void_p()) value.contents.value = 0 libc = ctypes.CDLL("libc.so.6") ret = libc.ptrace(PTRACE_ARCH_PRCTL, lwpid, value, ARCH_GET_FS) if ret == 0: # success return value.contents.value or 0 # slow path if not is_kvm_enabled() and not is_qiling(): codes = [b"\x64\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x00"] # movabs rax, qword ptr fs:[0x0] ret = ExecAsm(codes).exec_code() return ret["reg"]["$rax"] return None def get_gs(self): # fastest path gs = get_register("$gs_base") if gs is not None: return gs if is_rr(): # unsupported ptrace and ExecAsm when rr return None # fast path if not is_remote_debug() and not is_in_kernel() and not is_qiling(): PTRACE_ARCH_PRCTL = 30 ARCH_GET_GS = 0x1004 _pid, lwpid, _tid = gdb.selected_thread().ptid ppvoid = ctypes.POINTER(ctypes.c_void_p) value = ppvoid(ctypes.c_void_p()) value.contents.value = 0 libc = ctypes.CDLL("libc.so.6") ret = libc.ptrace(PTRACE_ARCH_PRCTL, lwpid, value, ARCH_GET_GS) if ret == 0: # success return value.contents.value or 0 # slow path if not is_kvm_enabled() and not is_qiling(): codes = [b"\x65\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x00"] # movabs rax, qword ptr gs:[0x0] ret = ExecAsm(codes).exec_code() return ret["reg"]["$rax"] return None def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: i -= len(self.function_parameters) if in_func: i += 1 # Account for RA being at the top of the stack sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val def read128(self, addr): codes = [ b"\x48\x8b\x00", # mov rax, qword ptr [rax] b"\x48\x8b\x09", # mov rcx, qword ptr [rcx] b"\x48\x8b\x12", # mov rdx, qword ptr [rdx] b"\x48\x8b\x1b", # mov rbx, qword ptr [rbx] b"\x48\x8b\x24\x24", # mov rsp, qword ptr [rsp] b"\x48\x8b\x6d\x00", # mov rbp, qword ptr [rbp] b"\x48\x8b\x36", # mov rsi, qword ptr [rsi] b"\x48\x8b\x3f", # mov rdi, qword ptr [rdi] b"\x4d\x8b\x00", # mov r8, qword ptr [r8] b"\x4d\x8b\x09", # mov r9, qword ptr [r9] b"\x4d\x8b\x12", # mov r10, qword ptr [r10] b"\x4d\x8b\x1b", # mov r11, qword ptr [r11] b"\x4d\x8b\x24\x24", # mov r12, qword ptr [r12] b"\x4d\x8b\x6d\x00", # mov r13, qword ptr [r13] b"\x4d\x8b\x36", # mov r14, qword ptr [r14] b"\x4d\x8b\x3f", # mov r15, qword ptr [r15] ] regs = [ "$rax", "$rcx", "$rdx", "$rbx", "$rsp", "$rbp", "$rsi", "$rdi", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", ] regs = {reg: addr + i * current_arch.ptrsize for i, reg in enumerate(regs)} ret = ExecAsm(codes, regs=regs, step=len(codes)).exec_code() values = [ret["reg"][reg] for reg in regs] return b"".join([p64(v) for v in values]) class X86_16(X86): """GEF representation of i8086 architecture.""" arch = "X86" mode = "16" load_condition = [ "I8086", ] seg_extended_registers = { "$cs:$ip": ["$cs", "$pc"], "$ss:$sp": ["$ss", "$sp"], "$ss:$bp": ["$ss", "$bp"], "$ds:$si": ["$ds", "$si"], "$es:$di": ["$es", "$di"], } # https://stanislavs.org/helppc/int_21.html return_register = None function_parameters = ["$sp"] # but unused because x86 uses stack syscall_register = "$ah" syscall_parameters = None bit_length = 16 def __init__(self): gdb.execute("gef config context_code.use_capstone True") return def is_syscall(self, insn): try: return insn.mnemonic == "int" and int(insn.operands[0].lstrip("$"), 0) == 0x21 except Exception: return False def is_jump(self, insn): return insn.mnemonic in ["jmp", "ljmp"] or self.is_conditional_branch(insn) def is_ret(self, insn): return insn.mnemonic in ["ret", "retf", "iret"] def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): if insn.mnemonic == "ret": reg = AddressUtil.dereference(current_arch.sp) & 0xffff ra = current_arch.real2phys("$cs", reg) elif insn.mnemonic == "retf": # ip, cs reg = AddressUtil.dereference(current_arch.sp) & 0xffff seg = AddressUtil.dereference(current_arch.sp + current_arch.ptrsize) & 0xffff ra = current_arch.real2phys(seg, reg) elif insn.mnemonic == "iret": # ip, cs, flags, sp, ss reg = AddressUtil.dereference(current_arch.sp) & 0xffff seg = AddressUtil.dereference(current_arch.sp + current_arch.ptrsize) & 0xffff ra = current_arch.real2phys(seg, reg) elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra A20 = True def real2phys(self, seg, reg): if isinstance(seg, str): segval = get_register(seg) & 0xffff else: segval = seg if isinstance(reg, str): regval = get_register(reg) & 0xffff else: regval = reg if self.A20: return ((segval << 4) + regval) & 0x1f_ffff else: return ((segval << 4) + regval) & 0x0f_ffff @property def pc(self): return self.real2phys("$cs", "$pc") @property def sp(self): return self.real2phys("$ss", "$sp") class PPC(Architecture): """GEF representation of PowerPC-32 architecture.""" arch = "PPC" mode = "32" load_condition = [ Elf.EM_PPC, "POWERPC", "PPC", "PPC32", "POWERPC:COMMON", ] all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", "$pc", "$msr", "$cr", "$lr", "$ctr", "$xer", "$fpscr", ] alias_registers = { "$r1": "$sp", "$r2": "$tp", } flag_register = "$cr" flags_table = { # cr0 31: "lt0", 30: "gt0", 29: "eq0", 28: "so0", # cr1 27: "lt1", 26: "gt1", 25: "eq1", 24: "so1", # cr2 23: "lt2", 22: "gt2", 21: "eq2", 20: "so2", # cr3 19: "lt3", 18: "gt3", 17: "eq3", 16: "so3", # cr4 15: "lt4", 14: "gt4", 13: "eq4", 12: "so4", # cr5 11: "lt5", 10: "gt5", 9: "eq5", 8: "so5", # cr6 7: "lt6", 6: "gt6", 5: "eq6", 4: "so6", # cr7 3: "lt7", 2: "gt7", 1: "eq7", 0: "so7", } return_register = "$r3" function_parameters = ["$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10"] syscall_register = "$r0" syscall_parameters = ["$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9"] bit_length = 32 endianness = "little / big" instruction_length = 4 has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = True capstone_support = True unicorn_support = True nop_insn = b"\x00\x00\x00\x60" # nop infloop_insn = b"\x00\x00\x00\x48" # b #0 trap_insn = b"\x08\x00\xe0\x7f" # trap ret_insn = b"\x20\x00\x80\x4e" # blr syscall_insn = b"\x02\x00\x00\x44" # sc def flag_register_to_human(self, val=None): # http://www.cebix.net/downloads/bebox/pem32b.pdf (% 2.1.3) if val is None: reg = self.flag_register val = get_register(reg) return Architecture.flags_to_human(val, self.flags_table) def is_syscall(self, insn): return insn.mnemonic in ["sc"] def is_call(self, insn): conditions = [ "", "lt", "le", "eq", "ge", "gt", "nl", "ne", "ng", "so", "ns", "un", "nu", ] mnemo = insn.mnemonic.rstrip("+-") for cc in conditions: if mnemo == f"b{cc}l": return True if mnemo == f"b{cc}la": return True if mnemo == f"b{cc}ctrl": return True if mnemo == f"b{cc}lrl": return True modes = ["dz", "dnzf", "dzt", "dzf", "dnzt", "dnz"] for m in modes: if mnemo == f"b{m}l": return True if mnemo == f"b{m}la": return True if mnemo == f"b{m}lrl": return True return False def is_jump(self, insn): conditions = [ "", "lt", "le", "eq", "ge", "gt", "nl", "ne", "ng", "so", "ns", "un", "nu", ] mnemo = insn.mnemonic.rstrip("+-") for cc in conditions: if mnemo == f"b{cc}": return True if mnemo == f"b{cc}a": return True if mnemo == f"b{cc}ctr": return True modes = ["dz", "dnzf", "dzt", "dzf", "dnzt", "dnz"] for m in modes: if mnemo == f"b{m}": return True if mnemo == f"b{m}a": return True return False def is_ret(self, insn): conditions = [ "", "lt", "le", "eq", "ge", "gt", "nl", "ne", "ng", "so", "ns", "un", "nu", ] mnemo = insn.mnemonic.rstrip("+-") for cc in conditions: if mnemo == f"b{cc}lr": return True if mnemo == f"b{cc}lrl": return True modes = ["dz", "dnzf", "dzt", "dzf", "dnzt", "dnz"] for m in modes: if mnemo == f"b{m}lr": return True if mnemo == f"b{m}lrl": return True return False def is_conditional_branch(self, insn): branch_mnemos = ( "beq", "bne", "ble", "blt", "bgt", "bge", "bso", "bns", "bdz", "bdnz", "bdzt", "bdnzt", "bdzf", "bdnzf", ) mnemo = insn.mnemonic.rstrip("+-") return mnemo.startswith(branch_mnemos) def is_branch_taken(self, insn): mnemo = insn.mnemonic.rstrip("+-") flags = {self.flags_table[k]: k for k in self.flags_table} val = get_register(self.flag_register) taken, reason = False, "" m = re.search(r"\bcr([0-7])\b", insn.operands[0] if insn.operands else "") cr_number = int(m.group(1)) if m else 0 equal = bool(val & (1 << flags["eq{}".format(cr_number)])) less = bool(val & (1 << flags["lt{}".format(cr_number)])) greater = bool(val & (1 << flags["gt{}".format(cr_number)])) overflow = bool(val & (1 << flags["so{}".format(cr_number)])) if mnemo.startswith("beq"): taken, reason = equal, "E" elif mnemo.startswith("bne"): taken, reason = not equal, "!E" elif mnemo.startswith("ble"): taken, reason = equal or less, "E || L" elif mnemo.startswith("blt"): taken, reason = less, "L" elif mnemo.startswith("bge"): taken, reason = equal or greater, "E || G" elif mnemo.startswith("bgt"): taken, reason = greater, "G" elif mnemo.startswith("bso"): taken, reason = overflow, "V" elif mnemo.startswith("bns"): taken, reason = not overflow, "!V" # todo: bdn?z[tf]? are unsupported return taken, reason def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: i -= len(self.function_parameters) i += 2 # for EABI, not SysV sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$lr") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): def adjust_offset(x): TLS_TCB_OFFSET = 0x7000 if x == 0: return x return x - TLS_TCB_OFFSET tls = get_register("$r2") return adjust_offset(tls) def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie class PPC64(PPC): """GEF representation of PowerPC-64 architecture.""" arch = "PPC" mode = "64" load_condition = [ Elf.EM_PPC64, "POWERPC64", "PPC64", "POWERPC:COMMON64", ] all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", "$pc", "$msr", "$cr", "$lr", "$ctr", "$xer", "$fpscr", "$vscr", "$vrsave", ] alias_registers = { "$r1": "$sp", "$r13": "$tp", } syscall_parameters = ["$r3", "$r4", "$r5", "$r6", "$r7", "$r8"] bit_length = 64 unicorn_support = False def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: i += 4 # ??? sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val def get_tls(self): def adjust_offset(x): TLS_TCB_OFFSET = 0x7000 if x == 0: return x return x - TLS_TCB_OFFSET tls = get_register("$r13") return adjust_offset(tls) class SPARC(Architecture): """GEF representation of SPARC-32 architecture.""" arch = "SPARC" mode = "32" load_condition = [ Elf.EM_SPARC, "SPARC", "SPARC32", "SPARC:V8", ] # http://www.cse.scu.edu/~atkinson/teaching/sp05/259/sparc.pdf all_registers = [ "$g0", "$g1", "$g2", "$g3", "$g4", "$g5", "$g6", "$g7", "$o0", "$o1", "$o2", "$o3", "$o4", "$o5", "$sp", "$o7", "$l0", "$l1", "$l2", "$l3", "$l4", "$l5", "$l6", "$l7", "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$fp", "$i7", "$y", "$psr", "$wim", "$tbr", "$pc", "$npc", "$fsr", "$csr", ] alias_registers = { "$g0": "$zero", "$g7": "$tp", "$sp": "$o6", "$fp": "$i6", } flag_register = "$psr" flags_table = { 23: "negative", 22: "zero", 21: "overflow", 20: "carry", 7: "supervisor", 5: "trap", } return_register = "$o0" function_parameters = ["$o0", "$o1", "$o2", "$o3", "$o4", "$o5"] function_parameters_infunc = ["$i0", "$i1", "$i2", "$i3", "$i4", "$i5"] syscall_register = "$g1" syscall_parameters = ["$o0", "$o1", "$o2", "$o3", "$o4", "$o5"] bit_length = 32 endianness = "big" instruction_length = 4 has_delay_slot = True has_syscall_delay_slot = True has_ret_delay_slot = True stack_grow_down = False tls_supported = True keystone_support = True capstone_support = True unicorn_support = True nop_insn = b"\x00\x00\x00\x01" # nop infloop_insn = b"\x00\x00\x80\x10" # b self trap_insn = None ret_insn = b"\x08\xe0\xc7\x81" # ret syscall_insn = b"\x10\x20\xd0\x91" # trap 0x10 def flag_register_to_human(self, val=None): # https://courses.grainger.illinois.edu/cs423/sp2011/lectures/sim_public/sparcv8.pdf if val is None: val = get_register(self.flag_register) return Architecture.flags_to_human(val, self.flags_table) def is_syscall(self, insn): try: return insn.mnemonic == "ta" and int(insn.operands[0], 0) == 0x10 except Exception: return False def is_call(self, insn): return insn.mnemonic in ["jmpl", "call"] def is_jump(self, insn): mnemo = insn.mnemonic.split(",")[0] if mnemo.startswith("b"): return mnemo not in ["btst", "bset", "bclr", "btog"] if mnemo.startswith("fb"): return True if mnemo == "jmpl": return True return False def is_ret(self, insn): return insn.mnemonic in ["ret", "retl", "return"] def is_conditional_branch(self, insn): branch_mnemos = [ # http://moss.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/condbranch.html "be", "bne", "bg", "bge", "bgeu", "bgu", "bl", "ble", "blu", "bleu", "bneg", "bpos", "bvs", "bvc", "bcs", "bcc", # https://www.gaisler.com/doc/sparcv8.pdf "fbu", "fbg", "fbug", "fbl", "fbul", "fblg", "fbne", "fbe", "fbue", "fbge", "fbuge", "fble", "fbule", "fbo", # https://docs.oracle.com/cd/E18752_01/html/816-1681/sparcv9-30990.html "bpne", "bpe", "bpg", "bple", "bpge", "bpl", "bpgu", "bpleu", "bpcc", "bpcs", "bppos", "bpneg", "bpvc", "bpvs", "brz", "brlez", "brlz", "brnz", "brgz", "brgez", "fbpu", "fbpg", "fbpug", "fbpl", "fbpul", "fbplg", "fbpne", "fbpe", "fbpue", "fbpge", "fbpuge", "fbple", "fbpule", "fbpo", ] # e.g., bne,pn -> bne mnemo = insn.mnemonic.split(",")[0] return mnemo in branch_mnemos def is_branch_taken(self, insn): mnemo = insn.mnemonic.split(",")[0] flags = {self.flags_table[k]: k for k in self.flags_table} val = get_register(self.flag_register) taken, reason = False, "" zero = bool(val & (1 << flags["zero"])) negative = bool(val & (1 << flags["negative"])) overflow = bool(val & (1 << flags["overflow"])) carry = bool(val & (1 << flags["carry"])) if mnemo in ["be", "bpe"]: taken, reason = zero, "Z" elif mnemo in ["bne", "bpne"]: taken, reason = not zero, "!Z" elif mnemo in ["bg", "bpg"]: taken, reason = not zero and (negative == overflow), "!Z && (N == V)" elif mnemo in ["bge", "bpge"]: taken, reason = negative == overflow, "N == V" elif mnemo in ["bgu", "bpgu"]: taken, reason = not carry and not zero, "!C && !Z" elif mnemo in ["bgeu"]: taken, reason = not carry, "!C" elif mnemo in ["bl", "bpl"]: taken, reason = negative != overflow, "N != V" elif mnemo in ["blu"]: taken, reason = carry, "C" elif mnemo in ["ble", "bple"]: taken, reason = zero or (negative != overflow), "Z || (N != V)" elif mnemo in ["bleu", "bpleu"]: taken, reason = carry or zero, "C || Z" elif mnemo in ["bneg", "bpneg"]: taken, reason = negative, "N" elif mnemo in ["bpos", "bppos"]: taken, reason = not negative, "!N" elif mnemo in ["bvs", "bpvs"]: taken, reason = overflow, "V" elif mnemo in ["bvc", "bpvc"]: taken, reason = not overflow, "!V" elif mnemo in ["bcs", "bpcs"]: taken, reason = carry, "C" elif mnemo in ["bcc", "bpcc"]: taken, reason = not carry, "!C" # todo: f* opcode, brn?z/br[lg]e?z are unsupported return taken, reason def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): if in_func: # TODO: Leaf functions use $o0...$o5 despite in_func=True reg = self.function_parameters_infunc[i] else: reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: BIAS = 0 OUT_ARG_OVERFLOW = 0x5c if in_func: regname, base_addr = "fp", get_register("$fp") else: regname, base_addr = "sp", current_arch.sp offset = BIAS + OUT_ARG_OVERFLOW + ((i - 6) * current_arch.ptrsize) val = read_int_from_memory(base_addr + offset) key = "[{:s} + {:#x}]".format(regname, offset) return key, val def get_ra(self, insn, frame): ra = None try: if insn.mnemonic == "retl": ra = get_register("$o7") + self.instruction_length * 2 # call, delay-slot elif insn.mnemonic == "ret": ra = get_register("$i7") + self.instruction_length * 2 # call, delay-slot elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$g7") def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie class SPARC32PLUS(SPARC): """GEF representation of SPARC-32+ architecture.""" arch = "SPARC" mode = "32PLUS" load_condition = [ Elf.EM_SPARC32PLUS, "SPARC32PLUS", "SPARC32+", "SPARCV8PLUS", "SPARCV8+", "SPARC:V8PLUS", "SPARC:V8PLUSA", "SPARC:V8PLUSB", "SPARC:V8PLUSC", "SPARC:V8PLUSD", "SPARC:V8PLUSE", "SPARC:V8PLUSM", "SPARC:V8PLUSV", ] class SPARC64(SPARC): """GEF representation of SPARC-64 architecture.""" arch = "SPARC" mode = "64" load_condition = [ Elf.EM_SPARCV9, "SPARC64", "SPARC:V9", "SPARC:V9A", "SPARC:V9B", "SPARC:V9C", "SPARC:V9D", "SPARC:V9E", "SPARC:V9M", "SPARC:V9V", ] # http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_sparc.pdf # https://cr.yp.to/2005-590/sparcv9.pdf all_registers = [ "$g0", "$g1", "$g2", "$g3", "$g4", "$g5", "$g6", "$g7", "$o0", "$o1", "$o2", "$o3", "$o4", "$o5", "$sp", "$o7", "$l0", "$l1", "$l2", "$l3", "$l4", "$l5", "$l6", "$l7", "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$fp", "$i7", "$pc", "$npc", "$state", "$fsr", "$fprs", "$y", "$cwp", "$pstate", "$asi", "$ccr", ] flag_register = "$state" # sparcv9.pdf, 5.1.5.1 (ccr), because $state includes $ccr. flags_table = { 35: "negative", 34: "zero", 33: "overflow", 32: "carry", } bit_length = 64 syscall_insn = b"\x6d\x20\xd0\x91" # trap 0x6d def is_syscall(self, insn): try: return insn.mnemonic == "ta" and int(insn.operands[0], 0) == 0x6d except Exception: return False def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): if in_func: # TODO: Leaf functions use $o0...$o5 despite in_func=True reg = self.function_parameters_infunc[i] else: reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: BIAS = 0x7ff OUT_ARG_OVERFLOW = 0xb0 if in_func: regname, base_addr = "fp", get_register("$fp") else: regname, base_addr = "sp", current_arch.sp offset = BIAS + OUT_ARG_OVERFLOW + ((i - 6) * current_arch.ptrsize) val = read_int_from_memory(base_addr + offset) key = "[{:s} + {:#x}]".format(regname, offset) return key, val class MIPS(Architecture): """GEF representation of MIPS-32 architecture.""" arch = "MIPS" mode = "32" load_condition = [ # Elf.EM_MIPS cannot determine whether it is 32-bit or 64-bit, so it should not be used. "MIPS", "MIPS32", "MIPS:ISA32", "MIPS:ISA32R2", "MIPS:ISA32R3", "MIPS:ISA32R5", "MIPS:ISA32R6", ] # http://vhouten.home.xs4all.nl/mipsel/r3000-isa.html all_registers = [ "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra", "$sr", "$lo", "$hi", "$bad", "$cause", "$fsr", "$fir", "$pc", ] alias_registers = { "$zero": "$r0", "$at": "$r1", "$v0": "$r2", "$v1": "$r3", "$a0": "$r4", "$a1": "$r5", "$a2": "$r6", "$a3": "$r7", "$t0": "$r8", "$t1": "$r9", "$t2": "$r10", "$t3": "$r11", "$t4": "$r12", "$t5": "$r13", "$t6": "$r14", "$t7": "$r15", "$s0": "$r16", "$s1": "$r17", "$s2": "$r18", "$s3": "$r19", "$s4": "$r20", "$s5": "$r21", "$s6": "$r22", "$s7": "$r23", "$t8": "$r24", "$t9": "$r25", "$k0": "$r26", "$k1": "$r27", "$gp": "$r28", "$sp": "$r29", "$fp": "$s8/$r30", "$ra": "$r31", } flag_register = None # MIPS has no flags register return_register = "$v0" function_parameters = ["$a0", "$a1", "$a2", "$a3"] syscall_register = "$v0" syscall_parameters = ["$a0", "$a1", "$a2", "$a3", "$sp+0x10", "$sp+0x14", "$sp+0x18", "$sp+0x1c"] bit_length = 32 bit_length_reg = 32 endianness = "little / big" instruction_length = 4 has_delay_slot = True has_syscall_delay_slot = True has_ret_delay_slot = True stack_grow_down = False tls_supported = True keystone_support = True capstone_support = True unicorn_support = True nop_insn = b"\x00\x00\x00\x00" # nop infloop_insn = b"\xff\xff\x00\x10" # b self trap_insn = b"\x0d\x00\x00\x00" # break ret_insn = b"\x08\x00\xe0\x03" # jr $ra syscall_insn = b"\x0c\x00\x00\x00" # syscall def is_syscall(self, insn): return insn.mnemonic in ["syscall"] def is_call(self, insn): branch_mnemos = [ "bal", "balc", "jal", "jalr", "jalrc", "jalrc.hb", "bgezal", "bgezall", "bltzal", "bltzall", ] return insn.mnemonic in branch_mnemos def is_jump(self, insn): if self.is_ret(insn): return False if insn.mnemonic in ["b", "bc", "j", "jr", "jrc", "balrsc", "brsc"]: return True if self.is_conditional_branch(insn): return True return False def is_ret(self, insn): if insn.mnemonic in ["jr", "jrc"] and insn.operands[0] == "ra": return True if insn.mnemonic in ["deret", "eret", "eretnc"]: return True if insn.mnemonic in ["restore", "restore.jrc"]: return True return False def is_conditional_branch(self, insn): branch_mnemos = [ "beq", "bne", "bgtz", "bgez", "bltz", "blez", "beqz", "bnez", "beql", "bnel", "bgtzl", "bgezl", "bltzl", "blezl", "bgezal", "bgezall", "bltzal", "bltzall", "bc1f", "bc1fl", "bc1t", "bc1tl", "bc2f", "bc2fl", "bc2t", "bc2tl", "beqc", "beqic", "beqzc", "bnec", "bneic", "bnezc", "bgec", "bgeic", "bgeiuc", "bgeuc", "bltc", "bltic", "bltiuc", "bltuc", "bbeqzc", "bbnezc", ] return insn.mnemonic in branch_mnemos def is_branch_taken(self, insn): mnemo, ops = insn.mnemonic, insn.operands taken, reason = False, "" def u2i(x): if self.bit_length_reg == 64: trans = lambda a: struct.unpack(" 0, "{0[0]} > 0".format(ops) elif mnemo in ["bgez", "bgezl"]: taken, reason = u2i(get_register(ops[0])) >= 0, "{0[0]} >= 0".format(ops) elif mnemo in ["bltz", "bltzl"]: taken, reason = u2i(get_register(ops[0])) < 0, "{0[0]} < 0".format(ops) elif mnemo in ["blez", "blezl"]: taken, reason = u2i(get_register(ops[0])) <= 0, "{0[0]} <= 0".format(ops) elif mnemo in ["bbeqzc"]: taken, reason = (get_register(ops[0]) >> int(ops[1], 0) & 1) == 0, "(({0[0]} >> {0[1]}) & 1) == 0".format(ops) elif mnemo in ["bbnezc"]: taken, reason = (get_register(ops[0]) >> int(ops[1], 0) & 1) != 0, "(({0[0]} >> {0[1]}) & 1) != 0".format(ops) elif mnemo in ["bgec"]: taken, reason = u2i(get_register(ops[0])) >= u2i(get_register(ops[1])), "{0[0]} >= {0[1]}".format(ops) elif mnemo in ["bgeic"]: taken, reason = u2i(get_register(ops[0])) >= u2i(int(ops[1], 0)), "{0[0]} >= {0[1]}".format(ops) elif mnemo in ["bgeuc"]: taken, reason = get_register(ops[0]) >= get_register(ops[1]), "{0[0]} >= {0[1]}".format(ops) elif mnemo in ["bgeiuc"]: taken, reason = get_register(ops[0]) >= int(ops[1], 0), "{0[0]} >= {0[1]}".format(ops) elif mnemo in ["bltc"]: taken, reason = u2i(get_register(ops[0])) < u2i(get_register(ops[1])), "{0[0]} < {0[1]}".format(ops) elif mnemo in ["bltic"]: taken, reason = u2i(get_register(ops[0])) < u2i(int(ops[1], 0)), "{0[0]} < {0[1]}".format(ops) elif mnemo in ["bltuc"]: taken, reason = get_register(ops[0]) < get_register(ops[1]), "{0[0]} < {0[1]}".format(ops) elif mnemo in ["bltiuc"]: taken, reason = get_register(ops[0]) < int(ops[1], 0), "{0[0]} < {0[1]}".format(ops) return taken, reason def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$ra") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): def adjust_offset(x): TLS_TCB_OFFSET = 0x7000 if x == 0: return x return x - TLS_TCB_OFFSET codes = [b"\x3b\xe8\x03\x7c"] # rdhwr v1, $29 ret = ExecAsm(codes).exec_code() tls = ret["reg"]["$v1"] return adjust_offset(tls) def decode_cookie(self, value, cookie): return value def encode_cookie(self, value, cookie): return value class MIPS64(MIPS): """GEF representation of MIPS-64 architecture.""" arch = "MIPS" mode = "64" load_condition = [ # Elf.EM_MIPS cannot determine whether it is 32-bit or 64-bit, so it should not be used. "MIPS64", "MIPS:ISA64", "MIPS:ISA64R2", "MIPS:ISA64R3", "MIPS:ISA64R5", "MIPS:ISA64R6", ] all_registers = [ "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3", "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra", "$sr", "$lo", "$hi", "$bad", "$cause", "$fsr", "$fir", "$pc", ] alias_registers = { "$zero": "$r0", "$at": "$r1", "$v0": "$r2", "$v1": "$r3", "$a0": "$r4", "$a1": "$r5", "$a2": "$r6", "$a3": "$r7", "$a4": "$r8", "$a5": "$r9", "$a6": "$r10", "$a7": "$r11", "$t0": "$r12", "$t1": "$r13", "$t2": "$r14", "$t3": "$r15", "$s0": "$r16", "$s1": "$r17", "$s2": "$r18", "$s3": "$r19", "$s4": "$r20", "$s5": "$r21", "$s6": "$r22", "$s7": "$r23", "$t8": "$r24", "$t9": "$r25", "$k0": "$r26", "$k1": "$r27", "$gp": "$r28", "$sp": "$r29", "$fp": "$s8/$r30", "$ra": "$r31", } function_parameters = ["$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7"] syscall_parameters = ["$a0", "$a1", "$a2", "$a3", "$a4", "$a5"] bit_length = 64 bit_length_reg = 64 unicorn_support = False def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: i -= len(self.function_parameters) sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val class MIPSN32(MIPS64): """GEF representation of MIPS-N32-ABI architecture.""" arch = "MIPS" mode = "n32" load_condition = [ "MIPSN32" ] bit_length = 32 bit_length_reg = 64 class S390X(Architecture): """GEF representation of s390x architecture.""" arch = "S390X" mode = "64" load_condition = [ # Elf.EM_S390 cannot determine whether it is 32 bit or 64 bit, # but since GEF only supports 64 bit (s390x), so we will use it. Elf.EM_S390, "S390X", "S390:64-BIT", ] # https://www.ibm.com/docs/en/SSQ2R2_15.0.0/com.ibm.tpf.toolkit.hlasm.doc/dz9zr006.pdf all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$pswm", "$pswa", "$fpc", "$gsd", "$gssm", "$gsepla", "$gs_reserved", "$pc", "$cc", ] alias_registers = { "$r14": "$lr", "$r15": "$sp", } flag_register = "$cc" flags_table = { 1: "cc1", 0: "cc0", } return_register = "$r2" function_parameters = ["$r2", "$r3", "$r4", "$r5", "$r6"] syscall_register = [r"svc\s+(\d+)", "$r1"] # $r1 is used when NR > 127 syscall_parameters = ["$r2", "$r3", "$r4", "$r5", "$r6", "$r7"] bit_length = 64 endianness = "big" instruction_length = None # variable length has_delay_slot = False has_syscall_delay_slot = True has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = True capstone_support = True unicorn_support = False # for some reason it doesn't work nop_insn = b"\x07\x07" # bcr 0, %r7 infloop_insn = b"\x00\x00\xf4\xa7" # j 0 trap_insn = None ret_insn = b"\xfe\x07" # br %r14 syscall_insn = b"\x00\x0a" # svc 0x0 def is_syscall(self, insn): return insn.mnemonic == "svc" # https://www.ibm.com/docs/en/zos/2.1.0?topic=statements-branching-extended-mnemonic-codes def is_call(self, insn): return insn.mnemonic in ["bal", "balr", "bas", "basr", "bassm", "bsm", "bras", "brasl"] def is_jump(self, insn): if self.is_ret(insn): return False if insn.mnemonic in ["b", "br", "j", "bru", "brul", "jlu"]: return True return self.is_conditional_branch(insn) def is_ret(self, insn): return insn.mnemonic == "br" and insn.operands[-1] == "%r14" def is_conditional_branch(self, insn): if insn.mnemonic in ["bc", "bcr", "brc", "brcl"]: return True conditions = ["h", "l", "e", "nh", "nl", "ne", "p", "m", "z", "o", "np", "nm", "nz", "no"] for cc in conditions: if insn.mnemonic == f"b{cc}": # alias for `bc N, ...` return True if insn.mnemonic == f"b{cc}r": # alias for `bcr N, ...` return True if insn.mnemonic in [f"br{cc}", f"j{cc}"]: # alias for `brc N, ...` return True if insn.mnemonic in [f"br{cc}l", f"jl{cc}"]: # alias for `brcl N, ...` return True if insn.mnemonic in ["bct", "bctr", "bctg", "bctgr", "brct", "brctg"]: return True if insn.mnemonic in ["bxh", "bxhg", "brxh", "brxhg"]: return True if insn.mnemonic in ["bxle", "bxleg", "brxle", "brxlg"]: return True if insn.mnemonic in ["crb", "cgrb", "crj", "cgrj"]: return True if insn.mnemonic in ["cib", "cgib", "cij", "cgij"]: return True if insn.mnemonic in ["clrb", "clgrb", "clrj", "clgrj"]: return True if insn.mnemonic in ["clib", "clgib", "clij", "clgij"]: return True conditions = ["h", "l", "ne"] for cc in conditions: if insn.mnemonic == f"crb{cc}": # alias for `crb r,r,N` return True if insn.mnemonic == f"cgrb{cc}": # alias for `cgrb r,r,N` return True if insn.mnemonic == f"crj{cc}": # alias for `crj r,r,N, ...` return True if insn.mnemonic == f"cgrj{cc}": # alias for `cgrj r,r,N, ...` return True if insn.mnemonic == f"cib{cc}": # alias for `cib r,i,N, ...` return True if insn.mnemonic == f"cgib{cc}": # alias for `cgib r,i,N, ...` return True if insn.mnemonic == f"cij{cc}": # alias for `cij r,i,N, ...` return True if insn.mnemonic == f"cgij{cc}": # alias for `cgij r,i,N, ...` return True if insn.mnemonic == f"clrb{cc}": # alias for `clrb r,r,N, ...` return True if insn.mnemonic == f"clgrb{cc}": # alias for `clgrb r,r,N, ...` return True if insn.mnemonic == f"clrj{cc}": # alias for `clrj r,r,N, ...` return True if insn.mnemonic == f"clgrj{cc}": # alias for `clgrj r,r,N, ...` return True if insn.mnemonic == f"clib{cc}": # alias for `clib r,i,N, ...` return True if insn.mnemonic == f"clgib{cc}": # alias for `clgib r,i,N, ...` return True if insn.mnemonic == f"clij{cc}": # alias for `clij r,i,N, ...` return True if insn.mnemonic == f"clgij{cc}": # alias for `clgij r,i,N, ...` return True return False def is_branch_taken(self, insn): taken, reason = False, "" def is_insn_condition_type1(insn): if insn.mnemonic in ["bc", "bcr", "brc", "brcl"]: return True conditions = [ "h", "l", "e", "nh", "nl", "ne", "p", "m", "z", "o", "np", "nm", "nz", "no", ] for cc in conditions: if insn.mnemonic == f"b{cc}": # alias for `bc N, ...` return True if insn.mnemonic == f"b{cc}r": # alias for `bcr N, ...` return True if insn.mnemonic in [f"br{cc}", f"j{cc}"]: # alias for `brc N, ...` return True if insn.mnemonic in [f"br{cc}l", f"jl{cc}"]: # alias for `brcl N, ...` return True return False def is_insn_condition_type2_signed32(insn): if insn.mnemonic in ["crb", "crj"]: return True if insn.mnemonic in ["cib", "cij"]: return True conditions = ["h", "l", "ne"] for cc in conditions: if insn.mnemonic == f"crb{cc}": # alias for `crb r,r,N` return True if insn.mnemonic == f"crj{cc}": # alias for `crj r,r,N, ...` return True if insn.mnemonic == f"cib{cc}": # alias for `cib r,i,N, ...` return True if insn.mnemonic == f"cij{cc}": # alias for `cij r,i,N, ...` return True return False def is_insn_condition_type2_signed64(insn): if insn.mnemonic in ["cgrb", "cgrj"]: return True if insn.mnemonic in ["cgib", "cgij"]: return True conditions = ["h", "l", "ne"] for cc in conditions: if insn.mnemonic == f"cgrb{cc}": # alias for `cgrb r,r,N` return True if insn.mnemonic == f"cgrj{cc}": # alias for `cgrj r,r,N, ...` return True if insn.mnemonic == f"cgib{cc}": # alias for `cgib r,i,N, ...` return True if insn.mnemonic == f"cgij{cc}": # alias for `cgij r,i,N, ...` return True return False def is_insn_condition_type2_unsigned32(insn): if insn.mnemonic in ["clrb", "clrj"]: return True if insn.mnemonic in ["clib", "clij"]: return True conditions = ["h", "l", "ne"] for cc in conditions: if insn.mnemonic == f"clrb{cc}": # alias for `clrb r,r,N, ...` return True if insn.mnemonic == f"clrj{cc}": # alias for `clrj r,r,N, ...` return True if insn.mnemonic == f"clib{cc}": # alias for `clib r,i,N, ...` return True if insn.mnemonic == f"clij{cc}": # alias for `clij r,i,N, ...` return True return False def is_insn_condition_type2_unsigned64(insn): if insn.mnemonic in ["clgrb", "clgrj"]: return True if insn.mnemonic in ["clgib", "clgij"]: return True conditions = ["h", "l", "ne"] for cc in conditions: if insn.mnemonic == f"clgrb{cc}": # alias for `clgrb r,r,N, ...` return True if insn.mnemonic == f"clgrj{cc}": # alias for `clgrj r,r,N, ...` return True if insn.mnemonic == f"clgib{cc}": # alias for `clgib r,i,N, ...` return True if insn.mnemonic == f"clgij{cc}": # alias for `clgij r,i,N, ...` return True return False if is_insn_condition_type1(insn): mask = insn.opcodes[1] >> 4 val = get_register(self.flag_register) flags = {self.flags_table[k]: k for k in self.flags_table} cc1 = (val >> flags["cc1"]) & 1 cc0 = (val >> flags["cc0"]) & 1 cc = (cc1 << 1) + cc0 if (mask & 0b1) and cc == 3: return True, "cc==3" if (mask & 0b10) and cc == 2: return True, "cc==2" if (mask & 0b100) and cc == 1: return True, "cc==1" if (mask & 0b1000) and cc == 0: return True, "cc==0" return False, "" if insn.mnemonic in ["bct", "bctr", "bctg", "bctgr", "brct", "brctg"]: reg = insn.operands[0] return get_register(reg) != 1, "{:s}!=1".format(reg) def get_cmp_regname(reg3): m = re.search(r"(\d+)$", reg3) if m is None: return None reg3_num = int(m.group(1), 10) if reg3_num % 2 == 0: cmp_reg = reg3_num + 1 else: cmp_reg = reg3_num return "%r{:d}".format(cmp_reg) def u2i(x, mnemo): if mnemo in ("bxhg", "brxhg", "bxleg", "brxlg"): trans = lambda a: struct.unpack(" u2i(get_register(regC), insn.mnemonic) reason = "({:s}+{:s})>{:s}".format(reg1, reg3, regC) return taken, reason if insn.mnemonic in ["bxle", "bxleg", "brxle", "brxlg"]: reg1, reg3 = insn.operands[0], insn.operands[1] regC = get_cmp_regname(reg3) if regC is not None: taken = u2i(get_register(reg1) + get_register(reg3), insn.mnemonic) <= u2i(get_register(regC), insn.mnemonic) reason = "({:s}+{:s})<={:s}".format(reg1, reg3, regC) return taken, reason def for_compare(insn, signed, bit): if len(insn.opcodes) < 5: return False, "" if signed and bit == 32: trans = lambda a: struct.unpack("> 4 reg1 = insn.operands[0] reg2_or_imm = insn.operands[1] val1 = trans(get_register(reg1)) if reg2_or_imm.startswith("%"): val2 = trans(get_register(reg2_or_imm)) else: val2 = trans(int(reg2_or_imm, 0)) if (mask & 0b1) and val1 == val2: return True, "{:s}=={:s}".format(reg1, reg2_or_imm) if (mask & 0b10) and val1 < val2: return True, "{:s}<{:s}".format(reg1, reg2_or_imm) if (mask & 0b100) and val1 > val2: return True, "{:s}>{:s}".format(reg1, reg2_or_imm) return False, "" if is_insn_condition_type2_signed32(insn): return for_compare(insn, signed=True, bit=32) if is_insn_condition_type2_signed64(insn): return for_compare(insn, signed=True, bit=64) if is_insn_condition_type2_unsigned32(insn): return for_compare(insn, signed=False, bit=32) if is_insn_condition_type2_unsigned64(insn): return for_compare(insn, signed=False, bit=64) return taken, reason def flag_register_to_human(self, val=None): if val is None: reg = self.flag_register val = get_register(reg) flags = {self.flags_table[k]: k for k in self.flags_table} extra_msg = " [" if get_register("$pswm") is not None: addressing0 = (get_register("$pswm") >> 31) & 1 addressing1 = (get_register("$pswm") >> 32) & 1 addressing_mode = { (0, 0): "24-bit", (0, 1): "31-bit", (1, 1): "64-bit", }[addressing0, addressing1] extra_msg += "AddressingMode={:s}, ".format(addressing_mode) cc1 = (val >> flags["cc1"]) & 1 cc0 = (val >> flags["cc0"]) & 1 condition_code = (cc1 << 1) + cc0 extra_msg += "ConditionCode={:d}]".format(condition_code) return Architecture.flags_to_human(val, self.flags_table) + extra_msg def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: STACK_ARG_BASE = 0xa0 n_reg_args = len(self.function_parameters) offset = STACK_ARG_BASE + ((i - n_reg_args) * current_arch.ptrsize) val = read_int_from_memory(current_arch.sp + offset) key = "[sp + {:#x}]".format(offset) return key, val def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$r14") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): hi = get_register("$acr0") lo = get_register("$acr1") return (hi << 32) | lo def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie class SH4(Architecture): """GEF representation of SH4 architecture.""" arch = "SH4" mode = "SH4" load_condition = [ Elf.EM_SH, "SH4", "SH4-NOFPU", "SH4A", "SH4A-NOFPU", "SH2A-OR-SH4", "SH2", "SH3", ] # https://www.renesas.com/us/en/document/man/705261 all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$pc", "$pr", "$gbr", "$mach", "$macl", "$sr", ] alias_registers = { "$r15": "$sp", } flag_register = "$sr" flags_table = { 0: "t", } return_register = "$r0" function_parameters = ["$r4", "$r5", "$r6", "$r7"] syscall_register = "$r3" syscall_parameters = ["$r4", "$r5", "$r6", "$r7", "$r0", "$r1", "$r2"] bit_length = 32 endianness = "little" instruction_length = 2 has_delay_slot = True has_syscall_delay_slot = True has_ret_delay_slot = True stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x09\x00" # nop infloop_insn = b"\xfe\xaf" # bra self trap_insn = None ret_insn = b"\x0b\x00" # rts syscall_insn = b"\x1f\xc3" # trapa #31 def is_syscall(self, insn): if insn.mnemonic != "trapa": return False try: imm = int(insn.operands[0].lstrip("#"), 0) return (0x10 <= imm <= 0x17) or (imm == 0x1f) except Exception: return False def is_call(self, insn): return insn.mnemonic in ["bsr", "bsrf", "jsr"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["bra", "braf", "jmp"] def is_ret(self, insn): return insn.mnemonic == "rts" def is_conditional_branch(self, insn): return insn.mnemonic in ["bf", "bf.s", "bt", "bt.s"] def is_branch_taken(self, insn): mnemo = insn.mnemonic val = get_register(self.flag_register) flags = {self.flags_table[k]: k for k in self.flags_table} taken, reason = False, "" t = bool(val & (1 << flags["t"])) if mnemo in ["bf", "bf.s"]: taken, reason = not t, "!T" elif mnemo in ["bt", "bt.s"]: taken, reason = t, "T" return taken, reason def flag_register_to_human(self, val=None): if val is None: reg = self.flag_register val = get_register(reg) return Architecture.flags_to_human(val, self.flags_table) def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$pr") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$gbr") def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie class M68K(Architecture): """GEF representation of m68000 architecture.""" arch = "M68K" mode = "32" load_condition = [ Elf.EM_68K, "M68K", "M68000", "M68K:68000", "M68K:68008", "M68K:68010", "M68K:68020", "M68K:68030", "M68K:68040", "M68K:68060", ] # https://www.nxp.com/files-static/archives/doc/ref_manual/M68000PRM.pdf all_registers = [ "$d0", "$d1", "$d2", "$d3", "$d4", "$d5", "$d6", "$d7", "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$fp", "$sp", "$ps", "$pc", ] alias_registers = { "$fp": "$a6", "$sp": "$a7", "$ps": "$sr", } flag_register = "$ps" flags_table = { 0: "carry", 1: "overflow", 2: "zero", 3: "negative", 4: "extend", } return_register = "$d0" function_parameters = ["$sp"] # but unused because m68k uses stack syscall_register = "$d0" syscall_parameters = ["$d1", "$d2", "$d3", "$d4", "$d5", "$a0"] bit_length = 32 endianness = "big" instruction_length = None # variable length has_delay_slot = False has_syscall_delay_slot = True has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = False capstone_support = True unicorn_support = True nop_insn = b"\x71\x4e" # nop infloop_insn = b"\xfe\x60" # bras self trap_insn = b"\x48\x48" # bkpt 0 ret_insn = b"\x75\x4e" # rts syscall_insn = b"\x40\x4e" # trap #0 def is_syscall(self, insn): try: return insn.mnemonic == "trap" and int(insn.operands[0].lstrip("#"), 0) == 0 except Exception: return False def is_call(self, insn): return insn.mnemonic in ["bsrs", "bsrw", "bsrl", "jsr"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["bras", "braw", "bral", "jmp"] def is_ret(self, insn): return insn.mnemonic == "rts" # https://sourceware.org/binutils/docs/as/M68K_002dBranch.html # https://web.njit.edu/~rosensta/classes/architecture/252software/code.pdf def is_conditional_branch(self, insn): conditions = [ "hi", "ls", "cc", "cs", "ne", "eq", "vc", "vs", "pl", "mi", "ge", "lt", "gt", "le", ] for cc in conditions: if insn.mnemonic in [f"b{cc}s", f"b{cc}w", f"b{cc}l"]: return True conditions = [ "hi", "ls", "cc", "cs", "ne", "eq", "vc", "vs", "pl", "mi", "ge", "lt", "gt", "le", "t", "f", ] for cc in conditions: if insn.mnemonic in [f"db{cc}w", f"db{cc}"]: return True conditions = [ "ne", "eq", "ge", "lt", "gt", "le", "f", "t", "gl", "gle", "nge", "ngl", "ngle", "ngt", "nle", "nlt", "oge", "ogl", "ogt", "ole", "olt", "or", # codespell:ignore "seq", "sf", "sne", "st", "ueq", "uge", "ugt", "ule", "ult", "un", ] for cc in conditions: if insn.mnemonic in [f"fb{cc}w", f"fb{cc}l"]: return True return False def is_branch_taken(self, insn): mnemo = insn.mnemonic flags = {self.flags_table[k]: k for k in self.flags_table} val = get_register(self.flag_register) taken, reason = False, "" carry = bool(val & (1 << flags["carry"])) overflow = bool(val & (1 << flags["overflow"])) zero = bool(val & (1 << flags["zero"])) negative = bool(val & (1 << flags["negative"])) if mnemo in ["bhis", "bhiw", "bhil"]: taken, reason = not carry and not zero, "!C && !Z" elif mnemo in ["blss", "blsw", "blsl"]: taken, reason = carry or zero, "C || Z" elif mnemo in ["bccs", "bccw", "bccl"]: taken, reason = not carry, "!C" elif mnemo in ["bcss", "bcsw", "bcsl"]: taken, reason = carry, "C" elif mnemo in ["bnes", "bnew", "bnel"]: taken, reason = not zero, "!Z" elif mnemo in ["beqs", "beqw", "beql"]: taken, reason = zero, "Z" elif mnemo in ["bvcs", "bvcw", "bvcl"]: taken, reason = not overflow, "!V" elif mnemo in ["bvss", "bvsw", "bvsl"]: taken, reason = overflow, "V" elif mnemo in ["bpls", "bplw", "bpll"]: taken, reason = not negative, "!N" elif mnemo in ["bmis", "bmiw", "bmil"]: taken, reason = negative, "N" elif mnemo in ["bges", "bgew", "bgel"]: taken, reason = (negative and overflow) or (not negative and not overflow), "(N && V) || (!N && !V)" elif mnemo in ["blts", "bltw", "bltl"]: taken, reason = (negative and not overflow) or (not negative and overflow), "(N && !V) || (!N && V)" elif mnemo in ["bgts", "bgtw", "bgtl"]: taken = (negative and overflow and not zero) or (not negative and not overflow and not zero) reason = "(N && V && !Z) || (!N && !V && !Z)" elif mnemo in ["bles", "blew", "blel"]: taken, reason = zero or (negative and not overflow) or (not negative and overflow), "Z || (N && !V) || (!N && V)" elif mnemo in ["dbhiw", "dbhi"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = (carry or zero) and val != 0, "(C || Z) && {:s}!=0".format(regname) elif mnemo in ["dblsw", "dbls"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = not carry and not zero and val != 0, "!C && !Z && {:s}!=0".format(regname) elif mnemo in ["dbccw", "dbcc"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = carry and val != 0, "C && {:s}!=0".format(regname) elif mnemo in ["dbcsw", "dbcs"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = not carry and val != 0, "!C && {:s}!=0".format(regname) elif mnemo in ["dbnew", "dbne"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = zero and val != 0, "Z && {:s}!=0".format(regname) elif mnemo in ["dbeqw", "dbeq"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = not zero and val != 0, "!Z && {:s}!=0".format(regname) elif mnemo in ["dbvcw", "dbvc"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = overflow and val != 0, "V && {:s}!=0".format(regname) elif mnemo in ["dbvsw", "dbvs"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = not overflow and val != 0, "!V && {:s}!=0".format(regname) elif mnemo in ["dbplw", "dbpl"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = negative and val != 0, "N && {:s}!=0".format(regname) elif mnemo in ["dbmiw", "dbmi"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = not negative and val != 0, "!N && {:s}!=0".format(regname) elif mnemo in ["dbgew", "dbge"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken = ((negative and not overflow) or (not negative and overflow)) and val != 0 reason = "((N && !V) || (!N && V)) && {:s}!=0".format(regname) elif mnemo in ["dbltw", "dblt"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken = ((negative and overflow) or (not negative and not overflow)) and val != 0 reason = "((N && V) || (!N && !V)) && {:s}!=0".format(regname) elif mnemo in ["dbgtw", "dbgt"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken = (zero or (negative and not overflow) or (not negative and overflow)) and val != 0 reason = "(Z || (N && !V) || (!N && V)) && {:s}!=0".format(regname) elif mnemo in ["dblew", "dble"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken = ((negative and overflow and not zero) or (not negative and not overflow and not zero)) and val != 0 reason = "((N && V && !Z) || (!N && !V && !Z)) && {:s}!=0".format(regname) elif mnemo in ["dbtw", "dbt"]: # branch never taken taken, reason = False, "" elif mnemo in ["dbfw", "dbf"]: regname = insn.operands[0].replace("%", "$") val = get_register(regname) & 0xffff taken, reason = val != 0, "{:s}!=0".format(regname) # TODO: fbXXw, fbXXl return taken, reason def flag_register_to_human(self, val=None): if val is None: reg = self.flag_register val = get_register(reg) return Architecture.flags_to_human(val, self.flags_table) def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = to_unsigned_long(AddressUtil.dereference(current_arch.sp)) elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_ith_parameter(self, i, in_func=True): if in_func: i += 1 # Account for RA being at the top of the stack sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val def get_tls(self): def adjust_offset(x): TLS_TCB_OFFSET = 0x7000 if x == 0: return x return x - TLS_TCB_OFFSET ret = ExecSyscall(0x14d, []).exec_code() # get_thread_area tls = ret["reg"]["$d0"] return adjust_offset(tls) def decode_cookie(self, value, cookie): return value def encode_cookie(self, value, cookie): return value class ALPHA(Architecture): """GEF representation of Alpha architecture.""" arch = "ALPHA" mode = "ALPHA" load_condition = [ Elf.EM_ALPHA, Elf.EM_ALPHA_UNOFFICIAL, "ALPHA", "ALPHA:EV4", "ALPHA:EV5", "ALPHA:EV6", ] # https://download.majix.org/dec/alpha_arch_ref.pdf all_registers = [ "$v0", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$fp", "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$t8", "$t9", "$t10", "$t11", "$ra", "$t12", "$at", "$gp", "$sp", "$zero", "$pc", ] alias_registers = { "$v0": "$r0", "$t0": "$r1", "$t1": "$r2", "$t2": "$r3", "$t3": "$r4", "$t4": "$r5", "$t5": "$r6", "$t6": "$r7", "$t7": "$r8", "$s0": "$r9", "$s1": "$r10", "$s2": "$r11", "$s3": "$r12", "$s4": "$r13", "$s5": "$r14", "$fp": "$r15/$s6", "$a0": "$r16", "$a1": "$r17", "$a2": "$r18", "$a3": "$r19", "$a4": "$r20", "$a5": "$r21", "$t8": "$r22", "$t9": "$r23", "$t10": "$r24", "$t11": "$r25", "$ra": "$r26", "$t12": "$r27/$pv", "$at": "$r28", "$gp": "$r29", "$sp": "$r30", "$zero": "$r31", } flag_register = None # alpha has no flags register return_register = "$v0" function_parameters = ["$a0", "$a1", "$a2", "$a3", "$a4", "$a5"] syscall_register = "$v0" syscall_parameters = ["$a0", "$a1", "$a2", "$a3", "$a4", "$a5"] bit_length = 64 endianness = "little" instruction_length = 4 has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x1f\x04\xff\x47" # nop infloop_insn = b"\xff\xff\xff\xc3" # br self trap_insn = b"\x80\x00\x00\x00" # bpt ret_insn = b"\x01\x80\xfa\x6b" # ret syscall_insn = b"\x83\x00\x00\x00" # callsys def is_syscall(self, insn): return insn.mnemonic in ["callsys"] def is_call(self, insn): return insn.mnemonic in ["br", "bsr", "jsr"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["jmp"] def is_ret(self, insn): return insn.mnemonic in ["ret", "jsr_coroutine"] def is_conditional_branch(self, insn): branch_mnemos = [ "beq", "bge", "bgt", "blbc", "blbs", "ble", "blt", "bne", ] return insn.mnemonic in branch_mnemos def is_branch_taken(self, insn): mnemo, ops = insn.mnemonic, insn.operands taken, reason = False, "" if len(ops) == 0: return taken, reason regname = ops[0] regval = get_register(regname) if regval is None: return taken, reason pQ = lambda a: struct.pack("= 0, "{:s} >= 0".format(regname) elif mnemo == "bgt": taken, reason = regval > 0, "{:s} > 0".format(regname) elif mnemo == "ble": taken, reason = regval <= 0, "{:s} <= 0".format(regname) elif mnemo == "blt": taken, reason = regval < 0, "{:s} < 0".format(regname) elif mnemo == "blbc": taken, reason = (regval & 1) == 0, "({:s}&1) == 0".format(regname) elif mnemo == "blbs": taken, reason = (regval & 1) == 1, "({:s}&1) == 1".format(regname) return taken, reason def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$ra") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): tls = get_register("$unique") if tls is not None: return tls codes = [b"\x9e\x00\x00\x00"] # rduniq ret = ExecAsm(codes).exec_code() return ret["reg"]["$v0"] def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie class HPPA(Architecture): """GEF representation of HP-PA-32 architecture.""" arch = "HPPA" mode = "32" load_condition = [ # Elf.EM_PARISC cannot determine whether it is 32-bit or 64-bit, so it should not be used "PARISC", "PARISC32", "PA-RISC", "PA-RISC32", "HPPA", "HPPA32", "HPPA1.0", "HPPA1.1", ] # http://ftp.parisc-linux.org/docs/arch/pa11_acd.pdf all_registers = [ "$r1", "$rp", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$dp", "$ret0", "$ret1", "$sp", "$r31", "$pc", "$flags", "$pcoqh", "$pcsqh", "$pcoqt", "$pcsqt", ] alias_registers = { "$rp": "$r2", "$dp": "$r27", "$ret0": "$r28", "$ret1": "$r29", "$sp": "$r30", } flag_register = None # HPPA has no flags register return_register = "$ret0" function_parameters = ["$r26", "$r25", "$r24", "$r23"] syscall_register = "$r20" syscall_parameters = ["$r26", "$r25", "$r24", "$r23", "$r22", "$r21"] bit_length = 32 endianness = "big" instruction_length = 4 has_delay_slot = True has_syscall_delay_slot = True has_ret_delay_slot = True stack_grow_down = True tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x40\x02\x00\x08" # nop infloop_insn = b"\xf7\x1f\x1f\xe8" # b,l,n self, r0 trap_insn = None ret_insn = b"\x02\xc0\x40\xe8" # bv.n r0(rp) syscall_insn = b"\x00\x82\x00\xe4" # be,l 100(sr2, r0), sr0, r31 def is_syscall(self, insn): return insn.mnemonic == "be,l" and insn.operands[:4] == ["100(sr2", "r0)", "sr0", "r31"] def is_call(self, insn): if self.is_syscall(insn): return False if insn.mnemonic in ["b,l", "b,l,n"]: # alias for BL,n return True if insn.mnemonic in ["blr", "blr,n"]: # alias for BLR,n return True if insn.mnemonic in ["be,l", "be,l,n"]: # alias for BLE,n return True return False def is_jump(self, insn): if self.is_ret(insn): return False if self.is_conditional_branch(insn): return True if insn.mnemonic in ["b,gate", "b,gate,n"]: # alias for GATE,n return True if insn.mnemonic in ["bv", "bv,n"]: # alias for BV,n return True if insn.mnemonic in ["be", "be,n"]: # alias for BE,n return True return False def is_ret(self, insn): return insn.mnemonic == "bv,n" and insn.operands[-1] == "r0(rp)" def is_conditional_branch(self, insn): if insn.mnemonic.startswith(("movb", "movib")): # alias for MOVB,cond,n / MOVIB,cond,n return True if insn.mnemonic.startswith(("cmpb", "cmpib")): # alias for COMB[TF],cond,n / COMIB[TF],cond,n return True if insn.mnemonic.startswith(("addb", "addib")): # alias for ADDB[TF],cond,n / ADDIB[TF],cond,n return True if insn.mnemonic.startswith("bb,"): # alias for BB,cond,n / BVB,cond,n return True return False def is_branch_taken(self, insn): def get_masked(x): bits = 64 if is_64bit() else 32 return x & ((1 << bits) - 1) def get_sign(x): bits = 64 if is_64bit() else 32 return (x >> (bits - 1)) & 1 def to_signed(x): bits = 64 if is_64bit() else 32 x &= ((1 << bits) - 1) sign = 1 << (bits - 1) # if sign bit set, subtract 2^bits return x - (1 << bits) if (x & sign) else x def check_cond_mov(c, name, val): v = get_masked(val) if c == 0: # never taken, reason = False, "" elif c == 1: # = taken, reason = v == 0, "{:s}==0".format(name) elif c == 2: # < taken, reason = get_sign(v) == 1, "MSB({:s})==1".format(name) elif c == 3: # OD taken, reason = (v & 1) == 1, "LSB({:s})==1".format(name) elif c == 4: # TR taken, reason = True, "Always True" elif c == 5: # <> taken, reason = v != 0, "{:s}!=0".format(name) elif c == 6: # EV taken, reason = (v & 1) == 0, "LSB({:s})==0".format(name) else: taken, reason = False, "" return taken, reason def check_cond_cmp(c, neg, name1, val1, name2, val2): v1 = get_masked(val1) v2 = get_masked(val2) s1 = to_signed(v1) s2 = to_signed(v2) res = get_masked(v1 - v2) if c == 0: # never if not neg: taken, reason = False, "" else: taken, reason = True, "Always True" elif c == 1: # = if not neg: taken, reason = v1 == v2, "{:s}=={:s}".format(name1, name2) else: taken, reason = v1 != v2, "{:s}!={:s}".format(name1, name2) elif c == 2: # < (signed) if not neg: taken, reason = s1 < s2, "{:s}<{:s}".format(name1, name2) else: taken, reason = s1 >= s2, "{:s}>={:s}".format(name1, name2) elif c == 3: # <= (signed) if not neg: taken, reason = s1 <= s2, "{:s}<={:s}".format(name1, name2) else: taken, reason = s1 > s2, "{:s}>{:s}".format(name1, name2) elif c == 4: # < (unsigned) if not neg: taken, reason = v1 < v2, "{:s}<{:s} (unsigned)".format(name1, name2) else: taken, reason = v1 >= v2, "{:s}>={:s} (unsigned)".format(name1, name2) elif c == 5: # <= (unsigned) if not neg: taken, reason = v1 <= v2, "{:s}<={:s} (unsigned)".format(name1, name2) else: taken, reason = v1 > v2, "{:s}>{:s} (unsigned)".format(name1, name2) elif c == 6: # SV overflow = (get_sign(v1) != get_sign(v2)) and (get_sign(v1) != get_sign(res)) # subtract overflow if not neg: taken, reason = overflow, "{:s}-{:s} overflows".format(name1, name2) else: taken, reason = not overflow, "{:s}-{:s} does not overflow".format(name1, name2) elif c == 7: # OD if not neg: taken, reason = (res & 1) == 1, "LSB({:s}-{:s})==1".format(name1, name2) else: taken, reason = (res & 1) == 0, "LSB({:s}-{:s})==0".format(name1, name2) return taken, reason def check_cond_add(c, neg, name1, val1, name2, val2): v1 = get_masked(val1) v2 = get_masked(val2) res = get_masked(v1 + v2) if c == 0: # never if not neg: taken, reason = False, "" else: taken, reason = True, "Always True" elif c == 1: # = if not neg: taken, reason = res == 0, "{:s}==-{:s}".format(name1, name2) else: taken, reason = res != 0, "{:s}!=-{:s}".format(name1, name2) elif c == 2: # < (signed) sres = to_signed(res) if not neg: taken, reason = sres < 0, "{:s}<-{:s} (signed)".format(name1, name2) else: taken, reason = sres >= 0, "{:s}>=-{:s} (signed)".format(name1, name2) elif c == 3: # <= (signed) sres = to_signed(res) if not neg: taken, reason = sres <= 0, "{:s}<=-{:s} (signed)".format(name1, name2) else: taken, reason = sres > 0, "{:s}>-{:s} (signed)".format(name1, name2) elif c == 4: # NUV (unsigned) full = v1 + v2 # addition overflow carry = (full >> (64 if is_64bit() else 32)) & 1 if not neg: taken, reason = not carry, "{:s}+{:s} does not overflow (unsigned)".format(name1, name2) else: taken, reason = carry, "{:s}+{:s} overflows (unsigned)".format(name1, name2) elif c == 5: # ZNV (unsigned) full = v1 + v2 # addition overflow carry = (full >> (64 if is_64bit() else 32)) & 1 zero = (res == 0) if not neg: taken, reason = zero or not carry, "{:s}+{:s} is zero or no overflow (unsigned)".format(name1, name2) else: taken, reason = not zero and carry, "{:s}+{:s} is nonzero and overflows (unsigned)".format(name1, name2) elif c == 6: # SV (signed) overflow = (get_sign(v1) == get_sign(v2)) and (get_sign(v1) != get_sign(res)) # addition overflow if not neg: taken, reason = overflow, "{:s}+{:s} overflows (signed)".format(name1, name2) else: taken, reason = not overflow, "{:s}+{:s} does not overflow (signed)".format(name1, name2) elif c == 7: # OD if not neg: taken, reason = (res & 1) == 1, "LSB({:s}+{:s})==1".format(name1, name2) else: taken, reason = (res & 1) == 0, "LSB({:s}+{:s})==0".format(name1, name2) return taken, reason def check_cond_bit(c, name1, val1, name2, val2): bits = (64 if is_64bit() else 32) - 1 if val2 < 0 or val2 > bits: return False, "" v1 = get_masked(val1) bit_on = ((v1 >> (bits - val2)) & 1) == 1 if c == 2: # < taken, reason = bit_on, "{:s}.bit({:s})==1".format(name1, name2) elif c == 6: # >= taken, reason = not bit_on, "{:s}.bit({:s})==0".format(name1, name2) else: taken, reason = False, "" return taken, reason taken, reason = False, "" if insn.mnemonic.startswith("movb"): # alias for MOVB,cond,n c = (insn.opcodes[2] >> 5) & 0b111 v1 = insn.operands[0] # source taken, reason = check_cond_mov(c, v1, get_register(v1)) elif insn.mnemonic.startswith("movib"): # alias for MOVIB,cond,n c = (insn.opcodes[2] >> 5) & 0b111 v1 = insn.operands[0] # source taken, reason = check_cond_mov(c, v1, int(v1, 16)) elif insn.mnemonic.startswith("cmpb"): # alias for COMB[TF],cond,n c = (insn.opcodes[2] >> 5) & 0b111 f = (insn.opcodes[0] >> 3) & 1 # True or False v1 = insn.operands[0] # source1 v2 = insn.operands[1] # source2 taken, reason = check_cond_cmp(c, f, v1, get_register(v1), v2, get_register(v2)) elif insn.mnemonic.startswith("cmpib"): # alias for COMIB[TF],cond,n c = (insn.opcodes[2] >> 5) & 0b111 f = (insn.opcodes[0] >> 3) & 1 # True or False v1 = insn.operands[0] # source1 v2 = insn.operands[1] # source2 taken, reason = check_cond_cmp(c, f, v1, int(v1, 16), v2, get_register(v2)) elif insn.mnemonic.startswith("addb"): # alias for ADDB[TF],cond,n c = (insn.opcodes[2] >> 5) & 0b111 f = (insn.opcodes[0] >> 3) & 1 # True or False v1 = insn.operands[0] # source1 v2 = insn.operands[1] # source2 taken, reason = check_cond_add(c, f, v1, get_register(v1), v2, get_register(v2)) elif insn.mnemonic.startswith("addib"): # alias for ADDIB[TF],cond,n c = (insn.opcodes[2] >> 5) & 0b111 f = (insn.opcodes[0] >> 3) & 1 # True or False v1 = insn.operands[0] # source1 v2 = insn.operands[1] # source2 taken, reason = check_cond_add(c, f, v1, int(v1, 16), v2, get_register(v2)) elif insn.mnemonic.startswith("bb,"): # alias for BVB,cond,n / BB,cond,n c = (insn.opcodes[2] >> 5) & 0b111 v1 = insn.operands[0] # source1 v2 = insn.operands[1] # source2 vbit = (insn.opcodes[0] >> 2) & 1 if vbit: taken, reason = check_cond_bit(c, v1, get_register(v1), v2, int(v2, 16)) # bb,cond,n else: taken, reason = check_cond_bit(c, v1, get_register(v1), v2, get_register(v2)) # bvb,cond,n if not taken: reason = "" return taken, reason def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: i -= len(self.function_parameters) ret0 = get_register("$ret0") sp = current_arch.sp sz = current_arch.ptrsize loc = ret0 - (i * sz) val = read_int_from_memory(loc) key = "[sp - {:#x}]".format(sp - loc) return key, val def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$rp") & ~0b11 elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): tls = get_register("$cr27") if tls is not None: return tls tls = get_register("$mpsfu_high") if tls is not None: return tls codes = [b"\xbc\x08\x60\x03"] # mfctl tr3, ret0 ret = ExecAsm(codes).exec_code() return ret["reg"]["$ret0"] def decode_cookie(self, value, cookie): return value def encode_cookie(self, value, cookie): return value class HPPA64(HPPA): """GEF representation of HP-PA-64 architecture.""" arch = "HPPA" mode = "64" # qemu does not support hppa64, so this could not be tested. load_condition = [ ] bit_length = 64 class OR1K(Architecture): """GEF representation of OpenRISC 1000 architecture.""" arch = "OR1K" mode = "OR1K" load_condition = [ Elf.EM_OPENRISC, "OPENRISC", "OPENRISC1000", "OR1K", ] # https://openrisc.io/or1k.html # https://sourceware.org/cgen/gen-doc/openrisc-insn.html all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", "$ppc", "$npc", "$sr", ] alias_registers = { "$r1": "$sp", "$r2": "$fp", "$r9": "$lr", } flag_register = "$sr" flags_table = { 11: "overflow", 10: "carry", 9: "flag", } return_register = "$r11" function_parameters = ["$r3", "$r4", "$r5", "$r6", "$r7", "$r8"] syscall_register = "$r11" syscall_parameters = ["$r3", "$r4", "$r5", "$r6", "$r7", "$r8"] bit_length = 32 endianness = "big" instruction_length = 4 has_delay_slot = True has_syscall_delay_slot = True has_ret_delay_slot = True stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x00\x00\x00\x15" # l.nop 0x0 infloop_insn = b"\x00\x00\x00\x00" # l.j self trap_insn = None ret_insn = b"\x00\x48\x00\x44" # l.jr r9 syscall_insn = b"\x01\x00\x00\x20" # l.sys 0x1 def is_syscall(self, insn): try: return insn.mnemonic == "l.sys" and int(insn.operands[0], 0) == 0x1 except Exception: return False def is_call(self, insn): return insn.mnemonic in ["l.bal", "l.jal", "l.jalr"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["l.j", "l.jr"] def is_ret(self, insn): if insn.mnemonic == "l.jr" and insn.operands[0] == "r9": return True if insn.mnemonic in ["l.rfe"]: return True return False def is_conditional_branch(self, insn): return insn.mnemonic in ["l.bf", "l.bnf"] def is_branch_taken(self, insn): mnemo = insn.mnemonic val = get_register(self.flag_register) flags = {self.flags_table[k]: k for k in self.flags_table} taken, reason = False, "" flag = bool(val & (1 << flags["flag"])) if mnemo == "l.bf": taken, reason = flag, "F" if mnemo == "l.bnf": taken, reason = not flag, "!F" return taken, reason def flag_register_to_human(self, val=None): if val is None: reg = self.flag_register val = get_register(reg) return Architecture.flags_to_human(val, self.flags_table) def get_ra(self, insn, frame): ra = None try: if insn.mnemonic == "l.jr" and insn.operands[0] == "r9": ra = get_register("$r9") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$r10") def decode_cookie(self, value, cookie): return value def encode_cookie(self, value, cookie): return value class NIOS2(Architecture): """GEF representation of NiosII architecture.""" arch = "NIOS2" mode = "NIOS2" load_condition = [ Elf.EM_ALTERA_NIOS2, "NIOS2", "NIOS2:R1", "NIOS2:R2", ] # https://www.intel.com/content/www/us/en/docs/programmable/683836/current/introduction.html # https://www.intel.co.jp/content/dam/altera-www/global/ja_JP/pdfs/literature/hb/nios2/n2cpu-nii5v1gen2-j.pdf all_registers = [ "$zero", "$at", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$et", "$bt", "$gp", "$sp", "$fp", "$ea", "$sstatus", "$ra", "$pc", ] alias_registers = { "$zero": "$r0", "$at": "$r1", "$et": "$r24", "$bt": "$r25", "$gp": "$r26", "$sp": "$r27", "$fp": "$r28", "$ea": "$r29", "$sstatus": "$r30", "$ra": "$r31", } flag_register = None # NIOS2 has no flags register return_register = "$r2" function_parameters = ["$r4", "$r5", "$r6", "$r7"] syscall_register = "$r2" syscall_parameters = ["$r4", "$r5", "$r6", "$r7", "$r8", "$r9"] bit_length = 32 endianness = "little" instruction_length = 4 has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x3a\x88\x01\x00" # nop infloop_insn = b"\x06\xff\x3f\x00" # br self trap_insn = None ret_insn = b"\x3a\x28\x00\xf8" # ret syscall_insn = b"\x3a\x68\x3b\x00" # trap 0 def is_syscall(self, insn): try: return insn.mnemonic == "trap" and int(insn.operands[0], 0) == 0 except Exception: return False def is_call(self, insn): return insn.mnemonic in ["call", "callr"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["br", "jmp", "jmpi"] def is_ret(self, insn): return insn.mnemonic in ["ret", "bret", "eret"] def is_conditional_branch(self, insn): branch_mnemos = [ "beq", "bne", "bge", "bgeu", "bgt", "bgtu", "ble", "bleu", "blt", "bltu", ] return insn.mnemonic in branch_mnemos def is_branch_taken(self, insn): mnemo, ops = insn.mnemonic, insn.operands v0 = get_register(ops[0]) v1 = get_register(ops[1]) pI = lambda a: struct.pack("= v1s, "{:s}>={:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bgeu": taken, reason = v0 >= v1, "{:s}>={:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "bgt": taken, reason = v0s > v1s, "{:s}>{:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bgtu": taken, reason = v0 > v1, "{:s}>{:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "ble": taken, reason = v0s <= v1s, "{:s}<={:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bleu": taken, reason = v0 <= v1, "{:s}<={:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "blt": taken, reason = v0s < v1s, "{:s}<{:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bltu": taken, reason = v0 < v1, "{:s}<{:s} (unsigned)".format(ops[0], ops[1]) return taken, reason def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$ra") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): def adjust_offset(x): TLS_TCB_OFFSET = 0x7000 if x == 0: return x return x - TLS_TCB_OFFSET tls = get_register("$r23") return adjust_offset(tls) def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie class MICROBLAZE(Architecture): """GEF representation of MicroBlaze architecture.""" arch = "MICROBLAZE" mode = "MICROBLAZE" load_condition = [ Elf.EM_MICROBLAZE, "MICROBLAZE", ] # https://www.xilinx.com/content/dam/xilinx/support/documents/sw_manuals/xilinx2021_2/ug984-vivado-microblaze-ref.pdf all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", "$rpc", "$rmsr", ] alias_registers = { "$r0": "$zero", "$r1": "$sp", "$r15": "$ra", } flag_register = None return_register = "$r3" function_parameters = ["$r5", "$r6", "$r7", "$r8", "$r9", "$r10"] syscall_register = "$r12" syscall_parameters = ["$r5", "$r6", "$r7", "$r8", "$r9", "$r10"] bit_length = 32 endianness = "little / big" instruction_length = 4 has_delay_slot = True has_syscall_delay_slot = True has_ret_delay_slot = True stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x00\x00\x00\x80" # or r0, r0, r0 infloop_insn = b"\x00\x00\x00\xb8" # bri self trap_insn = None ret_insn = b"\x08\x00\x0f\xb6" # rtsd r15, 8 syscall_insn = b"\x08\x00\xcc\xb9" # brki r14,8 def is_syscall(self, insn): return insn.mnemonic == "brki" and insn.operands[:2] == ["r14", "8"] def is_call(self, insn): return insn.mnemonic in ["brld", "brald", "brlid", "bralid"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True branch_mnemos = [ "br", "bra", "brd", "brad", "bri", "brai", "brid", "braid", ] return insn.mnemonic in branch_mnemos def is_ret(self, insn): return insn.mnemonic in ["rtsd"] def is_conditional_branch(self, insn): branch_mnemos = [ "beq", "beqd", "beqi", "beqid", "bne", "bned", "bnei", "bneid", "bge", "bged", "bgei", "bgeid", "bgt", "bgtd", "bgti", "bgtid", "ble", "bled", "blei", "bleid", "blt", "bltd", "blti", "bltid", ] return insn.mnemonic in branch_mnemos def is_branch_taken(self, insn): mnemo, ops = insn.mnemonic, [x.split()[0] for x in insn.operands] taken, reason = False, "" pI = lambda a: struct.pack("= 0, "{:s}>=0".format(ops[0]) elif mnemo in ["bgt", "bgtd", "bgti", "bgtid"]: taken, reason = v0 > 0, "{:s}>0".format(ops[0]) elif mnemo in ["ble", "bled", "blei", "bleid"]: taken, reason = v0 <= 0, "{:s}<=0".format(ops[0]) elif mnemo in ["blt", "bltd", "blti", "bltid"]: taken, reason = v0 < 0, "{:s}<0".format(ops[0]) return taken, reason def get_ith_parameter(self, i, in_func=True): if i < len(self.function_parameters): reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val else: sp = current_arch.sp sz = current_arch.ptrsize loc = sp + (i * sz) val = read_int_from_memory(loc) key = "[sp + {:#x}]".format(i * sz) return key, val def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$r15") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$r21") def decode_cookie(self, value, cookie): return value def encode_cookie(self, value, cookie): return value class XTENSA(Architecture): """GEF representation of Xtensa architecture.""" arch = "XTENSA" mode = "XTENSA" load_condition = [ Elf.EM_XTENSA, "XTENSA", ] # https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/ip/tensilica-ip/isa-summary.pdf # https://dl.espressif.com/github_assets/espressif/xtensa-isa-doc/releases/download/latest/Xtensa.pdf # https://usermanual.wiki/Document/Xtensa2020ASSEMBLER20GUIDE.1231659642/html all_registers = [ "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7", "$a8", "$a9", "$a10", "$a11", "$a12", "$a13", "$a14", "$a15", "$pc", "$lbeg", "$lend", "$lcount", "$sar", "$litbase", "$ps", "$threadptr", "$scompare1", "$acclo", "$acchi", "$expstate", ] alias_registers = { "$a0": "$lr", "$a1": "$sp", } flag_register = None return_register = "$a2" function_parameters = ["$a10", "$a11", "$a12", "$a13", "$a14", "$a15"] syscall_register = "$a2" syscall_parameters = ["$a6", "$a3", "$a4", "$a5", "$a8", "$a9"] bit_length = 32 endianness = "little / big" instruction_length = None # variable length has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x3d\xf0" # nop.n infloop_insn = b"\x06\xff\xff" # j self trap_insn = None ret_insn = b"\x1d\xf0" # retw.n syscall_insn = b"\x00\x50\x00" # syscall def is_syscall(self, insn): return insn.mnemonic in ["syscall"] def is_call(self, insn): call_mnemo = [ "call0", "call4", "call8", "call12", "callx0", "callx4", "callx8", "callx12", ] return insn.mnemonic in call_mnemo def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["j", "jx"] def is_ret(self, insn): return insn.mnemonic in ["ret", "retw", "ret.n", "retw.n"] def is_conditional_branch(self, insn): branch_mnemos = [ "beq", "beqz", "beqz.n", "beqi", "bne", "bnez", "bnez.n", "bnei", "bge", "bgez", "bgei", "bgeu", "bgeui", "blt", "bltz", "blti", "bltu", "bltui", "bbc", "bbs", "bbci", "bbsi", "bnone", "bany", "ball", "bnall", "bt", "bf", ] return insn.mnemonic in branch_mnemos def is_branch_taken(self, insn): mnemo, ops = insn.mnemonic, insn.operands taken, reason = False, "" pI = lambda a: struct.pack("= v1, "{:s}>={:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bgei": v0 = u2i(get_register(ops[0])) v1 = int(ops[1]) taken, reason = v0 >= v1, "{:s}>={:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bgeu": v0 = get_register(ops[0]) v1 = get_register(ops[1]) taken, reason = v0 >= v1, "{:s}>={:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "bgeui": v0 = get_register(ops[0]) v1 = int(ops[1]) taken, reason = v0 >= v1, "{:s}>={:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "bgez": v0 = get_register(ops[0]) taken, reason = (v0 >> 31) == 0, "({:s}>>31)==0".format(ops[0]) elif mnemo == "blt": v0 = u2i(get_register(ops[0])) v1 = u2i(get_register(ops[1])) taken, reason = v0 < v1, "{:s}<{:s} (signed)".format(ops[0], ops[1]) elif mnemo == "blti": v0 = u2i(get_register(ops[0])) v1 = int(ops[1]) taken, reason = v0 < v1, "{:s}<{:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bltu": v0 = get_register(ops[0]) v1 = get_register(ops[1]) taken, reason = v0 < v1, "{:s}<{:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "bltui": v0 = get_register(ops[0]) v1 = int(ops[1]) taken, reason = v0 < v1, "{:s}<{:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "bltz": v0 = get_register(ops[0]) taken, reason = (v0 >> 31) == 1, "({:s}>>31)==1".format(ops[0]) elif mnemo == "bany": v0 = get_register(ops[0]) v1 = get_register(ops[1]) taken, reason = (v0 & v1) != 0, "({:s}&{:s})!=0".format(ops[0], ops[1]) elif mnemo == "bnone": v0 = get_register(ops[0]) v1 = get_register(ops[1]) taken, reason = (v0 & v1) == 0, "({:s}&{:s})==0".format(ops[0], ops[1]) elif mnemo == "ball": v0 = get_register(ops[0]) v1 = get_register(ops[1]) taken, reason = (~v0 & v1) == 0, "(~{:s}&{:s})==0".format(ops[0], ops[1]) elif mnemo == "bnall": v0 = get_register(ops[0]) v1 = get_register(ops[1]) taken, reason = (~v0 & v1) != 0, "(~{:s}&{:s})!=0".format(ops[0], ops[1]) elif mnemo == "bt": v0 = get_register(ops[0]) taken, reason = v0 != 0, "{:s}!=0".format(ops[0]) elif mnemo == "bf": v0 = get_register(ops[0]) taken, reason = v0 == 0, "{:s}==0".format(ops[0]) elif mnemo == "bbs": v0 = get_register(ops[0]) v1 = get_register(ops[1]) taken, reason = ((v0 >> (v1 & 0b11111)) & 1) == 1, "Bit ({:s}&0b11111) of {:s} is 1".format(ops[1], ops[0]) elif mnemo == "bbsi": v0 = get_register(ops[0]) v1 = int(ops[1]) taken, reason = ((v0 >> v1) & 1) == 1, "Bit {:s} of {:s} is 1".format(ops[1], ops[0]) elif mnemo == "bbc": v0 = get_register(ops[0]) v1 = get_register(ops[1]) taken, reason = ((v0 >> (v1 & 0b11111)) & 1) == 0, "Bit ({:s}&0b11111) of {:s} is 0".format(ops[1], ops[0]) elif mnemo == "bbci": v0 = get_register(ops[0]) v1 = int(ops[1]) taken, reason = ((v0 >> v1) & 1) == 0, "Bit {:s} of {:s} is 0".format(ops[1], ops[0]) return taken, reason def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$a0") if insn.mnemonic in ["retw", "retw.n"]: ra &= ~(0b11 << 30) elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$threadptr") class CRIS(Architecture): """GEF representation of CRIS architecture.""" arch = "CRIS" mode = "CRIS" load_condition = [ Elf.EM_CRIS, "CRIS", ] # https://www.axis.com/dam/public/25/67/ab/etrax-100lx-programmer%E2%80%99s-manual-en-US-33419.pdf all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$sp", "$pc", "$srp", "$ccr", ] alias_registers = { "$sp": "$r14", "$pc": "$r15", } flag_register = "$ccr" flags_table = { 9: "write-fail", 3: "negative", 2: "zero", 1: "overflow", 0: "carry", } return_register = "$r10" function_parameters = ["$r10", "$r11", "$r12", "$r13"] # As of linux 4.17, CRIS is no longer supported. syscall_register = "$r9" syscall_parameters = ["$r10", "$r11", "$r12", "$r13", "$dcr1/mof", "$sp+0x4"] bit_length = 32 endianness = "little" instruction_length = None # variable length has_delay_slot = True has_syscall_delay_slot = True has_ret_delay_slot = True stack_grow_down = False tls_supported = False keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x0f\x05" # nop infloop_insn = b"\xff\xe0" # ba self trap_insn = None ret_insn = b"\x7f\xb6" # ret syscall_insn = b"\x3d\xe9" # break 13 def is_syscall(self, insn): try: return insn.mnemonic == "break" and int(insn.operands[0], 0) == 13 except Exception: return False def is_call(self, insn): return insn.mnemonic in ["jsr", "jsrc"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["ba", "jmpu", "jump"] def is_ret(self, insn): return insn.mnemonic == "ret" def is_conditional_branch(self, insn): conditions = [ "cc", "cs", "ne", "eq", "vc", "vs", "pl", "mi", "ls", "hi", "ge", "lt", "gt", "le", "wf" ] for cc in conditions: if insn.mnemonic in [f"b{cc}"]: return True return False def is_branch_taken(self, insn): mnemo = insn.mnemonic val = get_register(self.flag_register) flags = {self.flags_table[k]: k for k in self.flags_table} taken, reason = False, "" zero = bool(val & (1 << flags["zero"])) negative = bool(val & (1 << flags["negative"])) overflow = bool(val & (1 << flags["overflow"])) carry = bool(val & (1 << flags["carry"])) write_fail = bool(val & (1 << flags["write-fail"])) if mnemo == "bcc": taken, reason = not carry, "!C" elif mnemo == "bcs": taken, reason = carry, "C" elif mnemo == "bne": taken, reason = not zero, "!Z" elif mnemo == "beq": taken, reason = zero, "Z" elif mnemo == "bvc": taken, reason = not overflow, "!V" elif mnemo == "bvs": taken, reason = overflow, "V" elif mnemo == "bpl": taken, reason = not negative, "!N" elif mnemo == "bmi": taken, reason = negative, "N" elif mnemo == "bls": taken, reason = carry or zero, "C || Z" elif mnemo == "bhi": taken, reason = not carry and not zero, "!C && !Z" elif mnemo == "bge": taken, reason = (negative and overflow) or (not negative and not overflow), "(N && V) || (!N && !V)" elif mnemo == "blt": taken, reason = (negative and not overflow) or (not negative and overflow), "(N && !V) || (!N && V)" elif mnemo == "bgt": taken = (negative and overflow and not zero) or (not negative and not overflow and not zero) reason = "(N && V && !Z) || (!N && !V && !Z)" elif mnemo == "ble": taken, reason = zero or (negative and not overflow) or (not negative and overflow), "Z || (N && !V) || (!N && V)" elif mnemo == "bwf": taken, reason = write_fail, "WF" return taken, reason def flag_register_to_human(self, val=None): if val is None: reg = self.flag_register val = get_register(reg) return Architecture.flags_to_human(val, self.flags_table) def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$srp") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra class LOONGARCH64(Architecture): """GEF representation of Loongarch-64 architecture.""" arch = "LOONGARCH" mode = "64" load_condition = [ # Elf.EM_LOONGARCH cannot determine whether it is 32 bit or 64 bit, # but since GEF only supports 64 bit (LA64), so we will use it. Elf.EM_LOONGARCH, "LOONGARCH", "LOONGARCH64", ] # https://docs.kernel.org/loongarch/introduction.html # https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", "$orig_a0", "$pc", "$badv", ] alias_registers = { "$r0": "$zero", "$r1": "$ra", "$r2": "$tp", "$r3": "$sp", "$r4": "$a0/$v0", "$r5": "$a1/$v1", "$r6": "$a2", "$r7": "$a3", "$r8": "$a4", "$r9": "$a5", "$r10": "$a6", "$r11": "$a7", "$r12": "$t0", "$r13": "$t1", "$r14": "$t2", "$r15": "$t3", "$r16": "$t4", "$r17": "$t5", "$r18": "$t6", "$r19": "$t7", "$r20": "$t8", "$r21": "$u0", "$r22": "$fp", "$r23": "$s0", "$r24": "$s1", "$r25": "$s2", "$r26": "$s3", "$r27": "$s4", "$r28": "$s5", "$r29": "$s6", "$r30": "$s7", "$r31": "$s8", } flag_register = None # LOONGARCH has no flags register return_register = "$r4" function_parameters = ["$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11"] syscall_register = "$r11" syscall_parameters = ["$r4", "$r5", "$r6", "$r7", "$r8", "$r9"] bit_length = 64 endianness = "little" instruction_length = 4 has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x00\x00\x40\x03" # andi $zero, $zero, 0x0 infloop_insn = b"\x00\x00\x00\x50" # b self trap_insn = None ret_insn = b"\x20\x00\x00\x4c" # jirl syscall_insn = b"\x00\x00\x2b\x00" # syscall def is_syscall(self, insn): return insn.mnemonic in ["syscall"] def is_call(self, insn): mnemo = insn.mnemonic if mnemo == "bl": return True if mnemo == "jirl": return len(insn.operands) >= 1 and insn.operands[0] != "$zero" return False def is_jump(self, insn): if self.is_conditional_branch(insn): return True mnemo = insn.mnemonic if mnemo == "b": return True if mnemo == "jirl": return len(insn.operands) >= 1 and insn.operands[0] == "$zero" return False def is_ret(self, insn): return insn.mnemonic == "ret" # gdb interpret "jalr $zero, $ra, 0" as "ret" def is_conditional_branch(self, insn): return insn.mnemonic in ["beq", "bne", "bge", "bgeu", "blt", "bltu", "beqz", "bnez"] def is_branch_taken(self, insn): mnemo, ops = insn.mnemonic, insn.operands alias_inverse = {} for k, v in self.alias_registers.items(): for alias_reg in v.split("/"): alias_inverse[alias_reg] = k pQ = lambda a: struct.pack("= v1s, "{:s}>={:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bgeu": taken, reason = v0 >= v1, "{:s}>={:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "blt": taken, reason = v0s < v1s, "{:s}<{:s} (signed)".format(ops[0], ops[1]) elif mnemo == "bltu": taken, reason = v0 < v1, "{:s}<{:s} (unsigned)".format(ops[0], ops[1]) elif mnemo == "beqz": taken, reason = v0 == 0, "{:s}==0".format(ops[0]) elif mnemo == "bnez": taken, reason = v0 != 0, "{:s}!=0".format(ops[0]) return taken, reason def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$r1") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$r2") def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie class ARC(Architecture): """GEF representation of ARC-v2-32 architecture.""" arch = "ARC" mode = "32v2" load_condition = [ Elf.EM_ARC, Elf.EM_ARC_COMPACT, Elf.EM_ARCV2, "ARC600", "ARC601", "ARC700", "ARCV2", ] # http://me.bios.io/images/d/dd/ARCompactISA_ProgrammersReference.pdf all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$gp", "$fp", "$sp", "$ilink", "$r30", "$blink", "$pc", "$status32", "$bta", "$lp_count", ] alias_registers = { "$r25": "$tp", "$gp": "$r26", "$fp": "$r27", "$sp": "$r28", "$ilink": "$r29", "$blink": "$r31", "$lp_count": "$r60", "$pc": "$pcl/$r63", } flag_register = "$status32" flags_table = { 0: "halt", 1: "e1", 2: "e2", 3: "a1", 4: "a2", 5: "ae", 6: "de", 7: "user", 8: "overflow", 9: "carry", 10: "negative", 11: "zero", 12: "loop", } return_register = "$r0" function_parameters = ["$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7"] syscall_register = "$r8" syscall_parameters = ["$r0", "$r1", "$r2", "$r3", "$r4", "$r5"] bit_length = 32 endianness = "little" instruction_length = None # variable length has_delay_slot = True # if op includes `.d` has_syscall_delay_slot = False has_ret_delay_slot = True # if op includes `.d` stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\xe0\x78" # nop_s infloop_insn = b"\x00\xf0" # b_s 0 infloop_insn2 = b"\x01\xf0" # b_s 2 (if $pc % 4 == 2) trap_insn = None ret_insn = b"\xe0\x7e" # j_s [blink] syscall_insn = b"\x1e\x78" # trap_s 0 def is_syscall(self, insn): if insn.mnemonic == "trap0": return True try: return insn.mnemonic == "trap_s" and int(insn.operands[0], 0) == 0 except Exception: return False def is_call(self, insn): if insn.mnemonic in ["bl", "bl.d", "bl_s", "jl", "jl.d", "jl_s", "jl_s.d"]: return True # BLcc<.d> conditions = [ "al", "ra", "eq", "z", "ne", "nz", "pl", "p", "mi", "n", "cs", "c", "lo", "cc", "nc", "hs", "vs", "v", "vc", "nv", "gt", "ge", "lt", "le", "hi", "ls", "pnz", ] for cc in conditions: if insn.mnemonic in [f"bl{cc}", f"bl{cc}.d"]: return True # JLcc<.d> conditions = [ "eq", "ne", "lt", "ge", "lo", "hs", ] for cc in conditions: if insn.mnemonic in [f"jl{cc}", f"jl{cc}.d"]: return True return False def is_jump(self, insn): if self.is_conditional_branch(insn): return True if insn.mnemonic in ["b", "b.d", "b_s"]: return True if insn.mnemonic in ["j", "j.d"]: if insn.operands != ["[blink]"]: return True return False def is_ret(self, insn): if insn.mnemonic in ["j", "j_s", "j_s.d"] and insn.operands == ["[blink]"]: return True return False def is_conditional_branch(self, insn): # Bcc<.d> conditions = [ "al", "ra", "eq", "z", "ne", "nz", "pl", "p", "mi", "n", "cs", "c", "lo", "cc", "nc", "hs", "vs", "v", "vc", "nv", "gt", "ge", "lt", "le", "hi", "ls", "pnz", ] for cc in conditions: if insn.mnemonic in [f"b{cc}", f"b{cc}.d"]: return True # BRcc<.d> conditions = [ "eq", "ne", "lt", "ge", "lo", "hs", ] for cc in conditions: if insn.mnemonic in [f"br{cc}", f"br{cc}.d", f"br{cc}.nt", f"br{cc}.d.nt"]: return True if insn.mnemonic in ["bbit0", "bbit1", "bbit0.d", "bbit1.d"]: return True # BRcc_s conditions = [ "eq", "ne", "gt", "ge", "lt", "le", "hi", "hs", "lo", "ls", ] for cc in conditions: if insn.mnemonic in [f"br{cc}_s"]: return True # Jcc<.d>, Jcc.F conditions = [ "eq", "ne", "lt", "ge", "lo", "hs", ] for cc in conditions: if insn.mnemonic in [f"j{cc}", f"j{cc}.d", f"j{cc}.f"]: return True # Jcc_s if insn.mnemonic in ["jeq_s", "jne_s"]: return True return False def is_branch_taken(self, insn): mnemo = insn.mnemonic ops = [] for op in insn.operands: if op.startswith(";"): break ops.append(op) val = get_register(self.flag_register) flags = {self.flags_table[k]: k for k in self.flags_table} zero = bool(val & (1 << flags["zero"])) negative = bool(val & (1 << flags["negative"])) overflow = bool(val & (1 << flags["overflow"])) carry = bool(val & (1 << flags["carry"])) if len(ops) >= 2: pI = lambda a: struct.pack("= 2: taken, reason = v0u == v1u, "{:s}=={:s}".format(ops[0], ops[1]) else: taken, reason = zero, "Z" elif mnemo.startswith(("bne", "brne", "jne")): if len(ops) >= 2: taken, reason = v0u != v1u, "{:s}!={:s}".format(ops[0], ops[1]) else: taken, reason = not zero, "!Z" elif mnemo.startswith(("bgt", "brgt")): if len(ops) >= 2: taken, reason = v0s > v1s, "{:s}>{:s}".format(ops[0], ops[1]) else: taken = (negative and overflow and not zero) or (not negative and not overflow and not zero) reason = "(N && V && !Z) || (!N && !V && !Z)" elif mnemo.startswith(("bge", "brge", "jge")): if len(ops) >= 2: taken, reason = v0s >= v1s, "{:s}>={:s}".format(ops[0], ops[1]) else: taken, reason = (negative and overflow) or (not negative and not overflow), "(N && V) || (!N && !V)" elif mnemo.startswith(("blt", "brlt", "jlt")): if len(ops) >= 2: taken, reason = v0s < v1s, "{:s}<{:s}".format(ops[0], ops[1]) else: taken, reason = (negative and not overflow) or (not negative and overflow), "(N && !V) || (!N && V)" elif mnemo.startswith(("ble", "brle")): if len(ops) >= 2: taken, reason = v0s <= v1s, "{:s}<={:s}".format(ops[0], ops[1]) else: taken, reason = zero or (negative and not overflow) or (not negative and overflow), "Z || (N && !V) || (!N && V)" elif mnemo.startswith(("bhi", "brhi")): if len(ops) >= 2: taken, reason = v0u > v1u, "{:s}>{:s}".format(ops[0], ops[1]) else: taken, reason = not carry and not zero, "!C && !Z" elif mnemo.startswith(("bhs", "brhs", "jhs")): if len(ops) >= 2: taken, reason = v0u >= v1u, "{:s}>={:s}".format(ops[0], ops[1]) else: taken, reason = not carry, "!C" elif mnemo.startswith(("blo", "brlo", "jlo")): if len(ops) >= 2: taken, reason = v0u < v1u, "{:s}<{:s}".format(ops[0], ops[1]) else: taken, reason = carry, "C" elif mnemo.startswith(("bls", "brls")): if len(ops) >= 2: taken, reason = v0u <= v1u, "{:s}<={:s}".format(ops[0], ops[1]) else: taken, reason = carry or zero, "C || Z" return taken, reason def flag_register_to_human(self, val=None): if val is None: reg = self.flag_register val = get_register(reg) return Architecture.flags_to_human(val, self.flags_table) def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$blink") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$r25") def decode_cookie(self, value, cookie): return value def encode_cookie(self, value, cookie): return value class ARCv3(ARC): """GEF representation of ARC-v3-32 architecture.""" arch = "ARC" mode = "32v3" load_condition = [ Elf.EM_ARC_COMPACT3, "ARC64:32", ] all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$fp", "$sp", "$ilink", "$gp", "$blink", "$pc", "$status32", "$bta", "$eret", ] alias_registers = { "$fp": "$r27", "$sp": "$r28", "$ilink": "$r29", "$gp": "$r30", "$blink": "$r31", "$pc": "$pcl/$r63", } def get_tls(self): return get_register("$gp") class ARC64(ARCv3): """GEF representation of ARC-v3-64 architecture.""" arch = "ARC" mode = "64v3" load_condition = [ Elf.EM_ARC_COMPACT3_64, "ARC64:64", ] bit_length = 64 class CSKY(Architecture): """GEF representation of C-SKY architecture.""" arch = "CSKY" mode = "CSKY" load_condition = [ Elf.EM_CSKY, "CSKY", "CSKY:CK510", "CSKY:CK610", "CSKY:CK801", "CSKY:CK802", "CSKY:CK803", "CSKY:CK807", "CSKY:CK810", "CSKY:CK860", "CSKY:ANY", ] # https://github.com/c-sky/csky-doc/blob/master/CSKY%20Architecture%20user_guide.pdf all_registers = [ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", "$pc", "$psr", "$hi", "$lo", ] alias_registers = { "$r14": "$sp", "$r15": "$lr", "$r28": "$ds", "$r30": "$vec", "$r31": "$tp", } flag_register = "$psr" flags_table = { 0: "carry", } return_register = "$r0" function_parameters = ["$r0", "$r1", "$r2", "$r3"] syscall_register = "$r7" syscall_parameters = ["$r0", "$r1", "$r2", "$r3", "$r4", "$r5"] bit_length = 32 endianness = "little" instruction_length = None # variable length has_delay_slot = False has_syscall_delay_slot = False has_ret_delay_slot = False stack_grow_down = False tls_supported = True keystone_support = False capstone_support = False unicorn_support = False nop_insn = b"\x03\x6c" # mov r0, r0 infloop_insn = b"\x00\x04" # br self trap_insn = b"\x00\x00" # bkpt ret_insn = b"\x3c\x78" # rts syscall_insn = b"\x00\xc0\x20\x20" # trap 0 def is_syscall(self, insn): try: return insn.mnemonic == "trap" and int(insn.operands[0], 0) == 0 except Exception: return False def is_call(self, insn): return insn.mnemonic in ["bsr", "jsri", "jsr"] def is_jump(self, insn): if self.is_conditional_branch(insn): return True return insn.mnemonic in ["br", "jmpi", "jmp", "jmpix"] def is_ret(self, insn): return insn.mnemonic in ["rts"] def is_conditional_branch(self, insn): return insn.mnemonic in ["bt", "bf", "bez", "bnez", "bhz", "blsz", "blz", "bhsz"] def is_branch_taken(self, insn): mnemo, ops = insn.mnemonic, insn.operands val = get_register(self.flag_register) flags = {self.flags_table[k]: k for k in self.flags_table} taken, reason = False, "" carry = bool(val & (1 << flags["carry"])) pI = lambda a: struct.pack(" 0, "{:s}>0".format(ops[0]) elif mnemo == "blsz": v0s = u2i(get_register(ops[0])) taken, reason = v0s <= 0, "{:s}<=0".format(ops[0]) elif mnemo == "blz": v0s = u2i(get_register(ops[0])) taken, reason = v0s < 0, "{:s}<0".format(ops[0]) elif mnemo == "bhsz": v0s = u2i(get_register(ops[0])) taken, reason = v0s >= 0, "{:s}>=0".format(ops[0]) return taken, reason def flag_register_to_human(self, val=None): if val is None: reg = self.flag_register val = get_register(reg) return Architecture.flags_to_human(val, self.flags_table) def get_ra(self, insn, frame): ra = None try: if self.is_ret(insn): ra = get_register("$r15") elif frame.older(): ra = frame.older().pc() except gdb.error: pass return ra def get_tls(self): return get_register("$r31") def decode_cookie(self, value, cookie): return value ^ cookie def encode_cookie(self, value, cookie): return value ^ cookie # The prototype for new architecture. # #class XXX(Architecture): # """GEF representation of XXX architecture.""" # # arch = "XXX" # mode = "XXX" # # load_condition = [ # Elf.EM_XXX, # "XXX", # ] # # all_registers = [ # "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", # "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", # "$pc", "$sr", # ] # alias_registers = { # "$r15": "$sp", # } # #flag_register = "$flags" # #flags_table = { # # 0: "negative", # # 1: "zero", # #} # #return_register = "$r0" # #function_parameters = ["$r1", "$r2", "$r3", "$r4", "$r5", "$r6"] # #syscall_register = "$r0" # #syscall_parameters = ["$r1", "$r2", "$r3", "$r4", "$r5", "$r6"] # # bit_length = 32 # endianness = "little" # #instruction_length = 4 # #has_delay_slot = False # #has_syscall_delay_slot = False # #has_ret_delay_slot = False # #stack_grow_down = False # #tls_supported = False # # #keystone_support = False # #capstone_support = False # #unicorn_support = False # # #nop_insn = b"\x00\x00" # nop # #infloop_insn = b"\x11\x11" # bra self # #trap_insn = None # #ret_insn = b"\x22\x22" # ret # #syscall_insn = b"\x33\x33" # ecall # # #def is_syscall(self, insn): # # return insn.mnemonic in [] # # #def is_call(self, insn): # # return insn.mnemonic in [] # # #def is_jump(self, insn): # # if self.is_conditional_branch(insn): # # return True # # return insn.mnemonic in [] # # #def is_ret(self, insn): # # return insn.mnemonic in [] # # #def is_conditional_branch(self, insn): # # return insn.mnemonic in [] # # #def is_branch_taken(self, insn): # # mnemo = insn.mnemonic # # val = get_register(self.flag_register) # # flags = {self.flags_table[k]: k for k in self.flags_table} # # taken, reason = False, "" # # return taken, reason # # #def flag_register_to_human(self, val=None): # # if val is None: # # reg = self.flag_register # # val = get_register(reg) # # return Architecture.flags_to_human(val, self.flags_table) # # #def get_ith_parameter(self, i, in_func=True): # # if in_func: # # i += 1 # Account for RA being at the top of the stack # # sp = current_arch.sp # # sz = current_arch.ptrsize # # loc = sp + (i * sz) # # val = read_int_from_memory(loc) # # key = "[sp + {:#x}]".format(i * sz) # # return key, val # # #def get_ra(self, insn, frame): # # ra = None # # try: # # if self.is_ret(insn): # # ra = get_register("$sr") # # elif frame.older(): # # ra = frame.older().pc() # # except gdb.error: # # pass # # return ra # # #def get_tls(self): # # return None # # #def decode_cookie(self, value, cookie): # # return value ^ cookie # # #def encode_cookie(self, value, cookie): # # return value ^ cookie def write_memory(addr, data): """Write `data` at address `addr`.""" def write_memory_qemu_user(pid, addr, data, length): """Write `data` at address `addr` for qemu-user or Intel Pin.""" def read_memory_via_proc_mem(pid, addr, length): with open("/proc/{:d}/mem".format(pid), "rb") as fd: try: fd.seek(addr) return fd.read(length) except OSError: return None def write_memory_via_proc_mem(pid, addr, data, length): with open("/proc/{:d}/mem".format(pid), "wb") as fd: try: fd.seek(addr) ret = fd.write(data[:length]) fd.flush() gdb.execute("maintenance flush dcache", to_string=True) return ret except (OSError, gdb.error): return None def write_with_check(pid, addr, data, length, offset=0): before = read_memory_via_proc_mem(pid, addr + offset, length) if before is None: return None ret = write_memory_via_proc_mem(pid, addr + offset, data, length) after = read_memory(addr, length) if ret: if after == data[:length]: return ret else: # fail, revert write_memory_via_proc_mem(pid, addr + offset, before, length) return None return None # 1. qemu-user (32bit) maps the memory at +0x10000 (fast path) if is_qemu_user() and is_32bit(): # not Intel Pin ret = write_with_check(pid, addr, data, length, offset=0x10000) if ret: return ret # 2. we assume addr is same ret = write_with_check(pid, addr, data, length) if ret: return ret # 3. heuristic addr search and try use it if is_qemu_user(): # not Intel Pin inner_section = ProcessMap.lookup_address(addr).section target_path = inner_section.path outer_maps = ProcessMap.get_process_maps(outer=True) for m in outer_maps: if m.path != target_path: continue offset = m.page_start - inner_section.page_start ret = write_with_check(pid, addr, data, length, offset=offset) if ret: return ret raise Exception("Memory write error for qemu-user or Intel Pin") # ---- length = len(data) if length == 0: return 0 try: gdb.selected_inferior().write_memory(addr, data, length) return length except gdb.MemoryError: pass # Under qemu-user/pin, you can not patch to `code` areas, # so you have to patch via /proc/pid/mem if is_qemu_user() or is_pin(): pid = Pid.get_pid() if pid: return write_memory_qemu_user(pid, addr, data, length) raise Exception("Memory write error") def read_memory(addr, length): """Return a `length` long byte array with the copy of the process memory at `addr`.""" if length == 0: return b"" if is_pin(): # Memory read of Intel Pin is very slow, so speed it up try: pid = Pid.get_pid() fd = open("/proc/{:d}/mem".format(pid), "rb") fd.seek(addr) content = fd.read(length) fd.close() return content except Exception: pass if is_arm64() and is_qemu_system(): if Config.get_gef_setting("gef.read_memory_work_around_for_aarch64_secure_memory"): sm = QemuMonitor.get_secure_memory_map() if sm: target_phys = XSecureMemAddrCommand.v2p_secure(addr) # heavy if target_phys: if sm.sm_base <= target_phys < sm.sm_base + sm.sm_size: target_offset = target_phys - sm.sm_base data = XSecureMemAddrCommand.read_secure_memory(sm, target_offset, length) if data: return data # Don't include it in a try-catch, as we might expect a memory error on read_memory. return gdb.selected_inferior().read_memory(addr, length).tobytes() def read_int_from_memory(addr): """Return an integer read from memory.""" # It works even if current_arch is None sz = AddressUtil.get_memory_alignment() mem = read_memory(addr, sz) unpack = {2:u16, 4:u32, 8:u64}[sz] return unpack(mem) def read_int8_from_memory(addr): """Return a uint_8 read from memory.""" mem = read_memory(addr, 1) return u8(mem) def read_int16_from_memory(addr): """Return a uint_16 read from memory.""" mem = read_memory(addr, 2) return u16(mem) def read_int32_from_memory(addr): """Return a uint_32 read from memory.""" mem = read_memory(addr, 4) return u32(mem) def read_int64_from_memory(addr): """Return a uint_64 read from memory.""" mem = read_memory(addr, 8) return u64(mem) def read_cstring_from_memory(addr, max_length=None): """Return a C-string read from memory.""" if max_length is None: max_length = Config.get_gef_setting("context.nb_max_string_length") if is_kgdb(): # read_memory when kgdb is very slow, this is dirty hack block_size = 64 else: block_size = get_pagesize() # first, read to page boundary length = block_size - (addr % block_size) try: res = read_memory(addr, length) except gdb.MemoryError: return None # if too short, more read while len(res) < max_length: if b"\x00" in res: break try: read_length = min(max_length - len(res), block_size) res += read_memory(addr + len(res), read_length) except gdb.MemoryError: break # check if ascii res = res.split(b"\x00")[0] ustr = String.bytes2str(res) if ustr and any(x not in String.STRING_PRINTABLE for x in ustr): return None if len(ustr) > max_length: ustr = "{}[...]".format(ustr[:max_length]) return ustr def read_physmem(paddr, size): """Return a `size` long byte array with the copy of the physical memory at `paddr`.""" def transparent_read(paddr, size): # switch virt/phys mode orig_mode = QemuMonitor.get_current_mmu_mode() try: if orig_mode == "virt": enable_phys() out = read_memory(paddr, size) if orig_mode == "virt": disable_phys() return out except gdb.MemoryError: if orig_mode == "virt": disable_phys() return None def qemu_system_proc_mem(paddr, size): qemu_system_pid = Pid.get_pid() if qemu_system_pid is None: return None res = gdb.execute("monitor gpa2hva {:#x}".format(paddr), to_string=True) r = re.search("is (0x[0-9a-f]+)", res) if not r: return None virt_addr = int(r.group(1), 16) try: with open("/proc/{:d}/mem".format(qemu_system_pid), "rb") as fd: fd.seek(virt_addr) return fd.read(size) except Exception: pass return None def qemu_system_use_xp(paddr, size): # for older than qemu 4.1.0 try: res = gdb.execute("monitor xp/{:d}xb {:#x}".format(size, paddr), to_string=True) """ gef> monitor xp/16xb 0 0000000000000000: 0x53 0xff 0x00 0xf0 0x53 0xff 0x00 0xf0 0000000000000008: 0xc3 0xe2 0x00 0xf0 0x53 0xff 0x00 0xf0 """ except gdb.error: return None out = b"" for line in res.splitlines(): if not line: continue data = line.split()[1:] out += bytes([int(x, 16) for x in data]) return out def kgdb_use_physmap(paddr, size): # Use workaround value if provided. Useful if KGDB does not expose system registers. physmap = Config.get_gef_setting("gef.physmap_base_for_read_physmem_kgdb_work_around") if physmap == 0: if is_arm64(): # On arm64, calculate physmap address based on PAGE_OFFSET and memstart_addr. # This requires access to TCR_EL1 register to calculate PAGE_OFFSET. physmap = KernelAddressHeuristicFinder.consts().physmap_base elif is_x86_64(): physmap = KernelAddressHeuristicFinder.get_PAGE_OFFSET() else: return None if physmap is None: return None vaddr = physmap + paddr try: return read_memory(vaddr, size) except gdb.MemoryError: return None def kdb_use_mdp(paddr, size): # for KDB; not supported by KGDB # Note: `mdp` command can only handle aligned addresses. paddr_aligned = paddr & ~0xf read_n_line = (size + (paddr - paddr_aligned) + 15) // 16 try: res = gdb.execute("monitor mdp {:#x} {:d}".format(paddr_aligned, read_n_line), to_string=True) """ gef> monitor mdp 0 2 phys 0x0000000000000000 f000ff53f000ff53 f000ff53f000e2c3 S...S.......S... phys 0x0000000000000010 f000ff54f000ff53 f000ff53f0008488 S...T.......S... """ except gdb.error: return None out = b"" for line in res.splitlines(): if not line: continue if line.endswith("zero suppressed"): start, end = AddressUtil.parse_string_range(line.split(" ")[0]) zlen = (end + 1) - start out += b"\x00" * zlen continue out += b"".join([bytes.fromhex(x)[::-1] for x in line.split()[2:4]]) return out[paddr & 0xf:][:size] # ---- if size == 0: return b"" if is_qemu_system(): out = qemu_system_proc_mem(paddr, size) if out: return out if is_supported_physmode(): out = transparent_read(paddr, size) if out: return out out = qemu_system_use_xp(paddr, size) if out: return out return None if is_vmware(): return transparent_read(paddr, size) if is_kgdb(): out = kgdb_use_physmap(paddr, size) # fast path if out: return out if is_kdb(): return kdb_use_mdp(paddr, size) # slow path return None def write_physmem(paddr, data): """Write `data` at physical memory address `paddr`.""" def transparent_write(paddr, data): # switch virt/phys mode orig_mode = QemuMonitor.get_current_mmu_mode() try: if orig_mode == "virt": enable_phys() out = write_memory(paddr, data) if orig_mode == "virt": disable_phys() return out except Exception: if orig_mode == "virt": disable_phys() return None # ---- if len(data) == 0: return 0 if not is_qemu_system() and not is_vmware(): # kgdb is unsupported return None if not is_supported_physmode(): return None return transparent_write(paddr, data) @Cache.cache_until_next def is_valid_addr(addr): if not hasattr(addr, "__int__"): return False addr = int(addr) if addr < 0: return False if AddressUtil.get_vmem_end() <= addr: return False if is_qemu_system(): if QemuMonitor.check_gic_address(addr): return False try: gdb.selected_inferior().read_memory(addr, 1) return True except gdb.MemoryError: return False @Cache.cache_until_next def is_valid_addr_addr(addr): if is_valid_addr(addr): v = read_int_from_memory(addr) return is_valid_addr(v) return False @Cache.cache_until_next def is_single_link_list(addr): # +------+ +------+ +------+ # | head |-->| next |--> ... -->| next |--> NULL # +------+ +------+ +------+ seen = [] while True: if addr == 0: return True if addr in seen: return False if not is_valid_addr(addr): return False seen.append(addr) addr = read_int_from_memory(addr) @Cache.cache_until_next def is_double_link_list(addr, min_len=0): # +------+<-+ +------+<-+ <-+ +------+<-+ +------+ # | head |--|-->| next |--|--> ... --|-->| next |--|-->| head | # +------+ | +------+ | | +------+ | +------+ # | tail | +---| prev | +--- +---| prev | +---| tail | # +------+ +------+ +------+ +------+ # list next pointer seen = [] while True: if not is_valid_addr(addr): return False if addr in seen: break seen.append(addr) addr = read_int_from_memory(addr) if addr != seen[0]: return False # check prev pointer for i, x in enumerate(seen): p = read_int_from_memory(x + current_arch.ptrsize) if p != seen[i - 1]: return False # minimum length check return len(seen) > min_len class QemuMonitor: """A collection of utility functions that are related to qemu-monitor.""" @staticmethod @Cache.cache_this_session def get_gic_addrs(): """Return physical addresses of ARM GIC(General Interrupt Controller).""" if not is_qemu_system(): return [] if not is_arm32() and not is_arm64(): return [] try: res = gdb.execute("monitor info mtree -f", to_string=True) except gdb.error: return [] gic_list = [] for line in res.splitlines(): # gef> monitor info mtree -f # these are physical addresses # 0000000008000000-0000000008000fff (prio 0, i/o): gic_dist # 0000000008010000-0000000008011fff (prio 0, i/o): gic_cpu if not line.startswith(" "): continue m = re.search(r" ([0-9a-f]+)-([0-9a-f]+).*i/o\): gic_(dist|cpu)", line) if not m: continue paddr = int(m.group(1), 16) pend = int(m.group(2), 16) # fix size size = pend - paddr if (size & 0xfff) == 0xfff: size += 1 gic_list.append([paddr, paddr + size]) return gic_list @staticmethod @Cache.cache_until_next def check_gic_address(vaddr): gic_addrs = QemuMonitor.get_gic_addrs() if not gic_addrs: return False try: ret = gdb.execute("monitor gva2gpa {:#x}".format(vaddr), to_string=True) except gdb.error: return False r = re.search(r"gpa: (0x\S+)", ret) if not r: return False paddr = int(r.group(1), 16) for s, e in gic_addrs: if s <= paddr < e: return True return False @staticmethod def get_current_mmu_mode(): if is_qemu_system(): try: response = gdb.execute("maintenance packet qqemu.PhyMemMode", to_string=True, from_tty=False) if 'received: "0"' in response: return "virt" elif 'received: "1"' in response: return "phys" else: return False except gdb.error: return False elif is_vmware(): try: read_memory(0, 1) return "phys" except gdb.MemoryError: return "virt" return None @staticmethod def get_secure_memory_map(verbose=False): # find secure-ram base ret = gdb.execute("monitor info mtree -f", to_string=True) for line in ret.splitlines(): m = re.search(r"([0-9a-f]{16})-([0-9a-f]{16}).*: \S+.secure-ram", line) if m: secure_memory_base = int(m.group(1), 16) secure_memory_size = int(m.group(2), 16) + 1 - secure_memory_base if verbose: info("secure memory base: {:#x}-{:#x} ({:#x} bytes)".format( secure_memory_base, secure_memory_base + secure_memory_size, secure_memory_size, )) break else: return None # find virtual address ret = gdb.execute("monitor gpa2hva {:#x}".format(secure_memory_base), to_string=True) r = re.search("is (0x[0-9a-f]+)", ret) if not r: return None secure_memory_page_addr = int(r.group(1), 16) # find target map of qemu-system's pid qemu_system_pid = Pid.get_pid() if qemu_system_pid is None: if verbose: err("Could not find the qemu-system pid") return None maps = ProcessMap.get_process_maps_linux(qemu_system_pid) for m in maps: if m.page_start != secure_memory_page_addr: continue if m.size != secure_memory_size: continue if verbose: info("secure memory page of pid {:d}: {:#x}".format(qemu_system_pid, m.page_start)) m.sm_base = secure_memory_base m.sm_size = secure_memory_size return m return None def is_supported_physmode(): """GDB mode determination function for physmem support.""" return QemuMonitor.get_current_mmu_mode() in ["virt", "phys"] def enable_phys(): if is_qemu_system(): response = gdb.execute("maintenance packet Qqemu.PhyMemMode:1", to_string=True, from_tty=False) response = gdb.execute("maintenance packet qqemu.PhyMemMode", to_string=True, from_tty=False) gdb.execute("maintenance flush dcache", to_string=True) return 'received: "1"' in response elif is_vmware(): gdb.execute("monitor phys", to_string=True) gdb.execute("maintenance flush dcache", to_string=True) return True def disable_phys(): if is_qemu_system(): response = gdb.execute("maintenance packet Qqemu.PhyMemMode:0", to_string=True, from_tty=False) response = gdb.execute("maintenance packet qqemu.PhyMemMode", to_string=True, from_tty=False) gdb.execute("maintenance flush dcache", to_string=True) return 'received: "0"' in response elif is_vmware(): gdb.execute("monitor virt", to_string=True) gdb.execute("maintenance flush dcache", to_string=True) return True @Cache.cache_this_session def p8(x, s=False): """Pack one byte respecting the current architecture endianness.""" if not s: return struct.pack("{}B".format(Endian.endian_str()), x) else: return struct.pack("{}b".format(Endian.endian_str()), x) @Cache.cache_this_session def p16(x, s=False): """Pack one word respecting the current architecture endianness.""" if not s: return struct.pack("{}H".format(Endian.endian_str()), x) else: return struct.pack("{}h".format(Endian.endian_str()), x) @Cache.cache_this_session def p32(x, s=False): """Pack one dword respecting the current architecture endianness.""" if not s: return struct.pack("{}I".format(Endian.endian_str()), x) else: return struct.pack("{}i".format(Endian.endian_str()), x) @Cache.cache_this_session def p64(x, s=False): """Pack one qword respecting the current architecture endianness.""" if not s: return struct.pack("{}Q".format(Endian.endian_str()), x) else: return struct.pack("{}q".format(Endian.endian_str()), x) @Cache.cache_this_session def u8(x, s=False): """Unpack one byte respecting the current architecture endianness.""" if not s: return struct.unpack("{}B".format(Endian.endian_str()), x)[0] else: return struct.unpack("{}b".format(Endian.endian_str()), x)[0] @Cache.cache_this_session def u16(x, s=False): """Unpack one word respecting the current architecture endianness.""" if not s: return struct.unpack("{}H".format(Endian.endian_str()), x)[0] else: return struct.unpack("{}h".format(Endian.endian_str()), x)[0] @Cache.cache_this_session def u32(x, s=False): """Unpack one dword respecting the current architecture endianness.""" if not s: return struct.unpack("{}I".format(Endian.endian_str()), x)[0] else: return struct.unpack("{}i".format(Endian.endian_str()), x)[0] @Cache.cache_this_session def u64(x, s=False): """Unpack one qword respecting the current architecture endianness.""" if not s: return struct.unpack("{}Q".format(Endian.endian_str()), x)[0] else: return struct.unpack("{}q".format(Endian.endian_str()), x)[0] @Cache.cache_this_session def u128(x): """Unpack one oword respecting the current architecture endianness.""" upper = struct.unpack("{}Q".format(Endian.endian_str()), x[8:])[0] lower = struct.unpack("{}Q".format(Endian.endian_str()), x[:8])[0] return (upper << 64) | lower def is_ascii_string(addr): """Helper function to determine if the buffer pointed by `addr` is an ASCII string (in GDB)""" try: x = read_cstring_from_memory(addr) return x is not None and len(x) > 0 except gdb.MemoryError: return False def is_alive(): """GDB mode determination function for running.""" try: return gdb.selected_inferior().pid > 0 except gdb.error: return False def parse_args(f): """Decorator wrapper to parse args for command.""" @functools.wraps(f) def wrapper(self, argv, **kwargs): try: self.parser.exit = lambda *_: exec("if _: print(_[1]);\nraise(GefUtil.ArgparseExitProxyException(_))") args = self.parser.parse_args(argv) except GefUtil.ArgparseExitProxyException as e: if not e.args[0]: # when --help or -h self.usage(after_syntax_only=True) return except Exception as e: err("Invalid argument: {}".format(e)) return if hasattr(args, "help_simple") and args.help_simple: self.usage(simple=True) return self.args = args return f(self, args, **kwargs) return wrapper def switch_to_intel_syntax(f): """Decorator to temporarily switch to Intel syntax.""" @functools.wraps(f) def wrapper(*args, **kwargs): if not is_x86(): return f(*args, **kwargs) att = gdb.parameter("disassembly-flavor") == "att" if att: gdb.execute("set disassembly-flavor intel", to_string=True) ret = f(*args, **kwargs) if att: gdb.execute("set disassembly-flavor att", to_string=True) return ret return wrapper def only_if_gdb_running(f): """Decorator wrapper to check if GDB is running.""" @functools.wraps(f) def wrapper(*args, **kwargs): if is_alive(): return f(*args, **kwargs) else: warn("No debugging session active") return return wrapper def only_if_gdb_target_local(f): """Decorator wrapper to check if GDB is running locally (target not remote).""" @functools.wraps(f) def wrapper(*args, **kwargs): if not is_remote_debug(): return f(*args, **kwargs) else: warn("This command is not supported for remote sessions") return return wrapper def only_if_in_kernel(f): """Decorator wrapper to check if context is in kernel.""" @functools.wraps(f) def wrapper(*args, **kwargs): if is_in_kernel(): return f(*args, **kwargs) else: warn("Run in kernel context") return return wrapper def only_if_in_kernel_or_kpti_disabled(f): """Decorator wrapper to check if context is in kernel or kpti disabled.""" @functools.wraps(f) def wrapper(*args, **kwargs): def is_kpti_enabled(): try: s = KernelAddressHeuristicFinder.get_saved_command_line() except gdb.MemoryError: return True if s and is_valid_addr(s): # You can access the kernel's .data area while in userland. # This means KPTI is disabled. return False return True if is_in_kernel(): return f(*args, **kwargs) elif not is_kpti_enabled(): return f(*args, **kwargs) else: warn("Run in kernel context, or disable KPTI") return return wrapper def only_if_kvm_disabled(f): """Decorator wrapper to check if there is not -enable-kvm option.""" @functools.wraps(f) def wrapper(*args, **kwargs): if is_kvm_enabled(): err("Disable `-enable-kvm` option for qemu-system") return return f(*args, **kwargs) return wrapper def only_if_smp_disabled(f): """Decorator wrapper to check if there is not -smp N option.""" @functools.wraps(f) def wrapper(*args, **kwargs): if is_smp_enabled(): err("Disable `-smp N` option for qemu-system") return return f(*args, **kwargs) return wrapper def require_arch_set(f): """Decorator wrapper to check if current_arch is not None.""" @functools.wraps(f) def wrapper(*args, **kwargs): if current_arch is not None: return f(*args, **kwargs) else: err("Unsupported architecture") return return wrapper def only_if_specific_gdb_mode(mode=()): """Decorator wrapper to check if the gdb mode is specific.""" def wrapper(f): @functools.wraps(f) def inner_f(*args, **kwargs): dic = { "pin": is_pin, "qemu-system": is_qemu_system, "qemu-user": is_qemu_user, "vmware": is_vmware, "kgdb": is_kgdb, "kdb": is_kdb, "qiling": is_qiling, "rr": is_rr, "wine": is_wine, } for m in mode: if dic.get(m, lambda: False)(): return f(*args, **kwargs) warn("This command is not supported in this gdb mode") if "kgdb" in mode: if is_in_kernel() and not is_qemu_system() and not is_vmware(): info("For KGDB: Try `gef config gef.kgdb_force True`") return return inner_f return wrapper def exclude_specific_gdb_mode(mode=()): """Decorator wrapper to check if the gdb mode is specific.""" def wrapper(f): @functools.wraps(f) def inner_f(*args, **kwargs): dic = { "pin": is_pin, "qemu-system": is_qemu_system, "qemu-user": is_qemu_user, "vmware": is_vmware, "kgdb": is_kgdb, "kdb": is_kdb, "qiling": is_qiling, "rr": is_rr, "wine": is_wine, } for m in mode: if dic.get(m, lambda: False)(): warn("This command is not supported in this gdb mode") return return f(*args, **kwargs) return inner_f return wrapper def only_if_specific_arch(arch=()): """Decorator wrapper to check if the architecture is specific.""" def wrapper(f): @functools.wraps(f) def inner_f(*args, **kwargs): dic = { "x86_32": is_x86_32, "x86_64": is_x86_64, "x86_16": is_x86_16, "ARM32": is_arm32, "ARM32M": is_arm32_cortex_m, "ARM64": is_arm64, "MIPS32": is_mips32, "MIPS64": is_mips64, "MIPSN32": is_mipsn32, "PPC32": is_ppc32, "PPC64": is_ppc64, "SPARC32": is_sparc32, "SPARC32PLUS": is_sparc32plus, "SPARC64": is_sparc64, "RISCV32": is_riscv32, "RISCV64": is_riscv64, "S390X": is_s390x, "SH4": is_sh4, "M68K": is_m68k, "ALPHA": is_alpha, "HPPA32": is_hppa32, "HPPA64": is_hppa64, "OR1K": is_or1k, "NIOS2": is_nios2, "MICROBLAZE": is_microblaze, "XTENSA": is_xtensa, "CRIS": is_cris, "LOONGARCH64": is_loongarch64, "ARC32": is_arc32, "ARC64": is_arc64, "CSKY": is_csky, } for a in arch: if dic.get(a, lambda: False)(): return f(*args, **kwargs) warn("This command is not supported on this architecture") return return inner_f return wrapper def exclude_specific_arch(arch=()): """Decorator wrapper to check if the architecture is specific.""" def wrapper(f): @functools.wraps(f) def inner_f(*args, **kwargs): dic = { "x86_32": is_x86_32, "x86_64": is_x86_64, "x86_16": is_x86_16, "ARM32": is_arm32, "ARM32M": is_arm32_cortex_m, "ARM64": is_arm64, "MIPS32": is_mips32, "MIPS64": is_mips64, "MIPSN32": is_mipsn32, "PPC32": is_ppc32, "PPC64": is_ppc64, "SPARC32": is_sparc32, "SPARC32PLUS": is_sparc32plus, "SPARC64": is_sparc64, "RISCV32": is_riscv32, "RISCV64": is_riscv64, "S390X": is_s390x, "SH4": is_sh4, "M68K": is_m68k, "ALPHA": is_alpha, "HPPA32": is_hppa32, "HPPA64": is_hppa64, "OR1K": is_or1k, "NIOS2": is_nios2, "MICROBLAZE": is_microblaze, "XTENSA": is_xtensa, "CRIS": is_cris, "LOONGARCH64": is_loongarch64, "ARC32": is_arc32, "ARC64": is_arc64, "CSKY": is_csky, } for a in arch: if dic.get(a, lambda: False)(): warn("This command is not supported on this architecture") return return f(*args, **kwargs) return inner_f return wrapper def timeout(duration): """Decorator to handle timeout.""" def wrapper(function): import multiprocessing queue = multiprocessing.Queue(maxsize=1) def run_function(function, *args, **kwargs): try: # len(result) must be less than 0xffe8 result = function(*args, **kwargs) except Exception as e: queue.put((False, e)) else: queue.put((True, result)) return def inner_f(*args, **kwargs): fargs = [function] + list(args) p = multiprocessing.Process(target=run_function, args=fargs, kwargs=kwargs) p.start() p.join(duration) if p.is_alive(): p.kill() raise multiprocessing.TimeoutError assert queue.full() success, result = queue.get() if success: return result else: raise result return inner_f return wrapper def to_unsigned_long(v): """Cast a gdb.Value to unsigned long.""" mask = AddressUtil.get_vmem_end_mask() return int(v.cast(gdb.Value(mask).type)) & mask # Don't use cache. # This is because there is a command that performs step execution internally. def get_register(regname, use_mbed_exec=False, use_monitor=False): """Return a register's value.""" if regname[0] in ["%", "@"]: regname = "$" + regname[1:] if regname[0] != "$": regname = "$" + regname try: value = gdb.parse_and_eval(regname) if value.type.code == gdb.TYPE_CODE_INT: return to_unsigned_long(value) elif value.type.name == "vec128": if hasattr(value, "bytes"): return u128(value.bytes) else: return eval(str(value["uint128"])) else: return int(value) except gdb.error: if (is_hppa32() or is_hppa64()) and regname == "$r0": return 0 try: value = gdb.selected_frame().read_register(regname[1:]) return int(value) except (gdb.error, ValueError): pass if use_mbed_exec and is_qemu_system() and is_arm32(): # Note that attempting to read a non-existent register will jump to an Undefined exception try: r = gdb.execute("read-system-register-for-qemu-arm {:s}".format(regname), to_string=True) if r: return int(r.split("=")[1], 16) except gdb.error: pass if use_monitor and is_qemu_system() and is_x86(): regname = regname.lstrip("$").upper() res = gdb.execute("monitor info registers", to_string=True) r = re.search(r"{:s}=(\S+)".format(regname), res) if r: return int(r.group(1), 16) if use_monitor and is_vmware() and is_x86_64(): regname = regname.lstrip("$") res = gdb.execute("monitor r {:s}".format(regname), to_string=True) r = re.search(r"{:s}=(\S+)".format(regname), res) if r: return int(r.group(1), 16) if use_mbed_exec and is_kgdb() and (is_x86_64() or is_arm64()): if ReadSystemRegisterForKgdbCommand.is_supported_reg(regname): try: r = gdb.execute("read-system-register-for-kgdb {:s}".format(regname), to_string=True) if r: return int(r.split("=")[1], 16) except gdb.error: pass return None @Cache.cache_this_session def is_remote_debug(): """GDB mode determination function for remote debugging.""" try: connection = gdb.selected_inferior().connection if connection is None: return False return connection and connection.type == "remote" except AttributeError: # before gdb 11.x: AttributeError: 'gdb.Inferior' object has no attribute 'connection' res = gdb.execute("maintenance print target-stack", to_string=True) return "remote" in res # Removed is_remote_same_host. # It can detect that gdb connects to a process in the same host. # However, it cannot detect that traffic is being redirected to another host. @Cache.cache_this_session def is_normal_run(): """GDB mode determination function for normal running.""" ret = gdb.execute("info files", to_string=True) return "Using the running image of child" in ret @Cache.cache_this_session def is_attach(): """GDB mode determination function for attaching.""" try: return gdb.selected_inferior().was_attached except AttributeError: ret = gdb.execute("info files", to_string=True) return "Using the running image of attached" in ret @Cache.cache_this_session def is_container_attach(): """GDB mode determination function for attaching another namespace.""" filename = gdb.current_progspace().filename if filename and filename.startswith("target:"): return True pid = Pid.get_pid() if pid is None: return False path = "/proc/{:d}/status".format(pid) if os.path.exists(path): content = open(path, "rb").read() r = re.search(rb"\nNSpid:\s+(\d+)\s+(\d+)", content) return bool(r) return False @Cache.cache_this_session def is_pin(): """GDB mode determination function for pin and SDE.""" if not is_remote_debug(): return False try: response = gdb.execute("maintenance packet qSupported", to_string=True, from_tty=False) except gdb.error as e: err("{}".format(e)) os._exit(0) return "intel.name=" in response @Cache.cache_this_session def is_qemu(): """GDB mode determination function for qemu-user or qemu-system.""" if not is_remote_debug(): return False try: response = gdb.execute("maintenance packet Qqemu.sstepbits", to_string=True, from_tty=False) except gdb.error as e: err("{}".format(e)) os._exit(0) return "ENABLE=" in response @Cache.cache_this_session def is_qemu_user(): """GDB mode determination function for qemu-user gdb stub.""" if is_qemu() is False: return False try: response = gdb.execute("maintenance packet qOffsets", to_string=True, from_tty=False) except gdb.error as e: err("{}".format(e)) os._exit(0) return "Text=" in response @Cache.cache_this_session def is_qemu_system(): """GDB mode determination function for qemu-system gdb stub.""" if is_qemu() is False: return False try: response = gdb.execute("maintenance packet qOffsets", to_string=True, from_tty=False) except gdb.error as e: err("{}".format(e)) os._exit(0) return 'received: ""' in response @Cache.cache_this_session def is_over_serial(): """GDB mode determination function for serial device.""" if not is_remote_debug(): return False try: dev = gdb.selected_inferior().connection.details return dev.startswith(("/dev/ttyS", "/dev/ttyAMA", "/dev/ttyUSB")) except AttributeError: # before gdb 11.x: AttributeError: 'gdb.Inferior' object has no attribute 'connection' return False @Cache.cache_this_session def is_kgdb(): """GDB mode determination function for KGDB.""" # Forcing KGDB mode is useful when KGDB is being used via agent-proxy # and thus GDB cannot see the serial device name. if Config.get_gef_setting("gef.kgdb_force") is True: return True return bool((is_x86_64() or is_arm64()) and is_over_serial()) @Cache.cache_this_session def kgdb_has_system_registers(): return Config.get_gef_setting("gef.kgdb_system_registers") is True @Cache.cache_this_session def is_kdb(): """GDB mode determination function for KDB (over KGDB).""" if not is_kgdb(): return False try: res = gdb.execute("monitor _stext", to_string=True) except gdb.error: return False r = re.search(r"_stext = 0x(\S+)", res) return bool(r) @Cache.cache_this_session def is_vmware(): """GDB mode determination function for VMware gdb stub.""" if not is_remote_debug(): return False # The `monitor help` command takes a very long time in kgdb mode. # We can speed it up by making sure we're not in kgdb mode beforehand. if is_over_serial() or is_kgdb(): return False # https://xuanxuanblingbling.github.io/ctf/tools/2021/10/22/vmware/ try: res = gdb.execute("monitor help r", to_string=True) return "Dump hidden register" in res except gdb.error: return False @Cache.cache_this_session def is_qiling(): """GDB mode determination function for qiling framework gdb stub.""" if not is_remote_debug(): return False pid = Pid.get_pid(remote=True) if pid is None or pid < 42000: return False for m in ProcessMap.get_process_maps(): if m.path == "[hook_mem]": return True return False @Cache.cache_this_session def is_rr(): """GDB mode determination function for rr.""" return Pid.get_pid_from_tcp_session(filepath="rr") is not None @Cache.cache_this_session def is_wine(): """GDB mode determination function for winedbg.""" return Pid.get_pid_from_tcp_session(filepath="wineserver") is not None @Cache.cache_until_next def is_in_kernel(): """GDB mode determination function for kernel mode.""" if not is_alive(): return False if is_arm32_cortex_m(): return False if is_qiling(): return False # If it fails to obtain the flag register required for the judgment, # it will be considered as userland. if is_x86(): cs = get_register("$cs") if cs is None: return False return (cs & 0b11) != 3 elif is_arm32(): if is_in_secure(): return False cpsr = get_register(current_arch.flag_register) if cpsr is None: return False return (cpsr & 0b11111) not in [0b10000, 0b11010] elif is_arm64(): if is_in_secure(): return False cpsr = get_register(current_arch.flag_register) if cpsr is None: return False return ((cpsr >> 2) & 0b11) == 1 elif is_riscv64() or is_riscv32(): priv = get_register("priv") if priv is None: return False return priv == 1 # All other architectures are considered userland. return False @Cache.cache_this_session def is_support_secure_world(): if not is_arm32() and not is_arm64(): return False if not is_qemu_system(): return False ret = gdb.execute("monitor info mtree -f", to_string=True) return ".secure-ram" in ret @Cache.cache_until_next def is_in_secure(): """GDB mode determination function for secure world.""" if not is_support_secure_world(): return False if is_arm32(): scr = get_register("$SCR") elif is_arm64(): scr = get_register("$SCR_EL3") # In environments without a secure world: # Older qemu versions did not have the SCR register. (return None) # Newer qemu versions always return 0. if not scr: return False return (scr & 0b1) == 0 @Cache.cache_this_session def is_kvm_enabled(): """GDB mode determination function for KVM.""" try: res = gdb.execute("monitor info kvm", to_string=True) return "enabled" in res except gdb.error: return False @Cache.cache_this_session def is_smp_enabled(): """GDB mode determination function for smp.""" try: res = gdb.execute("monitor info cpus", to_string=True) return len(res.splitlines()) >= 2 except gdb.error: return False class Pid: """A collection of utility functions that obtains a pid.""" @staticmethod def get_tcp_sess(pid): # get inode information from opened file descriptor inodes = [] for openfd in os.listdir("/proc/{:d}/fd".format(pid)): try: fdname = os.readlink("/proc/{:d}/fd/{:s}".format(pid, openfd)) except (FileNotFoundError, ProcessLookupError, OSError): continue if fdname.startswith("socket:["): inode = fdname[8:-1] inodes.append(inode) def decode(addr): ip, port = addr.split(":") import socket ip = socket.inet_ntop(socket.AF_INET, bytes.fromhex(ip)[::-1]) port = int(port, 16) return (ip, port) # get connection information sessions = [] with open("/proc/{:d}/net/tcp".format(pid)) as fd: for line in fd.readlines()[1:]: _, laddr, raddr, status, _, _, _, _, _, inode = line.split()[:10] if status != "01": # ESTABLISHED continue if inode not in inodes: continue laddr = decode(laddr) raddr = decode(raddr) sessions.append({"laddr": laddr, "raddr": raddr}) return sessions @staticmethod def get_all_process(): pids = [int(x) for x in os.listdir("/proc") if x.isdigit()] process = [] for pid in pids: try: filepath = os.readlink("/proc/{:d}/exe".format(pid)) except (FileNotFoundError, ProcessLookupError, OSError): continue process.append({"pid": pid, "filepath": os.path.basename(filepath)}) return process @staticmethod def get_pid_from_name(filepath): all_process = Pid.get_all_process() # strict matching candidate = [process for process in all_process if process["filepath"] == filepath] if len(candidate) == 1: return candidate[0]["pid"] if len(candidate) > 1: # If it cannot be uniquely identified, return None return None # relax the restrictions candidate = [process for process in all_process if process["filepath"].startswith(filepath)] if len(candidate) == 1: return candidate[0]["pid"] if len(candidate) > 1: # If it cannot be uniquely identified, return None return None # more relax the restrictions candidate = [process for process in all_process if filepath in process["filepath"]] if len(candidate) == 1: return candidate[0]["pid"] if len(candidate) > 1: # If it cannot be uniquely identified, return None return None return None @staticmethod def get_pid_from_tcp_session(filepath=None): gdb_tcp_sess = [x["raddr"] for x in Pid.get_tcp_sess(os.getpid())] if not gdb_tcp_sess: return None for process in Pid.get_all_process(): if filepath and not process["filepath"].startswith(filepath): continue for c in Pid.get_tcp_sess(process["pid"]): if c["laddr"] in gdb_tcp_sess: return process["pid"] return None @staticmethod def get_pid_wine(): ws_pid = Pid.get_pid_from_tcp_session(filepath="wineserver") if ws_pid is None: return None def get_external_pipe_inodes(pid): inodes = set() if not os.path.exists("/proc/{:d}/".format(pid)): return inodes # get inode information from opened file descriptor for openfd in os.listdir("/proc/{:d}/fd".format(pid)): try: fdname = os.readlink("/proc/{:d}/fd/{:s}".format(pid, openfd)) except (FileNotFoundError, ProcessLookupError, OSError): continue if fdname.startswith("pipe:["): inode = fdname[6:-1] if inode in inodes: inodes.remove(inode) else: inodes.add(inode) return inodes ws_inodes = get_external_pipe_inodes(ws_pid) gdb_pid = os.getpid() for candidate_pid in range(gdb_pid - 1, ws_pid, -1): candidate_inodes = get_external_pipe_inodes(candidate_pid) if candidate_inodes & ws_inodes: return candidate_pid return None @staticmethod @Cache.cache_this_session def get_pid(remote=False): """Return the PID of the debuggee process.""" if is_pin(): return Pid.get_pid_from_tcp_session() elif is_qemu_user() or is_qemu_system(): pid = Pid.get_pid_from_tcp_session("qemu") # strict way if pid is None: pid = Pid.get_pid_from_name("qemu") # ambiguous way return pid elif is_wine(): return Pid.get_pid_wine() elif remote is False and is_remote_debug(): return None # gdbserver etc. return gdb.selected_inferior().pid @staticmethod def get_tid(): ptid = gdb.selected_thread().ptid return ptid[1] or ptid[2] class Path: """A collection of utility functions that obtains a path.""" @staticmethod def append_proc_root(filepath): if filepath is None: return None pid = Pid.get_pid() if pid is None: return None if pid == 0: # under gdbserver, when target exited then pid is 0 return None prefix = "/proc/{}/root".format(pid) relative_path = filepath.lstrip("/") return os.path.join(prefix, relative_path) @staticmethod @Cache.cache_this_session def get_filepath(append_proc_root_prefix=True): """Return the local absolute path of the file currently debugged.""" filepath = gdb.current_progspace().filename if is_remote_debug(): if filepath is None: return None elif filepath.startswith("target:"): return None elif filepath.startswith(".gnu_debugdata for target:"): return None else: return filepath else: # inferior probably did not have name, extract cmdline from info proc if filepath is None: filepath = Path.get_filepath_from_info_proc() if append_proc_root_prefix: # maybe different mnt namespace, so use /proc//root filepath = Path.append_proc_root(filepath) # not remote, but different PID namespace and attaching by pid. it shows with `target:` elif filepath.startswith("target:"): # /proc/PID/root is not given when used for purposes such as comparing with entry in vmmap filepath = filepath[len("target:"):] if append_proc_root_prefix: # maybe different mnt namespace, so use /proc//root filepath = Path.append_proc_root(filepath) # normal path return filepath @staticmethod def get_filepath_from_info_proc(): try: response = gdb.execute("info proc", to_string=True) except gdb.error: return None for x in response.splitlines(): if x.startswith("exe = "): return x.split(" = ")[1].replace("'", "") return None @staticmethod @Cache.cache_this_session def get_filename(): """Return the full filename of the file currently debugged.""" filename = Path.get_filepath() if filename is None: return None return os.path.basename(filename) @staticmethod def read_remote_file(filepath, as_byte=True): tmp_name = os.path.join(GEF_TEMP_DIR, "read_remote_file.tmp") try: gdb.execute("remote get {!r} {!r}".format(filepath, tmp_name), to_string=True) except gdb.error: return "" if as_byte: data = open(tmp_name, "rb").read() else: data = open(tmp_name, "r").read() os.unlink(tmp_name) return data class ProcessMap: """A collection of utility functions that obtains a process map.""" @staticmethod @Cache.cache_until_next def get_process_maps_linux(pid, remote=False): """Parse the Linux process `/proc/pid/maps` file.""" if Config.get_gef_setting("context.disable_vmmap"): return [] # open & read maps proc_map_file = "/proc/{:d}/maps".format(pid) if remote: data = Path.read_remote_file(proc_map_file, as_byte=False) if not data: return [] lines = data.splitlines() else: if not os.path.exists(proc_map_file): return [] lines = open(proc_map_file, "r").readlines() # tls and $sp of each threads extra_info = [] if is_x86(): tls_list = [] orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() if orig_thread: # orig_thread may be None if under winedbg for thread in gdb.selected_inferior().threads(): thread.switch() # change thread # note: for speed up, do not use current_arch.get_tls() tls = get_register("$fs_base" if is_x86_64() else "$gs_base") # get tls address tls_list.append([thread.num, tls, current_arch.sp]) orig_thread.switch() # revert thread orig_frame.select() extra_info = sorted(tls_list) # When using gdbserver, thread.num may start from 2 even though there is no thread. # This is confusing, so if there is only the main thread, force it to 1. if len(extra_info) == 1: extra_info[0][0] = 1 # parse maps = [] for line in lines: line = line.replace("\t", " ") # for qiling framework line = line.strip() addr, perm, off, _, rest = line.split(" ", 4) addr_start, addr_end = [int(x, 16) for x in addr.split("-")] rest = rest.split(" ", 1) if len(rest) == 1: pathname = "" else: pathname = rest[1].lstrip() inode = int(rest[0]) for th_num, tls_addr, _ in extra_info: if tls_addr and addr_start <= tls_addr < addr_end: pathname += "".format(th_num) break for th_num, _, stack_addr in extra_info: if th_num > 1 and stack_addr and addr_start <= stack_addr < addr_end: pathname += "".format(th_num) break off = int(off, 16) perm = Permission.from_process_maps(perm) sect = Section( page_start=addr_start, page_end=addr_end, offset=off, permission=perm, inode=inode, path=pathname, ) maps.append(sect) return maps # get_explored_regions (used at qemu-user mode) is very slow, # Because it repeats read_memory many times to find the upper and lower bounds of the page. # Cache.cache_until_next is ineffective due to frequent resets (each time the `stepi` runs). # Fortunately, memory maps rarely change. # The cache is cleared and rechecked when the `vmmap` command is called explicitly. @staticmethod @Cache.cache_this_session def get_explored_regions(): """Return sections from auxv exploring.""" if Config.get_gef_setting("context.disable_vmmap"): return [] if current_arch is None: return [] def is_valid_addr_fast(addr): try: gdb.selected_inferior().read_memory(addr, 1) return True except gdb.MemoryError: return False def get_region_start_end(addr): addr &= get_pagesize_mask_high() if not is_valid_addr_fast(addr): return None, None region_start = addr region_end = addr + get_pagesize() nonlocal regions end_addrs = [r.page_end for r in regions] start_addrs = [r.page_start for r in regions] # up search lower_bound = 0 while True: if region_start <= lower_bound: break if region_start in end_addrs: break if not is_valid_addr_fast(region_start - get_pagesize()): break region_start -= get_pagesize() upper_bound = 1 << AddressUtil.get_memory_alignment(in_bits=True) # down search while True: if region_end >= upper_bound: break if region_end in start_addrs: break if not is_valid_addr_fast(region_end): break region_end += get_pagesize() return region_start, region_end def make_regions(addr, label, perm="rw-"): if addr is None: return [] # check if already in region nonlocal regions for rg in regions: if rg.page_start <= addr < rg.page_end: return [] # make region start, end = get_region_start_end(addr) if start is None: return [] perm = Permission.from_process_maps(perm) sect = Section(page_start=start, page_end=end, permission=perm, path=label) return [sect] def get_ehdr(addr): upper_bound = 1 << AddressUtil.get_memory_alignment(in_bits=True) for _ in range(128): if addr < 0 or addr > upper_bound: return None try: e_magic = read_memory(addr, 4) except gdb.MemoryError: return None if e_magic == b"\x7fELF": return Elf.get_elf(addr) addr -= get_pagesize() return None def parse_region_from_ehdr(addr, label): elf = get_ehdr(addr & get_pagesize_mask_high()) if elf is None: return [] pages = [] for phdr in elf.phdrs: if not phdr.p_memsz: continue vaddr = phdr.p_vaddr if elf.is_pie(): vaddr += elf.addr vaddr_end = vaddr + phdr.p_memsz offset = phdr.p_offset flags = phdr.p_flags # align vaddr &= get_pagesize_mask_high() offset &= get_pagesize_mask_high() vaddr_end = (vaddr_end + get_pagesize_mask_low()) & get_pagesize_mask_high() # add per pages for page_addr in range(vaddr, vaddr_end, get_pagesize()): # check already exist for i, page in enumerate(pages): if page["vaddr"] == page_addr: # found, so fix flags if page["flags"] & Elf.Phdr.PF_X: # already has PF_X flags |= Elf.Phdr.PF_X pages[i]["flags"] = flags # overwrite, because RELRO break else: # not found, so add new page page = { "vaddr": page_addr, "memsize": get_pagesize(), "flags": flags, "offset": offset + (page_addr - vaddr), } pages.append(page) pages = sorted(pages, key=lambda x: x["vaddr"]) # merge contiguous prev = pages[0] for page in pages[1:]: prev_vend = prev["vaddr"] + prev["memsize"] if prev["flags"] == page["flags"] and prev_vend == page["vaddr"]: prev["memsize"] += page["memsize"] pages.remove(page) else: prev = page # page -> section sects = [] for page in pages: perm = Permission.from_process_maps(ElfInfoCommand.pflags[page["flags"]].lower()) page_start = page["vaddr"] page_end = page["vaddr"] + page["memsize"] off = page["offset"] sect = Section( page_start=page_start, page_end=page_end, offset=off, permission=perm, path=label, ) sects.append(sect) return sects def get_linker(addr): if addr is None: return None # get interp elf = get_ehdr(addr & get_pagesize_mask_high()) phdr = elf.get_phdr(Elf.Phdr.PT_INTERP) if phdr is None: return None vaddr = phdr.p_vaddr if elf.is_pie(): vaddr += elf.addr linker = read_cstring_from_memory(vaddr) return linker def get_link_map(addr): if addr is None: return None # get dynamic elf = get_ehdr(addr & get_pagesize_mask_high()) phdr = elf.get_phdr(Elf.Phdr.PT_DYNAMIC) if phdr is None: return None vaddr = phdr.p_vaddr vaddr_end = vaddr + phdr.p_memsz if elf.is_pie(): vaddr += elf.addr vaddr_end += elf.addr # search DT_DEBUG for tag_addr in range(vaddr, vaddr_end, current_arch.ptrsize * 2): tag = read_int_from_memory(tag_addr) if tag == 21: # DT_DEBUG dt_debug = read_int_from_memory(tag_addr + current_arch.ptrsize) break else: # not found return None # get link_map try: link_map = read_int_from_memory(dt_debug + current_arch.ptrsize) except gdb.MemoryError: return None return link_map def get_filepath_wrapper(): filepath = Path.get_filepath() if filepath: return filepath filepath = gdb.current_progspace().filename if filepath and filepath.startswith("target:"): filepath = filepath[7:] return filepath def parse_region_from_link_map(link_map): current = link_map new_regions = [] while True: l_addr = read_int_from_memory(current + current_arch.ptrsize * 0) l_name = read_int_from_memory(current + current_arch.ptrsize * 1) l_next = read_int_from_memory(current + current_arch.ptrsize * 3) name = read_cstring_from_memory(l_name) if not name: name = get_filepath_wrapper() or "[code]" new_regions += parse_region_from_ehdr(l_addr, name) if l_next == 0: break current = l_next return new_regions def parse_auxv(): auxv = Auxv.get_auxiliary_values() if not auxv: return [] new_regions = [] codebase = auxv.get("AT_PHDR", None) or auxv.get("AT_ENTRY", None) # plan1: from link_map info (code, all loaded shared library) link_map = get_link_map(codebase) if link_map: new_regions += parse_region_from_link_map(link_map) # plan2: use each auxv info (for code, linker) else: # code if "AT_PHDR" in auxv: new_regions += parse_region_from_ehdr(auxv["AT_PHDR"], get_filepath_wrapper() or "[code]") elif "AT_ENTRY" in auxv: new_regions += parse_region_from_ehdr(auxv["AT_ENTRY"], get_filepath_wrapper() or "[code]") # linker if "AT_BASE" in auxv: new_regions += parse_region_from_ehdr(auxv["AT_BASE"], get_linker(codebase) or "[linker]") # vdso if "AT_SYSINFO_EHDR" in auxv: new_regions += parse_region_from_ehdr(auxv["AT_SYSINFO_EHDR"], "[vdso]") elif "AT_SYSINFO" in auxv: new_regions += parse_region_from_ehdr(auxv["AT_SYSINFO"], "[vdso]") return new_regions def parse_stack_register(): # get permission stack_permission = "rw-" # default auxv = Auxv.get_auxiliary_values() if auxv and "AT_PHDR" in auxv: elf = get_ehdr(auxv["AT_PHDR"] & get_pagesize_mask_high()) phdr = elf.get_phdr(Elf.Phdr.PT_GNU_STACK) if phdr: stack_permission = ElfInfoCommand.pflags[phdr.p_flags].lower() else: stack_permission = "rwx" # no GNU_STACK phdr means no-NX # add region return make_regions(current_arch.sp, "[stack]", stack_permission) def parse_registers_and_stack(): queue = set() # registers for regname in current_arch.all_registers: v = get_register(regname) if v is None: continue queue.add(v & get_pagesize_mask_high()) # walk value from stack top sp = current_arch.sp if sp is not None: try: data = read_memory(sp & get_pagesize_mask_high(), get_pagesize()) data = slice_unpack(data, current_arch.ptrsize) queue |= {d & get_pagesize_mask_high() for d in set(data)} except gdb.MemoryError: pass # contiguous areas are removed from the queue merged_queue = [] for addr in sorted(queue): if not is_valid_addr(addr): continue if addr - get_pagesize() in merged_queue: continue merged_queue.append(addr) # add regions new_regions = [] for addr in merged_queue: skip = False for rg in new_regions: if rg.page_start <= addr < rg.page_end: skip = True if not skip: new_regions += make_regions(addr, "") return new_regions # ---- regions = [] regions += parse_auxv() regions += parse_stack_register() # walk from known map, because qemu may maps extra regions (?) for r in regions.copy(): regions += make_regions(r.page_start - 1, "", str(r.permission)) regions += make_regions(r.page_end + 1, "", str(r.permission)) regions += parse_registers_and_stack() # ok regions = sorted(regions, key=lambda x: x.page_start) return regions @staticmethod def get_process_maps_from_info_proc(): if Config.get_gef_setting("context.disable_vmmap"): return [] res = gdb.execute("info proc mappings", to_string=True) """ process 2897541 Mapped address spaces: Start Addr End Addr Size Offset Perms objfile 0x400000 0x478000 0x78000 0x0 r-xp /tmp/a.out 0x478000 0x48c000 0x14000 0x0 ---p 0x48c000 0x492000 0x6000 0x7c000 rw-p /tmp/a.out 0x492000 0x498000 0x6000 0x0 rw-p 0x400000000000 0x400000001000 0x1000 0x0 ---p 0x400000001000 0x400000801000 0x800000 0x0 rw-p [stack] 0x400000801000 0x400000802000 0x1000 0x0 r-xp """ maps = [] for line in res.splitlines()[4:]: line = line.strip() addr_start, addr_end, size, offset, perm, *path = line.split() addr_start = int(addr_start, 16) addr_end = int(addr_end, 16) size = int(size, 16) offset = int(offset, 16) perm = Permission.from_process_maps(perm) if len(path) == 1: path = path[0] else: path = "" sect = Section( page_start=addr_start, page_end=addr_end, offset=offset, permission=perm, inode=None, path=path, ) maps.append(sect) return maps __gef_use_info_proc_mappings__ = None # the flag to use `info proc mappings` @staticmethod def get_process_maps_heuristic(): if Config.get_gef_setting("context.disable_vmmap"): return [] if ProcessMap.__gef_use_info_proc_mappings__ is None: try: res = gdb.execute("info proc mappings", to_string=True) if "warning" in res: ProcessMap.__gef_use_info_proc_mappings__ = False elif "Perms" not in res: # xtensa-linux-gdb ProcessMap.__gef_use_info_proc_mappings__ = False else: # for core files ProcessMap.__gef_use_info_proc_mappings__ = True except gdb.error: ProcessMap.__gef_use_info_proc_mappings__ = False # fast path if ProcessMap.__gef_use_info_proc_mappings__ is True: res = ProcessMap.get_process_maps_from_info_proc() # don't use cache if res: return res # something is wrong ProcessMap.__gef_use_info_proc_mappings__ = False # slow path return ProcessMap.get_explored_regions() # use cache @staticmethod @Cache.cache_until_next def get_process_maps(outer=False): """Return the mapped memory sections.""" if Config.get_gef_setting("context.disable_vmmap"): return [] if is_qemu_user(): if outer: pid = Pid.get_pid() if pid: return ProcessMap.get_process_maps_linux(pid) return [] else: # scan heuristic return ProcessMap.get_process_maps_heuristic() elif is_pin(): pid = Pid.get_pid() if pid: return ProcessMap.get_process_maps_linux(pid) elif is_qemu_system(): return [] elif is_wine(): # Wine loads the EXE at Wine's own virtual address space. # For typical pwner use cases, Wine itself and its libraries should also be displayed. # However, Wine's `monitor mem` does not show them, so this approach is not used. # Resolving Wine's PID and retrieving its memory maps is more reliable. pid = Pid.get_pid() if pid: return ProcessMap.get_process_maps_linux(pid) return [] elif is_remote_debug(): remote_pid = Pid.get_pid(remote=True) if remote_pid: return ProcessMap.get_process_maps_linux(remote_pid, remote=True) elif is_rr(): return ProcessMap.get_process_maps_from_info_proc() else: # normal pattern pid = Pid.get_pid() if pid: r = ProcessMap.get_process_maps_linux(pid) if r: return r return ProcessMap.get_process_maps_heuristic() @staticmethod @Cache.cache_until_next def get_process_maps_exclude_special_regions(outer=False, allow_vdso=False, allow_vsyscall=False): """Return the mapped memory sections, exclude [vvar], [vvar_vclock], [vdso], [vsyscall], [sigpage], etc.""" vmmap = ProcessMap.get_process_maps(outer) if vmmap == []: return vmmap valid_maps = [] for m in vmmap: if "[vvar]" in m.path: continue if "[vvar_vclock]" in m.path: continue if "[vdso]" in m.path: if not allow_vdso: continue if "[vsyscall]" in m.path: if not allow_vsyscall: continue if "[sigpage]" in m.path: # ARM continue if "[vectors]" in m.path: # ARM continue valid_maps.append(m) return valid_maps @staticmethod @Cache.cache_until_next def get_loaded_files(): files = set() for m in ProcessMap.get_process_maps(): if not m.path: continue if m.path.startswith(("<", "[")): continue if not os.path.exists(m.path): continue files.add(m.path) return files # `info files` called from get_info_files is heavy processing. # Moreover, AddressUtil.recursive_dereference causes each address to be resolved every time. # Cache.cache_until_next is ineffective due to frequent resets (each time the `stepi` runs). # Fortunately, zone information rarely changes. # The cache is retained until explicitly cleared. @staticmethod @Cache.cache_this_session def get_info_files(): """Retrieve all the files loaded by debuggee.""" lines = gdb.execute("info files", to_string=True).splitlines() info_files = [] seen = [] for line in lines: line = line.strip() if not line: break if not line.startswith("0x"): continue if line in seen: continue seen.append(line) blobs = [x.strip() for x in line.split(" ")] addr_start = int(blobs[0], 16) addr_end = int(blobs[2], 16) if len(blobs) > 4: section_name = blobs[4] else: section_name = "" if "system-supplied DSO" in line: filepath = "[vdso]" elif len(blobs) == 7: filepath = blobs[6] else: filepath = Path.get_filepath(append_proc_root_prefix=False) Zone = collections.namedtuple("Zone", ["name", "zone_start", "zone_end", "filename"]) info = Zone(section_name, addr_start, addr_end, filepath) info_files.append(info) return info_files @staticmethod @Cache.cache_until_next def process_lookup_address(addr): """Look up for an address in memory. Return an Address object if found, None otherwise.""" if not is_alive(): err("Process is not running") return None if is_qemu_system() or is_vmware() or is_kgdb(): return None for sect in ProcessMap.get_process_maps(): if sect.page_start <= addr < sect.page_end: return sect return None @staticmethod @Cache.cache_until_next def process_lookup_path(names, perm_mask=Permission.ALL): """Look up for paths in the process memory mapping. Return a Section object of the load base address if found, None otherwise.""" if not is_alive(): err("Process is not running") return None if isinstance(names, str): names = (names,) # make tuple to iterate for sect in ProcessMap.get_process_maps(): for name in names: if name in sect.path and sect.permission.value & perm_mask: return sect return None @staticmethod @Cache.cache_until_next def file_lookup_address(addr): """Look up for a file by its address. Return a Zone object if found, None otherwise.""" if is_qemu_system() or is_vmware() or is_kgdb(): # If FGKASLR is enabled, there are too many sections and it will take a long time, so skip them. return None for info in ProcessMap.get_info_files(): if info.zone_start <= addr < info.zone_end: return info return None @staticmethod @Cache.cache_until_next def lookup_address(addr): """Try to find the address in the process address space. Return an Address object with caching.""" return Address(addr) @staticmethod @Cache.cache_until_next def get_section_base_address(name): if name is None: return None section = ProcessMap.process_lookup_path(name) if section: return section.page_start # Fail, retry with real path section = ProcessMap.process_lookup_path(os.path.realpath(name)) if section: return section.page_start return None @staticmethod def get_section_base_address_by_list(names): for name in names: page_start = ProcessMap.get_section_base_address(name) if page_start is not None: return page_start return None @staticmethod @Cache.cache_this_session def get_codebase(): filepath = Path.get_filepath(append_proc_root_prefix=is_container_attach()) bin_base = ProcessMap.get_section_base_address(filepath) if bin_base is not None: return bin_base filepath = Path.get_filepath_from_info_proc() bin_base = ProcessMap.get_section_base_address(filepath) return bin_base class EventHandler: """A collection of handler functions that are called when the specified events occur.""" @staticmethod def continue_handler(_event): """GDB event handler for new object continue cases.""" return __gef_check_once__ = True # the flag to process only once at startup __gef_check_disabled_bp__ = False # the flag to remove unnecessary breakpoints @staticmethod def hook_stop_handler(event): """GDB event handler for stop cases.""" Cache.reset_gef_caches() # There appears to be a bug on some architectures (e.g., i386) where temporary breakpoints # are not deleted even after being hit. The conditions under which this occurs are unknown, # so any remaining breakpoints are removed manually by GEF. if EventHandler.__gef_check_disabled_bp__: for bp in gdb.breakpoints(): if not bp.visible and bp.temporary: if not bp.enabled: bp.delete() EventHandler.__gef_check_disabled_bp__ = False # when kgdb, assume x86-64 if /dev/ttyS if EventHandler.__gef_check_once__: if is_over_serial(): dev = gdb.selected_inferior().connection.details if dev.startswith("/dev/ttyS"): gdb.execute("set architecture i386:x86-64:intel", to_string=True) else: # In the case of /dev/ttyAMA, it is not clear whether it is ARM64 or ARM32. # In some cases, /dev/ttyUSB is used. pass Cache.reset_gef_caches() # GEF will resolve the architecture if it is unknown. if current_arch is None: set_arch(get_arch()) # set `c`, `ni` and `si` command hooks for qemu-user and pin if EventHandler.__gef_check_once__: if is_qemu_user() or is_pin(): gdb.execute("define c\ncontinue-for-qemu-user\nend") if is_or1k() or is_cris(): gdb.execute("define si\nstepi-for-qemu-user\nend") gdb.execute("define ni\nnexti-for-qemu-user\nend") # disable for cortex-m if EventHandler.__gef_check_once__: if is_arm32_cortex_m(): gdb.execute("gef config context.disable_vmmap True") gdb.execute("gef config context.disable_auxv True") # If the silent command is specified for a breakpoint, skip `context` command. context_flag = True if isinstance(event, gdb.BreakpointEvent): if event.breakpoint.is_valid() and event.breakpoint.enabled: if event.breakpoint.commands: if event.breakpoint.commands.startswith("silent"): context_flag = False if context_flag: gdb.execute("context") # Message if file is not loaded. if EventHandler.__gef_check_once__: if not (is_qemu_system() or is_kgdb() or is_vmware()): if not gdb.current_progspace().filename: err("Missing info about architecture, please set: `file /path/to/target_binary`") err("If the architecture isn't automatically detected, use: `set architecture YOUR_ARCH`") EventHandler.__gef_check_once__ = False return @staticmethod def new_objfile_handler(_event): """GDB event handler for new object file cases.""" Cache.reset_gef_caches(all=True) if current_arch is None: set_arch(get_arch()) # delayed breakpoint for brva if BreakRelativeVirtualAddressCommand.delayed_bp_set is False and is_alive(): if not (is_qemu_system() or is_kgdb() or is_vmware()): codebase = ProcessMap.get_codebase() if codebase: for offset in BreakRelativeVirtualAddressCommand.delayed_breakpoints: gdb.execute("b *{:#x}".format(codebase + offset)) BreakRelativeVirtualAddressCommand.delayed_bp_set = True return @staticmethod def exit_handler(_event): """GDB event handler for exit cases.""" Cache.reset_gef_caches(all=True) return @staticmethod def memchanged_handler(_event): """GDB event handler for mem changes cases.""" Cache.reset_gef_caches() return @staticmethod def regchanged_handler(_event): """GDB event handler for reg changes cases.""" Cache.reset_gef_caches() return class UnicornKeystoneCapstone: """A collection of utility functions that are related to unicorn, keystone, and capstone.""" @staticmethod def get_generic_arch(module, prefix, arch, mode, big_endian, to_string): """Retrieve architecture and mode from the arguments for use for the holy capstone/keystone/unicorn trinity.""" if isinstance(mode, tuple): modes = list(mode) else: modes = [mode] if big_endian: modes.append("BIG_ENDIAN") else: modes.append("LITTLE_ENDIAN") if to_string: # arch arch = "{:s}.{:s}_ARCH_{:s}".format(module.__name__, prefix, arch) # mode tmp = [] for m in modes: if not m: tmp.append("0") else: tmp.append("{:s}.{:s}_MODE_{:s}".format(module.__name__, prefix, m)) mode = " + ".join(tmp) else: # arch arch = getattr(module, "{:s}_ARCH_{:s}".format(prefix, arch)) # mode mode = 0 for m in modes: if m: mode |= getattr(module, "{:s}_MODE_{:s}".format(prefix, m)) return arch, mode @staticmethod @ModuleLoader.load_unicorn def get_unicorn_arch(arch=None, mode=None, endian=None, to_string=False): if (arch, mode, endian) == (None, None, None): arch = current_arch.arch mode = current_arch.mode endian = Endian.is_big_endian() if arch is None: arch = current_arch.arch if (arch, mode) == ("RISCV", "32"): mode = "RISCV32" elif (arch, mode) == ("RISCV", "64"): mode = "RISCV64" elif (arch, mode) == ("PPC", "32"): mode = "PPC32" elif (arch, mode) == ("PPC", "64"): mode = "PPC64" elif (arch, mode) == ("SPARC", "32"): mode = "SPARC32" elif (arch, mode) == ("SPARC", "32PLUS"): mode = "SPARC32" elif (arch, mode) == ("SPARC", "64"): mode = "SPARC64" elif (arch, mode) == ("MIPS", "32"): mode = "MIPS32" elif (arch, mode) == ("MIPS", "64"): mode = "MIPS64" elif arch == "S390X": mode = None elif arch == "M68K": mode = None return UnicornKeystoneCapstone.get_generic_arch( sys.modules["unicorn"], "UC", arch, mode, endian, to_string, ) @staticmethod @ModuleLoader.load_capstone def get_capstone_arch(arch=None, mode=None, endian=None, to_string=False): if (arch, mode, endian) == (None, None, None): arch = current_arch.arch mode = current_arch.mode endian = Endian.is_big_endian() if arch is None: arch = current_arch.arch # hacky patch for applying to capstone's mode if arch == "ARM64": if sys.modules["capstone"].cs_version()[0] == 6: arch = "AARCH64" elif (arch, mode) == ("RISCV", "32"): mode = ("RISCV32", "RISCVC") elif (arch, mode) == ("RISCV", "64"): mode = ("RISCV64", "RISCVC") elif (arch, mode) == ("SPARC", "32"): mode = "" elif (arch, mode) == ("SPARC", "32PLUS"): mode = "" elif (arch, mode) == ("SPARC", "64"): mode = "V9" elif (arch, mode) == ("MIPS", "32"): mode = "MIPS32" elif (arch, mode) == ("MIPS", "64"): mode = "MIPS64" elif arch == "S390X": if sys.modules["capstone"].cs_version()[0] == 6: arch, mode = "SYSTEMZ", None else: arch, mode = "SYSZ", None elif arch == "M68K": mode = "M68K_060" elif (arch, mode) == ("LOONGARCH", "64"): # capstone v6.x~ mode = "LOONGARCH64" elif (arch, mode) == ("LOONGARCH", "32"): # capstone v6.x~ mode = "LOONGARCH32" elif arch == "ALPHA": # capstone v6.x~ mode = None elif (arch, mode) == ("HPPA", "64"): # capstone v6.x~ mode = "HPPA_20" elif (arch, mode) == ("HPPA", "32"): # capstone v6.x~ mode = "HPPA_11" return UnicornKeystoneCapstone.get_generic_arch( sys.modules["capstone"], "CS", arch, mode, endian, to_string, ) @staticmethod @ModuleLoader.load_keystone def get_keystone_arch(arch=None, mode=None, endian=None, to_string=False): if (arch, mode, endian) == (None, None, None): arch = current_arch.arch mode = current_arch.mode endian = Endian.is_big_endian() if arch is None: arch = current_arch.arch # hacky patch for applying to capstone's mode if arch == "ARM64": mode = None elif (arch, mode) == ("PPC", "32"): mode = "PPC32" elif (arch, mode) == ("PPC", "64"): mode = "PPC64" elif (arch, mode) == ("SPARC", "32"): mode = "SPARC32" elif (arch, mode) == ("SPARC", "32PLUS"): mode = "SPARC32" elif (arch, mode) == ("SPARC", "64"): mode = "SPARC64" elif (arch, mode) == ("MIPS", "32"): mode = "MIPS32" elif (arch, mode) == ("MIPS", "64"): mode = "MIPS64" elif arch == "S390X": arch, mode = "SYSTEMZ", None return UnicornKeystoneCapstone.get_generic_arch( sys.modules["keystone"], "KS", arch, mode, endian, to_string, ) @staticmethod @ModuleLoader.load_unicorn def get_unicorn_registers(to_string=False, add_sse=False): "Return a dict matching the Unicorn identifier for a specific register." unicorn = sys.modules["unicorn"] regs = {} if current_arch is not None: arch = current_arch.arch.lower() else: raise OSError("Oops") const = getattr(unicorn, "{}_const".format(arch)) extra_regs = [] if add_sse: if is_x86(): extra_regs = ["$xmm{:d}".format(i) for i in range(16)] if is_arm64(): extra_regs = ["$tpidr_el0"] # for tls if is_arm32(): extra_regs = ["$c13_c0_3"] # for tls for reg in current_arch.all_registers + extra_regs: if arch == "ppc" and reg.startswith("$r"): regname = "UC_{:s}_REG_{:s}".format(arch.upper(), reg.lstrip("$r").upper()) elif arch == "arm64" and reg == "$cpsr": regname = "UC_ARM64_REG_PSTATE" else: regname = "UC_{:s}_REG_{:s}".format(arch.upper(), reg.lstrip("$").upper()) try: getattr(const, regname) except AttributeError: continue if to_string: regs[reg] = "{:s}.{:s}".format(const.__name__, regname) else: regs[reg] = getattr(const, regname) return regs @staticmethod @ModuleLoader.load_keystone def keystone_assemble(code, arch, mode, *args, **kwargs): """Assembly encoding function based on keystone.""" import multiprocessing keystone = sys.modules["keystone"] code = String.str2bytes(code) addr = kwargs.get("addr", 0x1000) # `asm "[]"` returns no response @timeout(duration=1) def ks_asm(code, addr): return ks.asm(code, addr) try: ks = keystone.Ks(arch, mode) enc, cnt = ks_asm(code, addr) except keystone.KsError as e: err("Keystone assembler error: {!s}".format(e)) return None except multiprocessing.TimeoutError: err("Keystone assembler timeout error") return None if cnt == 0: return "" enc = bytearray(enc) if "raw" not in kwargs: s = binascii.hexlify(enc) enc = b"\\x" + b"\\x".join([s[i : i + 2] for i in range(0, len(s), 2)]) enc = enc.decode("utf-8") return enc def is_64bit(): """GDB mode determination function for 64-bit architecture.""" return AddressUtil.ptr_width() == 8 def is_32bit(): """GDB mode determination function for 32-bit architecture.""" return AddressUtil.ptr_width() == 4 def is_emulated32(): """GDB mode determination function for 32-bit architecture on 64-bit environment.""" if is_64bit(): return False if is_qemu_user(): # This case cannot be determined return False if is_qemu_system(): # corner case (e.g., using qemu-system-x86_64, but process is executed as 32bit mode) # is not able to be detected return True for m in ProcessMap.get_process_maps(): # native x86: # 0xbffdf000 0xc0000000 0x021000 0x000000 rw- [stack] # emulated x86 on x86_64 # 0xfffdd000 0xffffe000 0x021000 0x000000 rw- [stack] # native arm: # 0xbefdf000 0xbf000000 0x021000 0x000000 rw- [stack] # emulated arm on aarch64 # 0xfffcf000 0xffff0000 0x021000 0x000000 rw- [stack] if m.path == "[stack]": return (m.page_start >> 28) == 0xf else: return False # by default it considers on native def is_x86_64(): """Architecture determination function for x86-64.""" return current_arch and current_arch.arch == "X86" and current_arch.mode == "64" def is_x86_32(): """Architecture determination function for x86-32.""" return current_arch and current_arch.arch == "X86" and current_arch.mode == "32" def is_x86_16(): """Architecture determination function for x86-16.""" return current_arch and current_arch.arch == "X86" and current_arch.mode == "16" def is_x86(): """Architecture determination function for x86-32 or x86-64 or x86_16.""" return is_x86_32() or is_x86_64() or is_x86_16() def is_arm32(): """Architecture determination function for ARM 32 bit (Cortex-A).""" return current_arch and current_arch.arch == "ARM" and not current_arch.is_cortex_m() def is_arm32_cortex_m(): """Architecture determination function for ARM 32 bit (Cortex-M).""" return current_arch and current_arch.arch == "ARM" and current_arch.is_cortex_m() def is_arm64(): """Architecture determination function for ARM 64 bit.""" return current_arch and current_arch.arch == "ARM64" def is_mips32(): """Architecture determination function for mips 32 bit (o32 ABI).""" return current_arch and current_arch.arch == "MIPS" and current_arch.mode == "32" def is_mips64(): """Architecture determination function for mips 64 bit.""" return current_arch and current_arch.arch == "MIPS" and current_arch.mode == "64" def is_mipsn32(): """Architecture determination function for mips 32 bit (n32 ABI).""" return current_arch and current_arch.arch == "MIPS" and current_arch.mode == "n32" def is_ppc32(): """Architecture determination function for powerpc 32 bit.""" return current_arch and current_arch.arch == "PPC" and current_arch.mode == "32" def is_ppc64(): """Architecture determination function for powerpc 64 bit.""" return current_arch and current_arch.arch == "PPC" and current_arch.mode == "64" def is_sparc32(): """Architecture determination function for sparc 32 bit.""" return current_arch and current_arch.arch == "SPARC" and current_arch.mode == "32" def is_sparc32plus(): """Architecture determination function for sparc 32 bit (v8+).""" return current_arch and current_arch.arch == "SPARC" and current_arch.mode == "32PLUS" def is_sparc64(): """Architecture determination function for sparc 64 bit.""" return current_arch and current_arch.arch == "SPARC" and current_arch.mode == "64" def is_riscv32(): """Architecture determination function for RISC-V 32 bit.""" return current_arch and current_arch.arch == "RISCV" and current_arch.mode == "32" def is_riscv64(): """Architecture determination function for RISC-V 64 bit.""" return current_arch and current_arch.arch == "RISCV" and current_arch.mode == "64" def is_s390x(): """Architecture determination function for s390x.""" return current_arch and current_arch.arch == "S390X" def is_sh4(): """Architecture determination function for sh4.""" return current_arch and current_arch.arch == "SH4" def is_m68k(): """Architecture determination function for m68k.""" return current_arch and current_arch.arch == "M68K" def is_alpha(): """Architecture determination function for alpha.""" return current_arch and current_arch.arch == "ALPHA" def is_hppa32(): """Architecture determination function for HP-PA 32 bit.""" return current_arch and current_arch.arch == "HPPA" and current_arch.mode == "32" def is_hppa64(): """Architecture determination function for HP-PA 64 bit.""" return current_arch and current_arch.arch == "HPPA" and current_arch.mode == "64" def is_or1k(): """Architecture determination function for OpenRISC 1000.""" return current_arch and current_arch.arch == "OR1K" def is_nios2(): """Architecture determination function for Nios II.""" return current_arch and current_arch.arch == "NIOS2" def is_microblaze(): """Architecture determination function for Microblaze.""" return current_arch and current_arch.arch == "MICROBLAZE" def is_xtensa(): """Architecture determination function for Xtensa.""" return current_arch and current_arch.arch == "XTENSA" def is_cris(): """Architecture determination function for CRIS.""" return current_arch and current_arch.arch == "CRIS" def is_loongarch64(): """Architecture determination function for Loongarch 64 bit.""" return current_arch and current_arch.arch == "LOONGARCH" and current_arch.mode == "64" def is_arc32(): """Architecture determination function for ARC 32 bit.""" return current_arch and current_arch.arch == "ARC" and current_arch.mode in ["32v2", "32v3"] def is_arc64(): """Architecture determination function for ARC 64 bit.""" return current_arch and current_arch.arch == "ARC" and current_arch.mode == "64v3" def is_csky(): """Architecture determination function for csky.""" return current_arch and current_arch.arch == "CSKY" @Cache.cache_until_next def get_arch(): """Return the binary's architecture.""" if is_alive(): try: arch = gdb.selected_frame().architecture() name = arch.name() # check i386 or i8086 if name != "i386": return name except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). # Resolve by moving to the slow path. pass # slow path arch_str = gdb.execute("show architecture", to_string=True).strip() # The target architecture is set automatically (currently i386) # The target architecture is set to "auto" (currently "i386"). # The target architecture is assumed to be mips # The target architecture is set to "mips". if "The target architecture is set automatically (currently " in arch_str: arch_str = arch_str.split("(currently ", 1)[1] arch_str = arch_str.split(")", 1)[0] elif 'The target architecture is set to "auto" (currently "' in arch_str: arch_str = arch_str.split('(currently "', 1)[1] arch_str = arch_str.split('")', 1)[0] elif "The target architecture is assumed to be " in arch_str: arch_str = arch_str.replace("The target architecture is assumed to be ", "") elif "The target architecture is set to " in arch_str: arch_str = arch_str.split('"')[1] else: # Unknown, we throw an exception to be safe raise RuntimeError("Unknown architecture: {}".format(arch_str)) return arch_str def set_arch(arch_str=None): """Set the current architecture. If an arch is explicitly specified, use that one, otherwise try to parse it out of the current target. If that fails, and default is specified, select and set that arch. Return the selected arch, or raise an OSError.""" global current_arch # get defined arch arches = {} queue = Architecture.__subclasses__() while queue: cls = queue.pop(0) for lc in cls.load_condition: arches[lc] = cls queue.extend(cls.__subclasses__()) # Determined from the specified arch string if arch_str: key = arch_str.upper() else: # Determined from loaded ELF elf = Elf.get_elf() if elf is None or not elf.is_valid(): raise OSError("Could not determine architecture.") if elf.e_machine not in [Elf.EM_MIPS, Elf.EM_RISCV, Elf.EM_PARISC]: key = elf.e_machine else: # On some architectures, it is not possible to determine whether it is 32-bit or 64-bit # from the ELF header e_machine. so we use the detection result of gdb. key = get_arch().upper() # Even if it is determined to be MIPS64, if it is in 32-bit mode, it is n32. if key in MIPS64.load_condition and is_32bit(): key = "MIPSN32" if key not in arches: err("Specified arch {!s} is not supported".format(key)) info("A generic mode with minimal functionality will be applied") return current_arch = arches[key]() Cache.reset_gef_caches(all=True) return class Auxv: """A collection of utility functions that are related to ELF Auxiliary Vectors.""" @staticmethod def get_auxiliary_walk(offset=0): """Find AUXV by walking stack.""" if Config.get_gef_setting("context.disable_auxv"): return None if current_arch.sp is None: return None if is_in_kernel(): return None # do not use get_pagesize(), get_pagesize_mask_high(), etc. # because get_pagesize() -> Auxv.get_auxiliary_values() -> Auxv.get_auxiliary_walk() page_size = 0x1000 addr = current_arch.sp & ~(page_size - 1) # check readable or not if not is_valid_addr(addr): return None # find stack bottom try: while True: if b"\x7fELF" == read_memory(addr, 4): break addr += page_size except gdb.MemoryError: # if read error, that is stack bottom pass current = addr - current_arch.ptrsize * 2 - offset # check readable or not again if not is_valid_addr(current): # something is wrong, maybe stack is pivoted return None # find auxv end while True: a = read_int_from_memory(current) b = read_int_from_memory(current + current_arch.ptrsize) if a == b == 0: break current -= current_arch.ptrsize * 2 # skip dummy null if exist for _ in range(1024): a = read_int_from_memory(current) if a == 7: # AT_BASE break current -= current_arch.ptrsize * 2 else: return None # find auxv start auxv_keys = AuxvCommand.AT_CONSTANTS.keys() while read_int_from_memory(current) in auxv_keys: current -= current_arch.ptrsize * 2 current += current_arch.ptrsize * 2 # parse auxv res = {} while True: key = read_int_from_memory(current) val = read_int_from_memory(current + current_arch.ptrsize) if key not in AuxvCommand.AT_CONSTANTS: break res[AuxvCommand.AT_CONSTANTS[key]] = val if key == 0: break current += current_arch.ptrsize * 2 # test if "AT_ENTRY" not in res: return None if "AT_PHDR" not in res: return None if "AT_RANDOM" not in res: return None if "AT_BASE" not in res: return None if "AT_NULL" not in res: return None return res # Auxv.get_auxiliary_values (under qemu-user mode) is very slow, # Because it may call Auxv.get_auxiliary_walk that repeats read_memory many times to find the auxv value. # Cache.cache_until_next is ineffective due to frequent resets (each time the `stepi` runs). # Fortunately, auxv rarely changes. # The cache is retained until explicitly cleared. @staticmethod @Cache.cache_this_session def get_auxiliary_values(force_heuristic=False): """Retrieve the auxiliary values of the current execution. Return None if not running, or a dict() of values.""" if Config.get_gef_setting("context.disable_auxv"): return None if not is_alive(): return None if is_in_kernel(): return None if is_qemu_system() or is_kgdb() or is_vmware() or is_wine(): return None def fast_path(): try: result = gdb.execute("info auxv", to_string=True) except gdb.error: return None res = {} for line in result.splitlines(): tmp = line.split() auxv_type = tmp[1] if auxv_type in ("AT_PLATFORM", "AT_EXECFN", "AT_BASE_PLATFORM"): m = re.match("^.+?(0x[0-9a-f]+)", line) res[auxv_type] = int(m.group(1), 0) else: res[auxv_type] = int(tmp[-1], 0) return res def slow_path(): if current_arch is None: return None for offset in [0, current_arch.ptrsize]: res = Auxv.get_auxiliary_walk(offset) if res: return res return None # ---- if force_heuristic: return slow_path() return fast_path() or slow_path() @Cache.cache_this_session def get_pagesize(): """Get the page size from auxiliary values.""" auxval = Auxv.get_auxiliary_values() if not auxval or "AT_PAGESZ" not in auxval: return 0x1000 return auxval["AT_PAGESZ"] @Cache.cache_this_session def get_pagesize_mask_low(): """Get the page size mask from auxiliary values.""" auxval = Auxv.get_auxiliary_values() if not auxval or "AT_PAGESZ" not in auxval: return 0xfff return auxval["AT_PAGESZ"] - 1 @Cache.cache_this_session def get_pagesize_mask_high(): """Get the page size mask from auxiliary values.""" auxval = Auxv.get_auxiliary_values() if not auxval or "AT_PAGESZ" not in auxval: return ~0xfff return ~(auxval["AT_PAGESZ"] - 1) def only_if_events_supported(event_type): """Decorator for checking if GDB supports events without crashing.""" def wrap(f): def wrapped_f(*args, **kwargs): if hasattr(gdb.events, event_type): return f(*args, **kwargs) warn("GDB events cannot be set") return wrapped_f return wrap class EventHooking: """A collection of utility functions that hook up specified events.""" @staticmethod @only_if_events_supported("cont") def gef_on_continue_hook(func): return gdb.events.cont.connect(func) @staticmethod @only_if_events_supported("cont") def gef_on_continue_unhook(func): return gdb.events.cont.disconnect(func) @staticmethod @only_if_events_supported("stop") def gef_on_stop_hook(func): return gdb.events.stop.connect(func) @staticmethod @only_if_events_supported("stop") def gef_on_stop_unhook(func): return gdb.events.stop.disconnect(func) @staticmethod @only_if_events_supported("exited") def gef_on_exit_hook(func): return gdb.events.exited.connect(func) @staticmethod @only_if_events_supported("exited") def gef_on_exit_unhook(func): return gdb.events.exited.disconnect(func) @staticmethod @only_if_events_supported("new_objfile") def gef_on_new_hook(func): return gdb.events.new_objfile.connect(func) @staticmethod @only_if_events_supported("new_objfile") def gef_on_new_unhook(func): return gdb.events.new_objfile.disconnect(func) @staticmethod @only_if_events_supported("memory_changed") def gef_on_memchanged_hook(func): return gdb.events.memory_changed.connect(func) @staticmethod @only_if_events_supported("memory_changed") def gef_on_memchanged_unhook(func): return gdb.events.memory_changed.disconnect(func) @staticmethod @only_if_events_supported("register_changed") def gef_on_regchanged_hook(func): return gdb.events.register_changed.connect(func) @staticmethod @only_if_events_supported("register_changed") def gef_on_regchanged_unhook(func): return gdb.events.register_changed.disconnect(func) # # Commands # def register_command(cls): """Decorator for registering new GEF (sub-)command to GDB.""" global __gef_commands__ __gef_commands__.append(cls) return cls def register_priority_command(cls): """Decorator for registering new command with priority, meaning that it must loaded before the other generic commands.""" global __gef_commands__ __gef_commands__.insert(0, cls) return cls class GenericCommand(gdb.Command): """This is an abstract class for invoking commands, should not be instantiated.""" __metaclass__ = abc.ABCMeta @property @abc.abstractmethod def _cmdline_(self): pass @property @abc.abstractmethod def _syntax_(self): pass @property @abc.abstractmethod def _example_(self): pass @property @abc.abstractmethod def _note_(self): pass @property @abc.abstractmethod def _repeat_(self): pass @property @abc.abstractmethod def _aliases_(self): pass @abc.abstractmethod def do_invoke(self, argv): pass def __init__(self, *args, **kwargs): def tab(lines): return "\n".join([" " + line for line in lines.splitlines()]) self.__doc__ += "\n" if self._syntax_: self.__doc__ += "\n" self.__doc__ += Color.colorify("Syntax:", "bold yellow") self.__doc__ += "\n" self.__doc__ += self._syntax_.strip() self.__doc__ += "\n" if self._example_: self.__doc__ += "\n" self.__doc__ += Color.colorify("Example:", "bold yellow") self.__doc__ += "\n" self.__doc__ += tab(self._example_.strip()) self.__doc__ += "\n" if self._note_: self.__doc__ += "\n" self.__doc__ += Color.colorify("Note:", "bold yellow") self.__doc__ += "\n" self.__doc__ += tab(self._note_.strip()) self.__doc__ += "\n" if hasattr(self._aliases_, "__iter__") and self._aliases_: self.__doc__ += "\n" self.__doc__ += Color.colorify("Aliases:", "bold yellow") self.__doc__ += "\n" self.__doc__ += tab(str(self._aliases_)) self.__doc__ += "\n" self.repeat_count = 0 self.last_command = None command_type = kwargs.get("command", gdb.COMMAND_NONE) complete_type = kwargs.get("complete", gdb.COMPLETE_NONE) prefix = kwargs.get("prefix", False) if complete_type == "use_user_complete": super().__init__(self._cmdline_, command_type, prefix=prefix) else: super().__init__(self._cmdline_, command_type, complete_type, prefix) return def invoke(self, args, from_tty): # noqa try: argv = gdb.string_to_argv(args) if self._repeat_: self.set_repeat_count(argv, from_tty) else: self.dont_repeat() self.do_invoke(argv) except Exception: # Since we are intercepting cleaning exceptions here, commands preferably should avoid # catching generic Exception, but rather specific ones. This is allows a much cleaner use. GefUtil.show_last_exception() return def usage(self, simple=False, after_syntax_only=False): def tab(lines): return "\n".join([" " + line for line in lines.splitlines()]) if not after_syntax_only: gef_print(Color.colorify("Syntax:", "bold yellow")) gef_print(self._syntax_.strip()) if self._example_: gef_print("") gef_print(Color.colorify("Example:", "bold yellow")) gef_print(tab(self._example_.strip())) if self._note_: if not simple: gef_print("") gef_print(Color.colorify("Note:", "bold yellow")) gef_print(tab(self._note_.strip())) if self._aliases_: gef_print("") gef_print(Color.colorify("Aliases:", "bold yellow")) gef_print(" " + str(self._aliases_)) this_command_key = self._cmdline_.replace("-", "_").replace(" ", "_").split() configs = [k for k in Config.__gef_config__.keys() if k.split(".")[:-1] == this_command_key] if configs: gef_print("") gef_print(Color.colorify("Configs:", "bold yellow")) max_width = max(len(x) for x in configs) for key in configs: value, _types, desc = Config.__gef_config__[key] gef_print(" {:{:d}} : {:s} [{}]".format(key, max_width, desc, value)) return def add_setting(self, name, value, description=""): # make sure settings are always associated to the root command # which derives from GenericCommand directly. if "GenericCommand" not in [x.__name__ for x in self.__class__.__bases__]: return # sanitize class_name = self.__class__._cmdline_ class_name = class_name.replace(" ", "_") # gdb's user complete feature does not work well if "-" is included. class_name = class_name.replace("-", "_") # add key = "{:s}.{:s}".format(class_name, name) Config.__gef_config__[key] = [value, type(value), description] Config.__gef_config_orig__[key] = [value, type(value), description] # for debugging # reset cache Cache.reset_gef_caches() return def set_repeat_count(self, argv, from_tty): if not from_tty: self.repeat_count = 0 return command = gdb.execute("show commands", to_string=True).strip().split("\n")[-1] self.repeat_count = self.repeat_count + 1 if self.last_command == command else 0 self.last_command = command return def quiet_print(self, msg): if not hasattr(self.args, "quiet") or not self.args.quiet: gef_print(msg) return def quiet_ok(self, msg): if not hasattr(self.args, "quiet") or not self.args.quiet: ok(msg) return def quiet_info(self, msg): if not hasattr(self.args, "quiet") or not self.args.quiet: info(msg) return def quiet_warn(self, msg): if not hasattr(self.args, "quiet") or not self.args.quiet: warn(msg) return def quiet_err(self, msg): if not hasattr(self.args, "quiet") or not self.args.quiet: err(msg) return def verbose_info(self, msg): if hasattr(self.args, "verbose") and self.args.verbose: info(msg) return def verbose_err(self, msg): if hasattr(self.args, "verbose") and self.args.verbose: err(msg) return class BufferingOutput: """A collection of utility functions that append a messages to self.out.""" def info_add_out(self, msg): msg = "{} {}".format(Color.colorify("[+]", "bold blue"), msg) self.out.append(msg) return def warn_add_out(self, msg): msg = "{} {}".format(Color.colorify("[*]", "bold yellow"), msg) self.out.append(msg) return def err_add_out(self, msg): msg = "{} {}".format(Color.colorify("[!]", "bold red"), msg) self.out.append(msg) return def quiet_info_add_out(self, msg): if not hasattr(self.args, "quiet") or not self.args.quiet: msg = "{} {}".format(Color.colorify("[+]", "bold blue"), msg) self.out.append(msg) return def quiet_add_out(self, msg): if not hasattr(self.args, "quiet") or not self.args.quiet: self.out.append(msg) return def verbose_add_out(self, msg): if hasattr(self.args, "verbose") and self.args.verbose: self.out.append(msg) return def print_output(self, check_terminal_size=False, skip_color=False): if not hasattr(self, "out"): return if not self.out: return def do_check_term_size(lines): # rough check t_height, t_width = GefUtil.get_terminal_size() if len(lines) > t_height: return not self.args.no_pager # detailed check h = 0 for element in lines: for line in element.splitlines(): line = Color.remove_color(line) h += (len(line) // t_width) + 1 if h > t_height: return not self.args.no_pager return False if not check_terminal_size: less = not self.args.no_pager else: less = do_check_term_size(self.out) gef_print("\n".join(self.out), less=less, skip_color=skip_color) return # Copy/paste this template for new command # @register_command # class TemplateCommand(GenericCommand): # """TemplateCommand: description here will be seen in the help menu for the command.""" # # _cmdline_ = "template-fake" # _category_ = "99. GEF Maintenance Command" # _aliases_ = ["tpl-fk"] # # parser = argparse.ArgumentParser(prog=_cmdline_) # _syntax_ = parser.format_help() # # _example_ = "..." # _note_ = "..." # # def __init__(self): # super().__init__(complete=gdb.COMPLETE_FILENAME) # return # # @parse_args # def do_invoke(self, args): # return @register_priority_command class GefThemeCommand(GenericCommand, BufferingOutput): """Customize GEF appearance.""" _cmdline_ = "theme" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("key", metavar="KEY", nargs="?", help="color theme key.") parser.add_argument("value", metavar="VALUE", nargs="*", help="color theme value.") parser.add_argument("-c", "--color-sample", action="store_true", help="print available name of colors.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # show all theme settings", "{0:s} address_code # show specified theme setting", "{0:s} address_code bold cyan # set new theme", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ 'GDB 17 do not allow multiple color specifications (such as "bold red"). This bug had fixed in GDB 18.', ] _note_ = "\n".join(_note_) def __init__(self, *args, **kwargs): super().__init__() self.add_setting("context_title_line", "cyan", "Color of the borders in context window") self.add_setting("context_title_message", "cyan", "Color of the title in context window") self.add_setting("default_title_line", "cyan", "Default color of borders") self.add_setting("default_title_message", "cyan", "Default color of title") self.add_setting("table_heading", "bold blue", "Color of the column headings to tables (e.g., vmmap)") self.add_setting("context_code_past", "bright_black", "Color to display past code") self.add_setting("context_code_future", "", "Color to display future code") self.add_setting("disassemble_address", "", "Color of address when disassembling") self.add_setting("disassemble_address_highlight", "bold green", "Color of address when disassembling (=$pc)") self.add_setting("disassemble_opcode", "white", "Color of location when disassembling") self.add_setting("disassemble_opcode_highlight", "bold white", "Color of location when disassembling (=$pc)") self.add_setting("disassemble_mnemonic_normal", "yellow", "Color of normal mnemonic when disassembling") self.add_setting("disassemble_mnemonic_normal_highlight", "bold bright_yellow", "Color of normal mnemonic when disassembling (=$pc)") self.add_setting("disassemble_mnemonic_branch", "bold bright_yellow", "Color of branch mnemonic when disassembling") self.add_setting("disassemble_mnemonic_branch_highlight", "bold bright_yellow", "Color of branch mnemonic when disassembling (=$pc)") self.add_setting("disassemble_operands_normal", "cyan", "Color of normal operands when disassembling") self.add_setting("disassemble_operands_normal_highlight", "bold cyan", "Color of normal operands when disassembling (=$pc)") self.add_setting("disassemble_operands_const", "bright_blue", "Color of const operands when disassembling") self.add_setting("disassemble_operands_const_highlight", "bold bright_blue", "Color of const operands when disassembling (=$pc)") self.add_setting("disassemble_operands_symbol", "white", "Color of symbol operands when disassembling)") self.add_setting("disassemble_operands_symbol_highlight", "bold white", "Color of symbol operands when disassembling (=$pc)") self.add_setting("dereference_string", "yellow", "Color of dereferenced string") self.add_setting("dereference_base_address", "cyan", "Color of dereferenced address") self.add_setting("dereference_register_value", "bold blue", "Color of dereferenced register") self.add_setting("registers_register_name", "blue", "Color of the register name in the register window") self.add_setting("registers_value_changed", "bold red", "Color of the changed register in the register window") self.add_setting("address_stack", "magenta", "Color to use when a stack address is found") self.add_setting("address_heap", "bright_blue", "Color to use when a heap address is found") self.add_setting("address_code", "red", "Color to use when a code address is found") self.add_setting("address_writable", "green", "Color to use when a writable address is found") self.add_setting("address_readonly", "white", "Color to use when a read-only address is found") self.add_setting("address_rwx", "underline", "Color to use when a RWX address is found") self.add_setting("address_valid_but_none", "bright_black", "Color to use when a --- address is found") self.add_setting("source_current_line", "bold green", "Color to use for the current code line in the source window") self.add_setting("heap_arena_label", "bold cyan underline", "Color of the arena label used heap") self.add_setting("heap_chunk_label", "bold cyan underline", "Color of the chunk label used heap") self.add_setting("heap_label_active", "bold green underline", "Color of the (active) label used heap") self.add_setting("heap_label_inactive", "bold red underline", "Color of the (inactive) label used heap") self.add_setting("heap_chunk_address_used", "bright_black", "Color of the chunk address used heap") self.add_setting("heap_chunk_address_freed", "bold yellow", "Color of the freed chunk address used heap") self.add_setting("heap_chunk_used", "bright_black", "Color of the used chunk used heap") self.add_setting("heap_chunk_freed", "yellow", "Color of the freed chunk used heap") self.add_setting("heap_chunk_size", "bold magenta", "Color of the size used heap") self.add_setting("heap_chunk_flag_prev_inuse", "bold red", "Color of the prev_in_use flag used heap") self.add_setting("heap_chunk_flag_non_main_arena", "bold yellow", "Color of the non_main_arena flag used heap") self.add_setting("heap_chunk_flag_is_mmapped", "bold blue", "Color of the is_mmapped flag used heap") self.add_setting("heap_freelist_hint", "bold blue", "Color of the freelist hint used heap") self.add_setting("heap_page_address", "bold", "Color of the page address used heap") self.add_setting("heap_management_address", "bright_blue", "Color of the management address used heap") self.add_setting("heap_corrupted_msg", "bold red", "Color of the corrupted message used heap") return def show_all_config(self): self.out.append(titlify("settings")) settings = [] for x in Config.__gef_config__: if x.startswith("theme."): settings.append(x.split(".", 1)[1]) for setting in sorted(settings): value = Config.get_gef_setting("theme.{:s}".format(setting)) if value: value = Color.colorify(value, value) self.out.append("{:40s}: {:s}".format(setting, value)) else: self.out.append("{:40s}: {:s}".format(setting, "None")) return def show_color_sample(self): def list_all_color_sample(mod): i = 0 line = "" for k, v in Color.colors.items(): if k.endswith("_off") or k == "normal": continue line += ("{}{:20s}{} ".format(mod + v, k, Color.colors["normal"])) if k in ["blink", "cyan", "bright_white", "_black"]: # group terminators self.out.append(line) line = "" i = 0 continue if i % 5 == 4: self.out.append(line) line = "" i += 1 self.out.append(titlify("defined colors")) list_all_color_sample("") self.out.append(titlify("defined colors (bold)")) list_all_color_sample(Color.colors["bold"]) self.out.append(titlify("defined colors (highlight)")) list_all_color_sample(Color.colors["highlight"]) return @parse_args def do_invoke(self, args): self.out = [] # show all if args.key is None: self.show_all_config() if args.color_sample: self.show_color_sample() else: self.out.append("* use --color-sample to see available color name") self.print_output(check_terminal_size=True) return # show one key = "theme.{:s}".format(args.key) if key not in Config.__gef_config__: err("Invalid key") return if args.value == []: value = Config.get_gef_setting(key) value = Color.colorify(value, value) gef_print("{:40s}: {:s}".format(args.key, value)) return # set val = [x for x in args.value if x in Color.colors] gdb.execute("gef config theme.{:s} {!r}".format(args.key, " ".join(val))) return @register_command class HighlightCommand(GenericCommand): """The base command to highlight user-defined text matches, which modifies GEF output universally.""" _cmdline_ = "highlight" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("add") subparsers.add_parser("remove") subparsers.add_parser("list") subparsers.add_parser("clear") _syntax_ = parser.format_help() highlight_table = {} @staticmethod def highlight_text(text): """Highlight text using highlight_table { match -> color } settings. If RegEx is enabled it will create a match group around all items in the highlight_table and wrap the specified color in the highlight_table around those matches. If RegEx is disabled, split by ANSI codes and 'colorify' each match found within the specified string.""" if not HighlightCommand.highlight_table: return text if Config.get_gef_setting("highlight.regex"): for match, color in HighlightCommand.highlight_table.items(): text = re.sub("(" + match + ")", Color.colorify("\\1", color), text) return text ansiSplit = re.split(r"(\033\[[\d;]*m)", text) for match, color in HighlightCommand.highlight_table.items(): for index, val in enumerate(ansiSplit): found = val.find(match) if found > -1: ansiSplit[index] = val.replace(match, Color.colorify(match, color)) break text = "".join(ansiSplit) ansiSplit = re.split(r"(\033\[[\d;]*m)", text) return "".join(ansiSplit) def __init__(self): super().__init__(prefix=True) self.add_setting("regex", False, "Enable regex highlighting") return @parse_args def do_invoke(self, args): self.usage() return @register_command class HighlightListCommand(GenericCommand): """Display the current highlight table with matches to colors.""" _cmdline_ = "highlight list" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def print_highlight_table(self): if not HighlightCommand.highlight_table: err("No matches found") return left_pad = max(map(len, HighlightCommand.highlight_table.keys())) for match, color in sorted(HighlightCommand.highlight_table.items()): # do not use gef_print because the color will be overwrite print("{!s} | {!s}".format( Color.colorify(match.ljust(left_pad), color), Color.colorify(color, color), )) return @parse_args def do_invoke(self, args): self.print_highlight_table() return @register_command class HighlightClearCommand(GenericCommand): """Clear the highlight table.""" _cmdline_ = "highlight clear" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): HighlightCommand.highlight_table.clear() return @register_command class HighlightAddCommand(GenericCommand): """Add a match to the highlight table.""" _cmdline_ = "highlight add" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("match", metavar="MATCH", help="the keyword phrase to highlight.") parser.add_argument("color", metavar="COLOR", nargs="+", help="the color used to highlight.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "call rcx" bold yellow', ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "use config `gef config highlight.regex true` if need regex.", ] _note_ = "\n".join(_note_) @parse_args def do_invoke(self, args): for a in args.color: if a not in Color.colors.keys(): err("Invalid color") return HighlightCommand.highlight_table[args.match] = " ".join(args.color) return @register_command class HighlightRemoveCommand(GenericCommand): """Remove a match in the highlight table.""" _cmdline_ = "highlight remove" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("match", metavar="MATCH", help="the keyword phrase to remove from highlight.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "call rcx"', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args def do_invoke(self, args): HighlightCommand.highlight_table.pop(args.match, None) return class SimpleInternalTemporaryBreakpoint(gdb.Breakpoint): """A simple wrapper that takes into account the bug where temporary breakpoints isn't deleted after it is hit.""" def __init__(self, loc): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=True, temporary=True) return def stop(self): EventHandler.__gef_check_disabled_bp__ = True self.enabled = False Cache.reset_gef_caches() return True class SecondBreakpoint(gdb.Breakpoint): """Breakpoint which sets a 2nd breakpoint, when hit.""" def __init__(self, loc, second_loc): self.second_loc = second_loc super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=True, temporary=True) return def stop(self): EventHandler.__gef_check_disabled_bp__ = True self.enabled = False Cache.reset_gef_caches() SimpleInternalTemporaryBreakpoint(loc=self.second_loc) return True @register_command class NextiForQemuUserCommand(GenericCommand): """`ni` wrapper for some specific architectures (OpenRISC 1000 and CRIS).""" _cmdline_ = "nexti-for-qemu-user" _category_ = "01-c. Debugging Support - Basic Command Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("args", metavar="ARGS", nargs="*", help="An array of arguments to pass as is to the nexti command. (default: %(default)s)") _syntax_ = parser.format_help() _note_ = [ "Only when qemu-user with specific architecture, the `ni` command is redirected to `nexti-for-qemu-user`.", "This setting is done only once, when `hook_stop_handler` is called for the first time.", "", "Target architecture:", " OpenRISC 1000: branch operations don't work well, so GEF uses breakpoints to simulate.", " CRIS: si/ni commands don't work well. so GEF uses breakpoints to simulate.", ] _note_ = "\n".join(_note_) def ni_set_bp_for_branch(self): target = None delay_slot = False try: frame = gdb.selected_frame() except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). frame = None insn = get_insn() insn_next = get_insn_next() if insn and current_arch.is_jump(insn): target = ContextCodeCommand.get_branch_addr(insn) delay_slot = current_arch.has_delay_slot elif insn and current_arch.is_ret(insn): target = current_arch.get_ra(insn, frame) delay_slot = current_arch.has_ret_delay_slot if target is None: return # something wrong if infinity loop on CRIS architecture if is_cris() and target == insn.address: SecondBreakpoint(loc=insn_next.address, second_loc=target) return SimpleInternalTemporaryBreakpoint(loc=target) if delay_slot: SimpleInternalTemporaryBreakpoint(loc=insn_next.address) return def ni_set_bp_next(self): insn_next = get_insn_next() SimpleInternalTemporaryBreakpoint(loc=insn_next.address) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-user",)) @only_if_specific_arch(arch=("OR1K", "CRIS")) def do_invoke(self, args): if is_cris(): self.ni_set_bp_for_branch() self.ni_set_bp_next() gdb.execute("c") # use c wrapper return if is_or1k(): self.ni_set_bp_for_branch() cmd = "nexti " + " ".join(args.args) try: gdb.execute(cmd.rstrip()) except gdb.error: exc_type, exc_value, exc_traceback = sys.exc_info() if str(exc_value).startswith("Cannot access memory at address"): if is_valid_addr(current_arch.pc): gdb.execute("xuntil --from-wrapper") else: err(exc_value) else: err(exc_value) return @register_command class StepiForQemuUserCommand(GenericCommand): """`si` wrapper for some specific architectures (OpenRISC 1000 and CRIS).""" _cmdline_ = "stepi-for-qemu-user" _category_ = "01-c. Debugging Support - Basic Command Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("args", metavar="ARGS", nargs="*", help="An array of arguments to pass as is to the stepi command. (default: %(default)s)") _syntax_ = parser.format_help() _note_ = [ "Only when qemu-user with specific architecture, the `si` command is redirected to `stepi-for-qemu-user`.", "This setting is done only once, when `hook_stop_handler` is called for the first time.", "", "Target architecture:", " OpenRISC 1000: branch operations don't work well, so GEF uses breakpoints to simulate.", " CRIS: si/ni commands don't work well. so GEF uses breakpoints to simulate.", ] _note_ = "\n".join(_note_) def si_set_bp_for_branch(self): target = None delay_slot = False try: frame = gdb.selected_frame() except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). frame = None insn = get_insn() insn_next = get_insn_next() if insn and (current_arch.is_jump(insn) or current_arch.is_call(insn)): # si also stops at `call` target target = ContextCodeCommand.get_branch_addr(insn) delay_slot = current_arch.has_delay_slot elif insn and current_arch.is_ret(insn): target = current_arch.get_ra(insn, frame) delay_slot = current_arch.has_ret_delay_slot if target is None: return # something wrong if infinity loop on CRIS architecture if is_cris() and target == insn.address: SecondBreakpoint(loc=insn_next.address, second_loc=target) return SimpleInternalTemporaryBreakpoint(loc=target) if delay_slot: SimpleInternalTemporaryBreakpoint(loc=insn_next.address) return def si_set_bp_next(self): insn_next = get_insn_next() SimpleInternalTemporaryBreakpoint(loc=insn_next.address) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-user",)) @only_if_specific_arch(arch=("OR1K", "CRIS")) def do_invoke(self, args): if is_cris(): self.si_set_bp_for_branch() self.si_set_bp_next() gdb.execute("c") # use c wrapper return if is_or1k(): self.si_set_bp_for_branch() cmd = "stepi " + " ".join(args.args) try: gdb.execute(cmd.rstrip()) except gdb.error: exc_type, exc_value, exc_traceback = sys.exc_info() if str(exc_value).startswith("Cannot access memory at address"): if is_valid_addr(current_arch.pc): gdb.execute("xuntil --from-wrapper") else: err(exc_value) else: err(exc_value) return @register_command class ContinueForQemuUserCommand(GenericCommand): """`c` wrapper to resolve the Ctrl+C problem for qemu-user or Intel Pin.""" _cmdline_ = "continue-for-qemu-user" _category_ = "01-c. Debugging Support - Basic Command Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("args", metavar="ARGS", nargs="*", help="An array of arguments to pass as is to the continue command. (default: %(default)s)") _syntax_ = parser.format_help() _note_ = [ "Only when qemu-user or pin, the `c` command is redirected to `continue-for-qemu-user`.", "This setting is done only once, when hook_stop_handler is called for the first time.", "Nested `c` command causes a problem, so in that case gef executes the original continue command instead.", "Internally, SIGINT is monitored in a forked child process (default) or another thread.", ] _note_ = "\n".join(_note_) nested = False def __init__(self): super().__init__() # In the previous old implementation, Ctrl+C signal was monitored by thread. It was quite stable. # However, if you use this method before libc.so is loaded, gdb will crash on non-x86 architectures. # This is because the code executes gdb.execute("continue") in a non-main thread. # However, signals can only be monitored in the main thread, so there was no way to avoid this. # In the new implementation, Ctrl+C signal is monitored by forked child process. # It seems to work well so far, but there may be cases where it doesn't work properly. self.add_setting("use_fork", True, "Ctrl+C is monitored by forked process. If False, monitored by thread.") return def continue_for_qemu_thread(self): import signal import threading thread_started = False thread_finished = False pid = Pid.get_pid() def continue_thread(): nonlocal thread_started, thread_finished thread_started = True try: gdb.execute("continue") except gdb.error: exc_type, exc_value, exc_traceback = sys.exc_info() err(exc_value) thread_finished = True return def sig_handler(_signum, _frame): # do not use get_pid() in this func. # get_pid() uses `maintenance packet` command internally, # but it cannot be used when the non-static program is running. os.kill(pid, signal.SIGTRAP) return th = threading.Thread(target=continue_thread, daemon=True) th.start() while thread_started is False: time.sleep(0.1) old = signal.signal(signal.SIGINT, sig_handler) while thread_finished is False: time.sleep(0.1) th.join() signal.signal(signal.SIGINT, old) return def pid_is_alive(self, pid): try: os.kill(pid, 0) except OSError: return False return True def continue_for_qemu_fork(self): import signal parent_pid = Pid.get_pid() child_pid = os.fork() if child_pid == 0: # child def sig_handler(_signum, _frame): nonlocal signal_monitoring os.kill(parent_pid, signal.SIGTRAP) signal_monitoring = False return signal_monitoring = True old = signal.signal(signal.SIGINT, sig_handler) while signal_monitoring: time.sleep(0.1) signal.signal(signal.SIGINT, old) os._exit(0) # parent try: gdb.execute("continue") except gdb.error: exc_type, exc_value, exc_traceback = sys.exc_info() err(exc_value) # clean up try: if self.pid_is_alive(child_pid): os.kill(child_pid, signal.SIGKILL) os.waitpid(child_pid, os.WNOHANG) except (ProcessLookupError, ChildProcessError): pass return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-user", "pin")) def do_invoke(self, args): if is_qemu_user() or is_pin(): if Pid.get_pid(): if not self.nested: self.nested = True if Config.get_gef_setting("continue_for_qemu_user.use_fork"): self.continue_for_qemu_fork() else: self.continue_for_qemu_thread() self.nested = False return # fall back to original continue command try: cmd = "continue " + " ".join(args.args) gdb.execute(cmd.rstrip()) except gdb.error: exc_type, exc_value, exc_traceback = sys.exc_info() err(exc_value) return @register_command class StepiForKGDBCommand(GenericCommand): """`si` wrapper for AArch64 KGDB that avoids stepping into pending IRQ handlers.""" _cmdline_ = "stepi-for-kgdb" _category_ = "01-c. Debugging Support - Basic Command Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() _note_ = [ "Only for AArch64 + kgdb.", "Temporarily masks IRQ before `stepi`, then restores the original state", "unless the stepped instruction intentionally modified DAIF.I.", ] _note_ = "\n".join(_note_) @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("kgdb",)) @only_if_specific_arch(arch=("ARM64",)) def do_invoke(self, args): old_cpsr = get_register("$cpsr") try: instr = read_int32_from_memory(current_arch.pc) except gdb.error: err("Memory read error") return irq_mask_bit = 0x80 # DAIF.I gdb.execute("set $cpsr = {:#x}".format(old_cpsr | irq_mask_bit), to_string=True) try: gdb.execute("stepi", from_tty=True) except gdb.error: gdb.execute("set $cpsr = {:#x}".format(old_cpsr), to_string=True) raise if old_cpsr & irq_mask_bit: return new_cpsr = get_register("$cpsr") # If the stepped instruction itself modified DAIF.I, preserve that result. if (instr & 0xffff_f0ff) == 0xd503_40df: # MSR DAIFSet/DAIFClr, #imm if (instr & 0x200) == 0: new_cpsr &= ~irq_mask_bit elif (instr & 0xffff_ffe0) == 0xd51b_4220: # MSR DAIF, Xn regval = get_register("$x{:d}".format(instr & 0x1f)) if (regval & irq_mask_bit) == 0: new_cpsr &= ~irq_mask_bit else: new_cpsr &= ~irq_mask_bit gdb.execute("set $cpsr = {:#x}".format(new_cpsr), to_string=True) return @register_command class UpCommand(GenericCommand): """`up` wrapper.""" _cmdline_ = "up" _category_ = "01-c. Debugging Support - Basic Command Extension" _repeat_ = True parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("n", metavar="N", nargs="?", type=int, default=1, help="Number of frames to move. (default: %(default)s)") _syntax_ = parser.format_help() def do_up(self, current_frame): # check if target frame is available n = self.args.n while current_frame and n: if not current_frame.is_valid(): break current_frame = current_frame.older() n -= 1 # go to target frame if n == 0 and current_frame: current_frame.select() # back up nb_lines_before = Config.get_gef_setting("context_trace.nb_lines_before") nb_lines = Config.get_gef_setting("context_trace.nb_lines") # change temporarily Config.set_gef_setting("context_trace.nb_lines_before", 0x100) Config.set_gef_setting("context_trace.nb_lines", 0x100) # print gdb.execute("context trace -i") # restore Config.set_gef_setting("context_trace.nb_lines_before", nb_lines_before) Config.set_gef_setting("context_trace.nb_lines", nb_lines) return @parse_args @only_if_gdb_running def do_invoke(self, args): try: current_frame = gdb.selected_frame() except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). err("Failed to get frame information") return self.do_up(current_frame) return @register_command class DownCommand(GenericCommand): """`down` wrapper.""" _cmdline_ = "down" _category_ = "01-c. Debugging Support - Basic Command Extension" _repeat_ = True parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("n", metavar="N", nargs="?", type=int, default=1, help="Number of frames to move. (default: %(default)s)") _syntax_ = parser.format_help() def do_down(self, current_frame): # check if target frame is available n = self.args.n while current_frame and n: if not current_frame.is_valid(): break current_frame = current_frame.newer() n -= 1 # go to target frame if n == 0 and current_frame: current_frame.select() # back up nb_lines_before = Config.get_gef_setting("context_trace.nb_lines_before") nb_lines = Config.get_gef_setting("context_trace.nb_lines") # change temporarily Config.set_gef_setting("context_trace.nb_lines_before", 0x100) Config.set_gef_setting("context_trace.nb_lines", 0x100) # print gdb.execute("context trace -i") # restore Config.set_gef_setting("context_trace.nb_lines_before", nb_lines_before) Config.set_gef_setting("context_trace.nb_lines", nb_lines) return @parse_args @only_if_gdb_running def do_invoke(self, args): try: current_frame = gdb.selected_frame() except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). err("Failed to get frame information") return self.do_down(current_frame) return @register_command class HistoryCommand(GenericCommand, BufferingOutput): """Show gdb command history easily.""" _cmdline_ = "history" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def get_history(self): history = {} line_idx = 0 prev_ret = None while True: ret = gdb.execute("show commands {:d}".format(line_idx), to_string=True) for line in ret.splitlines(): line_idx = int(line.split()[0]) if line_idx in history: continue history[line_idx] = line if prev_ret == ret: break prev_ret = ret return list(history.values()) @parse_args def do_invoke(self, args): self.out = self.get_history() self.print_output(skip_color=True) return @register_command class DisplayTypeCommand(GenericCommand, BufferingOutput): """Make it easier to use `ptype /ox TYPE` and `p ((TYPE*) ADDRESS)[0]`.""" _cmdline_ = "dt" _category_ = "02-h. Process Information - Type" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("type", metavar="TYPE", help="the type name.") parser.add_argument("address", metavar="ADDRESS", nargs="?", type=AddressUtil.parse_address, help="the address to apply the type.") parser.add_argument("-s", "--smart", action="store_true", help="override `context.smart_cpp_function_name = True` temporarily.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "struct malloc_state" # shortcut for `ptype /ox struct malloc_state`', '{0:s} "struct malloc_state" $rsp # shortcut for `p ((struct malloc_state*) $rsp)[0]`', ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command is designed for several purposes.", "1. When displaying very large struct, you may want to go through a pager because the results will not fit on one screen.", " However, using a pager, the color information disappears. This command calls the pager with preserving colors.", "2. When `ptype /ox TYPE`, interpreting member type recursively often result is too long and difficult to read.", " This command keeps result compact by displaying only top-level members.", "3. When `p ((TYPE*) ADDRESS)[0]` for large struct, the gdb setting of `max-value-size` is too small to display.", " This command adjusts it automatically.", "4. When debugging a binary written in the Golang, the offset information of the type is not displayed.", " This command also displays the offset.", "5. When debugging a binary written in the Golang, the `p ((TYPE*) ADDRESS)[0]` command will be broken.", " This is because the Golang helper script is automatically loaded and overwrites the behavior of `p` command.", " This command creates the display results on the python side, so we can display it without any problems.", ] _note_ = "\n".join(_note_) def dump_type(self, tp, args_type): if tp.code == gdb.TYPE_CODE_STRUCT: type_prefix = "struct" elif tp.code == gdb.TYPE_CODE_UNION: type_prefix = "union" elif tp.code == gdb.TYPE_CODE_ENUM: type_prefix = "enum" else: err("{:s} is not struct or union".format(tp.name or args_type)) return False self.out = [ "{:s} {:s} {{".format(type_prefix, Instruction.smartify_text(tp.name or args_type)), " /* offset | size */", ] for name, field in tp.items(): if tp.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]: if hasattr(field, "bitpos") and hasattr(field.type, "sizeof"): offsz_str = "/* {:#06x} | {:#06x} */".format(field.bitpos // 8, field.type.sizeof) elif hasattr(field.type, "sizeof"): offsz_str = "/* | {:#06x} */".format(field.type.sizeof) elif hasattr(field, "bitpos"): offsz_str = "/* {:#06x} | */".format(field.bitpos // 8) else: offsz_str = "/* | */" type_str = Instruction.smartify_text(str(field.type)) name_str = Color.cyanify(Instruction.smartify_text(name)) if field.bitsize == 0: msg = " {:s} {} {:s};".format(offsz_str, type_str, name_str) else: msg = " {:s} {} {:s} : {:d};".format(offsz_str, type_str, name_str, field.bitsize) else: # gdb.TYPE_CODE_ENUM offsz_str = "/* {:#06x} | {:#06x} */".format(0, 4) type_str = "int" name_str = Color.cyanify(Instruction.smartify_text(name)) msg = " {:s} {} {:s} = {:#x};".format(offsz_str, type_str, name_str, field.enumval) self.out.append(msg) self.out.append("}} // total: {:#x} bytes".format(tp.sizeof)) return True def apply_type(self, tp, args_address): if not is_valid_addr(args_address): err("Memory read error") return False # change setting if tp.sizeof > 2200: # 2200 is default value of max-value-size gdb.execute("set max-value-size {:#x}".format(tp.sizeof)) v = gdb.Value(args_address) s = v.cast(tp.pointer()).dereference() self.out = s.format_string(styling=True).splitlines() return True @parse_args @only_if_gdb_running def do_invoke(self, args): # lookup type tp = GefUtil.cached_lookup_type(args.type) if not args.type.startswith(("struct", "union", "enum")): if tp is None: tp = GefUtil.cached_lookup_type("struct {:s}".format(args.type)) if tp is None: tp = GefUtil.cached_lookup_type("union {:s}".format(args.type)) if tp is None: tp = GefUtil.cached_lookup_type("enum {:s}".format(args.type)) if tp is None: err("Could not find {:s}".format(args.type)) return # remove pointer while str(tp).endswith("*"): tp = tp.target() # check if valid type if tp.code not in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION, gdb.TYPE_CODE_ENUM]: err("{:s} is not struct or union or enum".format(tp.name or args.type)) return # change setting temporarily if args.smart: old_smart_setting = Config.get_gef_setting("context.smart_cpp_function_name") Config.set_gef_setting("context.smart_cpp_function_name", True) # doit if args.address is None: ret = self.dump_type(tp, args.type) else: ret = self.apply_type(tp, args.address) if ret: self.print_output(check_terminal_size=True) # revert setting if args.smart: Config.set_gef_setting("context.smart_cpp_function_name", old_smart_setting) return @register_command class BreakRelativeVirtualAddressCommand(GenericCommand): """Set a breakpoint at relative offset from codebase.""" _cmdline_ = "break-rva" _category_ = "01-b. Debugging Support - Breakpoint" _aliases_ = ["brva"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("offset", metavar="OFFSET", type=AddressUtil.parse_address, help="the offset from codebase to set a breakpoint.") _syntax_ = parser.format_help() delayed_breakpoints = set() delayed_bp_set = False @parse_args @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): elf = Elf.get_elf() if elf is None or not elf.is_valid(): err("Invalid ELF") return if not elf.is_pie(): err("Non-PIE ELF is unsupported") return if is_alive(): codebase = ProcessMap.get_codebase() if codebase is None: gef_print("Could not find the codebase") return gdb.execute("b *{:#x}".format(codebase + args.offset)) else: # use delayed breakpoints BreakRelativeVirtualAddressCommand.delayed_breakpoints.add(args.offset) info("Add delayed breakpoint to codebase+{:#x}".format(args.offset)) return @register_command class PrintFormatCommand(GenericCommand): """Print bytes format in high level languages.""" _cmdline_ = "print-format" _category_ = "07-c. Misc - Generation" _aliases_ = ["pf", "gethex"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", dest="format", default="hex", choices=["py", "c", "js", "asm", "hex", "hexn", "hexs", "hexsn"], help="the output format. (default: %(default)s)") parser.add_argument("-b", dest="bitlen", type=int, default=8, choices=[8, 16, 32, 64], help="the size of bit. (default: %(default)s)") group = parser.add_mutually_exclusive_group(required=False) group.add_argument("-l", dest="length", type=AddressUtil.parse_address, default=256, help="the length of array. (default: %(default)s)") group.add_argument("-t", dest="to_addr", type=AddressUtil.parse_address, help="specify the end address instead of the length.") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address of data to dump.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -f py -b 8 -l 256 $rsp", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ '"hexn" means hex with new-line.', '"hexs" means hex with separator.', ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def extract_memory(self): unit_size = self.args.bitlen // 8 if self.args.to_addr is not None: end_addr = self.args.to_addr else: end_addr = self.args.location + self.args.length * unit_size bit_format = {8: "= 100: self.out.append("...") break s = read_cstring_from_memory(addr, get_pagesize()) s = Color.yellowify(repr(s)) if len(s) > max_size: s = s[:max_size] + "[...]" self.out.append("{:03d} {!s}: {!s} -> {:s}".format( i, ProcessMap.lookup_address(pos), ProcessMap.lookup_address(addr), s, )) i += 1 return def print_from_proc(self, filename): fmt = "{:3s} {:s}" legend = ["#", "String"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) max_size = get_pagesize() if self.args.increase_limit else 128 lines = open(filename, "rb").read() lines = String.bytes2str(lines) for i, elem in enumerate(lines.split("\0")): if not elem: break if not self.args.verbose and i >= 100: self.out.append("...") break if len(elem) > max_size: elem = elem[:max_size] + "[...]" self.out.append("{:03d} {!s}".format(i, elem)) return def dump_dl_argv(self): paddr = self.get_address_from_symbol("&_dl_argv") addr = self.get_address_from_symbol("_dl_argv") if paddr and addr: self.out.append(titlify("ARGV from _dl_argv")) self.info_add_out("_dl_argv @ {}".format(ProcessMap.lookup_address(paddr))) self.print_from_mem(addr) return elif addr == 0: self.err_add_out("_dl_argv is 0x0") else: self.err_add_out("Could not find _dl_argv") return def dump_libc_argv(self): paddr = self.get_address_from_symbol("&__libc_argv") addr = self.get_address_from_symbol("__libc_argv") if paddr and addr: self.out.append(titlify("ARGV from __libc_argv")) self.info_add_out("__libc_argv @ {}".format(ProcessMap.lookup_address(paddr))) self.print_from_mem(addr) elif addr == 0: self.err_add_out("__libc_argv is 0x0") else: self.err_add_out("Could not find __libc_argv") return def dump_proc_cmdline(self): if is_remote_debug(): return self.out.append(titlify("ARGV from /proc/{:d}/cmdline".format(Pid.get_pid()))) self.print_from_proc("/proc/{:d}/cmdline".format(Pid.get_pid())) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): self.out = [] self.dump_dl_argv() self.dump_libc_argv() self.dump_proc_cmdline() self.print_output(check_terminal_size=True) return @register_command class EnvpCommand(GenericCommand, BufferingOutput): """Display initial envp from __environ@ld, or modified envp from last_environ@libc.""" _cmdline_ = "envp" _category_ = "02-d. Process Information - Trivial Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-v", "--verbose", action="store_true", help="print all elements. (default: outputs up to 100)") parser.add_argument("-i", "--increase-limit", action="store_true", help="increase rounding limit from 128 bytes to 4096 bytes.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def get_address_from_symbol(self, symbol): try: return AddressUtil.parse_address(symbol) except Exception: return None def print_from_mem(self, array): fmt = "{:3s} {:{:d}s} {:{:d}s} -> {:s}" legend = [ "#", "ArrAddr", AddressUtil.get_format_address_width(), "StrAddr", AddressUtil.get_format_address_width(), "String", ] self.out.append(GefUtil.make_legend(fmt.format(*legend))) max_size = get_pagesize() if self.args.increase_limit else 128 i = 0 while True: pos = array + i * current_arch.ptrsize addr = read_int_from_memory(pos) if addr == 0: break if not self.args.verbose and i >= 100: self.out.append("...") break s = read_cstring_from_memory(addr, get_pagesize()) s = Color.yellowify(repr(s)) if len(s) > max_size: s = s[:max_size] + "[...]" self.out.append("{:03d} {!s}: {!s} -> {:s}".format( i, ProcessMap.lookup_address(pos), ProcessMap.lookup_address(addr), s, )) i += 1 return def print_from_proc(self, filename): fmt = "{:3s} {:s}" legend = ["#", "Name=Value"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) max_size = get_pagesize() if self.args.increase_limit else 128 lines = open(filename, "rb").read() lines = String.bytes2str(lines) for i, elem in enumerate(lines.split("\0")): if not elem: break if not self.args.verbose and i >= 100: self.out.append("...") break elem = re.sub(r"^(.*?=)", Color.boldify("\\1"), elem) if len(elem) > max_size: elem = elem[:max_size] + "[...]" self.out.append("{:03d} {:s}".format(i, elem)) return def dump_environ(self): self.out.append(titlify("ENVP from __environ")) paddr = self.get_address_from_symbol("&__environ") addr = self.get_address_from_symbol("__environ") if paddr and addr: self.info_add_out("__environ @ {}".format(ProcessMap.lookup_address(paddr))) self.print_from_mem(addr) elif addr == 0: self.err_add_out("___environ is 0x0") else: self.err_add_out("Could not find __environ") return def dump_last_environ(self): self.out.append(titlify("ENVP from last_environ (for putenv, etc.)")) paddr = self.get_address_from_symbol("&last_environ") addr = self.get_address_from_symbol("last_environ") if paddr and addr: self.info_add_out("last_environ @ {}".format(ProcessMap.lookup_address(paddr))) self.print_from_mem(addr) elif addr == 0: self.err_add_out("last_environ is 0x0") else: self.err_add_out("Could not find last_environ") return def dump_proc_environ(self): if is_remote_debug(): return self.out.append(titlify("ENVP from /proc/{:d}/environ".format(Pid.get_pid()))) self.print_from_proc("/proc/{:d}/environ".format(Pid.get_pid())) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): self.out = [] self.dump_environ() self.dump_last_environ() self.dump_proc_environ() self.print_output(check_terminal_size=True) return @register_command class DumpArgsCommand(GenericCommand): """Dump arguments of current function.""" _cmdline_ = "dumpargs" _category_ = "02-d. Process Information - Trivial Information" _aliases_ = ["args"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-c", "--count", type=AddressUtil.parse_address, help="number of arguments to guess.") parser.add_argument("-o", "--out-of-function", action="store_true", help="assume here is out of the function.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): gef_print(titlify("info args (snapshot)")) gdb.execute("info args") gef_print(titlify("guessed arguments (current value)")) ret = gdb.execute("info args", to_string=True).strip() if args.count is not None: count = args.count elif "No symbol table info available" in ret: count = len(current_arch.function_parameters) else: count = len(ret.splitlines()) for i in range(count): key, val = current_arch.get_ith_parameter(i, in_func=not args.out_of_function) width = len(key) while width % 4: width += 1 gef_print("{:>{:d}s} = {:s}".format(key, width, AddressUtil.recursive_dereference_to_string(val))) return @register_command class VdsoCommand(GenericCommand, BufferingOutput): """Disassemble the text area of vdso smartly.""" _cmdline_ = "vdso" _category_ = "02-d. Process Information - Trivial Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @require_arch_set def do_invoke(self, args): # get map entry maps = ProcessMap.get_process_maps() if maps is None: err("Failed to get maps") return for entry in maps: if entry.path == "[vdso]": break else: err("Could not find the vdso") return # get dump area elf = Elf.get_elf(entry.page_start) if elf is None or not elf.is_valid(): err("Failed to parse") return shdr = elf.get_shdr(".text") text_start = entry.page_start + shdr.sh_addr text_size = shdr.sh_size text_end = text_start + text_size # disassemble try: __import__("capstone") ret = gdb.execute("capstone-disassemble {:#x} -l {:#x}".format(text_start, text_size), to_string=True) result_lines = ret.splitlines() except ImportError: gen = Disasm.gdb_disassemble(text_start, end_pc=text_end - 1) result_lines = [str(x) for x in gen] self.out = [] for line in result_lines: if int(Color.remove_color(line.split()[0]), 16) < text_end: self.out.append(line) else: break self.print_output(check_terminal_size=True) return @register_command class VvarCommand(GenericCommand, BufferingOutput): """Dump the vvar area (x64/x86 only).""" _cmdline_ = "vvar" _category_ = "02-d. Process Information - Trivial Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def read(self, addr, size): if is_x86_64(): block_size = 128 dynamic_read = current_arch.read128 elif is_x86_32(): block_size = 28 dynamic_read = current_arch.read28 out = b"" pos = 0 while pos < size: out += dynamic_read(addr + pos) pos += block_size return out[:size] @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "rr")) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): # get map entry maps = ProcessMap.get_process_maps() if maps is None: err("Failed to get maps") return for entry in maps: if entry.path == "[vvar]": break else: err("Could not find the vvar") return # dump # arch/x86/include/asm/vvar.h self.out = [] start = entry.page_start + 128 # DECLARE_VVAR(128, struct vdso_data, _vdso_data) size = 0x180 # >= sizeof(struct vdso_data) data = self.read(start, size) hex_data = hexdump(data, base=start, unit=current_arch.ptrsize) self.out.extend(hex_data.splitlines()) # print self.print_output(check_terminal_size=True) return @register_command class IouringDumpCommand(GenericCommand, BufferingOutput): """Dump the iouring area (x64 only).""" _cmdline_ = "iouring-dump" _category_ = "02-e. Process Information - Complex Structure Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def read(self, addr, size): block_size = 128 dynamic_read = current_arch.read128 out = b"" pos = 0 while pos < size: out += dynamic_read(addr + pos) pos += block_size return out[:size] @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "rr", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): # get map entry maps = ProcessMap.get_process_maps() if maps is None: err("Failed to get maps") return # get anon_inode:[io_uring] iouring_entries = [] for entry in maps: if entry.path == "anon_inode:[io_uring]": iouring_entries.append(entry) # dump self.out = [] for entry in iouring_entries: if entry.offset in [0, 0x0800_0000]: # IORING_OFF_SQ_RING ,IORING_OFF_CQ_RING self.out.append(titlify("struct io_rings: {:#x}".format(entry.page_start))) elif entry.offset == 0x1000_0000: # IORING_OFF_SQES self.out.append(titlify("struct io_uring_sqe: {:#x}".format(entry.page_start))) data = self.read(entry.page_start, entry.size) hex_data = hexdump(data, base=entry.page_start, unit=current_arch.ptrsize) hex_data_merged = HexdumpCommand.merge_lines(hex_data.splitlines(), nb_skip_merge=0x10) self.out.extend(hex_data_merged) # print if not self.out: err("Could not find io_uring region") return self.print_output(check_terminal_size=True) return @register_command class PidCommand(GenericCommand): """Display the local PID or remote PID.""" _cmdline_ = "pid" _category_ = "02-d. Process Information - Trivial Information" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running def do_invoke(self, args): pid = Pid.get_pid() if pid: if is_qemu_user() or is_qemu_system(): gef_print("Local qemu PID: {:d}".format(pid)) else: gef_print("Local PID: {:d}".format(pid)) return if is_remote_debug(): pid = Pid.get_pid(remote=True) if pid: gef_print("Remote PID: {:d}".format(pid)) return err("Failed to get pid") return @register_command class TidCommand(GenericCommand): """Display the Thread ID.""" _cmdline_ = "tid" _category_ = "02-d. Process Information - Trivial Information" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) def do_invoke(self, args): ptid = gdb.selected_thread().ptid gef_print("TID: {:d}".format(ptid[1] or ptid[2])) return @register_command class FilenameCommand(GenericCommand): """Display current debugged filename.""" _cmdline_ = "filename" _category_ = "02-d. Process Information - Trivial Information" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) def do_invoke(self, args): filepath = Path.get_filepath() if filepath: gef_print(repr(filepath)) return elif is_remote_debug(): filepath = gdb.current_progspace().filename if filepath and filepath.startswith("target:"): filepath = filepath[7:] if filepath: gef_print(repr(filepath)) return err("Failed to get filename") return @register_command class ProcInfoCommand(GenericCommand): """Extend the info given by GDB `info proc`.""" _cmdline_ = "proc-info" _category_ = "02-a. Process Information - General" _aliases_ = ["pr"] parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def get_state_of(self, pid): try: status = open("/proc/{:d}/status".format(pid), "r").read() except (FileNotFoundError, OSError): return {} res = {} for line in status.splitlines(): key, value = line.split(":", 1) res[key.strip()] = value.strip() return res def get_stat_of(self, pid): try: stat = open("/proc/{:d}/stat".format(pid), "r").read() except (FileNotFoundError, OSError): return [] try: name = re.search(r"\((.+)\)", stat).group(1) except IndexError: name = "???" other = re.sub(r"\(.+\) ", "", stat).split() res = [int(other[0]), name, other[1]] + [int(x) for x in other[2:]] return res def get_cmdline_of(self, pid): try: cmdline = open("/proc/{:d}/cmdline".format(pid), "r").read() except (FileNotFoundError, OSError): return "" return cmdline.replace("\0", " ").strip() def get_process_path_of(self, pid): try: return os.readlink("/proc/{:d}/exe".format(pid)) except (FileNotFoundError, OSError): return "Not found" def get_process_cwd(self, pid): try: return os.readlink("/proc/{:d}/cwd".format(pid)) except (FileNotFoundError, OSError): return "Not found" def get_process_root(self, pid): try: return os.readlink("/proc/{:d}/root".format(pid)) except (FileNotFoundError, OSError): return "Not found" def get_thread_ids(self, pid): try: tids = os.listdir("/proc/{:d}/task".format(pid)) return [int(x) for x in tids] except (FileNotFoundError, OSError): return [] def get_children_pids(self, pid): try: ps = GefUtil.which("ps") except FileNotFoundError as e: err("{}".format(e)) return [] # Trick: If there are no child processes, `ps` exit code will be non-zero, # causing a subprocess.CalledProcessError exception to be raised. cmd = [ps, "-o", "pid", "--ppid", str(pid), "--noheaders"] try: return [int(x) for x in GefUtil.gef_execute_external(cmd, as_list=True)] except (subprocess.CalledProcessError, ValueError): return [] def get_uid_map(self, pid): try: uid_map = open("/proc/{:d}/uid_map".format(pid), "r").read().strip() except (FileNotFoundError, OSError): return [] return slicer([int(x) for x in uid_map.split()], 3) def get_gid_map(self, pid): try: gid_map = open("/proc/{:d}/gid_map".format(pid), "r").read().strip() except (FileNotFoundError, OSError): return [] return slicer([int(x) for x in gid_map.split()], 3) def get_tty_str(self, major, minor): try: devices = open("/proc/devices", "r").read() except (FileNotFoundError, OSError): return "Not found" for line in devices.splitlines(): if not line or line.endswith(":"): continue n, name = line.strip().split() if major == int(n): if not name.startswith("/dev"): name = os.path.join("/dev", name) break else: return "Not found" def get_major_minor(name): devnum = os.stat(name).st_rdev major = (devnum >> 8) & 0xff minor = devnum & 0xff return major, minor if not os.path.exists(name): return "Not found" if os.path.islink(name): return "Not found" if os.path.isfile(name): if get_major_minor(name) == (major, minor): return name if os.path.isdir(name): for path in GefUtil.walk(name): if get_major_minor(path) == (major, minor): return path return "Not found" def show_info_proc(self): gef_print(titlify("Process Information")) pid = Pid.get_pid() executable = self.get_process_path_of(pid) cmdline = self.get_cmdline_of(pid) cwd = self.get_process_cwd(pid) root = self.get_process_root(pid) gef_print("{:30s} -> {:d}".format("PID", pid)) gef_print("{:30s} -> {!r}".format(" Executable", executable)) gef_print("{:30s} -> {!r}".format(" Command Line", cmdline)) gef_print("{:30s} -> {!r}".format(" Current Working Directory", cwd)) gef_print("{:30s} -> {!r}".format(" Root Directory", root)) uids = re.sub(r"\s+", " : ", self.get_state_of(pid)["Uid"]) gids = re.sub(r"\s+", " : ", self.get_state_of(pid)["Gid"]) gef_print("{:30s} -> {:s}".format(" RUID:EUID:SavedUID:FSUID", uids)) gef_print("{:30s} -> {:s}".format(" RGID:EGID:SavedGID:FSGID", gids)) seccomp_n = self.get_state_of(pid)["Seccomp"] seccomp_s = {"0": "Disabled", "1": "Strict", "2": "CustomFilter"}[seccomp_n] gef_print("{:30s} -> {:s} ({:s})".format(" Seccomp Mode", seccomp_n, seccomp_s)) return def show_info_proc_extra(self): gef_print(titlify("Process Information Additional")) pid = Pid.get_pid() stat = self.get_stat_of(pid) pgid = stat[4] pgid_exec = self.get_process_path_of(pgid) pgid_cmdline = self.get_cmdline_of(pgid) gef_print("{:30s} -> {:d}".format("Process Group ID", pgid)) gef_print("{:30s} -> {!r}".format(" Executable", pgid_exec)) gef_print("{:30s} -> {!r}".format(" Command Line", pgid_cmdline)) sid = stat[5] sid_exec = self.get_process_path_of(sid) sid_cmdline = self.get_cmdline_of(sid) gef_print("{:30s} -> {:d}".format("Session ID", sid)) gef_print("{:30s} -> {!r}".format(" Executable", sid_exec)) gef_print("{:30s} -> {!r}".format(" Command Line", sid_cmdline)) tpgid = stat[7] tpgid_exec = self.get_process_path_of(tpgid) tpgid_cmdline = self.get_cmdline_of(tpgid) gef_print("{:30s} -> {:d}".format("TTY Process Group ID", tpgid)) gef_print("{:30s} -> {!r}".format(" Executable", tpgid_exec)) gef_print("{:30s} -> {!r}".format(" Command Line", tpgid_cmdline)) ttynr = stat[6] major, minor = (ttynr >> 8) & 0xff, ((ttynr >> 20) << 8) | (ttynr & 0xff) ttystr = self.get_tty_str(major, minor) gef_print("{:30s} -> {:d} ({!r})".format(" TTY Device Number", ttynr, ttystr)) return def show_parent(self): gef_print(titlify("Parent Process Information")) ppid = int(self.get_state_of(Pid.get_pid())["PPid"]) ppid_exec = self.get_process_path_of(ppid) ppid_cmdline = self.get_cmdline_of(ppid) gef_print("{:30s} -> {:d}".format("Parent PID", ppid)) gef_print("{:30s} -> {!r}".format(" Executable", ppid_exec)) gef_print("{:30s} -> {!r}".format(" Command Line", ppid_cmdline)) return def show_childs(self): gef_print(titlify("Child Process Information")) children = self.get_children_pids(Pid.get_pid()) if not children: gef_print("No child process") return for i, cpid in enumerate(children, start=1): cpid_exec = self.get_process_path_of(cpid) cpid_cmdline = self.get_cmdline_of(cpid) gef_print("{:30s} -> {:d}".format("Child {:d} PID".format(i), cpid)) gef_print("{:30s} -> {!r}".format(" Executable", cpid_exec)) gef_print("{:30s} -> {!r}".format(" Command Line", cpid_cmdline)) return def show_info_thread(self): gef_print(titlify("Thread Information")) pid = Pid.get_pid() nthreads = self.get_state_of(pid)["Threads"] tgid = self.get_state_of(pid)["Tgid"] gef_print("{:30s} -> {:s}".format("Num of Threads", nthreads)) gef_print("{:30s} -> {:s}".format("Thread Group ID", tgid)) tids = self.get_thread_ids(pid) gef_print("{:30s} -> {!r}".format("Thread ID List", tids)) return def show_info_proc_ns(self): gef_print(titlify("Namespace Information")) pid = Pid.get_pid() gdb_pid = os.getpid() ns_symbols = ["cgroup", "ipc", "mnt", "net", "pid", "time", "user", "uts"] for ns in ns_symbols: if not os.path.exists("/proc/{:d}/ns/{:s}".format(pid, ns)): continue if not os.path.exists("/proc/{:d}/ns/{:s}".format(gdb_pid, ns)): continue sym1 = os.readlink("/proc/{:d}/ns/{:s}".format(pid, ns)) sym2 = os.readlink("/proc/{:d}/ns/{:s}".format(gdb_pid, ns)) m = "{:s} namespace separation".format(ns.upper()) gef_print("{:30s} -> {!s}".format(m, sym1 != sym2)) gef_print(titlify("Pid Namespace Information")) state = self.get_state_of(pid) if len(state["NSpid"].split()) > 1: gef_print("{:30s} -> {:s}".format( "Host PID : Namespace PID", re.sub(r"\s+", " : ", state["NSpid"]), )) gef_print("{:30s} -> {:s}".format( "Host PGID : Namespace PGID", re.sub(r"\s+", " : ", state["NSpgid"]), )) gef_print("{:30s} -> {:s}".format( "Host SID : Namespace SID", re.sub(r"\s+", " : ", state["NSsid"]), )) gef_print("{:30s} -> {:s}".format( "Host TGID : Namespace TGID", re.sub(r"\s+", " : ", state["NStgid"]), )) else: gef_print("{:30s}".format("No pid namespace")) gef_print(titlify("User Namespace Information")) for u in self.get_uid_map(pid): gef_print("{:30s} -> [{:#x} : {:#x} : {:#x}]".format( "UID_MAP [NameSpace:Host:Range]", u[0], u[1], u[2], )) for g in self.get_gid_map(pid): gef_print("{:30s} -> [{:#x} : {:#x} : {:#x}]".format( "GID_MAP [NameSpace:Host:Range]", g[0], g[1], g[2], )) return def get_state_string(self, proto, state): if proto in ["tcp", "tcp6"]: dic = { 0x01: "ESTABLISHED", 0x02: "SYN_SENT", 0x03: "SYN_RECV", 0x04: "FIN_WAIT1", 0x05: "FIN_WAIT2", 0x06: "TIME_WAIT", 0x07: "CLOSE", 0x08: "CLOSE_WAIT", 0x09: "LAST_ACK", 0x0a: "LISTEN", 0x0b: "CLOSING", 0x0c: "NEW_SYN_RECV", 0x0d: "BOUND_INACTIVE", } elif proto in ["udp", "udp6"]: dic = { 0x07: "LISTEN", } elif proto in ["unix"]: dic = { 0x00: "FREE", 0x01: "LISTEN", # "UNCONNECTED" 0x02: "CONNECTING", 0x03: "CONNECTED", 0x04: "DISCONNECTING", } else: return str(int(state, 16)) return dic.get(int(state, 16), "???") def get_proto_string(self, proto): # proto string (for raw/raw6) dic = { 0x00: "IP", 0x01: "ICMP", 0x02: "IGMP", 0x04: "IPIP", 0x06: "TCP", 0x08: "EGP", 0x0c: "PUP", 0x11: "UDP", 0x16: "IDP", 0x1d: "TP", 0x21: "DCCP", 0x29: "IPV6", 0x2b: "ROUTING", 0x2c: "FRAGMENT", 0x2e: "RSVP", 0x2f: "GRE", 0x32: "ESP", 0x33: "AH", 0x3a: "ICMPV6", 0x3b: "NONE", 0x3c: "DSTOPTS", 0x5c: "MTP", 0x5e: "BEETPH", 0x62: "ENCAP", 0x67: "PIM", 0x6c: "COMP", 0x73: "L2TP", 0x84: "SCTP", 0x87: "MH", 0x88: "UDPLITE", 0x89: "MPLS", 0x8f: "ETHERNET", 0x90: "AGGFRAG", 0xff: "RAW", 0x100: "SMC", 0x106: "MPTCP", } return dic.get(proto, str(proto)) def parse_ip_port(self, addr, proto): # tcp/udp: return (ip, port) # other: return (ip, proto) ip, port = addr.split(":") import socket if len(ip) == 8: # ipv4 # 0100007F -> 127.0.0.1 ip = bytes.fromhex(ip)[::-1] ip = socket.inet_ntop(socket.AF_INET, ip) else: # ipv6 # 00000000000000000000000001000000 -> ::1 ip = bytes.fromhex(ip) ip = b"".join([x[::-1] for x in slicer(ip, 4)]) ip = socket.inet_ntop(socket.AF_INET6, ip) ip = "[{:s}]".format(ip) if proto in ["tcp", "tcp6", "udp", "udp6"]: port = int(port, 16) return ip, port # str, int # other protocol real_proto = self.get_proto_string(int(port, 16)) return ip, real_proto # str, str def get_extra_info(self): # e.g., inode -> "[tcp] 127.0.0.1:34588 -> 127.0.0.1:5001 (ESTABLISHED)" # get all sockets pid = Pid.get_pid() sockets = {} path = "/proc/{:d}/fd".format(pid) for fname in os.listdir(path): fullpath = os.path.join(path, fname) if os.path.islink(fullpath) and os.readlink(fullpath).startswith("socket:"): inode = os.readlink(fullpath).replace("socket:", "")[1:-1] sockets[int(inode)] = fname if not sockets: return sockets # get entries protocols = ["tcp", "udp", "tcp6", "udp6", "unix", "raw", "raw6"] entries = {} for prot in protocols: lines = open(f"/proc/{pid}/net/{prot}", "r").readlines() entries[prot] = [x.split() for x in lines[1:]] extra_info = {} for proto, proto_entries in entries.items(): for proto_entry in proto_entries: # parse if proto in ["tcp", "tcp6", "udp", "udp6", "raw", "raw6"]: _, local, remote, state, _, _, _, _, _, inode, *_ = proto_entry elif proto == "unix": _, _, _, _, _, state, inode, *path = proto_entry path = path[0] if path else "" # check if socket inode = int(inode) if inode not in sockets: continue # get state state = self.get_state_string(proto, state) # make extra info if proto in ["tcp", "tcp6", "udp", "udp6"]: local = self.parse_ip_port(local, proto) if state == "LISTEN": extra_info[inode] = "{:14s} {:s}:{:d} ({:s})".format( "[{:s}]".format(proto), *local, state, ) else: remote = self.parse_ip_port(remote, proto) extra_info[inode] = "{:14s} {:s}:{:d} -> {:s}:{:d} ({:s})".format( "[{:s}]".format(proto), *local, *remote, state, ) elif proto in ["raw", "raw6"]: local = self.parse_ip_port(local, proto) remote = self.parse_ip_port(remote, proto) extra_info[inode] = "{:14s} {:s} -> {:s} (st={:s})".format( "[{:s}/{:s}]".format(proto, local[1]), local[0], remote[0], state, ) elif proto == "unix": extra_info[inode] = "{:14s} {!r} ({:s})".format( "[{:s}]".format(proto), path, state, ) return extra_info def show_fds(self): gef_print(titlify("File Descriptors")) pid = Pid.get_pid() path = "/proc/{:d}/fd".format(pid) gef_print("{:30s} -> {:s}".format("Num of FD slots", self.get_state_of(pid)["FDSize"])) items = os.listdir(path) if not items: gef_print("No FD opened") return extra_info = self.get_extra_info() for fname in items: fullpath = os.path.join(path, fname) if os.path.islink(fullpath): content = os.readlink(fullpath) if content.startswith("socket:["): inode = int(content.replace("socket:", "")[1:-1]) extra = extra_info.get(inode, "") gef_print("{:30s} -> {:s} {:s}".format(fullpath, content, extra)) else: gef_print("{:30s} -> {:s}".format(fullpath, content)) return @parse_args @only_if_gdb_running @only_if_gdb_target_local @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "rr")) def do_invoke(self, args): self.show_info_proc() self.show_info_proc_extra() self.show_parent() self.show_childs() self.show_info_thread() self.show_info_proc_ns() self.show_fds() return @register_command class FileDescriptorsCommand(GenericCommand): """Display opened file descriptors.""" _cmdline_ = "fds" _category_ = "02-a. Process Information - General" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def fd_dump(self): pid = Pid.get_pid() if not pid: err("Could not find the local pid") return path = "/proc/{:d}/fd".format(pid) items = os.listdir(path) if not items: err("No FD opened") return for fname in items: fullpath = os.path.join(path, fname) if os.path.islink(fullpath): gef_print("{:32s} -> {:s}".format(fullpath, os.readlink(fullpath))) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): self.fd_dump() return @register_command class ProcDumpCommand(GenericCommand, BufferingOutput): """Dump each file under `/proc/PID`.""" _cmdline_ = "proc-dump" _category_ = "02-a. Process Information - General" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def dump_environ(self, path): data = open(path, "rb").read() for line in sorted(data.split(b"\0")): if line: line = String.bytes2str(line) key, val = line.split("=", 1) self.out.append("{:s}={:s}".format(Color.boldify(key), val)) return def dump_cmdline(self, path): data = open(path, "rb").read() for line in data.split(b"\0"): if line: self.out.append(String.bytes2str(line)) return def dump_auxv(self, path): data = open(path, "rb").read() data = slice_unpack(data, current_arch.ptrsize) for i in range(0, len(data), 2): typ = data[i] val = data[i + 1] self.out.append("{:#8x}: {:#x}".format(typ, val)) return def dump_syscall(self, path): data = String.bytes2str(open(path, "rb").read()) self.out.append(data.strip()) self.out.append("----- parsed -----") if int(data.split()[0]) < 0: tag = ["NR", "sp", "pc"] else: tag = ["NR", "arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "sp", "pc"] for i, elem in enumerate(data.split()): if i < len(tag): elem_name = tag[i] else: elem_name = "???" elem_name = Color.boldify("{:4s}".format(elem_name)) if i == 0: # NR nr = int(elem) syscall_table = get_syscall_table() if syscall_table is None: self.out.append("Could not find the syscall table") return if nr >= 0 and syscall_table and nr in syscall_table.nr_table: syscall_name = syscall_table.nr_table[nr].name self.out.append("{:2d} {:s}: {:s} ({:s})".format(i + 1, elem_name, elem, syscall_name)) else: self.out.append("{:2d} {:s}: {:s}".format(i + 1, elem_name, elem)) else: # argN, sp, pc address = int(elem, 0) sym = Symbol.get_symbol_string(address) elem = "{!s}{:s}".format(ProcessMap.lookup_address(address), sym) self.out.append("{:2d} {:s}: {:s}".format(i + 1, elem_name, elem)) return def dump_stat(self, path): data = String.bytes2str(open(path, "rb").read()) self.out.append(data.strip()) self.out.append("----- parsed -----") tag = [ "pid", "comm", "state", "ppid", "pgrp", "session", "tty_nr", "tpgid", "flags", "minflt", "cminflt", "majflt", "cmajflt", "utime", "stime", "cutime", "cstime", "priority", "nice", "num_threads", "itrealvalue", "starttime", "vsize", "rss", "rsslim", "startcode", "endcode", "startstack", "kstkesp", "kstkeip", "signal", "blocked", "sigignore", "sigcatch", "wchan", "nswap", "cnswap", "exit_signal", "processor", "rt_priority", "policy", "delayacct_blkio_ticks", "guest_time", "cguest_time", "start_data", "end_data", "start_brk", "arg_start", "arg_end", "env_startr", "env_end", "exit_code", ] max_width = max(len(x) for x in tag) lpos = data.find("(") rpos = data.rfind(")") + 1 data = [data[:lpos].strip(), data[lpos:rpos]] + data[rpos:].split() for i, elem in enumerate(data): if i < len(tag): elem_name = tag[i] else: elem_name = "???" elem_name = Color.boldify("{:{:d}s}".format(elem_name, max_width)) if i + 1 in [25, 26, 27, 28, 29, 30, 45, 46, 47, 48, 49, 50, 51]: address = int(elem) sym = Symbol.get_symbol_string(address) elem = "{!s}{:s}".format(ProcessMap.lookup_address(address), sym) elif i + 1 in [23, 33, 34]: elem = hex(int(elem)) self.out.append("{:2d} {:s}: {:s}".format(i + 1, elem_name, elem)) return def dump_statm(self, path): data = String.bytes2str(open(path, "rb").read()) self.out.append(data.strip()) self.out.append("----- parsed -----") tag = ["size", "resident", "shared", "text", "lib", "data", "dt"] max_width = max(len(x) for x in tag) for i, elem in enumerate(data.split()): if i < len(tag): elem_name = tag[i] else: elem_name = "???" elem_name = Color.boldify("{:{:d}s}".format(elem_name, max_width)) self.out.append("{:2d} {:s}: {:s}".format(i + 1, elem_name, elem)) return def dump_status(self, path): try: column_command = GefUtil.which("column") except FileNotFoundError as e: self.out.append("{}".format(e)) return ret = GefUtil.gef_execute_external([column_command, "-s:", "-t", path], as_list=True) for line in ret: r = line.split(maxsplit=1) if len(r) == 2: k, v = r[0], r[1] else: k, v = r[0], "" k = k.strip() + ":" v = v.replace("\t", "").strip() self.out.append("{:30s} {:s}".format(k, v)) return def dump_rt_acct(self, path): try: hexdump_command = GefUtil.which("hexdump") except FileNotFoundError as e: self.out.append("{}".format(e)) return ret = GefUtil.gef_execute_external([hexdump_command, "-C", path], as_list=True) self.out.extend(ret) return def dump_mounts(self, path): try: column_command = GefUtil.which("column") except FileNotFoundError as e: self.out.append("{}".format(e)) return ret = GefUtil.gef_execute_external([column_command, "-t", path], as_list=True) self.out.extend(ret) return def dump_raw(self, path): try: column_command = GefUtil.which("column") except FileNotFoundError as e: self.out.append("{}".format(e)) return data = open(path, "rb").read() data = data.replace(b"tx_queue ", b"tx_queue:") data = data.replace(b" tr ", b" tr:") tmp_fd, tmp_filename = GefUtil.mkstemp(prefix="proc-dump") os.fdopen(tmp_fd, "wb").write(data) ret = GefUtil.gef_execute_external([column_command, "-t", tmp_filename], as_list=True) os.unlink(tmp_filename) self.out.extend(ret) return def dump_dev(self, path): try: column_command = GefUtil.which("column") except FileNotFoundError as e: self.out.append("{}".format(e)) return data = open(path, "rb").read() data = data.replace(b"|", b" |") data = re.sub(rb"\|\s*Receive", b"|Receive", data) data = re.sub(rb"\|\s*Transmit", b"|Transmit", data) tmp_fd, tmp_filename = GefUtil.mkstemp(prefix="proc-dump") os.fdopen(tmp_fd, "wb").write(data) ret = GefUtil.gef_execute_external([column_command, "-t", tmp_filename], as_list=True) os.unlink(tmp_filename) wrong = ret[0].rfind("|") rright = ret[1].rfind("|") lright = ret[1].find("|") ret[0] = (ret[0][:wrong] + " " * (rright - wrong) + ret[0][wrong:]).rstrip() ret[0] = re.sub(r"\|(\S+)", "| \\1 ", ret[0]) ret[1] = re.sub(r"\|(\S+)", "| \\1 ", ret[1]) for i in range(2, len(ret)): ret[i] = ret[i][:lright] + " " + ret[i][lright:] ret[i] = ret[i][:rright] + " " + ret[i][rright:] self.out.extend(ret) return def dump_igmp(self, path): fd = open(path, "rb") for line in fd.readlines(): self.out.append(String.bytes2str(line.rstrip())) # no-lstrip return def dump_netstat(self, path): data = String.bytes2str(open(path, "rb").read()) table = [line.split() for line in data.splitlines()] for idx in range(0, len(table), 2): for i, (k, v) in enumerate(zip(*table[idx:idx + 2])): if i == 0: self.out.append("{:s}".format(k)) else: self.out.append(" {:30s} {:s}".format(k + ":", v)) return def dump_default(self, path): if not os.access(path, os.R_OK): self.out.append("{:s} No permission to read".format(Color.colorify("[!]", "bold red"))) return try: fd = open(path, "rb") except OSError: self.out.append("{:s} Failed to open".format(Color.colorify("[!]", "bold red"))) return try: for line in fd.readlines(): self.out.append(String.bytes2str(line).strip()) except OSError: self.out.append("{:s} Parse failed".format(Color.colorify("[!]", "bold red"))) return def proc_dump(self): pid = Pid.get_pid() for root, dirs, files in os.walk("/proc/{:d}/".format(pid)): files = sorted(files) if "task" in dirs: dirs.remove("task") # in-place change to skip /proc//task/ for f in files: path = os.path.join(root, f) self.out.append(titlify(path)) if os.path.islink(path): self.out.append(Color.colorify("{:s} -> {:s}".format(path, os.readlink(path)), "blue")) continue if f in ["pagemap", "mem"]: self.out.append("{:s} skipped".format(Color.colorify("[*]", "bold yellow"))) # too large continue if f == "environ": self.dump_environ(path) continue if f in ["cmdline", "context"]: self.dump_cmdline(path) continue if f == "auxv": self.dump_auxv(path) continue if f == "syscall": self.dump_syscall(path) continue if f == "stat": self.dump_stat(path) continue if f == "statm": self.dump_statm(path) continue if f == "rt_acct": self.dump_rt_acct(path) continue if f == "status": self.dump_status(path) continue if f in ["mounts", "mountinfo", "mountstats", "unix", "protocols"]: self.dump_mounts(path) continue if f in ["raw", "tcp", "udp", "icmp", "raw6", "tcp6", "udp6", "icmp6", "udplite", "udplite6"]: self.dump_raw(path) continue if f == "dev": self.dump_dev(path) continue if f in ["igmp", "fib_trie", "wireless"]: self.dump_igmp(path) continue if f in ["netstat", "snmp"]: self.dump_netstat(path) continue self.dump_default(path) return @parse_args @only_if_gdb_running @only_if_gdb_target_local @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @require_arch_set def do_invoke(self, args): self.out = [] self.proc_dump() self.print_output(check_terminal_size=True) return @register_command class CapabilityCommand(GenericCommand, BufferingOutput): """Display the capabilities of the debugging process.""" _cmdline_ = "capability" _category_ = "02-f. Process Information - Security" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="also display detailed bit information other than cap_eff.") _syntax_ = parser.format_help() def get_thread_ids(self, pid): try: tids = os.listdir("/proc/{:d}/task".format(pid)) return [int(x) for x in tids] except (FileNotFoundError, OSError): return [] def print_cap_details(self, name, cap): bit_info = [ [40, "CAP_CHECKPOINT_RESTORE", "Update /proc/sys/kernel/ns_last_pid; read /proc/[another_pid]/map_files; etc."], [39, "CAP_BPF", "Allow privileged BPF operations"], [38, "CAP_PERFMON", "Allow various performance-monitoring mechanisms; allow perf_event_open(2); allow some BPF operations"], [37, "CAP_AUDIT_READ", "Allow reading the audit log via a multicast netlink socket"], [36, "CAP_BLOCK_SUSPEND", "Allow features that can block system suspend"], [35, "CAP_WAKE_ALARM", "Trigger something that will wake up the system"], [34, "CAP_SYSLOG", "Allow privileged syslog(2) operations; View kernel addresses exposed via /proc even if kptr_restrict=1"], [33, "CAP_MAC_ADMIN", "Allow MAC configuration or state changes"], [32, "CAP_MAC_OVERRIDE", "Override MAC"], [31, "CAP_SETFCAP", "Set arbitrary capabilities on a file"], [30, "CAP_AUDIT_CONTROL", "Enable/disable kernel audit; change audit filter rules; retrieve audit status and filter rules"], [29, "CAP_AUDIT_WRITE", "Write records to kernel audit log"], [28, "CAP_LEASE", "Establish leases"], [27, "CAP_MKNOD", "Create special files using mknod(2)"], [26, "CAP_SYS_TTY_CONFIG", "Allow vhangup(2); allow various privileged ioctl(2) operations on virtual terminals"], [25, "CAP_SYS_TIME", "Set system clock; set real-time (hardware) clock"], [24, "CAP_SYS_RESOURCE", "Override disk quota limits; override RLIMIT_NPROC resource limit; etc."], [23, "CAP_SYS_NICE", "Lower the process nice value and change the nice value for arbitrary processes; etc."], [22, "CAP_SYS_BOOT", "Allow reboot(2) and kexec_load(2)"], [21, "CAP_SYS_ADMIN", "Allow various privileges operations"], [20, "CAP_SYS_PACCT", "Allow acct(2)"], [19, "CAP_SYS_PTRACE", "Trace arbitrary processes using ptrace(2); etc."], [18, "CAP_SYS_CHROOT", "Allow chroot(2); change mount namespaces using setns(2)"], [17, "CAP_SYS_RAWIO", "Perform I/O port operations; etc."], [16, "CAP_SYS_MODULE", "Load and unload kernel modules"], [15, "CAP_IPC_OWNER", "Bypass permission checks for operations on SystemV IPC objects"], [14, "CAP_IPC_LOCK", "Lock memory; allocate memory using huge pages"], [13, "CAP_NET_RAW", "Use RAW and PACKET sockets; bind to any address for transparent proxying"], [12, "CAP_NET_ADMIN", "Perform various network-related operations"], [11, "CAP_NET_BROADCAST", "(Unused) Make socket broadcasts, and listen to multicast"], [10, "CAP_NET_BIND_SERVICE", "Bind a socket to Internet domain privileged ports (less than 1024)"], [9, "CAP_LINUX_IMMUTABLE", "Set the FS_APPEND_FL and FS_IMMUTABLE_FL inode flags"], [8, "CAP_SETPCAP", "Add any capability from the calling thread's bounding set to its inheritable set; etc."], [7, "CAP_SETUID", "Make arbitrary manipulations of process UIDs; etc."], [6, "CAP_SETGID", "Make arbitrary manipulations of process GIDs and supplementary GID list; etc."], [5, "CAP_KILL", "Bypass permission checks for sending signals"], [4, "CAP_FSETID", "Don't clear SUID and SGID bits when a file is modified; etc."], [3, "CAP_FOWNER", "Bypass permission checks whether FSUID == file UID; set ACLs; etc."], [2, "CAP_DAC_READ_SEARCH", "Bypass permission checks of file read, dir read/exec; etc."], [1, "CAP_DAC_OVERRIDE", "Bypass permission checks of file read/write/exec"], [0, "CAP_CHOWN", "Make arbitrary changes to file UIDs and GIDs"], ] out = BitInfo(name, 64, bit_info).make_out(cap) self.out.append(titlify("")) self.out.extend(out) self.out.append(titlify("")) return def print_capability_from_pid(self): pid = Pid.get_pid() if pid is None: return tids = self.get_thread_ids(pid) for tid in tids: self.out.append(titlify("Thread capability set [PID={:d}, TID={:d}]".format(pid, tid))) try: status_path = "/proc/{:d}/task/{:d}/status".format(pid, tid) status = open(status_path, "r").read() except (FileNotFoundError, OSError): self.err_add_out("Failed to get the information of capability from {:s}".format(status_path)) continue caps = {} m = re.search(r"CapInh:\s+(.+)", status) if m: caps["cap_inh"] = int(m.group(1), 16) m = re.search(r"CapPrm:\s+(.+)", status) if m: caps["cap_prm"] = int(m.group(1), 16) m = re.search(r"CapEff:\s+(.+)", status) if m: caps["cap_eff"] = int(m.group(1), 16) m = re.search(r"CapBnd:\s+(.+)", status) if m: caps["cap_bnd"] = int(m.group(1), 16) m = re.search(r"CapAmb:\s+(.+)", status) if m: caps["cap_amb"] = int(m.group(1), 16) if "cap_prm" in caps: msg = "Capability set that Effective and Inheritable are allowed to have" self.out.append("Permitted : {:#018x} - {:s}".format(caps["cap_prm"], msg)) if self.args.verbose: self.print_cap_details("cap_prm", caps["cap_prm"]) if "cap_inh" in caps: msg = "Capability set that can be inherited when execve(2)" self.out.append("Inheritable: {:#018x} - {:s}".format(caps["cap_inh"], msg)) if self.args.verbose: self.print_cap_details("cap_inh", caps["cap_inh"]) if "cap_amb" in caps: msg = "Capability set that inherited when execve(2) not suid/sgid program" self.out.append("Ambient : {:#018x} - {:s}".format(caps["cap_amb"], msg)) if self.args.verbose: self.print_cap_details("cap_amb", caps["cap_amb"]) if "cap_eff" in caps: msg = "Capability set that kernel actually uses to determine privileges" self.out.append("Effective : {:#018x} - {:s}".format(caps["cap_eff"], msg)) if self.args.verbose: self.print_cap_details("cap_eff", caps["cap_eff"]) if "cap_bnd" in caps: msg = "Capability set that limits the capabilities set that can be acquired" self.out.append("Bounding : {:#018x} - {:s}".format(caps["cap_bnd"], msg)) if self.args.verbose: self.print_cap_details("cap_bnd", caps["cap_bnd"]) return def print_capability_from_file(self): filepath = Path.get_filepath() if filepath is None: return self.out.append(titlify("File capability set [{:s}]".format(filepath))) try: raw_caps = os.getxattr(filepath, "security.capability") except OSError: self.err_add_out("No data available") return caps = {} magic = struct.unpack("", "") # consider , path = path.replace(" ", "_") # consider deleted case. e.g., /path/to/file (deleted) dumpfile_name = "{:s}{:0{:d}x}-{:0{:d}x}_{:s}_{:s}{:s}.raw".format( prefix, start, width, end, width, perm, path, suffix, ) filepath = os.path.join(dirpath, dumpfile_name) # filtering if self.args.filter and not any(filt.search(dumpfile_name) for filt in self.args.filter): continue if self.args.exclude and any(ex.search(dumpfile_name) for ex in self.args.exclude): continue # dump self.do_dump(filepath, start, size) total_size += size info("Total size: {:s}".format(GefUtil.get_size_str(total_size))) if not self.args.commit: info("The directory name is replaced with the latest timestamp") warn('This dry run mode skips dumping; add "--commit" to proceed') return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb", "vmware")) @require_arch_set def do_invoke(self, args): if is_qemu_system(): maps = Kernel.get_maps() else: maps = ProcessMap.get_process_maps_exclude_special_regions(allow_vdso=True) if maps is None: err("Failed to get maps") return prefix = self.args.prefix if prefix: prefix = prefix + "_" suffix = self.args.suffix if suffix: suffix = "_" + suffix self.smart_memory_dump(maps, prefix, suffix) return @register_command class HijackFdCommand(GenericCommand): """Redirect the file descriptor during execution.""" _cmdline_ = "hijack-fd" _category_ = "01-g. Debugging Support - Syscall" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("old_fd", metavar="OLD_FD", type=int, help="file descriptor number to redirect.") parser.add_argument("new_output", metavar="NEW_OUTPUT", type=str, help="the location redirected data is stored.") parser.add_argument("--fd-adjust-connect", type=int, default=0, help="slide value when `connect` syscall result and the actual opened FD differ (for old qemu-user).") parser.add_argument("--fd-adjust-dup3", type=int, default=0, help="slide value when `dup3` syscall result and the actual opened FD differ (for old qemu-user).") parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 2 /tmp/gef/stderr.txt", "{0:s} 2 localhost:8000 # determined as the socket by the presence of `:`.", ] _example_ = "\n".join(_example_).format(_cmdline_) def call_syscall(self, syscall_name, args): args = " ".join([hex(x) if x >= 0 else str(x) for x in args]) cmd = "call-syscall {:s} {:s}".format(syscall_name, args) self.quiet_info(cmd) res = gdb.execute(cmd, to_string=True) output_line = res.splitlines()[-1] self.quiet_info(output_line) return int(output_line.split()[2], 0) def write_stack(self, data): data = String.str2bytes(data) # get stack address vmmap = ProcessMap.get_process_maps() stack_addrs = [entry.page_start for entry in vmmap if entry.path == "[stack]"] if len(stack_addrs) == 0: err("Could not find the stack") return None, None stack_addr = stack_addrs[0] # read original contents try: original_contents = read_memory(stack_addr, len(data)) except gdb.MemoryError: err("Failed to read stack") return None, None self.quiet_info("Original contents: {!s} @ {:#x}".format(original_contents, stack_addr)) self.quiet_info("Overwrite data: {!s}".format(data)) # overwrite it try: write_memory(stack_addr, data) except Exception: err("Failed to write stack") return None, None # read again and check if read_memory(stack_addr, len(data)) != data: err("Failed to write stack") return None, None return stack_addr, original_contents def get_fd_from_file_open(self): # call open stack_addr, original_contents = self.write_stack(self.args.new_output + "\0") if stack_addr is None: return None self.quiet_info("Trying to open {:s}".format(Color.boldify(self.args.new_output))) AT_FDCWD = -100 flags = self.O_APPEND | self.O_CREAT | self.O_RDWR mode = 0o666 # open does not exist in aarch64. So use openat instead of open. open_fd = self.call_syscall("openat", [AT_FDCWD, stack_addr, flags, mode]) write_memory(stack_addr, original_contents) # revert if AddressUtil.is_msb_on(open_fd): err("Failed to open {:s}".format(self.args.new_output)) return None self.quiet_info("Opened {:s} with 0o666 as fd #{:d}".format(Color.boldify(self.args.new_output), open_fd)) return open_fd def get_fd_from_connect_server(self): import socket address = socket.gethostbyname(self.args.new_output.split(":")[0]) port = int(self.args.new_output.split(":")[1]) # call socket sock_fd = self.call_syscall("socket", [self.AF_INET, self.SOCK_STREAM, 0]) if AddressUtil.is_msb_on(sock_fd): err("Failed to create socket") return None self.quiet_info("Created socket fd #{:d}".format(sock_fd)) # call connect (Also supports big endian) sockaddr_in = p16(self.AF_INET) + struct.pack(" #{:d}".format(new_fd, self.args.old_fd)) # call close close_result = self.call_syscall("close", [new_fd]) if AddressUtil.is_msb_on(close_result): err("Failed to close fd #{:d}".format(new_fd)) return self.quiet_info("Closed extra fd #{:d}".format(new_fd)) ok("Success") return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "rr", "wine")) @exclude_specific_arch(arch=("CRIS",)) @require_arch_set def do_invoke(self, args): # In old version of qemu, the file descriptor was sometimes shifted by a # constant value on i386 (fd returned by the syscall == actual opened fd + 80). # This had been hard-coded as a workaround, but the issue appears to be fixed, # so it should now be specified via a command argument. # Currently supported: dup3, connect self.AF_INET = 2 self.SOCK_STREAM = 1 self.O_APPEND = 0o2000 self.O_CREAT = 0o100 self.O_RDWR = 0o2 # some architecture use different consts. if is_mips32() or is_mips64() or is_mipsn32(): # /usr/mipsel-linux-gnu/include/bits/socket_type.h # /usr/mips64el-linux-gnuabi64/include/bits/socket_type.h self.SOCK_STREAM = 2 # /usr/mipsel-linux-gnu/include/bits/fcntl.h # /usr/mips64el-linux-gnuabi64/include/bits/fcntl.h self.O_APPEND = 0x0008 self.O_CREAT = 0x0100 elif is_sparc32() or is_sparc32plus() or is_sparc64(): # sparcv8--uclibc--stable-2022.08-1/sparc-buildroot-linux-uclibc/sysroot/usr/include/bits/fcntl.h # sparc64--glibc--stable-2022.08-1/sparc64-buildroot-linux-gnu/sysroot/usr/include/bits/fcntl.h self.O_APPEND = 0x0008 self.O_CREAT = 0x0200 elif is_alpha(): # /usr/alpha-linux-gnu/include/bits/fcntl.h self.O_APPEND = 0o0010 self.O_CREAT = 0o1000 elif is_hppa32() or is_hppa64(): # /usr/hppa-linux-gnu/include/bits/fcntl.h self.O_APPEND = 0o0010 self.O_CREAT = 0o0400 self.hijack_fd() return @register_command class ScanSectionCommand(GenericCommand): """Find memory addresses mapped across different regions.""" _cmdline_ = "scan-section" _category_ = "03-a. Memory - Search" _aliases_ = ["peek-pointers", "leakfind", "p2p"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("haystack", metavar="HAYSTACK", nargs="?", help="where to search for the needle.") parser.add_argument("needle", metavar="NEEDLE", nargs="?", help="what to search for.") _syntax_ = parser.format_help() _example_ = [ "{0:s} stack binary # scan binary address from stack", "{0:s} stack libc # scan libc address from stack", "{0:s} stack heap # scan heap address from stack", "{0:s} heap libc # scan libc address from heap", "{0:s} 0x555555772000-0x555555774000 libc # support address range", "{0:s} any any", ] _example_ = "\n".join(_example_).format(_cmdline_) def scan(self, haystack, needle): needle_sections = [] haystack_sections = [] if haystack and "0x" in haystack: try: start, end = AddressUtil.parse_string_range(haystack) haystack_sections.append((start, end, "")) except ValueError: pass if needle and "0x" in needle: try: start, end = AddressUtil.parse_string_range(needle) needle_sections.append((start, end)) except ValueError: pass for sect in ProcessMap.get_process_maps(): if haystack is None or haystack in sect.path: haystack_sections.append((sect.page_start, sect.page_end, os.path.basename(sect.path))) if needle is None or needle in sect.path: needle_sections.append((sect.page_start, sect.page_end)) for hstart, hend, hname in haystack_sections: try: mem = read_memory(hstart, hend - hstart) except gdb.MemoryError: continue for i, target in enumerate(slice_unpack(mem, current_arch.ptrsize)): for nstart, nend in needle_sections: if not (nstart <= target < nend): continue # match deref = AddressUtil.recursive_dereference_to_string(hstart + i * current_arch.ptrsize) if hname != "": name = Color.colorify(hname, "yellow") gef_print("{:s}: {:s}".format(name, deref)) else: gef_print(" {:s}".format(deref)) break return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @require_arch_set def do_invoke(self, args): haystack = args.haystack needle = args.needle info("Searching for addresses in '{:s}' that point to '{:s}'".format( Color.yellowify(haystack), Color.yellowify(needle), )) if haystack == "any": haystack = None elif haystack in ["binary", "bin"]: haystack = Path.get_filepath(append_proc_root_prefix=False) if is_qemu_user() and haystack is None: haystack = "[code]" if needle == "any": needle = None elif needle in ["binary", "bin"]: needle = Path.get_filepath(append_proc_root_prefix=False) if is_qemu_user() and needle is None: needle = "[code]" self.scan(haystack, needle) return @register_command class FindSyscallCommand(GenericCommand, BufferingOutput): """Find the syscall gadget.""" _cmdline_ = "find-syscall" _category_ = "03-a. Memory - Search" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("section", metavar="SECTION_OR_START_ADDR", nargs="?", help="section name or starting address of search range.") parser.add_argument("size", metavar="SIZE", nargs="?", help="search range size. valid only when a start address is specified.") parser.add_argument("-b", "--nb-insns-before", type=AddressUtil.parse_address, default=0, help="the number of previous lines when print syscall instruction.") parser.add_argument("-s", "--max-region-size", type=AddressUtil.parse_address, default=0x1000_0000, help="maximum search region size. (default: %(default)#x; 0: infinity)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} libc # search syscall from libc .text", "{0:s} binary # 'binary' means the area executable itself (usermode only)", "{0:s} 0x400000-0x404000 # search syscall from specific range", "{0:s} 0x400000 0x4000 # another valid format", ] _example_ = "\n".join(_example_).format(_cmdline_) def print_section(self, section): if isinstance(section, Address): section = section.section if section is None: return title = "In " if section.path: title += "'{}' ".format(Color.blueify(section.path)) title += "{:#x}-{:#x} [{}] ({:#x} bytes)".format( section.page_start, section.page_end, section.permission, section.page_end - section.page_start, ) self.info_add_out(title) return def print_loc(self, loc): if is_x86(): show_opcodes_size = Config.get_gef_setting("context_code.show_opcodes_size_x64_x86") else: show_opcodes_size = Config.get_gef_setting("context_code.show_opcodes_size") nb_lines = 1 # fix loc and nb_lines if self.args.nb_insns_before > 0: a = Disasm.gdb_get_nth_previous_instruction_address(loc, self.args.nb_insns_before) if a is not None: loc = a nb_lines += self.args.nb_insns_before # disasm res = Disasm.gdb_disassemble(loc, nb_lines) if res: for insn in res: self.out.append(insn.colored_text(show_opcodes_size)) # add blank line if self.args.nb_insns_before > 0: self.out.append("") return def read_data(self, chunk_addr, size, end_address): read_size = min(size, end_address - chunk_addr) try: mem = read_memory(chunk_addr, read_size) except (gdb.MemoryError, ValueError, OverflowError): # cannot access memory this range. It doesn't make sense to try any more self.err_add_out("skip due to memory access error") return None return mem def search_pattern_by_address(self, pattern, start_address, end_address): """Search for a pattern within a range defined by arguments.""" step = 0x400 * get_pagesize() locations = [] old_mem = b"" tqdm = GefUtil.get_tqdm() for chunk_addr in tqdm(range(start_address, end_address, step), leave=False): # read mem = self.read_data(chunk_addr, step, end_address) if mem is None: break # cases where step boundaries are crossed if old_mem and mem: ofs = len(pattern) - 1 tmp = old_mem[-ofs:] + mem[:ofs] r = tmp.find(pattern) if r >= 0: locations.append(chunk_addr - ofs + r) # normal case for match in re.finditer(pattern, mem): start = chunk_addr + match.start() locations.append(start) old_mem = mem return locations def process_by_address(self, pattern, start_address, end_address): info("Searching for {:s} in {:#x}-{:#x}".format( Color.yellowify(pattern), start_address, end_address, )) ret = self.search_pattern_by_address(pattern, start_address, end_address) for loc in ret: self.print_loc(loc) return def process_by_section(self, pattern, section_name=None): """Search for a pattern within selected (or whole) memory.""" if section_name is None: self.info_add_out("Searching for {:s} in {:s}".format(Color.yellowify(pattern), "whole memory")) else: self.info_add_out("Searching for {:s} in {:s}".format(Color.yellowify(pattern), section_name)) maps_generator = ProcessMap.get_process_maps_exclude_special_regions(allow_vdso=True) for section in maps_generator: # too big if self.args.max_region_size != 0: if section.size >= self.args.max_region_size: self.quiet_info_add_out( "{:#x}-{:#x} is skipped due to size ({:#x}) >= MAX_REGION_SIZE ({:#x})".format( section.page_start, section.page_end, section.size, self.args.max_region_size, ) ) continue # permission filter if not section.is_executable(): continue # specific section name filter if section_name and section_name not in section.path: continue # search self.print_section(section) start = section.page_start end = section.page_end ret = self.search_pattern_by_address(pattern, start, end) # search for loc in ret: self.print_loc(loc) # check process is alive if not is_alive(): self.err_add_out("The process is dead") break return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @require_arch_set def do_invoke(self, args): if not current_arch.syscall_insn: err("Unsupported arch") return pattern = current_arch.syscall_insn self.out = [] if args.section and args.size: # the case `find-syscall 0x400000 0x4000` try: start = int(args.section, 16) end = start + int(args.size, 16) except ValueError: self.usage() return self.process_by_address(pattern, start, end) elif args.section and re.match(r"(0x)?[0-9a-fA-F]+-(0x)?[0-9a-fA-F]+", args.section): # the case `find-syscall 0x400000-0x404000` etc. try: start, end = AddressUtil.parse_string_range(args.section) except ValueError: self.usage() return self.process_by_address(pattern, start, end) elif args.section: # search from specific section if args.section in ["binary", "bin"]: section_name = Path.get_filepath(append_proc_root_prefix=False) else: section_name = args.section self.process_by_section(pattern, section_name) else: # search whole memory self.process_by_section(pattern) self.print_output(check_terminal_size=True) return @register_command class SearchPatternCommand(GenericCommand): """Search for a pattern in memory.""" _cmdline_ = "search-pattern" _category_ = "03-a. Memory - Search" _aliases_ = ["xfind", "xf"] parser = argparse.ArgumentParser(prog=_cmdline_) group1 = parser.add_mutually_exclusive_group() group1.add_argument("--hex", action="store_true", help="interpret PATTERN as hex. invalid characters are ignored.") group1.add_argument("--hex-regex", action="store_true", help="interpret PATTERN as hex with REGEX-style. space is ignored.") parser.add_argument("-d", "--disable-utf16", action="store_true", help="disable utf16 search if PATTERN is ascii string.") parser.add_argument("-b", "--big", action="store_true", help="interpret PATTERN as big endian if PATTERN is 0xXXXXXXXX style.") parser.add_argument("-a", "--aligned", type=AddressUtil.parse_address, default=1, help="alignment unit. (default: %(default)s)") parser.add_argument("-p", "--perm", default="r??", help="the filter by permission. (default: %(default)s)") parser.add_argument("-i", "--interval", type=AddressUtil.parse_address, help="the interval to skip searching from the last found position within the same section.") parser.add_argument("-l", "--limit", type=AddressUtil.parse_address, help="the limit of the search result.") parser.add_argument("-s", "--max-region-size", type=AddressUtil.parse_address, default=0x1000_0000, help="maximum search region size. (default: %(default)#x; 0: infinity)") parser.add_argument("--phys", action="store_true", help="treat START_ADDR as a physical address (available in qemu-system).") group2 = parser.add_mutually_exclusive_group() group2.add_argument("-k", "--kernel-only", action="store_true", help="search from kernel area (available in qemu-system).") group2.add_argument("-u", "--user-only", action="store_true", help="search from user area (available in qemu-system).") parser.add_argument("-v", "--verbose", action="store_true", help="shows the section currently being searched.") parser.add_argument("-q", "--quiet", action="store_true", help="suppress warnings.") parser.add_argument("-Q", "--quiet-region", action="store_true", help="suppress region information.") parser.add_argument("-S", "--quiet-symbol", action="store_true", help="not shown even if symbol exists.") parser.add_argument("pattern", metavar="PATTERN", help='search target value. "double-escaped string" or 0xXXXXXXXX style.') parser.add_argument("section", metavar="SECTION_OR_START_ADDR", nargs="?", help="section name or starting address of search range.") parser.add_argument("size", metavar="SIZE", nargs="?", help="search range size. valid only when a start address is specified.") _syntax_ = parser.format_help() _example_ = [ "{0:s} ABCD # search for 'ABCD' from whole memory", '{0:s} "\\\\x41\\\\x42\\\\x43\\\\x44" # double-escaped string is also valid', '{0:s} --hex "41 42 43 44" # another valid format', '{0:s} --hex-regex "4[0-9]424344" # hex regex search', "{0:s} 0x44434241 # search for 0x44434241 (='ABCD') from whole memory", "{0:s} 0x555555554000 stack # search for 0x555555554000 (6byte) from stack", "{0:s} 0x0000555555554000 stack # search for 0x0000555555554000 (8byte) from stack", "{0:s} AAAA binary # 'binary' means the area executable itself (usermode only)", "{0:s} AAAA 0x400000-0x404000 # search for 'AAAA' from specific range", "{0:s} AAAA 0x400000 0x4000 # another valid format", "{0:s} AAAA heap --aligned 16 # search for 'AAAA' with 16-byte alignment", "{0:s} AAAA -p r?- # search for 'AAAA' from r-- or rw-, but not from r-x or rwx", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "To efficiently search large memory regions, the search is usually performed internally by dividing", "the region into chunks. The chunk size is 0x10 pages for qemu-system, and 0x400 pages for others.", "", "However, when the --hex-regex option is enabled, this chunked search is disabled,", "because it is difficult to implement regular expression searches that span multiple chunks.", ] _note_ = "\n".join(_note_) def is_aligned(self, addr): a = self.args.aligned or 1 return (a <= 1) or (addr % a == 0) def accept_match(self, start_addr, locations): # alignment if not self.is_aligned(start_addr): return False # interval if self.args.interval: if locations: last_loc = locations[-1] if start_addr < last_loc[0] + self.args.interval: return False return True def check_limit(self): if self.args.limit: if self.args.limit <= self.found_count: return True return False def print_section(self, section): if self.args.quiet_region: return if isinstance(section, Address): section = section.section if section is None: return title = "In " if section.path: title += "'{}' ".format(Color.blueify(section.path)) title += "{:#x}-{:#x} [{}] ({:#x} bytes)".format( section.page_start, section.page_end, section.permission, section.page_end - section.page_start, ) ok(title) return def print_loc(self, loc): if self.args.aligned and loc[0] % self.args.aligned: return h = hexdump(loc[1], 0x10, base=loc[0], show_symbol=not self.args.quiet_symbol) gef_print(" {:s}".format(h)) return def read_data(self, chunk_addr, size, end_address): read_size = min(size, end_address - chunk_addr) try: if self.args.phys: mem = read_physmem(chunk_addr, read_size) else: mem = read_memory(chunk_addr, read_size) except (gdb.MemoryError, ValueError, OverflowError): # cannot access memory this range. It doesn't make sense to try any more if self.args.verbose: err("Skip due to memory access error") return None return mem def search_pattern_by_address(self, pattern, start_address, end_address): """Search for a pattern within a range defined by arguments.""" if isinstance(pattern, str): pattern = String.str2bytes(pattern) if self.args.hex_regex: step = end_address - start_address elif is_qemu_system(): step = 0x10 * get_pagesize() else: step = 0x400 * get_pagesize() locations = [] old_mem = b"" tqdm = GefUtil.get_tqdm(self.args.verbose) for chunk_addr in tqdm(range(start_address, end_address, step), leave=False): # read mem = self.read_data(chunk_addr, step, end_address) if mem is None: break # for regex if self.args.hex_regex: mem = mem.hex().encode() # cases where step boundaries are crossed if not self.args.hex_regex and old_mem and mem: # Considering cases where the step size boundary is crossed, # the check is performed within the range of -ofs to ofs size. """ pattern "ABCD" ofs = len(pattern) - 1 = 3 The scope of consideration is limited to this range. <-- step --> <-- step --> | ... xxxA | BCDxxx ... | | ... xxxAB | CDxxx ... | | ... xxxABC | Dxxx ... | <-------> -ofs ofs """ ofs = len(pattern) - 1 tmp_mem = old_mem[-ofs:] + mem[:ofs] r = tmp_mem.find(pattern) if r >= 0: if self.accept_match(chunk_addr - ofs + r, locations): # read dump data data = (old_mem[-ofs:] + mem)[r:][:0x10] # add locations.append((chunk_addr - ofs + r, data)) self.found_count += 1 # loop check if self.check_limit(): info("Limit reached, no more matches shown") return locations # fall through # normal case for match in re.finditer(pattern, mem): if self.args.hex_regex: if match.start() % 2: continue start_pos = match.start() // 2 else: start_pos = match.start() start = chunk_addr + start_pos # check filter if not self.accept_match(start, locations): continue # read dump data if self.args.hex_regex: data = bytes.fromhex(mem.decode())[start_pos:][:0x10] else: data = mem[start_pos:][:0x10] if len(data) < 0x10: lack = self.read_data(chunk_addr + step, 0x10 - len(data), end_address) if lack: data += lack # add locations.append((start, data)) self.found_count += 1 # loop check if self.check_limit(): info("Limit reached, no more matches shown") return locations old_mem = mem return locations def process_by_address(self, patterns, start, end): extra = " (phys)" if self.args.phys else "" for pattern in patterns: if not pattern: continue info("Searching for '{:s}' in {:#x}-{:#x}{:s}".format( Color.yellowify(pattern), start, end, extra, )) self.found_count = 0 ret = self.search_pattern_by_address(pattern, start, end) for found_loc in ret: self.print_loc(found_loc) return def get_process_maps_qemu_system(self): res = PageMap.get_page_maps_by_pagewalk("pagewalk --quiet --no-pager --disable-color") res = sorted(set(res.splitlines())) res = list(filter(lambda line: line.endswith("]"), res)) res = list(filter(lambda line: "[+]" not in line, res)) res = list(filter(lambda line: "*" not in line, res)) for line in res: if is_x86() and "ACCESSED" not in line: continue # extract lines = line.split() addr_start, addr_end = [int(x, 16) for x in lines[0].split("-")] # non valid addr if not is_valid_addr(addr_start): continue # parse if is_x86(): perm = Permission.from_process_maps(lines[5][1:].lower()) elif is_arm32(): perm = line.split("/")[-1][:3] perm = Permission.from_process_maps(perm.lower()) elif is_arm64(): perm = line.split("/")[-1][:3] perm = Permission.from_process_maps(perm.lower()) yield Section(page_start=addr_start, page_end=addr_end, permission=perm) return None def search_pattern_by_section(self, pattern, section_name=None): """Search for a pattern within selected (or whole) memory.""" if is_qemu_system(): maps_generator = self.get_process_maps_qemu_system() else: maps_generator = ProcessMap.get_process_maps_exclude_special_regions(allow_vdso=True) for section in maps_generator: # user or kernel memory address filter if self.args.user_only: if AddressUtil.is_msb_on(section.page_start): continue if self.args.kernel_only: if not AddressUtil.is_msb_on(section.page_start): continue # too big if self.args.max_region_size != 0: if section.size >= self.args.max_region_size: self.quiet_info( "{:#x}-{:#x} is skipped due to size ({:#x}) >= MAX_REGION_SIZE ({:#x})".format( section.page_start, section.page_end, section.size, self.args.max_region_size, ) ) continue # permission filter if not section.is_readable(): continue if self.args.perm[1] in "wW" and not section.is_writable(): continue if self.args.perm[1] in "-_" and section.is_writable(): continue if self.args.perm[2] in "xX" and not section.is_executable(): continue if self.args.perm[2] in "-_" and section.is_executable(): continue # specific section name filter if section_name and section_name not in section.path: continue # verbose: always print section before search if self.args.verbose: self.print_section(section) # search ret = self.search_pattern_by_address(pattern, section.page_start, section.page_end) # default: print section if only found if not self.args.verbose: if ret: self.print_section(section) # print for loc in ret: self.print_loc(loc) # loop check if not is_alive(): err("The process is dead") break if self.check_limit(): info("Limit reached, no more matches shown") break return def process_by_section(self, patterns, section_name=None): extra = " (phys)" if self.args.phys else "" for pattern in patterns: if not pattern: continue if section_name is None: area = "whole memory" else: area = section_name gef_print(titlify("")) info("Searching for '{:s}' in {:s}{:s}".format( Color.yellowify(pattern), area, extra, )) self.found_count = 0 self.search_pattern_by_section(pattern, section_name) return def create_patterns(self): if self.args.hex_regex: pattern = self.args.pattern.lower().replace(" ", "") return (pattern,) # create normal pattern if self.args.hex: # "41414141" -> "\x41\x41\x41\x41" pattern = re.sub(r"[^0-9a-fA-F]", "", self.args.pattern) if len(pattern) % 2 != 0: err("Hex pattern length is odd") return None pattern = "".join(["\\x" + x for x in slicer(pattern, 2)]) elif String.is_hex(self.args.pattern): # "0x41414141" -> "\x41\x41\x41\x41" if self.args.big or Endian.is_big_endian(): pattern = "".join(["\\x" + x for x in slicer(self.args.pattern[2:], 2)]) else: pattern = "".join(["\\x" + x for x in slicer(self.args.pattern[2:], 2)[::-1]]) else: pattern = self.args.pattern def isascii(string): val = codecs.escape_decode(string)[0] return all(0x20 <= c < 0x7f for c in val) # create utf16 pattern pattern_utf16 = None if not self.args.disable_utf16: if isascii(pattern) and "\\" not in pattern: pattern_utf16 = "".join([x + "\\x00" for x in pattern]) return (pattern, pattern_utf16) @parse_args @only_if_gdb_running def do_invoke(self, args): if args.kernel_only or args.user_only: if not is_qemu_system(): err("Unsupported in this gdb mode (qemu-system only)") return # check permission if args.phys: info("Permission is ignored") else: if len(args.perm) != 3: err("Invalid permission length") return if args.perm[0] not in "rR": err("Permission needs to start by `r`") return if args.perm[1] not in "wW-_?" or args.perm[2] not in "xX-_?": err("Invalid permission") return # check parameters if args.interval and args.interval <= 0: err("Invalid interval value") return if args.limit and args.limit <= 0: err("Invalid limit value") return if args.max_region_size and args.max_region_size < 0x1000: err("Invalid max-region-size") return # prepare pattern patterns = self.create_patterns() if patterns is None: return if args.section and args.size: # the case `find AAAA 0x400000 0x4000` try: start = int(args.section, 16) end = start + int(args.size, 16) except ValueError: self.usage() return self.process_by_address(patterns, start, end) elif args.section and re.match(r"(0x)?[0-9a-fA-F]+-(0x)?[0-9a-fA-F]+", args.section): # the case `find AAAA 0x400000-0x404000` try: start, end = AddressUtil.parse_string_range(args.section) except ValueError: self.usage() return self.process_by_address(patterns, start, end) else: if args.phys: err("--phys mode needs address information") return if args.section: # search from specific section if is_qemu_system() or is_kgdb() or is_vmware(): err("Unsupported") return if args.section in ["binary", "bin"]: section_name = Path.get_filepath(append_proc_root_prefix=False) else: section_name = args.section self.process_by_section(patterns, section_name) else: # search whole memory self.process_by_section(patterns) return @register_command class PtrDemangleCommand(GenericCommand): """Demangle a mangled value by PTR_MANGLE.""" _cmdline_ = "ptr-demangle" _category_ = "02-f. Process Information - Security" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("value", metavar="VALUE", nargs="?", type=AddressUtil.parse_address, help="the value to demangle.") group.add_argument("--source", action="store_true", help="shows the source instead of displaying demangled value.") _syntax_ = parser.format_help() @staticmethod @Cache.cache_until_next def get_cookie(): if is_in_kernel(): return None if is_arm32_cortex_m(): return None if is_qiling(): return None try: if is_x86_64(): tls = current_arch.get_tls() cookie = read_int_from_memory(tls + 0x30) return cookie elif is_x86_32(): tls = current_arch.get_tls() cookie = read_int_from_memory(tls + 0x18) return cookie elif is_arm32() or is_arm64(): cookie_ptr = AddressUtil.parse_address("&__pointer_chk_guard_local") cookie = read_int_from_memory(cookie_ptr) return cookie except (gdb.error, OverflowError): pass # generic try: auxv = Auxv.get_auxiliary_values() if auxv is None: Cache.reset_gef_caches() auxv = Auxv.get_auxiliary_values() if auxv and "AT_RANDOM" in auxv: if is_s390x(): cookie = read_int_from_memory(auxv["AT_RANDOM"]) & 0x00ff_ffff_ffff_ffff else: cookie = read_int_from_memory(auxv["AT_RANDOM"] + current_arch.ptrsize) if cookie != 0: return cookie except gdb.error: pass return None @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @exclude_specific_arch(arch=("SPARC32", "XTENSA", "CRIS")) @require_arch_set def do_invoke(self, args): if args.source: s = GefUtil.get_source(current_arch.decode_cookie) gef_print(s) return cookie = self.get_cookie() if cookie is None: return info("Cookie is {:s}".format(Color.colorify_hex(cookie, "bold"))) decoded = current_arch.decode_cookie(args.value, cookie) decoded = ProcessMap.lookup_address(decoded) decoded_sym = Symbol.get_symbol_string(decoded.value) if is_valid_addr(decoded.value): valid_msg = Color.colorify("valid", "bold green") else: valid_msg = Color.colorify("invalid", "bold red") info("Decoded value is {!s}{:s} [{:s}]".format(decoded, decoded_sym, valid_msg)) return @register_command class PtrMangleCommand(GenericCommand): """Mangle a pointer value by PTR_MANGLE.""" _cmdline_ = "ptr-mangle" _category_ = "02-f. Process Information - Security" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("value", metavar="VALUE", nargs="?", type=AddressUtil.parse_address, help="the value to mangle.") group.add_argument("--source", action="store_true", help="shows the source instead of displaying mangled value.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @exclude_specific_arch(arch=("SPARC32", "XTENSA", "CRIS")) @require_arch_set def do_invoke(self, args): if args.source: s = GefUtil.get_source(current_arch.encode_cookie) gef_print(s) return cookie = PtrDemangleCommand.get_cookie() if cookie is None: return info("Cookie is {:s}".format(Color.colorify_hex(cookie, "bold"))) encoded = current_arch.encode_cookie(args.value, cookie) info("Encoded value is {:#x}".format(encoded)) return @register_command class SearchMangledPtrCommand(GenericCommand): """Search for mangled values in RW memory.""" _cmdline_ = "search-mangled-ptr" _category_ = "02-f. Process Information - Security" _aliases_ = ["cookie"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-v", "--verbose", action="store_true", help="shows the section currently being searched.") _syntax_ = parser.format_help() def print_section(self, section): if isinstance(section, Address): section = section.section if section is None: return title = "In " if section.path: title += "'{}' ".format(Color.blueify(section.path)) title += "({:#x}-{:#x} [{}])".format(section.page_start, section.page_end, section.permission) ok(title) return def print_loc(self, addr, value, decoded): addr_sym = Symbol.get_symbol_string(addr) decoded = ProcessMap.lookup_address(decoded) decoded_sym = Symbol.get_symbol_string(decoded.value) if is_valid_addr(decoded.value): valid_msg = Color.colorify("valid", "bold green") else: valid_msg = Color.colorify("invalid", "bold red") base_address_color = Config.get_gef_setting("theme.dereference_base_address") width = AddressUtil.get_format_address_width() addr = Color.colorify("{:#0{:d}x}".format(addr, width), base_address_color) gef_print(" {:s}{:s}: {:#x} (={!s}{:s}) [{:s}]".format(addr, addr_sym, value, decoded, decoded_sym, valid_msg)) return def search_mangled_ptr(self, start_address, end_address, cookie): """Search for a mangled pointer within a range defined by arguments.""" if is_qemu_system(): step = get_pagesize() else: step = 0x400 * get_pagesize() locations = [] for chunk_addr in range(start_address, end_address, step): if chunk_addr + step > end_address: chunk_size = end_address - chunk_addr else: chunk_size = step try: mem = read_memory(chunk_addr, chunk_size) except gdb.MemoryError: # cannot access memory this range. It doesn't make sense to try any more break for i, value in enumerate(slice_unpack(mem, current_arch.ptrsize)): decoded = current_arch.decode_cookie(value, cookie) if not is_valid_addr(decoded): continue addr = chunk_addr + i * current_arch.ptrsize locations.append((addr, value, decoded)) del mem return locations @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @exclude_specific_arch(arch=("SPARC32", "XTENSA", "CRIS")) @require_arch_set def do_invoke(self, args): # init cookie = PtrDemangleCommand.get_cookie() if cookie is None: return info("Cookie is {:s}".format(Color.colorify_hex(cookie, "bold"))) # check if current_arch.decode_cookie(0, 1) == 0: err("In this architecture, the value is not encrypted with cookies") return # search maps_generator = ProcessMap.get_process_maps() for section in maps_generator: if not section.permission & Permission.READ: continue if not section.permission & Permission.WRITE: continue if args.verbose: self.print_section(section) # verbose: always print section before search start = section.page_start end = section.page_end ret = self.search_mangled_ptr(start, end, cookie) if ret: if not args.verbose: self.print_section(section) # default: print section if found for addr, value, decoded in ret: self.print_loc(addr, value, decoded) if not is_alive(): err("The process is dead") break return @register_command class SearchCfiGadgetsCommand(GenericCommand, BufferingOutput): """Search for CFI-valid, controllable gadgets in the executable area.""" _cmdline_ = "search-cfi-gadgets" _category_ = "03-a. Memory - Search" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def find_endbr(self, start, end): if is_x86_32(): endbr = b"\xf3\x0f\x1e\xfb" # endbr32 else: endbr = b"\xf3\x0f\x1e\xfa" # endbr64 data = read_memory(start, end - start) pos = -1 addrs = [] while True: pos = data.find(endbr, pos + 1) if pos == -1: break addrs.append(start + pos) return addrs def filter_gadgets(self, endbr_addrs): filtered_addrs = [] for addr in endbr_addrs: start = addr inscount = 0 valid = True while True: insn = get_insn(addr) inscount += 1 length = len(insn.opcodes) addr += length if current_arch.is_ret(insn): break if current_arch.is_call(insn): if insn.operands[0].startswith("0x"): valid = False break if current_arch.is_jump(insn): if insn.operands[0].startswith("0x"): valid = False # If the GOT cannot be modified, you can jump directly to the jump destination, # so there is no need to consider it in practice. elif inscount == 2 and "[rip" in insn.operands[0]: # 0x555555558630 f30f1efa endbr64 # 0x555555558634 ff2536e90100 jmp QWORD PTR [rip+0x1e936] # 0x555555576f70 r = re.search(r"# (0x\w+)", insn.operands[0]) if r: v = ProcessMap.lookup_address(int(r.group(1), 16)) if not v.section.is_writable(): valid = False break if "XMMWORD" in "".join(insn.operands): valid = False break if valid: filtered_addrs.append([start, inscount]) return filtered_addrs def disasm_addrs(self, candidates): try: __import__("capstone") for start, inscount in candidates: res = gdb.execute("capstone-disassemble {:#x} --length {:d}".format(start, inscount), to_string=True) self.out.append(res) except ImportError: for start, inscount in candidates: self.out.extend([str(x) for x in Disasm.gdb_disassemble(start, nb_insn=inscount)]) return def exec_search(self): # get map entry maps = ProcessMap.get_process_maps() if maps is None: err("Failed to get maps") return for entry in maps: if not entry.is_executable(): continue if entry.path in ["[vsyscall]"]: continue info("Search from {:s}".format(entry.path)) self.out.append(titlify(entry.path)) addrs = self.find_endbr(entry.page_start, entry.page_end) filtered_addrs = self.filter_gadgets(addrs) self.disasm_addrs(filtered_addrs) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): # get saved filename (for caching) filepath = Path.get_filepath() if filepath is None: output_path = "" else: output_file = "cfi_{:s}.txt".format(os.path.basename(filepath)) output_path = os.path.join(GEF_TEMP_DIR, output_file) if os.path.exists(output_path) and not args.rescan: # read previous output info("A previously used file found, will be reused") self.out = open(output_path).read().splitlines() else: # doit self.out = [] self.exec_search() # save if output_path: open(output_path, "w").write("\n".join(self.out).rstrip()) # output self.print_output() return @register_command class EditFlagsCommand(GenericCommand): """Edit flags in a human friendly way.""" _cmdline_ = "edit-flags" _category_ = "04-b. Register - Modify" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("flagname", metavar="[FLAGNAME(+|-|~) ...]", nargs="*", help="the flag name to edit.") parser.add_argument("-v", "--verbose", action="store_true", help="show the bit information of the flag register.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # show the flag register", "{0:s} zero+ # set ZERO flag", "{0:s} direction- # unset DIRECTION flag", "{0:s} sign~ # toggle SIGN flag", "{0:s} -v # verbose output", ] _example_ = "\n".join(_example_).format(_cmdline_) def edit_flags(self, flag_names): for flag in flag_names: if len(flag) < 2: err("Too short length of the name") return if flag[-1] not in ("+", "-", "~"): err("Invalid action for flag '{:s}'".format(flag)) return for flag in flag_names: action = flag[-1] name = flag[:-1].lower() if is_x86(): dic = { "id": "identification", "ac": "align", "vm": "virtualx86", "rf": "resume", "nt": "nested", "of": "overflow", "df": "direction", "if": "interrupt", "tf": "trap", "sf": "sign", "zf": "zero", "af": "adjust", "pf": "parity", "cf": "carry", } if name in dic: name = dic[name] if name not in current_arch.flags_table.values(): err("Invalid flag name '{:s}'".format(flag[:-1])) continue for off in current_arch.flags_table: if current_arch.flags_table[off] != name: continue old_flag = get_register(current_arch.flag_register) if action == "+": new_flags = old_flag | (1 << off) elif action == "-": new_flags = old_flag & ~(1 << off) else: new_flags = old_flag ^ (1 << off) gdb.execute("set ({:s}) = {:#x}".format(current_arch.flag_register, new_flags)) return def verbose_x86(self): eflags = get_register("$eflags") gef_print("{:s} {:s}".format(BitInfo.bits_split(eflags, 32), Color.boldify("MASK"))) def c(msg): mask = int(msg.split()[0], 16) if eflags & mask: color = "bold" else: color = "" return Color.colorify(msg, color) elements = [ "|| |||| |||| |||| |||| |||+- " + c("0x000001 [CF] Carry flag"), "|| |||| |||| |||| |||| ||+-- " + c("0x000002 Reserved (always 1)"), "|| |||| |||| |||| |||| |+--- " + c("0x000004 [PF] Parity flag"), "|| |||| |||| |||| |||| +---- " + c("0x000008 Reserved (always 0)"), "|| |||| |||| |||| ||||", "|| |||| |||| |||| |||+------ " + c("0x000010 [AF] Adjust flag (for BCD calc)"), "|| |||| |||| |||| ||+------- " + c("0x000020 Reserved (always 0)"), "|| |||| |||| |||| |+-------- " + c("0x000040 [ZF] Zero flag"), "|| |||| |||| |||| +--------- " + c("0x000080 [SF] Sign flag"), "|| |||| |||| ||||", "|| |||| |||| |||+----------- " + c("0x000100 [TF] Trap flag (single step)"), "|| |||| |||| ||+------------ " + c("0x000200 [IF] Interrupt enable flag"), "|| |||| |||| |+------------- " + c("0x000400 [DF] Direction flag"), "|| |||| |||| +-------------- " + c("0x000800 [OF] Overflow flag"), "|| |||| ||||", "|| |||| ||++---------------- " + c("0x003000 [IOPL] I/O privilege level (2bit)"), "|| |||| |+------------------ " + c("0x004000 [NT] Nested task flag"), "|| |||| +------------------- " + c("0x008000 Reserved (always 0)"), "|| ||||", "|| |||+--------------------- " + c("0x010000 [RF] Resume flag"), "|| ||+---------------------- " + c("0x020000 [VM] Virtual 8086 mode flag"), "|| |+----------------------- " + c("0x040000 [AC] Alignment check flag"), "|| +------------------------ " + c("0x080000 [VIF] Virtual interrupt flag"), "||", "|+-------------------------- " + c("0x100000 [VIP] Virtual interrupt pending"), "+--------------------------- " + c("0x200000 [ID] Able to use CPUID instruction"), ] gef_print("\n".join([" " * 14 + e for e in elements])) return def verbose_arm32(self): cpsr = get_register("$cpsr") gef_print("{:s} {:s}".format(BitInfo.bits_split(cpsr, 32), Color.boldify("MASK"))) def c(msg): mask = int(msg.split()[0], 16) if cpsr & mask: color = "bold" else: color = "" return Color.colorify(msg, color) elements = [ "|||| |||| |||| |||| |||| |||| |||+-++++- " + c("0x0000001f [M] Mode field (5bit)"), "|||| |||| |||| |||| |||| |||| ||| " + " User:0b10000 FIQ:0b10001 IRQ:0b10010", "|||| |||| |||| |||| |||| |||| ||| " + " Supervisor:0b10011 Monitor:0b10110 Abort:0b10111", "|||| |||| |||| |||| |||| |||| ||| " + " Hyp:0b11010 Undefined:0b11011 System:0b11111", "|||| |||| |||| |||| |||| |||| |||", "|||| |||| |||| |||| |||| |||| ||+------- " + c("0x00000020 [T] Thumb execution state bit"), "|||| |||| |||| |||| |||| |||| |+-------- " + c("0x00000040 [F] FIQ mask bit"), "|||| |||| |||| |||| |||| |||| +--------- " + c("0x00000080 [I] IRQ mask bit"), "|||| |||| |||| |||| |||| ||||", "|||| |||| |||| |||| |||| |||+----------- " + c("0x00000100 [A] Asynchronous abort mask bit"), "|||| |||| |||| |||| |||| ||+------------ " + c("0x00000200 [E] Endianness execution state bit"), "|||| |++------------++++-++------------- " + c("0x0600fc00 [IT] If-Then execution state bits for Thumb IT instruction"), "|||| | | |||| ||||", "|||| | | |||| ++++--------------------- " + c("0x000f0000 [GE] Greater than or Equal flags for SIMD instruction"), "|||| | | ||||", "|||| | | ++++-------------------------- " + c("0x00f00000 Reserved"), "|||| | |", "|||| | +------------------------------- " + c("0x01000000 [J] Jazelle bit"), "|||| +---------------------------------- " + c("0x08000000 [Q] Cumulative saturation bit"), "||||", "|||+------------------------------------ " + c("0x10000000 [V] Overflow condition flag"), "||+------------------------------------- " + c("0x20000000 [C] Carry condition flag"), "|+-------------------------------------- " + c("0x40000000 [Z] Zero condition flag"), "+--------------------------------------- " + c("0x80000000 [N] Negative condition flag"), ] gef_print("\n".join([" " * 2 + e for e in elements])) return def verbose_arm64(self): cpsr = get_register("$cpsr") gef_print("{:s} {:s}".format(BitInfo.bits_split(cpsr, 32), Color.boldify("MASK"))) def c(msg): mask = int(msg.split()[0], 16) if cpsr & mask: color = "bold" else: color = "" return Color.colorify(msg, color) elements_aarch64_state = [ "|||| |||| |||| |||| |||| |||| |||| ||++- " + c("0x00000003 [M.SP] Selected stack pointer (2bit)"), "|||| |||| |||| |||| |||| |||| |||| ++--- " + c("0x0000000c [M.EL] Exception level (2bit)"), "|||| |||| |||| |||| |||| |||| ||||", "|||| |||| |||| |||| |||| |||| |||+------ " + c("0x00000010 [M.S] Execution state (AArch64:0, AArch32:1)"), "|||| |||| |||| |||| |||| |||| ||+------- " + c("0x00000020 Reserved (always 0)"), "|||| |||| |||| |||| |||| |||| |+-------- " + c("0x00000040 [F] FIQ interrupt mask bit"), "|||| |||| |||| |||| |||| |||| +--------- " + c("0x00000080 [I] IRQ interrupt mask bit"), "|||| |||| |||| |||| |||| ||||", "|||| |||| |||| |||| |||| |||+----------- " + c("0x00000100 [A] SError interrupt mask bit"), "|||| |||| |||| |||| |||| ||+------------ " + c("0x00000200 [D] Debug exception mask bit"), "|||| |||| |||| |||| |||| ++------------- " + c("0x00000c00 [BTYPE] Branch Type Indicator if FEAT_BTI is implemented"), "|||| |||| |||| |||| ||||", "|||| |||| |||| |||| |||+---------------- " + c("0x00001000 [SSBS] Speculative Store Bypass if FEAT_SSBS is implemented"), "|||| |||| |||| ++++-+++----------------- " + c("0x000fe000 Reserved"), "|||| |||| ||||", "|||| |||| |||+-------------------------- " + c("0x00100000 [IL] Illegal execution state"), "|||| |||| ||+--------------------------- " + c("0x00200000 [SS] Software step flag"), "|||| |||| |+---------------------------- " + c("0x00400000 [PAN] Privileged Access Never if FEAT_PAN is implemented"), "|||| |||| +----------------------------- " + c("0x00800000 [UAO] User Access Override if FEAT_UAO is implemented"), "|||| ||||", "|||| |||+------------------------------- " + c("0x01000000 [DIT] Data Independent Timing if FEAT_DIT is implemented"), "|||| ||+-------------------------------- " + c("0x02000000 [TCO] Tag Check Override if FEAT_MTE is implemented"), "|||| ++--------------------------------- " + c("0x0c000000 Reserved"), "||||", "|||+------------------------------------ " + c("0x10000000 [V] Overflow condition flag"), "||+------------------------------------- " + c("0x20000000 [C] Carry condition flag"), "|+-------------------------------------- " + c("0x40000000 [Z] Zero condition flag"), "+--------------------------------------- " + c("0x80000000 [N] Negative condition flag"), ] elements_aarch32_state = [ "|||| |||| |||| |||| |||| |||| |||| ++++- " + c("0x0000000f [M.A32] AArch32 mode (4bit)"), "|||| |||| |||| |||| |||| |||| |||| " + " User:0b0000 FIQ:0b0001 IRQ:0b0010", "|||| |||| |||| |||| |||| |||| |||| " + " Supervisor:0b0011 Monitor:0b0110 Abort:0b0111", "|||| |||| |||| |||| |||| |||| |||| " + " Hyp:0b1010 Undefined:0b1011 System:0b1111", "|||| |||| |||| |||| |||| |||| ||||", "|||| |||| |||| |||| |||| |||| |||+------ " + c("0x00000010 [M.S] Execution state (AAch64:0, AArch32:1)"), "|||| |||| |||| |||| |||| |||| ||+------- " + c("0x00000020 [T] T32 instruction set (Thumb) state bit"), "|||| |||| |||| |||| |||| |||| |+-------- " + c("0x00000040 [F] FIQ interrupt mask bit"), "|||| |||| |||| |||| |||| |||| +--------- " + c("0x00000080 [I] IRQ interrupt mask bit"), "|||| |||| |||| |||| |||| ||||", "|||| |||| |||| |||| |||| |||+----------- " + c("0x00000100 [A] SError interrupt mask bit"), "|||| |||| |||| |||| |||| ||+------------ " + c("0x00000200 [E] Endianness execution state bit"), "|||| |++------------++++-++------------- " + c("0x0600fc00 [IT] If-Then execution state bits for Thumb IT instruction"), "|||| | | |||| ||||", "|||| | | |||| ++++--------------------- " + c("0x000f0000 [GE] Greater than or Equal flags for SIMD instruction"), "|||| | | ||||", "|||| | | |||+-------------------------- " + c("0x00100000 [IL] Illegal execution state"), "|||| | | ||+--------------------------- " + c("0x00200000 [SS] Software step flag"), "|||| | | |+---------------------------- " + c("0x00400000 [PAN] Privileged Access Never if FEAT_PAN is implemented"), "|||| | | +----------------------------- " + c("0x00800000 [SSBS] Speculative Store Bypass if FEAT_SBSS is implemented"), "|||| | |", "|||| | +------------------------------- " + c("0x01000000 [DIT] Data Independent Timing if FEAT_DIT is implemented"), "|||| +---------------------------------- " + c("0x08000000 [Q] Overflow or saturation flag"), "||||", "|||+------------------------------------ " + c("0x10000000 [V] Overflow condition flag"), "||+------------------------------------- " + c("0x20000000 [C] Carry condition flag"), "|+-------------------------------------- " + c("0x40000000 [Z] Zero condition flag"), "+--------------------------------------- " + c("0x80000000 [N] Negative condition flag"), ] if ((cpsr >> 4) & 1) == 0: elements = elements_aarch64_state else: elements = elements_aarch32_state gef_print("\n".join([" " * 2 + e for e in elements])) return @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): if current_arch.flag_register is None: warn("This command is not supported on this architecture") return self.edit_flags(args.flagname) gef_print(current_arch.flag_register_to_human()) if args.verbose: if is_x86(): self.verbose_x86() elif is_arm32(): self.verbose_arm32() elif is_arm64(): self.verbose_arm64() return @register_command class KillThreadsCommand(GenericCommand): """Invoke pthread_exit(0) for a specific THREAD_ID.""" _cmdline_ = "killthreads" _category_ = "01-g. Debugging Support - Syscall" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("thread_id", metavar="THREAD_ID", nargs="*", type=int, help="the thread id (not TID) to kill.") parser.add_argument("-a", "--all", action="store_true", help="kill all threads except current thread.") parser.add_argument("-e", "--exclude", action="append", type=int, default=[], help="the thread id not to kill.") parser.add_argument("-c", "--commit", action="store_true", help="commit to kill.") _syntax_ = parser.format_help() _example_ = [ '{0:s} 2 3 # kill threads that `Thread Id` is 2 or 3', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "qemu-user", "kgdb", "vmware", "wine")) @exclude_specific_arch(arch=("CRIS",)) @require_arch_set def do_invoke(self, args): # print tid list and exit if not args.all and not args.thread_id and not args.exclude: info("Non-current `Thread Id`(s) from the list are available") gdb.execute("context-threads -i -1") return # list target thread id orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() target_threads = [] for th in gdb.selected_inferior().threads(): if th.num == orig_thread.num: continue if args.all: if th.num not in args.exclude: target_threads.append(th) continue elif args.thread_id: if th.num in args.thread_id: if th.num not in args.exclude: target_threads.append(th) continue else: if th.num not in args.exclude: target_threads.append(th) continue target_threads = sorted(target_threads, key=lambda x: x.num) gef_print("target Thread Id(s) to kill: {}".format([th.num for th in target_threads])) # kill if args.commit: # backup sched_lock = gdb.parameter("scheduler-locking") # change temporarily gdb.execute("set scheduler-locking on", to_string=True) # kill for th in target_threads: th.switch() try: gdb.execute("call pthread_exit(0)") except gdb.error: pass # restore orig_thread.switch() orig_frame.select() gdb.execute("set scheduler-locking {:s}".format(sched_lock), to_string=True) else: warn('This dry run mode skips killing; add "--commit" to proceed') return @register_command class CallSyscallCommand(GenericCommand): """A wrapper for calling syscall easily.""" _cmdline_ = "call-syscall" _category_ = "01-g. Debugging Support - Syscall" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("syscall_name", metavar="SYSCALL_NAME", help="system call name to invoke.") parser.add_argument("syscall_args", metavar="SYSCALL_ARG", nargs="*", type=AddressUtil.parse_address, help="arguments of system call.") _syntax_ = parser.format_help() _example_ = [ '{0:s} write 1 "*(void**)($rsp+0x18)" 15', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "rr", "wine")) @exclude_specific_arch(arch=("CRIS",)) @require_arch_set def do_invoke(self, args): if current_arch is None: err("current_arch is not set") return syscall_table = get_syscall_table() if syscall_table is None: err("The syscall table does not exist") return # get syscall entry for nr, entry in syscall_table.nr_table.items(): if is_x86_64() and nr >= 0x4000_0000: continue if args.syscall_name == entry.name: break else: err("Could not find the system call `{:s}`".format(args.syscall_name)) return # length check if len(args.syscall_args) != len(entry.args_full): err("Argument count mismatch") params = "(" + ", ".join(entry.args_full) + ");" gef_print("Prototype: {:s}{:s}".format(Color.boldify(args.syscall_name), params)) return # title title = "{:s}({:s})".format(args.syscall_name, ", ".join(["{:#x}".format(x) for x in args.syscall_args])) gef_print(titlify(title)) ret = ExecSyscall(nr, args.syscall_args).exec_code() if isinstance(current_arch.return_register, list): for ret_regs in current_arch.return_register: gef_print("{:s} = {:#x}".format(ret_regs, ret["reg"][ret_regs])) else: gef_print("{:s} = {:#x}".format(current_arch.return_register, ret["reg"][current_arch.return_register])) return @register_command class MmapMemoryCommand(GenericCommand): """Allocate a new memory.""" _cmdline_ = "mmap" _category_ = "01-g. Debugging Support - Syscall" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, default=None, help="the address to allocate. (default: %(default)s)") parser.add_argument("size", metavar="SIZE", nargs="?", type=AddressUtil.parse_address, default=get_pagesize(), help="the size to allocate. (default: %(default)s)") parser.add_argument("permission", metavar="PERMISSION", nargs="?", default="rwx", help="the permission to allocate. `_` is interpreted as `-`. (default: %(default)s)") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x10000 0x1000 r-x", "{0:s} 0 0x1000 _wx # '_' means '-'", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return # On the CRIS architecture, setting a value to a register using the gdb `set` command will cause strange behavior. # So even if the assembly code is correct, it should not use this command. @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "rr", "wine")) @exclude_specific_arch(arch=("CRIS",)) @require_arch_set def do_invoke(self, args): # syscall name (mmap or mmap2 or arch-specific) syscall_table = get_syscall_table() if syscall_table is None: err("The syscall table does not exist") return mmap_syscall_name = None for entry in syscall_table.nr_table.values(): if "mmap" not in entry.name: continue if len(entry.arg_regs) != 6: continue mmap_syscall_name = entry.name break if mmap_syscall_name is None: err("Could not find the mmap syscall") return # location if args.location and args.location % get_pagesize(): err("Address is not a multiple of {:#x}".format(get_pagesize())) return # size if args.size < 0 or AddressUtil.get_vmem_end() <= args.size: err("Invalid size") return if args.size % get_pagesize(): err("Size is not a multiple of {:#x}".format(get_pagesize())) return # permission if len(args.permission) != 3: err("Invalid permission") return if re.match(r"[-_r][-_w][-_x]", args.permission): perm = Permission.NONE if args.permission[0] == "r": perm |= Permission.READ if args.permission[1] == "w": perm |= Permission.WRITE if args.permission[2] == "x": perm |= Permission.EXECUTE else: err("Invalid permission") return # flags flags = 0x22 # MAP_ANONYMOUS | MAP_PRIVATE if args.location is not None: flags |= 0x10 # MAP_FIXED if is_mips32() or is_mips64() or is_mipsn32(): flags |= 0x800 # MAP_DENYWRITE (why?) # doit cmd = "call-syscall {:s} {:#x} {:#x} {:#x} {:#x} -1 0".format( mmap_syscall_name, args.location or 0, args.size, perm, flags, ) gdb.execute(cmd) Cache.reset_gef_caches() return @register_command class MunmapMemoryCommand(GenericCommand): """Unmap a mapped memory.""" _cmdline_ = "munmap" _category_ = "01-g. Debugging Support - Syscall" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address to unmap.") parser.add_argument("size", metavar="SIZE", nargs="?", type=AddressUtil.parse_address, help="the size to unmap.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $sp # unmap whole stack area", "{0:s} 0x7ffffffde000 0x1000 # unmap specified area", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "By default, the entire map containing the specified address is freed.", "If a size is specified, the area from the specified address to that size will be unmapped.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return # On the CRIS architecture, setting a value to a register using the gdb `set` command will cause strange behavior. # So even if the assembly code is correct, it should not use this command. @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "rr", "wine")) @exclude_specific_arch(arch=("CRIS",)) @require_arch_set def do_invoke(self, args): # location sect = ProcessMap.process_lookup_address(args.location) if sect is None: err("Unmapped address") return # size if args.size is not None: if args.location % get_pagesize(): err("Address is not a multiple of {:#x}".format(get_pagesize())) return if args.location < 0: err("Invalid address") return if args.size % get_pagesize(): err("Size is not a multiple of {:#x}".format(get_pagesize())) return if args.size < 0 or AddressUtil.get_vmem_end() <= args.size: err("Invalid size") return # not estimation location = args.location size = args.size else: # use estimation location = sect.page_start size = sect.page_end - sect.page_start # doit cmd = "call-syscall munmap {:#x} {:#x}".format(location, size) gdb.execute(cmd) Cache.reset_gef_caches() return @register_command class MprotectCommand(GenericCommand): """Change a page permission (default: RWX).""" _cmdline_ = "mprotect" _category_ = "01-g. Debugging Support - Syscall" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address to change the permission.") parser.add_argument("permission", metavar="PERMISSION", nargs="?", default="rwx", help="the permission you set to the LOCATION. (default: %(default)s)") parser.add_argument("-s", "--size", type=AddressUtil.parse_address, help="the size to change the permission (0x1000 align).") _syntax_ = parser.format_help() _example_ = [ "{0:s} $sp rwx", "{0:s} 0x7ffff7e1b000 ___ # '_' means '-'", "{0:s} 0x7ffff7e1b000 ___ -s 0x1000 # change only first 0x1000 bytes", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "By default, the permissions will be changed for the entire map including the specified address.", "If a size is specified, the permissions will only be changed for the range of the specified address up to the size.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return # On the CRIS architecture, setting a value to a register using the gdb `set` command will cause strange behavior. # So even if the assembly code is correct, it should not use this command. @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "rr", "wine")) @exclude_specific_arch(arch=("CRIS",)) @require_arch_set def do_invoke(self, args): # location sect = ProcessMap.process_lookup_address(args.location) if sect is None: err("Unmapped address") return # size if args.size is not None: if args.location % get_pagesize(): err("Address is not a multiple of {:#x}".format(get_pagesize())) return if args.location < 0: err("Invalid address") return if args.size % get_pagesize(): err("Size is not a multiple of {:#x}".format(get_pagesize())) return if args.size < 0 or AddressUtil.get_vmem_end() <= args.size: err("Invalid size") return # not estimation location = args.location size = args.size else: # use estimation location = sect.page_start size = sect.page_end - sect.page_start # permission if re.match(r"[-_r][-_w][-_x]", args.permission): perm = Permission.NONE if args.permission[0] == "r": perm |= Permission.READ if args.permission[1] == "w": perm |= Permission.WRITE if args.permission[2] == "x": perm |= Permission.EXECUTE else: err("Invalid permission") return # doit cmd = "call-syscall mprotect {:#x} {:#x} {:#x}".format(location, size, perm) gdb.execute(cmd) Cache.reset_gef_caches() return @register_command class ReadSystemRegisterForKgdbCommand(GenericCommand): """Read system register for kgdb / kdb.""" _cmdline_ = "read-system-register-for-kgdb" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("reg_name", metavar="REGISTER_NAME", nargs="?", help="register name to read a value.") group.add_argument("-l", "--list", action="store_true", help="show the supported register names.") _syntax_ = parser.format_help() _example_ = [ "{0:s} cr0", "{0:s} TTBR0_EL1", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete="use_user_complete") return REGISTER_DICT = { "x64": { "cr0": { "sym": [ "native_read_cr0", ], "insn": [ ["$rax", b"\x0f\x20\xc0"], # mov rax, cr0 ], }, "cr2": { "sym": [ "native_read_cr2", "pv_native_read_cr2", ], "insn": [ ["$rax", b"\x0f\x20\xd0"], # mov rax, cr2 ], }, "cr3": { "sym": [ "native_read_cr3", "__native_read_cr3", ], "insn": [ ["$rax", b"\x0f\x20\xd8"], # mov rax, cr3 ], }, "cr4": { "sym": [ "native_read_cr4", "cr4_init", ], "insn": [ ["$rax", b"\x0f\x20\xe0"], # mov rax, cr4 ], }, }, "arm64": { "TTBR0_EL1": { "sym": [ "mte_cpu_setup", # 5.19~ ], "insn": [[f"$x{i}", bytes([0x00 + i]) + b"\x20\x38\xd5"] for i in range(30)], # mrs x0, TTBR0_EL1 }, "TTBR1_EL1": { "sym": [ "mte_cpu_setup", # 5.19~ "__sdei_asm_entry_trampoline", # 4.16~ ], "insn": [[f"$x{i}", bytes([0x20 + i]) + b"\x20\x38\xd5"] for i in range(30)], # mrs x0, TTBR1_EL1 }, "TCR_EL1": { "sym": [ "cpu_do_suspend", # 3.14~ ], "insn": [[f"$x{i}", bytes([0x40 + i]) + b"\x20\x38\xd5"] for i in range(30)], # mrs x0, TCR_EL1 }, "SCTLR_EL1": { "sym": [ "cpu_enable_pan", # 4.3~ ], "insn": [[f"$x{i}", bytes([0x00 + i]) + b"\x10\x38\xd5"] for i in range(30)], # mrs x0, SCTLR_EL1 }, "ID_AA64MMFR0_EL1": { "sym": [ "__cpuinfo_store_cpu", # 3.17~ ], "insn": [[f"$x{i}", bytes([0x00 + i]) + b"\x07\x38\xd5"] for i in range(30)], # mrs x0, ID_AA64MMFR0_EL1 }, "ID_AA64MMFR1_EL1": { "sym": [ "__cpuinfo_store_cpu", # 3.17~ ], "insn": [[f"$x{i}", bytes([0x20 + i]) + b"\x07\x38\xd5"] for i in range(30)], # mrs x0, ID_AA64MMFR1_EL1 }, "ID_AA64MMFR2_EL1": { "sym": [ "__cpuinfo_store_cpu", # 3.17~ ], "insn": [[f"$x{i}", bytes([0x40 + i]) + b"\x07\x38\xd5"] for i in range(30)], # mrs x0, ID_AA64MMFR2_EL1 }, "VBAR_EL1": { "sym": [ "cpu_do_suspend", # 3.14~ ], "insn": [[f"$x{i}", bytes([0x00 + i]) + b"\xc0\x38\xd5"] for i in range(30)], # mrs x0, VBAR_EL1 }, "SP_EL0": { "sym": [ "cpu_die_early", # 4.6~ "sched_setaffinity", # 3.7~ ], "insn": [[f"$x{i}", bytes([0x00 + i]) + b"\x41\x38\xd5"] for i in range(30)], # mrs x0, SP_EL0 }, }, } @staticmethod def get_supported_regs(): if not is_alive(): return [] if is_x86_64(): dic = ReadSystemRegisterForKgdbCommand.REGISTER_DICT["x64"] elif is_arm64(): dic = ReadSystemRegisterForKgdbCommand.REGISTER_DICT["arm64"] else: return [] # filter if sym is defined or not regs = [] for k, v in dic.items(): if not v["sym"]: continue regs.append(k) return regs @staticmethod def is_supported_reg(reg_name): regs = [r.lower() for r in ReadSystemRegisterForKgdbCommand.get_supported_regs()] return reg_name.lstrip("$").lower() in regs def complete(self, text, word): # noqa regs = ReadSystemRegisterForKgdbCommand.get_supported_regs() if text.strip() in regs: # already matched return [] if text == "": # no prefix return [s for s in regs if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in regs if s and s.startswith(text.strip())] @Cache.cache_this_session def get_stub_address(self, reg_name): if not is_alive(): return None if is_x86_64(): dic = ReadSystemRegisterForKgdbCommand.REGISTER_DICT["x64"] elif is_arm64(): dic = ReadSystemRegisterForKgdbCommand.REGISTER_DICT["arm64"] else: return None reg_name = reg_name.lstrip("$") # In kgdb mode, direct modification (patching) of text memory is not permitted. # Therefore, we instead utilize legitimate kernel-provided symbols and mechanisms # to achieve the same goal. # This implementation locates the address where the target instruction is used, # executes that instruction exactly once, and captures the resulting value. # TODO: Implement an alternative approach using the direct physical mapping (physmap) # to allow patching via physical address, or execute hand-crafted assembly # to obtain the value directly (without symbol). d = dic.get(reg_name.lower(), None) or dic.get(reg_name.upper(), None) if not d: return None for symbol in d["sym"]: # resolve symbol if is_kdb(): address = Symbol.get_symbol_by_monitor(symbol) else: address = Symbol.get_ksymaddr(symbol) if address is None: continue try: data = read_memory(address, 0x100) except gdb.MemoryError: continue if not data: continue for return_register, byte_code in d["insn"]: # adjust offset index = data.find(byte_code) if index >= 0: return address + index, return_register return None def execute_stub(self, stub_address, return_register): codes = [] regs = { "$pc": stub_address, return_register: 0xdead_beef, } use_bp = False if is_arm64(): # Step execution often fails due to an interrupt on ARM64 # but succeeds with the use of breakpoints. use_bp = True # It may fail on the first run, possibly due to gdb cache, but succeed on the second run. for _ in range(2): ret = ExecAsm(codes, regs=regs, step=1, use_bp=use_bp).exec_code() if abs(ret["reg"]["$pc"] - stub_address) > 0x10: return None if ret["reg"][return_register] != 0xdead_beef: return ret["reg"][return_register] return None @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("kgdb",)) @only_if_specific_arch(arch=("x86_64", "ARM64")) def do_invoke(self, args): if current_arch is None: err("current_arch is not set") return if args.list: for reg in ReadSystemRegisterForKgdbCommand.get_supported_regs(): gef_print(reg) return if not ReadSystemRegisterForKgdbCommand.is_supported_reg(args.reg_name): err("Unsupported register") return ret = self.get_stub_address(args.reg_name) if ret is None: err("Failed to get the target stub") return ret = self.execute_stub(*ret) if ret is not None: gef_print("{:s} = {:#x}".format(args.reg_name, ret)) return @register_command class ReadSystemRegisterForQemuArmCommand(GenericCommand): """Read system register for old qemu-system-arm.""" _cmdline_ = "read-system-register-for-qemu-arm" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("reg_name", metavar="REGISTER_NAME", help="register name to read a value.") _syntax_ = parser.format_help() _example_ = [ "{0:s} TTBR0", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Attempting to read a non-existing register raises an undefined exception.", ] _note_ = "\n".join(_note_) # thanks to https://github.com/gdelugre/ida-arm-system-highlight # Extracted from the XML specifications for v8.7-A (2021-06). AARCH32_COPROC_REGISTERS = { ("p15", "c0", 0, "c0", 0): ("MIDR", "Main ID Register"), ("p15", "c0", 0, "c0", 1): ("CTR", "Cache Type Register"), ("p15", "c0", 0, "c0", 2): ("TCMTR", "TCM Type Register"), ("p15", "c0", 0, "c0", 3): ("TLBTR", "TLB Type Register"), ("p15", "c0", 0, "c0", 5): ("MPIDR", "Multiprocessor Affinity Register"), ("p15", "c0", 0, "c0", 6): ("REVIDR", "Revision ID Register"), # Aliases ("p15", "c0", 0, "c0", 4): ("MIDR", "Main ID Register"), ("p15", "c0", 0, "c0", 7): ("MIDR", "Main ID Register"), # CPUID registers ("p15", "c0", 0, "c1", 0): ("ID_PFR0", "Processor Feature Register 0"), ("p15", "c0", 0, "c1", 1): ("ID_PFR1", "Processor Feature Register 1"), ("p15", "c0", 0, "c3", 4): ("ID_PFR2", "Processor Feature Register 2"), ("p15", "c0", 0, "c1", 2): ("ID_DFR0", "Debug Feature Register 0"), ("p15", "c0", 0, "c1", 3): ("ID_AFR0", "Auxiliary Feature Register 0"), ("p15", "c0", 0, "c1", 4): ("ID_MMFR0", "Memory Model Feature Register 0"), ("p15", "c0", 0, "c1", 5): ("ID_MMFR1", "Memory Model Feature Register 1"), ("p15", "c0", 0, "c1", 6): ("ID_MMFR2", "Memory Model Feature Register 2"), ("p15", "c0", 0, "c1", 7): ("ID_MMFR3", "Memory Model Feature Register 3"), ("p15", "c0", 0, "c2", 6): ("ID_MMFR4", "Memory Model Feature Register 4"), ("p15", "c0", 0, "c3", 6): ("ID_MMFR5", "Memory Model Feature Register 5"), ("p15", "c0", 0, "c2", 0): ("ID_ISAR0", "Instruction Set Attribute Register 0"), ("p15", "c0", 0, "c2", 1): ("ID_ISAR1", "Instruction Set Attribute Register 1"), ("p15", "c0", 0, "c2", 2): ("ID_ISAR2", "Instruction Set Attribute Register 2"), ("p15", "c0", 0, "c2", 3): ("ID_ISAR3", "Instruction Set Attribute Register 3"), ("p15", "c0", 0, "c2", 4): ("ID_ISAR4", "Instruction Set Attribute Register 4"), ("p15", "c0", 0, "c2", 5): ("ID_ISAR5", "Instruction Set Attribute Register 5"), ("p15", "c0", 0, "c2", 7): ("ID_ISAR6", "Instruction Set Attribute Register 6"), ("p15", "c0", 1, "c0", 0): ("CCSIDR", "Current Cache Size ID Register"), ("p15", "c0", 1, "c0", 2): ("CCSIDR2", "Current Cache Size ID Register 2"), ("p15", "c0", 1, "c0", 1): ("CLIDR", "Cache Level ID Register"), ("p15", "c0", 1, "c0", 7): ("AIDR", "Auxiliary ID Register"), ("p15", "c0", 2, "c0", 0): ("CSSELR", "Cache Size Selection Register"), ("p15", "c0", 4, "c0", 0): ("VPIDR", "Virtualization Processor ID Register"), ("p15", "c0", 4, "c0", 5): ("VMPIDR", "Virtualization Multiprocessor ID Register"), # System control registers ("p15", "c1", 0, "c0", 0): ("SCTLR", "System Control Register"), ("p15", "c1", 0, "c0", 1): ("ACTLR", "Auxiliary Control Register"), ("p15", "c1", 0, "c0", 3): ("ACTLR2", "Auxiliary Control Register 2"), ("p15", "c1", 0, "c0", 2): ("CPACR", "Architectural Feature Access Control Register"), ("p15", "c1", 0, "c1", 0): ("SCR", "Secure Configuration Register"), ("p15", "c1", 0, "c1", 1): ("SDER", "Secure Debug Enable Register"), ("p15", "c1", 0, "c3", 1): ("SDCR", "Secure Debug Control Register"), ("p15", "c1", 0, "c1", 2): ("NSACR", "Non-Secure Access Control Register"), ("p15", "c1", 4, "c0", 0): ("HSCTLR", "Hyp System Control Register"), ("p15", "c1", 4, "c0", 1): ("HACTLR", "Hyp Auxiliary Control Register"), ("p15", "c1", 4, "c0", 3): ("HACTLR2", "Hyp Auxiliary Control Register 2"), ("p15", "c1", 4, "c1", 0): ("HCR", "Hyp Configuration Register"), ("p15", "c1", 4, "c1", 4): ("HCR2", "Hyp Configuration Register 2"), ("p15", "c1", 4, "c1", 1): ("HDCR", "Hyp Debug Control Register"), ("p15", "c1", 4, "c1", 2): ("HCPTR", "Hyp Architectural Feature Trap Register"), ("p15", "c1", 4, "c1", 3): ("HSTR", "Hyp System Trap Register"), ("p15", "c1", 4, "c1", 7): ("HACR", "Hyp Auxiliary Configuration Register"), # Translation Table Base Registers ("p15", "c2", 0, "c0", 0): ("TTBR0", "Translation Table Base Register 0"), ("p15", "c2", 0, "c0", 1): ("TTBR1", "Translation Table Base Register 1"), ("p15", "c2", 4, "c0", 2): ("HTCR", "Hyp Translation Control Register"), ("p15", "c2", 4, "c1", 2): ("VTCR", "Virtualization Translation Control Register"), ("p15", "c2", 0, "c0", 2): ("TTBCR", "Translation Table Base Control Register"), ("p15", "c2", 0, "c0", 3): ("TTBCR2", "Translation Table Base Control Register 2"), # Domain Access Control registers ("p15", "c3", 0, "c0", 0): ("DACR", "Domain Access Control Register"), # Fault Status registers ("p15", "c5", 0, "c0", 0): ("DFSR", "Data Fault Status Register"), ("p15", "c5", 0, "c0", 1): ("IFSR", "Instruction Fault Status Register"), ("p15", "c5", 0, "c1", 0): ("ADFSR", "Auxiliary Data Fault Status Register"), ("p15", "c5", 0, "c1", 1): ("AIFSR", "Auxiliary Instruction Fault Status Register"), ("p15", "c5", 4, "c1", 0): ("HADFSR", "Hyp Auxiliary Data Fault Status Register"), ("p15", "c5", 4, "c1", 1): ("HAIFSR", "Hyp Auxiliary Instruction Fault Status Register"), ("p15", "c5", 4, "c2", 0): ("HSR", "Hyp Syndrome Register"), # Fault Address registers ("p15", "c6", 0, "c0", 0): ("DFAR", "Data Fault Address Register"), ("p15", "c6", 0, "c0", 1): ("N/A", "Watchpoint Fault Address"), # ARM11 ("p15", "c6", 0, "c0", 2): ("IFAR", "Instruction Fault Address Register"), ("p15", "c6", 4, "c0", 0): ("HDFAR", "Hyp Data Fault Address Register"), ("p15", "c6", 4, "c0", 2): ("HIFAR", "Hyp Instruction Fault Address Register"), ("p15", "c6", 4, "c0", 4): ("HPFAR", "Hyp IPA Fault Address Register"), # Cache maintenance registers ("p15", "c7", 0, "c0", 4): ("NOP", "No Operation / Wait For Interrupt"), ("p15", "c7", 0, "c1", 0): ("ICIALLUIS", "Instruction Cache Invalidate All to PoU, Inner Shareable"), ("p15", "c7", 0, "c1", 6): ("BPIALLIS", "Branch Predictor Invalidate All, Inner Shareable"), ("p15", "c7", 0, "c4", 0): ("PAR", "Physical Address Register"), ("p15", "c7", 0, "c5", 0): ("ICIALLU", "Instruction Cache Invalidate All to PoU"), ("p15", "c7", 0, "c5", 1): ("ICIMVAU", "Instruction Cache line Invalidate by VA to PoU"), ("p15", "c7", 0, "c5", 2): ("N/A", "Invalidate all instruction caches by set/way"), # ARM11 ("p15", "c7", 0, "c5", 4): ("CP15ISB", "Instruction Synchronization Barrier System instruction"), ("p15", "c7", 0, "c5", 6): ("BPIALL", "Branch Predictor Invalidate All"), ("p15", "c7", 0, "c5", 7): ("BPIMVA", "Branch Predictor Invalidate by VA"), ("p15", "c7", 0, "c6", 0): ("N/A", "Invalidate entire data cache"), ("p15", "c7", 0, "c6", 1): ("DCIMVAC", "Data Cache line Invalidate by VA to PoC"), ("p15", "c7", 0, "c6", 2): ("DCISW", "Data Cache line Invalidate by Set/Way"), ("p15", "c7", 0, "c7", 0): ("N/A", "Invalidate instruction cache and data cache"), # ARM11 ("p15", "c7", 0, "c8", 0): ("ATS1CPR", "Address Translate Stage 1 Current state PL1 Read"), ("p15", "c7", 0, "c8", 1): ("ATS1CPW", "Address Translate Stage 1 Current state PL1 Write"), ("p15", "c7", 0, "c8", 2): ("ATS1CUR", "Address Translate Stage 1 Current state Unprivileged Read"), ("p15", "c7", 0, "c8", 3): ("ATS1CUW", "Address Translate Stage 1 Current state Unprivileged Write"), ("p15", "c7", 0, "c8", 4): ("ATS12NSOPR", "Address Translate Stages 1 and 2 Non-secure Only PL1 Read"), ("p15", "c7", 0, "c8", 5): ("ATS12NSOPW", "Address Translate Stages 1 and 2 Non-secure Only PL1 Write"), ("p15", "c7", 0, "c8", 6): ("ATS12NSOUR", "Address Translate Stages 1 and 2 Non-secure Only Unprivileged Read"), ("p15", "c7", 0, "c8", 7): ("ATS12NSOUW", "Address Translate Stages 1 and 2 Non-secure Only Unprivileged Write"), ("p15", "c7", 0, "c9", 0): ("ATS1CPRP", "Address Translate Stage 1 Current state PL1 Read PAN"), ("p15", "c7", 0, "c9", 1): ("ATS1CPWP", "Address Translate Stage 1 Current state PL1 Write PAN"), ("p15", "c7", 0, "c10", 0): ("N/A", "Clean entire data cache"), # ARM11 ("p15", "c7", 0, "c10", 1): ("DCCMVAC", "Data Cache line Clean by VA to PoC"), ("p15", "c7", 0, "c10", 2): ("DCCSW", "Data Cache line Clean by Set/Way"), ("p15", "c7", 0, "c10", 3): ("N/A", "Test and clean data cache"), # ARM9 ("p15", "c7", 0, "c10", 4): ("CP15DSB", "Data Synchronization Barrier System instruction"), ("p15", "c7", 0, "c10", 5): ("CP15DMB", "Data Memory Barrier System instruction"), ("p15", "c7", 0, "c10", 6): ("N/A", "Read Cache Dirty Status Register"), # ARM11 ("p15", "c7", 0, "c11", 1): ("DCCMVAU", "Data Cache line Clean by VA to PoU"), ("p15", "c7", 0, "c12", 4): ("N/A", "Read Block Transfer Status Register"), # ARM11 ("p15", "c7", 0, "c12", 5): ("N/A", "Stop Prefetch Range"), # ARM11 ("p15", "c7", 0, "c13", 1): ("NOP", "No Operation / Prefetch Instruction Cache Line"), ("p15", "c7", 0, "c14", 0): ("N/A", "Clean and invalidate entire data cache"), # ARM11 ("p15", "c7", 0, "c14", 1): ("DCCIMVAC", "Data Cache line Clean and Invalidate by VA to PoC"), ("p15", "c7", 0, "c14", 2): ("DCCISW", "Data Cache line Clean and Invalidate by Set/Way"), ("p15", "c7", 0, "c14", 3): ("N/A", "Test, clean, and invalidate data cache"), # ARM9 ("p15", "c7", 4, "c8", 0): ("ATS1HR", "Address Translate Stage 1 Hyp mode Read"), ("p15", "c7", 4, "c8", 1): ("ATS1HW", "Stage 1 Hyp mode write"), # TLB maintenance operations ("p15", "c8", 0, "c3", 0): ("TLBIALLIS", "TLB Invalidate All, Inner Shareable"), ("p15", "c8", 0, "c3", 1): ("TLBIMVAIS", "TLB Invalidate by VA, Inner Shareable"), ("p15", "c8", 0, "c3", 2): ("TLBIASIDIS", "TLB Invalidate by ASID match, Inner Shareable"), ("p15", "c8", 0, "c3", 3): ("TLBIMVAAIS", "TLB Invalidate by VA, All ASID, Inner Shareable"), ("p15", "c8", 0, "c3", 5): ("TLBIMVALIS", "TLB Invalidate by VA, Last level, Inner Shareable"), ("p15", "c8", 0, "c3", 7): ("TLBIMVAALIS", "TLB Invalidate by VA, All ASID, Last level, Inner Shareable"), ("p15", "c8", 0, "c5", 0): ("ITLBIALL", "Instruction TLB Invalidate All"), ("p15", "c8", 0, "c5", 1): ("ITLBIMVA", "Instruction TLB Invalidate by VA"), ("p15", "c8", 0, "c5", 2): ("ITLBIASID", "Instruction TLB Invalidate by ASID match"), ("p15", "c8", 0, "c6", 0): ("DTLBIALL", "Data TLB Invalidate All"), ("p15", "c8", 0, "c6", 1): ("DTLBIMVA", "Data TLB Invalidate by VA"), ("p15", "c8", 0, "c6", 2): ("DTLBIASID", "Data TLB Invalidate by ASID match"), ("p15", "c8", 0, "c7", 0): ("TLBIALL", "TLB Invalidate All"), ("p15", "c8", 0, "c7", 1): ("TLBIMVA", "TLB Invalidate by VA"), ("p15", "c8", 0, "c7", 2): ("TLBIASID", "TLB Invalidate by ASID match"), ("p15", "c8", 0, "c7", 3): ("TLBIMVAA", "TLB Invalidate by VA, All ASID"), ("p15", "c8", 0, "c7", 5): ("TLBIMVAL", "TLB Invalidate by VA, Last level"), ("p15", "c8", 0, "c7", 7): ("TLBIMVAAL", "TLB Invalidate by VA, All ASID, Last level"), ("p15", "c8", 4, "c0", 1): ("TLBIIPAS2IS", "TLB Invalidate by Intermediate Physical Address, Stage 2, Inner Shareable"), ("p15", "c8", 4, "c0", 5): ("TLBIIPAS2LIS", "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, Inner Shareable"), ("p15", "c8", 4, "c3", 0): ("TLBIALLHIS", "TLB Invalidate All, Hyp mode, Inner Shareable"), ("p15", "c8", 4, "c3", 1): ("TLBIMVAHIS", "TLB Invalidate by VA, Hyp mode, Inner Shareable"), ("p15", "c8", 4, "c3", 4): ("TLBIALLNSNHIS", "TLB Invalidate All, Non-Secure Non-Hyp, Inner Shareable"), ("p15", "c8", 4, "c3", 5): ("TLBIMVALHIS", "TLB Invalidate by VA, Last level, Hyp mode, Inner Shareable"), ("p15", "c8", 4, "c4", 1): ("TLBIIPAS2", "TLB Invalidate by Intermediate Physical Address, Stage 2"), ("p15", "c8", 4, "c4", 5): ("TLBIIPAS2L", "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level"), ("p15", "c8", 4, "c7", 0): ("TLBIALLH", "TLB Invalidate All, Hyp mode"), ("p15", "c8", 4, "c7", 1): ("TLBIMVAH", "TLB Invalidate by VA, Hyp mode"), ("p15", "c8", 4, "c7", 4): ("TLBIALLNSNH", "TLB Invalidate All, Non-Secure Non-Hyp"), ("p15", "c8", 4, "c7", 5): ("TLBIMVALH", "TLB Invalidate by VA, Last level, Hyp mode"), ("p15", "c9", 0, "c0", 0): ("N/A", "Data Cache Lockdown"), # ARM11 ("p15", "c9", 0, "c0", 1): ("N/A", "Instruction Cache Lockdown"), # ARM11 ("p15", "c9", 0, "c1", 0): ("N/A", "Data TCM Region"), # ARM11 ("p15", "c9", 0, "c1", 1): ("N/A", "Instruction TCM Region"), # ARM11 ("p15", "c9", 1, "c0", 2): ("L2CTLR", "L2 Control Register"), ("p15", "c9", 1, "c0", 3): ("L2ECTLR", "L2 Extended Control Register"), # Performance monitor registers ("p15", "c9", 0, "c12", 0): ("PMCR", "Performance Monitors Control Register"), ("p15", "c9", 0, "c12", 1): ("PMCNTENSET", "Performance Monitor Count Enable Set Register"), ("p15", "c9", 0, "c12", 2): ("PMCNTENCLR", "Performance Monitor Control Enable Clear Register"), ("p15", "c9", 0, "c12", 3): ("PMOVSR", "Performance Monitors Overflow Flag Status Register"), ("p15", "c9", 0, "c12", 4): ("PMSWINC", "Performance Monitors Software Increment register"), ("p15", "c9", 0, "c12", 5): ("PMSELR", "Performance Monitors Event Counter Selection Register"), ("p15", "c9", 0, "c12", 6): ("PMCEID0", "Performance Monitors Common Event Identification register 0"), ("p15", "c9", 0, "c12", 7): ("PMCEID1", "Performance Monitors Common Event Identification register 1"), ("p15", "c9", 0, "c13", 0): ("PMCCNTR", "Performance Monitors Cycle Count Register"), ("p15", "c9", 0, "c13", 1): ("PMXEVTYPER", "Performance Monitors Selected Event Type Register"), ("p15", "c9", 0, "c13", 2): ("PMXEVCNTR", "Performance Monitors Selected Event Count Register"), ("p15", "c9", 0, "c14", 0): ("PMUSERENR", "Performance Monitors User Enable Register"), ("p15", "c9", 0, "c14", 1): ("PMINTENSET", "Performance Monitors Interrupt Enable Set register"), ("p15", "c9", 0, "c14", 2): ("PMINTENCLR", "Performance Monitors Interrupt Enable Clear register"), ("p15", "c9", 0, "c14", 3): ("PMOVSSET", "Performance Monitors Overflow Flag Status Set register"), ("p15", "c9", 0, "c14", 4): ("PMCEID2", "Performance Monitors Common Event Identification register 2"), ("p15", "c9", 0, "c14", 5): ("PMCEID3", "Performance Monitors Common Event Identification register 3"), ("p15", "c9", 0, "c14", 6): ("PMMIR", "Performance Monitors Machine Identification Register"), ("p15", "c14", 0, "c8", 0): ("PMEVCNTR0", "Performance Monitors Event Count Register 0"), ("p15", "c14", 0, "c8", 1): ("PMEVCNTR1", "Performance Monitors Event Count Register 1"), ("p15", "c14", 0, "c8", 2): ("PMEVCNTR2", "Performance Monitors Event Count Register 2"), ("p15", "c14", 0, "c8", 3): ("PMEVCNTR3", "Performance Monitors Event Count Register 3"), ("p15", "c14", 0, "c8", 4): ("PMEVCNTR4", "Performance Monitors Event Count Register 4"), ("p15", "c14", 0, "c8", 5): ("PMEVCNTR5", "Performance Monitors Event Count Register 5"), ("p15", "c14", 0, "c8", 6): ("PMEVCNTR6", "Performance Monitors Event Count Register 6"), ("p15", "c14", 0, "c8", 7): ("PMEVCNTR7", "Performance Monitors Event Count Register 7"), ("p15", "c14", 0, "c9", 0): ("PMEVCNTR8", "Performance Monitors Event Count Register 8"), ("p15", "c14", 0, "c9", 1): ("PMEVCNTR9", "Performance Monitors Event Count Register 9"), ("p15", "c14", 0, "c9", 2): ("PMEVCNTR10", "Performance Monitors Event Count Register 10"), ("p15", "c14", 0, "c9", 3): ("PMEVCNTR11", "Performance Monitors Event Count Register 11"), ("p15", "c14", 0, "c9", 4): ("PMEVCNTR12", "Performance Monitors Event Count Register 12"), ("p15", "c14", 0, "c9", 5): ("PMEVCNTR13", "Performance Monitors Event Count Register 13"), ("p15", "c14", 0, "c9", 6): ("PMEVCNTR14", "Performance Monitors Event Count Register 14"), ("p15", "c14", 0, "c9", 7): ("PMEVCNTR15", "Performance Monitors Event Count Register 15"), ("p15", "c14", 0, "c10", 0): ("PMEVCNTR16", "Performance Monitors Event Count Register 16"), ("p15", "c14", 0, "c10", 1): ("PMEVCNTR17", "Performance Monitors Event Count Register 17"), ("p15", "c14", 0, "c10", 2): ("PMEVCNTR18", "Performance Monitors Event Count Register 18"), ("p15", "c14", 0, "c10", 3): ("PMEVCNTR19", "Performance Monitors Event Count Register 19"), ("p15", "c14", 0, "c10", 4): ("PMEVCNTR20", "Performance Monitors Event Count Register 20"), ("p15", "c14", 0, "c10", 5): ("PMEVCNTR21", "Performance Monitors Event Count Register 21"), ("p15", "c14", 0, "c10", 6): ("PMEVCNTR22", "Performance Monitors Event Count Register 22"), ("p15", "c14", 0, "c10", 7): ("PMEVCNTR23", "Performance Monitors Event Count Register 23"), ("p15", "c14", 0, "c11", 0): ("PMEVCNTR24", "Performance Monitors Event Count Register 24"), ("p15", "c14", 0, "c11", 1): ("PMEVCNTR25", "Performance Monitors Event Count Register 25"), ("p15", "c14", 0, "c11", 2): ("PMEVCNTR26", "Performance Monitors Event Count Register 26"), ("p15", "c14", 0, "c11", 3): ("PMEVCNTR27", "Performance Monitors Event Count Register 27"), ("p15", "c14", 0, "c11", 4): ("PMEVCNTR28", "Performance Monitors Event Count Register 28"), ("p15", "c14", 0, "c11", 5): ("PMEVCNTR29", "Performance Monitors Event Count Register 29"), ("p15", "c14", 0, "c11", 6): ("PMEVCNTR30", "Performance Monitors Event Count Register 30"), ("p15", "c14", 0, "c12", 0): ("PMEVTYPER0", "Performance Monitors Event Type Register 0"), ("p15", "c14", 0, "c12", 1): ("PMEVTYPER1", "Performance Monitors Event Type Register 1"), ("p15", "c14", 0, "c12", 2): ("PMEVTYPER2", "Performance Monitors Event Type Register 2"), ("p15", "c14", 0, "c12", 3): ("PMEVTYPER3", "Performance Monitors Event Type Register 3"), ("p15", "c14", 0, "c12", 4): ("PMEVTYPER4", "Performance Monitors Event Type Register 4"), ("p15", "c14", 0, "c12", 5): ("PMEVTYPER5", "Performance Monitors Event Type Register 5"), ("p15", "c14", 0, "c12", 6): ("PMEVTYPER6", "Performance Monitors Event Type Register 6"), ("p15", "c14", 0, "c12", 7): ("PMEVTYPER7", "Performance Monitors Event Type Register 7"), ("p15", "c14", 0, "c13", 0): ("PMEVTYPER8", "Performance Monitors Event Type Register 8"), ("p15", "c14", 0, "c13", 1): ("PMEVTYPER9", "Performance Monitors Event Type Register 9"), ("p15", "c14", 0, "c13", 2): ("PMEVTYPER10", "Performance Monitors Event Type Register 10"), ("p15", "c14", 0, "c13", 3): ("PMEVTYPER11", "Performance Monitors Event Type Register 11"), ("p15", "c14", 0, "c13", 4): ("PMEVTYPER12", "Performance Monitors Event Type Register 12"), ("p15", "c14", 0, "c13", 5): ("PMEVTYPER13", "Performance Monitors Event Type Register 13"), ("p15", "c14", 0, "c13", 6): ("PMEVTYPER14", "Performance Monitors Event Type Register 14"), ("p15", "c14", 0, "c13", 7): ("PMEVTYPER15", "Performance Monitors Event Type Register 15"), ("p15", "c14", 0, "c14", 0): ("PMEVTYPER16", "Performance Monitors Event Type Register 16"), ("p15", "c14", 0, "c14", 1): ("PMEVTYPER17", "Performance Monitors Event Type Register 17"), ("p15", "c14", 0, "c14", 2): ("PMEVTYPER18", "Performance Monitors Event Type Register 18"), ("p15", "c14", 0, "c14", 3): ("PMEVTYPER19", "Performance Monitors Event Type Register 19"), ("p15", "c14", 0, "c14", 4): ("PMEVTYPER20", "Performance Monitors Event Type Register 20"), ("p15", "c14", 0, "c14", 5): ("PMEVTYPER21", "Performance Monitors Event Type Register 21"), ("p15", "c14", 0, "c14", 6): ("PMEVTYPER22", "Performance Monitors Event Type Register 22"), ("p15", "c14", 0, "c14", 7): ("PMEVTYPER23", "Performance Monitors Event Type Register 23"), ("p15", "c14", 0, "c15", 0): ("PMEVTYPER24", "Performance Monitors Event Type Register 24"), ("p15", "c14", 0, "c15", 1): ("PMEVTYPER25", "Performance Monitors Event Type Register 25"), ("p15", "c14", 0, "c15", 2): ("PMEVTYPER26", "Performance Monitors Event Type Register 26"), ("p15", "c14", 0, "c15", 3): ("PMEVTYPER27", "Performance Monitors Event Type Register 27"), ("p15", "c14", 0, "c15", 4): ("PMEVTYPER28", "Performance Monitors Event Type Register 28"), ("p15", "c14", 0, "c15", 5): ("PMEVTYPER29", "Performance Monitors Event Type Register 29"), ("p15", "c14", 0, "c15", 6): ("PMEVTYPER30", "Performance Monitors Event Type Register 30"), ("p15", "c14", 0, "c15", 7): ("PMCCFILTR", "Performance Monitors Cycle Count Filter Register"), # Activity Monitors ("p15", "c13", 0, "c2", 1): ("AMCFGR", "Activity Monitors Configuration Register"), ("p15", "c13", 0, "c2", 2): ("AMCGCR", "Activity Monitors Counter Group Configuration Register"), ("p15", "c13", 0, "c2", 4): ("AMCNTENCLR0", "Activity Monitors Count Enable Clear Register 0"), ("p15", "c13", 0, "c3", 0): ("AMCNTENCLR1", "Activity Monitors Count Enable Clear Register 1"), ("p15", "c13", 0, "c2", 5): ("AMCNTENSET0", "Activity Monitors Count Enable Set Register 0"), ("p15", "c13", 0, "c3", 1): ("AMCNTENSET1", "Activity Monitors Count Enable Set Register 1"), ("p15", "c13", 0, "c2", 0): ("AMCR", "Activity Monitors Control Register"), ("p15", "c13", 0, "c6", 0): ("AMEVTYPER00", "Activity Monitors Event Type Registers 0"), ("p15", "c13", 0, "c6", 1): ("AMEVTYPER01", "Activity Monitors Event Type Registers 0"), ("p15", "c13", 0, "c6", 2): ("AMEVTYPER02", "Activity Monitors Event Type Registers 0"), ("p15", "c13", 0, "c14", 0): ("AMEVTYPER10", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c14", 1): ("AMEVTYPER11", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c14", 2): ("AMEVTYPER12", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c14", 3): ("AMEVTYPER13", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c14", 4): ("AMEVTYPER14", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c14", 5): ("AMEVTYPER15", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c14", 6): ("AMEVTYPER16", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c14", 7): ("AMEVTYPER17", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c15", 0): ("AMEVTYPER18", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c15", 1): ("AMEVTYPER19", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c15", 2): ("AMEVTYPER110", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c15", 3): ("AMEVTYPER111", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c15", 4): ("AMEVTYPER112", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c15", 5): ("AMEVTYPER113", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c15", 6): ("AMEVTYPER114", "Activity Monitors Event Type Registers 1"), ("p15", "c13", 0, "c2", 3): ("AMUSERENR", "Activity Monitors User Enable Register"), # Reliability ("p15", "c12", 0, "c1", 1): ("DISR", "Deferred Interrupt Status Register"), ("p15", "c5", 0, "c3", 0): ("ERRIDR", "Error Record ID Register"), ("p15", "c5", 0, "c3", 1): ("ERRSELR", "Error Record Select Register"), ("p15", "c5", 0, "c4", 3): ("ERXADDR", "Selected Error Record Address Register"), ("p15", "c5", 0, "c4", 7): ("ERXADDR2", "Selected Error Record Address Register 2"), ("p15", "c5", 0, "c4", 1): ("ERXCTLR", "Selected Error Record Control Register"), ("p15", "c5", 0, "c4", 5): ("ERXCTLR2", "Selected Error Record Control Register 2"), ("p15", "c5", 0, "c4", 0): ("ERXFR", "Selected Error Record Feature Register"), ("p15", "c5", 0, "c4", 4): ("ERXFR2", "Selected Error Record Feature Register 2"), ("p15", "c5", 0, "c5", 0): ("ERXMISC0", "Selected Error Record Miscellaneous Register 0"), ("p15", "c5", 0, "c5", 1): ("ERXMISC1", "Selected Error Record Miscellaneous Register 1"), ("p15", "c5", 0, "c5", 4): ("ERXMISC2", "Selected Error Record Miscellaneous Register 2"), ("p15", "c5", 0, "c5", 5): ("ERXMISC3", "Selected Error Record Miscellaneous Register 3"), ("p15", "c5", 0, "c5", 2): ("ERXMISC4", "Selected Error Record Miscellaneous Register 4"), ("p15", "c5", 0, "c5", 3): ("ERXMISC5", "Selected Error Record Miscellaneous Register 5"), ("p15", "c5", 0, "c5", 6): ("ERXMISC6", "Selected Error Record Miscellaneous Register 6"), ("p15", "c5", 0, "c5", 7): ("ERXMISC7", "Selected Error Record Miscellaneous Register 7"), ("p15", "c5", 0, "c4", 2): ("ERXSTATUS", "Selected Error Record Primary Status Register"), ("p15", "c5", 4, "c2", 3): ("VDFSR", "Virtual SError Exception Syndrome Register"), ("p15", "c12", 4, "c1", 1): ("VDISR", "Virtual Deferred Interrupt Status Register"), # Memory attribute registers ("p15", "c10", 0, "c0", 0): ("N/A", "TLB Lockdown"), # ARM11 ("p15", "c10", 0, "c2", 0): ("MAIR0", "Memory Attribute Indirection Register 0", "PRRR", "Primary Region Remap Register"), ("p15", "c10", 0, "c2", 1): ("MAIR1", "Memory Attribute Indirection Register 1", "NMRR", "Normal Memory Remap Register"), ("p15", "c10", 0, "c3", 0): ("AMAIR0", "Auxiliary Memory Attribute Indirection Register 0"), ("p15", "c10", 0, "c3", 1): ("AMAIR1", "Auxiliary Memory Attribute Indirection Register 1"), ("p15", "c10", 4, "c2", 0): ("HMAIR0", "Hyp Memory Attribute Indirection Register 0"), ("p15", "c10", 4, "c2", 1): ("HMAIR1", "Hyp Memory Attribute Indirection Register 1"), ("p15", "c10", 4, "c3", 0): ("HAMAIR0", "Hyp Auxiliary Memory Attribute Indirection Register 0"), ("p15", "c10", 4, "c3", 1): ("HAMAIR1", "Hyp Auxiliary Memory Attribute Indirection Register 1"), # DMA registers (ARM11) # This definition conflicts with other coprocessor definitions. # ARM v6 architecture (ARM11 core) is old and will be abandoned. #("p15", "c11", 0, "c0", 0): ("N/A", "DMA Identification and Status (Present)"), #("p15", "c11", 0, "c0", 1): ("N/A", "DMA Identification and Status (Queued)"), #("p15", "c11", 0, "c0", 2): ("N/A", "DMA Identification and Status (Running)"), #("p15", "c11", 0, "c0", 3): ("N/A", "DMA Identification and Status (Interrupting)"), #("p15", "c11", 0, "c1", 0): ("N/A", "DMA User Accessibility"), #("p15", "c11", 0, "c2", 0): ("N/A", "DMA Channel Number"), #("p15", "c11", 0, "c3", 0): ("N/A", "DMA Enable (Stop)"), #("p15", "c11", 0, "c3", 1): ("N/A", "DMA Enable (Start)"), #("p15", "c11", 0, "c3", 2): ("N/A", "DMA Enable (Clear)"), #("p15", "c11", 0, "c4", 0): ("N/A", "DMA Control"), #("p15", "c11", 0, "c5", 0): ("N/A", "DMA Internal Start Address"), #("p15", "c11", 0, "c6", 0): ("N/A", "DMA External Start Address"), #("p15", "c11", 0, "c7", 0): ("N/A", "DMA Internal End Address"), #("p15", "c11", 0, "c8", 0): ("N/A", "DMA Channel Status"), #("p15", "c11", 0, "c15", 0): ("N/A", "DMA Context ID"), # Reset management registers. ("p15", "c12", 0, "c0", 0): ("VBAR", "Vector Base Address Register"), ("p15", "c12", 0, "c0", 1): ("RVBAR", "Reset Vector Base Address Register" , "MVBAR", "Monitor Vector Base Address Register"), ("p15", "c12", 0, "c0", 2): ("RMR", "Reset Management Register"), ("p15", "c12", 4, "c0", 2): ("HRMR", "Hyp Reset Management Register"), ("p15", "c12", 0, "c1", 0): ("ISR", "Interrupt Status Register"), ("p15", "c12", 4, "c0", 0): ("HVBAR", "Hyp Vector Base Address Register"), ("p15", "c13", 0, "c0", 0): ("FCSEIDR", "FCSE Process ID register"), ("p15", "c13", 0, "c0", 1): ("CONTEXTIDR", "Context ID Register"), ("p15", "c13", 0, "c0", 2): ("TPIDRURW", "PL0 Read/Write Software Thread ID Register"), ("p15", "c13", 0, "c0", 3): ("TPIDRURO", "PL0 Read-Only Software Thread ID Register"), ("p15", "c13", 0, "c0", 4): ("TPIDRPRW", "PL1 Software Thread ID Register"), ("p15", "c13", 4, "c0", 2): ("HTPIDR", "Hyp Software Thread ID Register"), # Generic timer registers. ("p15", "c14", 0, "c0", 0): ("CNTFRQ", "Counter-timer Frequency register"), ("p15", "c14", 0, "c1", 0): ("CNTKCTL", "Counter-timer Kernel Control register"), ("p15", "c14", 0, "c2", 0): ("CNTP_TVAL", "Counter-timer Physical Timer TimerValue register", "CNTHP_TVAL", "Counter-timer Hyp Physical Timer TimerValue register", "CNTHPS_TVAL", "Counter-timer Secure Physical Timer TimerValue Register (EL2)"), ("p15", "c14", 0, "c2", 1): ("CNTP_CTL", "Counter-timer Physical Timer Control register", "CNTHP_CTL", "Counter-timer Hyp Physical Timer Control register", "CNTHPS_CTL", "Counter-timer Secure Physical Timer Control Register (EL2)"), ("p15", "c14", 0, "c3", 0): ("CNTV_TVAL", "Counter-timer Virtual Timer TimerValue register", "CNTHV_TVAL", "Counter-timer Virtual Timer TimerValue register (EL2)", "CNTHVS_TVAL", "Counter-timer Secure Virtual Timer TimerValue Register (EL2)"), ("p15", "c14", 0, "c3", 1): ("CNTV_CTL", "Counter-timer Virtual Timer Control register", "CNTHV_CTL", "Counter-timer Virtual Timer Control register (EL2)", "CNTHVS_CTL", "Counter-timer Secure Virtual Timer Control Register (EL2)"), ("p15", "c14", 4, "c1", 0): ("CNTHCTL", "Counter-timer Hyp Control register"), ("p15", "c14", 4, "c2", 0): ("CNTHP_TVAL", "Counter-timer Hyp Physical Timer TimerValue register"), ("p15", "c14", 4, "c2", 1): ("CNTHP_CTL", "Counter-timer Hyp Physical Timer Control register"), # Generic interrupt controller registers. ("p15", "c4", 0, "c6", 0): ("ICC_PMR", "Interrupt Controller Interrupt Priority Mask Register", "ICV_PMR", "Interrupt Controller Virtual Interrupt Priority Mask Register"), ("p15", "c12", 0, "c8", 0): ("ICC_IAR0", "Interrupt Controller Interrupt Acknowledge Register 0", "ICV_IAR0", "Interrupt Controller Virtual Interrupt Acknowledge Register 0"), ("p15", "c12", 0, "c8", 1): ("ICC_EOIR0", "Interrupt Controller End Of Interrupt Register 0", "ICV_EOIR0", "Interrupt Controller Virtual End Of Interrupt Register 0"), ("p15", "c12", 0, "c8", 2): ("ICC_HPPIR0", "Interrupt Controller Highest Priority Pending Interrupt Register 0", "ICV_HPPIR0", "Interrupt Controller Virtual Highest Priority Pending Interrupt Register 0"), ("p15", "c12", 0, "c8", 3): ("ICC_BPR0", "Interrupt Controller Binary Point Register 0", "ICV_BPR0", "Interrupt Controller Virtual Binary Point Register 0"), ("p15", "c12", 0, "c8", 4): ("ICC_AP0R0", "Interrupt Controller Active Priorities Group 0 Register 0", "ICV_AP0R0", "Interrupt Controller Virtual Active Priorities Group 0 Register 0"), ("p15", "c12", 0, "c8", 5): ("ICC_AP0R1", "Interrupt Controller Active Priorities Group 0 Register 1", "ICV_AP0R1", "Interrupt Controller Virtual Active Priorities Group 0 Register 1"), ("p15", "c12", 0, "c8", 6): ("ICC_AP0R2", "Interrupt Controller Active Priorities Group 0 Register 2", "ICV_AP0R2", "Interrupt Controller Virtual Active Priorities Group 0 Register 2"), ("p15", "c12", 0, "c8", 7): ("ICC_AP0R3", "Interrupt Controller Active Priorities Group 0 Register 3", "ICV_AP0R3", "Interrupt Controller Virtual Active Priorities Group 0 Register 3"), ("p15", "c12", 0, "c9", 0): ("ICC_AP1R0", "Interrupt Controller Active Priorities Group 1 Register 0", "ICV_AP1R0", "Interrupt Controller Virtual Active Priorities Group 1 Register 0"), ("p15", "c12", 0, "c9", 1): ("ICC_AP1R1", "Interrupt Controller Active Priorities Group 1 Register 1", "ICV_AP1R1", "Interrupt Controller Virtual Active Priorities Group 1 Register 1"), ("p15", "c12", 0, "c9", 2): ("ICC_AP1R2", "Interrupt Controller Active Priorities Group 1 Register 2", "ICV_AP1R2", "Interrupt Controller Virtual Active Priorities Group 1 Register 2"), ("p15", "c12", 0, "c9", 3): ("ICC_AP1R3", "Interrupt Controller Active Priorities Group 1 Register 3", "ICV_AP1R3", "Interrupt Controller Virtual Active Priorities Group 1 Register 3"), ("p15", "c12", 0, "c11", 1): ("ICC_DIR", "Interrupt Controller Deactivate Interrupt Register", "ICV_DIR", "Interrupt Controller Deactivate Virtual Interrupt Register"), ("p15", "c12", 0, "c11", 3): ("ICC_RPR", "Interrupt Controller Running Priority Register", "ICV_RPR", "Interrupt Controller Virtual Running Priority Register"), ("p15", "c12", 0, "c12", 0): ("ICC_IAR1", "Interrupt Controller Interrupt Acknowledge Register 1", "ICV_IAR1", "Interrupt Controller Virtual Interrupt Acknowledge Register 1"), ("p15", "c12", 0, "c12", 1): ("ICC_EOIR1", "Interrupt Controller End Of Interrupt Register 1", "ICV_EOIR1", "Interrupt Controller Virtual End Of Interrupt Register 1"), ("p15", "c12", 0, "c12", 2): ("ICC_HPPIR1", "Interrupt Controller Highest Priority Pending Interrupt Register 1", "ICV_HPPIR1", "Interrupt Controller Virtual Highest Priority Pending Interrupt Register 1"), ("p15", "c12", 0, "c12", 3): ("ICC_BPR1", "Interrupt Controller Binary Point Register 1", "ICV_BPR1", "Interrupt Controller Virtual Binary Point Register 1"), ("p15", "c12", 0, "c12", 4): ("ICC_CTLR", "Interrupt Controller Control Register", "ICV_CTLR", "Interrupt Controller Virtual Control Register"), ("p15", "c12", 0, "c12", 5): ("ICC_SRE", "Interrupt Controller System Register Enable register"), ("p15", "c12", 0, "c12", 6): ("ICC_IGRPEN0", "Interrupt Controller Interrupt Group 0 Enable register", "ICV_IGRPEN0", "Interrupt Controller Virtual Interrupt Group 0 Enable register"), ("p15", "c12", 0, "c12", 7): ("ICC_IGRPEN1", "Interrupt Controller Interrupt Group 1 Enable register", "ICV_IGRPEN1", "Interrupt Controller Virtual Interrupt Group 1 Enable register"), ("p15", "c12", 4, "c8", 0): ("ICH_AP0R0", "Interrupt Controller Hyp Active Priorities Group 0 Register 0"), ("p15", "c12", 4, "c8", 1): ("ICH_AP0R1", "Interrupt Controller Hyp Active Priorities Group 0 Register 1"), ("p15", "c12", 4, "c8", 2): ("ICH_AP0R2", "Interrupt Controller Hyp Active Priorities Group 0 Register 2"), ("p15", "c12", 4, "c8", 3): ("ICH_AP0R3", "Interrupt Controller Hyp Active Priorities Group 0 Register 3"), ("p15", "c12", 4, "c9", 0): ("ICH_AP1R0", "Interrupt Controller Hyp Active Priorities Group 1 Register 0"), ("p15", "c12", 4, "c9", 1): ("ICH_AP1R1", "Interrupt Controller Hyp Active Priorities Group 1 Register 1"), ("p15", "c12", 4, "c9", 2): ("ICH_AP1R2", "Interrupt Controller Hyp Active Priorities Group 1 Register 2"), ("p15", "c12", 4, "c9", 3): ("ICH_AP1R3", "Interrupt Controller Hyp Active Priorities Group 1 Register 3"), ("p15", "c12", 4, "c9", 5): ("ICC_HSRE", "Interrupt Controller Hyp System Register Enable register"), ("p15", "c12", 4, "c11", 0): ("ICH_HCR", "Interrupt Controller Hyp Control Register"), ("p15", "c12", 4, "c11", 1): ("ICH_VTR", "Interrupt Controller VGIC Type Register"), ("p15", "c12", 4, "c11", 2): ("ICH_MISR", "Interrupt Controller Maintenance Interrupt State Register"), ("p15", "c12", 4, "c11", 3): ("ICH_EISR", "Interrupt Controller End of Interrupt Status Register"), ("p15", "c12", 4, "c11", 5): ("ICH_ELRSR", "Interrupt Controller Empty List Register Status Register"), ("p15", "c12", 4, "c11", 7): ("ICH_VMCR", "Interrupt Controller Virtual Machine Control Register"), ("p15", "c12", 4, "c12", 0): ("ICH_LR0", "Interrupt Controller List Register 0"), ("p15", "c12", 4, "c12", 1): ("ICH_LR1", "Interrupt Controller List Register 1"), ("p15", "c12", 4, "c12", 2): ("ICH_LR2", "Interrupt Controller List Register 2"), ("p15", "c12", 4, "c12", 3): ("ICH_LR3", "Interrupt Controller List Register 3"), ("p15", "c12", 4, "c12", 4): ("ICH_LR4", "Interrupt Controller List Register 4"), ("p15", "c12", 4, "c12", 5): ("ICH_LR5", "Interrupt Controller List Register 5"), ("p15", "c12", 4, "c12", 6): ("ICH_LR6", "Interrupt Controller List Register 6"), ("p15", "c12", 4, "c12", 7): ("ICH_LR7", "Interrupt Controller List Register 7"), ("p15", "c12", 4, "c13", 0): ("ICH_LR8", "Interrupt Controller List Register 8"), ("p15", "c12", 4, "c13", 1): ("ICH_LR9", "Interrupt Controller List Register 9"), ("p15", "c12", 4, "c13", 2): ("ICH_LR10", "Interrupt Controller List Register 10"), ("p15", "c12", 4, "c13", 3): ("ICH_LR11", "Interrupt Controller List Register 11"), ("p15", "c12", 4, "c13", 4): ("ICH_LR12", "Interrupt Controller List Register 12"), ("p15", "c12", 4, "c13", 5): ("ICH_LR13", "Interrupt Controller List Register 13"), ("p15", "c12", 4, "c13", 6): ("ICH_LR14", "Interrupt Controller List Register 14"), ("p15", "c12", 4, "c13", 7): ("ICH_LR15", "Interrupt Controller List Register 15"), ("p15", "c12", 4, "c14", 0): ("ICH_LRC0", "Interrupt Controller List Register 0"), ("p15", "c12", 4, "c14", 1): ("ICH_LRC1", "Interrupt Controller List Register 1"), ("p15", "c12", 4, "c14", 2): ("ICH_LRC2", "Interrupt Controller List Register 2"), ("p15", "c12", 4, "c14", 3): ("ICH_LRC3", "Interrupt Controller List Register 3"), ("p15", "c12", 4, "c14", 4): ("ICH_LRC4", "Interrupt Controller List Register 4"), ("p15", "c12", 4, "c14", 5): ("ICH_LRC5", "Interrupt Controller List Register 5"), ("p15", "c12", 4, "c14", 6): ("ICH_LRC6", "Interrupt Controller List Register 6"), ("p15", "c12", 4, "c14", 7): ("ICH_LRC7", "Interrupt Controller List Register 7"), ("p15", "c12", 4, "c15", 0): ("ICH_LRC8", "Interrupt Controller List Register 8"), ("p15", "c12", 4, "c15", 1): ("ICH_LRC9", "Interrupt Controller List Register 9"), ("p15", "c12", 4, "c15", 2): ("ICH_LRC10", "Interrupt Controller List Register 10"), ("p15", "c12", 4, "c15", 3): ("ICH_LRC11", "Interrupt Controller List Register 11"), ("p15", "c12", 4, "c15", 4): ("ICH_LRC12", "Interrupt Controller List Register 12"), ("p15", "c12", 4, "c15", 5): ("ICH_LRC13", "Interrupt Controller List Register 13"), ("p15", "c12", 4, "c15", 6): ("ICH_LRC14", "Interrupt Controller List Register 14"), ("p15", "c12", 4, "c15", 7): ("ICH_LRC15", "Interrupt Controller List Register 15"), ("p15", "c12", 6, "c12", 4): ("ICC_MCTLR", "Interrupt Controller Monitor Control Register"), ("p15", "c12", 6, "c12", 5): ("ICC_MSRE", "Interrupt Controller Monitor System Register Enable register"), ("p15", "c12", 6, "c12", 7): ("ICC_MGRPEN1", "Interrupt Controller Monitor Interrupt Group 1 Enable register"), ("p15", "c15", 0, "c0", 0): ("IL1Data0", "Instruction L1 Data n Register"), ("p15", "c15", 0, "c0", 1): ("IL1Data1", "Instruction L1 Data n Register"), ("p15", "c15", 0, "c0", 2): ("IL1Data2", "Instruction L1 Data n Register"), ("p15", "c15", 0, "c1", 0): ("DL1Data0", "Data L1 Data n Register"), ("p15", "c15", 0, "c1", 1): ("DL1Data1", "Data L1 Data n Register"), ("p15", "c15", 0, "c1", 2): ("DL1Data2", "Data L1 Data n Register"), ("p15", "c15", 0, "c2", 0): ("N/A", "Data Memory Remap"), # ARM11 ("p15", "c15", 0, "c2", 1): ("N/A", "Instruction Memory Remap"), # ARM11 ("p15", "c15", 0, "c2", 2): ("N/A", "DMA Memory Remap"), # ARM11 ("p15", "c15", 0, "c2", 3): ("N/A", "Peripheral Port Memory Remap"), # ARM11 ("p15", "c15", 0, "c4", 0): ("RAMINDEX", "RAM Index Register"), ("p15", "c15", 0, "c12", 0): ("N/A", "Performance Monitor Control"), # ARM11 ("p15", "c15", 0, "c12", 1): ("CCNT", "Cycle Counter"), # ARM11 ("p15", "c15", 0, "c12", 2): ("PMN0", "Count 0"), # ARM11 ("p15", "c15", 0, "c12", 3): ("PMN1", "Count 1"), # ARM11 ("p15", "c15", 1, "c0", 0): ("L2ACTLR", "L2 Auxiliary Control Register"), ("p15", "c15", 1, "c0", 3): ("L2FPR", "L2 Prefetch Control Register"), ("p15", "c15", 3, "c0", 0): ("N/A", "Data Debug Cache"), # ARM11 ("p15", "c15", 3, "c0", 1): ("N/A", "Instruction Debug Cache"), # ARM11 ("p15", "c15", 3, "c2", 0): ("N/A", "Data Tag RAM Read Operation"), # ARM11 ("p15", "c15", 3, "c2", 1): ("N/A", "Instruction Tag RAM Read Operation"), # ARM11 ("p15", "c15", 4, "c0", 0): ("CBAR", "Configuration Base Address Register"), ("p15", "c15", 5, "c4", 0): ("N/A", "Data MicroTLB Index"), # ARM11 ("p15", "c15", 5, "c4", 1): ("N/A", "Instruction MicroTLB Index"), # ARM11 ("p15", "c15", 5, "c4", 2): ("N/A", "Read Main TLB Entry"), # ARM11 ("p15", "c15", 5, "c4", 4): ("N/A", "Write Main TLB Entry"), # ARM11 ("p15", "c15", 5, "c5", 0): ("N/A", "Data MicroTLB VA"), # ARM11 ("p15", "c15", 5, "c5", 1): ("N/A", "Instruction MicroTLB VA"), # ARM11 ("p15", "c15", 5, "c5", 2): ("N/A", "Main TLB VA"), # ARM11 ("p15", "c15", 5, "c7", 0): ("N/A", "Data MicroTLB Attribute"), # ARM11 ("p15", "c15", 5, "c7", 1): ("N/A", "Instruction MicroTLB Attribute"), # ARM11 ("p15", "c15", 5, "c7", 2): ("N/A", "Main TLB Attribute"), # ARM11 ("p15", "c15", 7, "c0", 0): ("N/A", "Cache Debug Control"), # ARM11 ("p15", "c15", 7, "c1", 0): ("N/A", "TLB Debug Control"), # ARM11 # Preload Engine control registers ("p15", "c11", 0, "c0", 0): ("PLEIDR", "Preload Engine ID Register"), ("p15", "c11", 0, "c0", 2): ("PLEASR", "Preload Engine Activity Status Register"), ("p15", "c11", 0, "c0", 4): ("PLEFSR", "Preload Engine FIFO Status Register"), ("p15", "c11", 0, "c1", 0): ("PLEUAR", "Preload Engine User Accessibility Register"), ("p15", "c11", 0, "c1", 1): ("PLEPCR", "Preload Engine Parameters Control Register"), # Preload Engine operations ("p15", "c11", 0, "c2", 1): ("PLEFF", "Preload Engine FIFO flush operation"), ("p15", "c11", 0, "c3", 0): ("PLEPC", "Preload Engine pause channel operation"), ("p15", "c11", 0, "c3", 1): ("PLERC", "Preload Engine resume channel operation"), ("p15", "c11", 0, "c3", 2): ("PLEKC", "Preload Engine kill channel operation"), # Jazelle registers ("p14", "c0", 7, "c0", 0): ("JIDR", "Jazelle ID Register"), ("p14", "c1", 7, "c0", 0): ("JOSCR", "Jazelle OS Control Register"), ("p14", "c2", 7, "c0", 0): ("JMCR", "Jazelle Main Configuration Register"), # Debug registers ("p15", "c4", 3, "c5", 0): ("DSPSR", "Debug Saved Program Status Register"), ("p15", "c4", 3, "c5", 1): ("DLR", "Debug Link Register"), ("p15", "c0", 0, "c3", 5): ("ID_DFR1", "Debug Feature Register 1"), ("p14", "c0", 0, "c0", 0): ("DBGDIDR", "Debug ID Register"), ("p14", "c0", 0, "c6", 0): ("DBGWFAR", "Debug Watchpoint Fault Address Register"), ("p14", "c0", 0, "c6", 2): ("DBGOSECCR", "Debug OS Lock Exception Catch Control Register"), ("p14", "c0", 0, "c7", 0): ("DBGVCR", "Debug Vector Catch Register"), ("p14", "c0", 0, "c0", 2): ("DBGDTRRXext", "Debug OS Lock Data Transfer Register, Receive, External View"), ("p14", "c0", 0, "c2", 0): ("DBGDCCINT", "DCC Interrupt Enable Register"), ("p14", "c0", 0, "c2", 2): ("DBGDSCRext", "Debug Status and Control Register, External View"), ("p14", "c0", 0, "c3", 2): ("DBGDTRTXext", "Debug OS Lock Data Transfer Register, Transmit"), ("p14", "c0", 0, "c0", 4): ("DBGBVR0", "Debug Breakpoint Value Register 0"), ("p14", "c0", 0, "c1", 4): ("DBGBVR1", "Debug Breakpoint Value Register 1"), ("p14", "c0", 0, "c2", 4): ("DBGBVR2", "Debug Breakpoint Value Register 2"), ("p14", "c0", 0, "c3", 4): ("DBGBVR3", "Debug Breakpoint Value Register 3"), ("p14", "c0", 0, "c4", 4): ("DBGBVR4", "Debug Breakpoint Value Register 4"), ("p14", "c0", 0, "c5", 4): ("DBGBVR5", "Debug Breakpoint Value Register 5"), ("p14", "c0", 0, "c6", 4): ("DBGBVR6", "Debug Breakpoint Value Register 6"), ("p14", "c0", 0, "c7", 4): ("DBGBVR7", "Debug Breakpoint Value Register 7"), ("p14", "c0", 0, "c8", 4): ("DBGBVR8", "Debug Breakpoint Value Register 8"), ("p14", "c0", 0, "c9", 4): ("DBGBVR9", "Debug Breakpoint Value Register 9"), ("p14", "c0", 0, "c10", 4): ("DBGBVR10", "Debug Breakpoint Value Register 10"), ("p14", "c0", 0, "c11", 4): ("DBGBVR11", "Debug Breakpoint Value Register 11"), ("p14", "c0", 0, "c12", 4): ("DBGBVR12", "Debug Breakpoint Value Register 12"), ("p14", "c0", 0, "c13", 4): ("DBGBVR13", "Debug Breakpoint Value Register 13"), ("p14", "c0", 0, "c14", 4): ("DBGBVR14", "Debug Breakpoint Value Register 14"), ("p14", "c0", 0, "c15", 4): ("DBGBVR15", "Debug Breakpoint Value Register 15"), ("p14", "c0", 0, "c0", 5): ("DBGBCR0", "Debug Breakpoint Control Register 0"), ("p14", "c0", 0, "c1", 5): ("DBGBCR1", "Debug Breakpoint Control Register 1"), ("p14", "c0", 0, "c2", 5): ("DBGBCR2", "Debug Breakpoint Control Register 2"), ("p14", "c0", 0, "c3", 5): ("DBGBCR3", "Debug Breakpoint Control Register 3"), ("p14", "c0", 0, "c4", 5): ("DBGBCR4", "Debug Breakpoint Control Register 4"), ("p14", "c0", 0, "c5", 5): ("DBGBCR5", "Debug Breakpoint Control Register 5"), ("p14", "c0", 0, "c6", 5): ("DBGBCR6", "Debug Breakpoint Control Register 6"), ("p14", "c0", 0, "c7", 5): ("DBGBCR7", "Debug Breakpoint Control Register 7"), ("p14", "c0", 0, "c8", 5): ("DBGBCR8", "Debug Breakpoint Control Register 8"), ("p14", "c0", 0, "c9", 5): ("DBGBCR9", "Debug Breakpoint Control Register 9"), ("p14", "c0", 0, "c10", 5): ("DBGBCR10", "Debug Breakpoint Control Register 10"), ("p14", "c0", 0, "c11", 5): ("DBGBCR11", "Debug Breakpoint Control Register 11"), ("p14", "c0", 0, "c12", 5): ("DBGBCR12", "Debug Breakpoint Control Register 12"), ("p14", "c0", 0, "c13", 5): ("DBGBCR13", "Debug Breakpoint Control Register 13"), ("p14", "c0", 0, "c14", 5): ("DBGBCR14", "Debug Breakpoint Control Register 14"), ("p14", "c0", 0, "c15", 5): ("DBGBCR15", "Debug Breakpoint Control Register 15"), ("p14", "c0", 0, "c0", 6): ("DBGWVR0", "Debug Watchpoint Value Register 0"), ("p14", "c0", 0, "c1", 6): ("DBGWVR1", "Debug Watchpoint Value Register 1"), ("p14", "c0", 0, "c2", 6): ("DBGWVR2", "Debug Watchpoint Value Register 2"), ("p14", "c0", 0, "c3", 6): ("DBGWVR3", "Debug Watchpoint Value Register 3"), ("p14", "c0", 0, "c4", 6): ("DBGWVR4", "Debug Watchpoint Value Register 4"), ("p14", "c0", 0, "c5", 6): ("DBGWVR5", "Debug Watchpoint Value Register 5"), ("p14", "c0", 0, "c6", 6): ("DBGWVR6", "Debug Watchpoint Value Register 6"), ("p14", "c0", 0, "c7", 6): ("DBGWVR7", "Debug Watchpoint Value Register 7"), ("p14", "c0", 0, "c8", 6): ("DBGWVR8", "Debug Watchpoint Value Register 8"), ("p14", "c0", 0, "c9", 6): ("DBGWVR9", "Debug Watchpoint Value Register 9"), ("p14", "c0", 0, "c10", 6): ("DBGWVR10", "Debug Watchpoint Value Register 10"), ("p14", "c0", 0, "c11", 6): ("DBGWVR11", "Debug Watchpoint Value Register 11"), ("p14", "c0", 0, "c12", 6): ("DBGWVR12", "Debug Watchpoint Value Register 12"), ("p14", "c0", 0, "c13", 6): ("DBGWVR13", "Debug Watchpoint Value Register 13"), ("p14", "c0", 0, "c14", 6): ("DBGWVR14", "Debug Watchpoint Value Register 14"), ("p14", "c0", 0, "c15", 6): ("DBGWVR15", "Debug Watchpoint Value Register 15"), ("p14", "c0", 0, "c0", 7): ("DBGWCR0", "Debug Watchpoint Control Register 0"), ("p14", "c0", 0, "c1", 7): ("DBGWCR1", "Debug Watchpoint Control Register 1"), ("p14", "c0", 0, "c2", 7): ("DBGWCR2", "Debug Watchpoint Control Register 2"), ("p14", "c0", 0, "c3", 7): ("DBGWCR3", "Debug Watchpoint Control Register 3"), ("p14", "c0", 0, "c4", 7): ("DBGWCR4", "Debug Watchpoint Control Register 4"), ("p14", "c0", 0, "c5", 7): ("DBGWCR5", "Debug Watchpoint Control Register 5"), ("p14", "c0", 0, "c6", 7): ("DBGWCR6", "Debug Watchpoint Control Register 6"), ("p14", "c0", 0, "c7", 7): ("DBGWCR7", "Debug Watchpoint Control Register 7"), ("p14", "c0", 0, "c8", 7): ("DBGWCR8", "Debug Watchpoint Control Register 8"), ("p14", "c0", 0, "c9", 7): ("DBGWCR9", "Debug Watchpoint Control Register 9"), ("p14", "c0", 0, "c10", 7): ("DBGWCR10", "Debug Watchpoint Control Register 10"), ("p14", "c0", 0, "c11", 7): ("DBGWCR11", "Debug Watchpoint Control Register 11"), ("p14", "c0", 0, "c12", 7): ("DBGWCR12", "Debug Watchpoint Control Register 12"), ("p14", "c0", 0, "c13", 7): ("DBGWCR13", "Debug Watchpoint Control Register 13"), ("p14", "c0", 0, "c14", 7): ("DBGWCR14", "Debug Watchpoint Control Register 14"), ("p14", "c0", 0, "c15", 7): ("DBGWCR15", "Debug Watchpoint Control Register 15"), ("p14", "c1", 0, "c0", 1): ("DBGBXVR0", "Debug Breakpoint Extended Value Register 0"), ("p14", "c1", 0, "c1", 1): ("DBGBXVR1", "Debug Breakpoint Extended Value Register 1"), ("p14", "c1", 0, "c2", 1): ("DBGBXVR2", "Debug Breakpoint Extended Value Register 2"), ("p14", "c1", 0, "c3", 1): ("DBGBXVR3", "Debug Breakpoint Extended Value Register 3"), ("p14", "c1", 0, "c4", 1): ("DBGBXVR4", "Debug Breakpoint Extended Value Register 4"), ("p14", "c1", 0, "c5", 1): ("DBGBXVR5", "Debug Breakpoint Extended Value Register 5"), ("p14", "c1", 0, "c6", 1): ("DBGBXVR6", "Debug Breakpoint Extended Value Register 6"), ("p14", "c1", 0, "c7", 1): ("DBGBXVR7", "Debug Breakpoint Extended Value Register 7"), ("p14", "c1", 0, "c8", 1): ("DBGBXVR8", "Debug Breakpoint Extended Value Register 8"), ("p14", "c1", 0, "c9", 1): ("DBGBXVR9", "Debug Breakpoint Extended Value Register 9"), ("p14", "c1", 0, "c10", 1): ("DBGBXVR10", "Debug Breakpoint Extended Value Register 10"), ("p14", "c1", 0, "c11", 1): ("DBGBXVR11", "Debug Breakpoint Extended Value Register 11"), ("p14", "c1", 0, "c12", 1): ("DBGBXVR12", "Debug Breakpoint Extended Value Register 12"), ("p14", "c1", 0, "c13", 1): ("DBGBXVR13", "Debug Breakpoint Extended Value Register 13"), ("p14", "c1", 0, "c14", 1): ("DBGBXVR14", "Debug Breakpoint Extended Value Register 14"), ("p14", "c1", 0, "c15", 1): ("DBGBXVR15", "Debug Breakpoint Extended Value Register 15"), ("p14", "c1", 0, "c0", 4): ("DBGOSLAR", "Debug OS Lock Access Register"), ("p14", "c1", 0, "c1", 4): ("DBGOSLSR", "Debug OS Lock Status Register"), ("p14", "c1", 0, "c4", 4): ("DBGPRCR", "Debug Power Control Register"), ("p14", "c7", 0, "c14", 6): ("DBGAUTHSTATUS", "Debug Authentication Status register"), ("p14", "c7", 0, "c0", 7): ("DBGDEVID2", "Debug Device ID register 2"), ("p14", "c7", 0, "c1", 7): ("DBGDEVID1", "Debug Device ID register 1"), ("p14", "c7", 0, "c2", 7): ("DBGDEVID", "Debug Device ID register 0"), ("p14", "c7", 0, "c8", 6): ("DBGCLAIMSET", "Debug Claim Tag Set register"), ("p14", "c7", 0, "c9", 6): ("DBGCLAIMCLR", "Debug Claim Tag Clear register"), ("p14", "c0", 0, "c1", 0): ("DBGDSCRint", "Debug Status and Control Register, Internal View"), ("p14", "c0", 0, "c5", 0): ("DBGDTRRXint", "Debug Data Transfer Register, Receive", "DBGDTRTXint", "Debug Data Transfer Register, Transmit"), ("p14", "c1", 0, "c0", 0): ("DBGDRAR", "Debug ROM Address Register"), ("p14", "c1", 0, "c3", 4): ("DBGOSDLR", "Debug OS Double Lock Register"), ("p14", "c2", 0, "c0", 0): ("DBGDSAR", "Debug Self Address Register"), ("p15", "c1", 4, "c2", 1): ("HTRFCR", "Hyp Trace Filter Control Register"), ("p15", "c1", 0, "c2", 1): ("TRFCR", "Trace Filter Control Register"), } def get_coproc_info(self, target_reg_name): for k, v in self.AARCH32_COPROC_REGISTERS.items(): for reg_name, _desc in slicer(v, 2): if target_reg_name == reg_name: return k return None def get_mrc_code(self, cp_info): code = "mrc {:s}, {:d}, r0, {:s}, {:s}, {:d}".format(cp_info[0], cp_info[2], cp_info[1], cp_info[3], cp_info[4]) arch, mode = UnicornKeystoneCapstone.get_keystone_arch() raw_insns = UnicornKeystoneCapstone.keystone_assemble(code, arch, mode, raw=True) return raw_insns def mrc_execute(self, reg_name): cp_info = self.get_coproc_info(reg_name) if cp_info is None: return None codes = [self.get_mrc_code(cp_info)] before_pc = current_arch.pc ret = ExecAsm(codes).exec_code() after_pc = ret["reg"]["$pc"] # It jumps to the undefined exception vector when an access is made to a non-existent register. # Even when execution is single-stepped, the PC register values can differ significantly. if abs(after_pc - before_pc) > 0x10: err("Undefined register, it probably crashes the kernel") return None return ret["reg"][current_arch.return_register] @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32",)) def do_invoke(self, args): if current_arch is None: err("current_arch is not set") return reg_name = args.reg_name.upper() if reg_name.startswith("$"): reg_name = reg_name[1:] ret = self.mrc_execute(reg_name) if ret is not None: gef_print("{:s} = {:#x}".format(reg_name, ret)) return @register_command class UnicornEmulateCommand(GenericCommand): """Use Unicorn-Engine to emulate the behavior of the binary.""" _cmdline_ = "unicorn-emulate" _category_ = "01-h. Debugging Support - Emulation" _aliases_ = ["emulate"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--from-location", type=AddressUtil.parse_address, help="specifies the start address of the emulated run. (default: current_arch.pc)") group = parser.add_mutually_exclusive_group() group.add_argument("-g", "--nb-gadget", type=AddressUtil.parse_address, help="the number of gadgets to execute. (default mode, NB_GADGET: 10)") group.add_argument("-t", "--to-location", type=AddressUtil.parse_address, help="the end address of the emulated run.") group.add_argument("-n", "--nb-insn", type=AddressUtil.parse_address, help="the number of instructions from `FROM_LOCATION`.") parser.add_argument("-i", "--only-insns", action="store_true", help="show only instructions (no registers, memories, etc).") parser.add_argument("-s", "--skip-emulation", "--save", action="store_true", help="do not run, just save the script.") parser.add_argument("-v", "--verbose", action="store_true", help="displays the register values for each executed instruction.") parser.add_argument("-S", "--add-sse", action="store_true", help="initialization and display XMM registers (x64/x86 only).") parser.add_argument("-A", "--avoid-avx-neon-opt-func", action="store_true", help="patch GOT to replace (e.g., __XXX_avx2 with XXX), as Unicorn does not support them.") parser.add_argument("-E", "--emulate-mmap", action="store_true", help="[FOR DEVELOPER] used internally in gef, please don't use it.") parser.add_argument("-I", "--emulate-insn", action="store_true", help="[FOR DEVELOPER] used internally in gef, please don't use it.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -g 10 # from $pc to the point where 4 instructions are executed", "{0:s} -n 5 # from $pc to 5 later instructions (assume it is no branch)", "{0:s} -t 0x805678a4 -s # from $pc to specified address with saving script", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "unicorn does not support emulating syscall.", "unicorn does not support some instructions. (e.g., xsavec, xrstor, vpbroadcastb, vldr, etc.)", "unicorn does not emulate ARM kernel-provided-user-helpers like $pc=0xffff0fe0, 0xffff0fc0, etc.", "see: https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def get_unicorn_end_addr(self, start_addr, nb): dis = list(Disasm.gef_disassemble(start_addr, nb + 1)) last_insn = dis[-1] return last_insn.address def get_filename(self): if is_remote_debug(): filepath = gdb.current_progspace().filename if filepath.startswith("target:"): filepath = filepath[7:] filename = os.path.basename(filepath) else: filename = Path.get_filename() return filename def make_script(self, kwargs): arch, mode = UnicornKeystoneCapstone.get_unicorn_arch(to_string=True) unicorn_registers = UnicornKeystoneCapstone.get_unicorn_registers(to_string=True, add_sse=kwargs["add_sse"]) cs_arch, cs_mode = UnicornKeystoneCapstone.get_capstone_arch(to_string=True) filename = self.get_filename() Cache.reset_gef_caches(all=True) vmmap = ProcessMap.get_process_maps_exclude_special_regions(allow_vdso=True, allow_vsyscall=True) if not vmmap: warn("An error occurred when reading memory map") return # header content = "#!{:s} -i\n".format(GefUtil.which("python3")) content += "#\n" content += "# Emulation script for '{:s}'".format(filename) if kwargs["nb_gadget"]: content += " from {:#x} to after {:#x} gadgets\n".format(kwargs["start_insn"], kwargs["nb_gadget"]) else: content += " from {:#x} to {:#x}\n".format(kwargs["start_insn"], kwargs["end_insn"]) content += "#\n" content += "# Powered by gef, unicorn-engine, and capstone-engine\n" content += "#\n" content += "# Original: by @_hugsy_\n" content += "# Improvement: by @bata_24\n" content += "#\n" # imports content += "import sys\n" content += "import re\n" content += "import traceback\n" content += "import collections\n" content += "import capstone\n" content += "import unicorn\n" if is_ppc64() or is_ppc32(): content += "import unicorn.ppc_const\n" elif is_riscv32() or is_riscv64(): content += "import unicorn.riscv_const\n" elif is_s390x(): content += "import unicorn.s390x_const\n" content += "\n" # options, consts content += "uc = None\n" content += "verbose = {!s}\n".format(kwargs["verbose"]) content += "quiet = {!s}\n".format(kwargs["quiet"]) content += "only_insns = {!s}\n".format(kwargs["only_insns"]) content += "syscall_register = '{:s}'\n".format(current_arch.syscall_register) content += "count = 0\n" content += "changed_mem = {}\n" content += "\n" content += "registers = collections.OrderedDict({\n" for r in unicorn_registers: content += " '{:s}': {:s},\n".format(r.strip(), unicorn_registers[r]) content += "})\n" content += "\n" # capstone, disassembler if is_arm32(): content += "# hack: unicorn can handle if thumb or not, but capstone can't.\n" content += "# we have to handle it manually for capstone.\n" cs_endian = cs_mode.split(" + ")[-1] content += "cs_arm = capstone.Cs({:s}, {:s})\n".format(cs_arch, cs_endian) content += "cs_thumb = capstone.Cs({:s}, {:s})\n".format(cs_arch, "capstone.CS_MODE_THUMB + " + cs_endian) else: content += "cs = capstone.Cs({:s}, {:s})\n".format(cs_arch, cs_mode) content += "\n" content += "def disassemble(emu, code, addr):\n" if is_arm32(): content += " enable_thumb = emu.reg_read(registers['$cpsr']) & 0x20\n" content += " cs = cs_thumb if enable_thumb else cs_arm\n" content += " for insn in cs.disasm(code, addr):\n" content += " return insn\n" content += "\n" # hook functions content += "def code_hook(emu, address, size, user_data):\n" content += " global count\n" content += " if not quiet:\n" content += " # unicorn passes 0xf1f1_f1f1 as size if opcode is unsupported.\n" content += " # this causes memory read error, so we need to fix the size.\n" content += " if size >= 0x40:\n" content += " size = 0x10\n" content += " # from unicorn 2.1.0, size as 4 if opcode is unsupported.\n" content += " for i in range(10):\n" content += " code = emu.mem_read(address, size + i)\n" content += " insn = disassemble(emu, code, address)\n" content += " if insn:\n" content += " break\n" content += " else:\n" content += " raise\n" content += " code_hex = code[:insn.size].hex()\n" content += " if verbose:\n" content += " print_regs(emu, registers)\n" content += " fmt = '>>> {:d} {:#x}: {:24s} {:s} {:s}'\n" content += " print(fmt.format(count, insn.address, code_hex, insn.mnemonic, insn.op_str))\n" content += " count += 1\n" content += " return\n" content += "\n" content += "def mem_invalid_hook(emu, access, address, size, value, user_data):\n" content += " if access == unicorn.UC_MEM_WRITE_INVALID:\n" content += " fmt = ' --> Invalid memory access; addr:{:#x}, size:{:#x}, value:{:#x}'\n" content += " print(fmt.format(address, size, value))\n" content += " elif access == unicorn.UC_MEM_READ_INVALID:\n" content += " fmt = ' --> Invalid memory access; addr:{:#x}, size:{:#x}'\n" content += " print(fmt.format(address, size))\n" content += " return\n" content += "\n" content += "def mem_write_hook(emu, access, address, size, value, user_data):\n" content += " if only_insns:\n" content += " return\n" content += " before = emu.mem_read(address, size)\n" content += " for i in range(size):\n" content += " accessed_address = address + i\n" content += " if accessed_address not in changed_mem:\n" content += " changed_mem[accessed_address] = {}\n" content += " changed_mem[accessed_address]['before'] = before[i]\n" content += " changed_mem[accessed_address]['after'] = (value >> (8 * i)) & 0xff\n" content += " changed_mem[accessed_address]['type'] = 'modified'\n" content += " return\n" content += "\n" content += "def intr_hook(emu, intno, user_data):\n" if is_x86_32() or is_arm32() or is_arm64(): if is_x86_32(): intno = 0x80 elif is_arm32() or is_arm64(): intno = 0x2 content += " if intno == {:d}:\n".format(intno) content += " syscall_hook(emu, user_data)\n" content += " return\n" if kwargs["emulate_insn"] and is_arm64(): content += " if emulate_swpa_swpl(emu):\n" content += " return\n" content += " if emulate_casa_casl(emu):\n" content += " return\n" content += " print(' --> interrupt={:d}'.format(intno))\n" content += " raise\n" content += "\n" content += "def syscall_hook(emu, user_data):\n" content += " sysno = emu.reg_read(registers[syscall_register])\n" if kwargs["emulate_mmap"]: content += " if emulate_mmap(emu, sysno):\n" content += " return\n" content += " if emulate_munmap(emu, sysno):\n" content += " return\n" content += " if emulate_brk(emu, sysno):\n" content += " return\n" content += " print(' --> syscall={:d} (not emulated)'.format(sysno))\n" content += " raise\n" content += "\n" # syscall emulation if kwargs["emulate_mmap"]: name_table = get_syscall_table().name_table if is_x86_32() or is_arm32(): mmap_entry = name_table["mmap2"] else: mmap_entry = name_table["mmap"] content += "def emulate_mmap(emu, sysno):\n" content += " if sysno != {:d}:\n".format(mmap_entry.nr) content += " return False\n" content += "\n" content += " a1 = emu.reg_read(registers['{:s}'])\n".format(mmap_entry.arg_regs[0]) content += " if a1 != 0:\n" content += " return False\n" content += " a2 = emu.reg_read(registers['{:s}'])\n".format(mmap_entry.arg_regs[1]) content += " if a2 == 0 or (a2 & 0xfff) != 0:\n" content += " return False\n" content += " if a2 > 0x1000_0000: # heuristic value (0x800_0000 is used to create thread arena)\n" content += " return False\n" content += " a3 = emu.reg_read(registers['{:s}'])\n".format(mmap_entry.arg_regs[2]) content += " a3 &= 7\n" content += " a4 = emu.reg_read(registers['{:s}'])\n".format(mmap_entry.arg_regs[3]) content += " if a4 != 0x22: # MAP_ANONYMOUS|MAP_PRIVATE\n" content += " return False\n" content += " a5 = emu.reg_read(registers['{:s}'])\n".format(mmap_entry.arg_regs[4]) content += " if a5 != 0xffff_ffff:\n" content += " return False\n" content += " a6 = emu.reg_read(registers['{:s}'])\n".format(mmap_entry.arg_regs[5]) content += "\n" content += " regions = [(None, 0, None)] + list(emu.mem_regions())\n" content += " regions = regions[::-1]\n" content += " for (mr1, mr2) in zip(regions[:-1], regions[1:]):\n" if is_64bit(): content += " if mr1[0] >= 0x8000_0000_0000_0000: # avoid around [vsyscall]\n" content += " continue\n" content += " if mr1[0] - mr2[1] >= a2:\n" content += " map_start = mr1[0] - a2\n" content += " break\n" content += " else:\n" content += " return False # not found space\n" content += " try:\n" content += " emu.mem_map(map_start, a2, a3)\n" content += " emu.reg_write(registers['{:s}'], map_start)\n".format(mmap_entry.ret_regs[0]) content += " except Exception:\n" content += " return False\n" content += " print(' --> syscall={:d} (emulated)'.format(sysno))\n" content += " print(f' --> {map_start:#x} = mmap({a1:#x}, {a2:#x}, {a3:#x}, {a4:#x}, {a5:#x}, {a6:#x})')\n" content += " return True\n" content += "\n" munmap_entry = name_table["munmap"] content += "def emulate_munmap(emu, sysno):\n" content += " if sysno != {:d}:\n".format(munmap_entry.nr) content += " return False\n" content += "\n" content += " a1 = emu.reg_read(registers['{:s}'])\n".format(munmap_entry.arg_regs[0]) content += " a2 = emu.reg_read(registers['{:s}'])\n".format(munmap_entry.arg_regs[1]) content += " if a2 == 0 or (a2 & 0xfff) != 0:\n" content += " return False\n" content += "\n" content += " try:\n" content += " emu.mem_unmap(a1, a2)\n" content += " emu.reg_write(registers['{:s}'], 0)\n".format(munmap_entry.ret_regs[0]) content += " except Exception:\n" content += " return False\n" content += " print(' --> syscall={:d} (emulated)'.format(sysno))\n" content += " print(f' --> munmap({a1:#x}, {a2:#x})')\n" content += " return True\n" content += "\n" brk_entry = name_table["brk"] current_brk = ExecSyscall(brk_entry.nr, [0x0]).exec_code()["reg"][brk_entry.ret_regs[0]] content += "current_brk = {:#x}\n".format(current_brk) content += "\n" content += "# for main_arena expansion\n" content += "def emulate_brk(emu, sysno):\n" content += " if sysno != {:d}:\n".format(brk_entry.nr) content += " return False\n" content += "\n" content += " global current_brk\n" content += " a1 = emu.reg_read(registers['{:s}'])\n".format(brk_entry.arg_regs[0]) content += "\n" content += " if a1 == 0 or a1 == current_brk:\n" content += " emu.reg_write(registers['{:s}'], current_brk)\n".format(brk_entry.ret_regs[0]) content += " return True\n" content += "\n" content += " if a1 > current_brk:\n" content += " for r in emu.mem_regions(): # get the permission of current brk region\n" content += " if r[1] + 1 == current_brk:\n" content += " map_perm = r[2]\n" content += " break\n" content += " else:\n" content += " return False # something is wrong\n" content += " try:\n" content += " emu.mem_map(current_brk, a1 - current_brk, map_perm)\n" content += " except Exception:\n" content += " return False\n" content += " else:\n" content += " try:\n" content += " emu.mem_unmap(a1, current_brk - a1)\n" content += " except Exception:\n" content += " return False\n" content += "\n" content += " emu.reg_write(registers['{:s}'], a1)\n".format(brk_entry.ret_regs[0]) content += " print(' --> syscall={:d} (emulated)'.format(sysno))\n" content += " print(f' --> {a1:#x} = brk({a1:#x})')\n" content += " current_brk = a1\n" content += " return True\n" content += "\n" # insn emulation if kwargs["emulate_insn"] and is_arm64(): content += "def i2b(x, width={:d}):\n".format(current_arch.ptrsize) content += " return x.to_bytes(width, byteorder='little')\n" content += "\n" content += "def b2i(x, width={:d}):\n".format(current_arch.ptrsize) content += " i = int.from_bytes(x, byteorder='little')\n" content += " if width == 4:\n" content += " return i & 0xffff_ffff\n" content += " return i\n" content += "\n" content += "def add_4_pc(emu):\n" content += " address = emu.reg_read(registers['$pc'])\n" content += " emu.reg_write(registers['$pc'], address + 4)\n" content += " return\n" content += "\n" content += "def get_insn(emu):\n" content += " address = emu.reg_read(registers['$pc'])\n" content += " code = emu.mem_read(address, 4)\n" content += " return disassemble(emu, code, address)\n" content += "\n" content += "def get_reg_and_width(m, i):\n" content += " reg = registers['$x' + m.group(i)[1:]]\n" content += " if m.group(i)[0] == 'x':\n" content += " width = 8\n" content += " elif m.group(i)[0] == 'w':\n" content += " width = 4\n" content += " return reg, width\n" content += "\n" content += "def reg_read(emu, reg, width):\n" content += " val = emu.reg_read(reg)\n" content += " val &= ((1 << (width * 8)) - 1)\n" content += " return val\n" content += "\n" content += "def reg_write(emu, reg, width, val):\n" content += " if isinstance(val, bytearray):\n" content += " val = b2i(val, width)\n" content += " else:\n" content += " val &= (1 << (width * 8)) - 1\n" content += " emu.reg_write(reg, val)\n" content += " return\n" content += "\n" content += "def emulate_swpa_swpl(emu):\n" content += " insn = get_insn(emu)\n" content += " if insn.mnemonic not in ['swpa', 'swpl']:\n" content += " return False\n" content += " m = re.search(r'([xw]\\d+), ([xw]\\d+), \\[(x\\d+)\\]', insn.op_str)\n" content += " if not m:\n" content += " return False\n" content += "" content += " reg1, width1 = get_reg_and_width(m, 1)\n" content += " reg2, width2 = get_reg_and_width(m, 2)\n" content += " reg3, _ = get_reg_and_width(m, 3)\n" content += "" content += " mem_addr = emu.reg_read(reg3)\n" content += " mem_val = emu.mem_read(mem_addr, 8)\n" content += "" content += " reg_write(emu, reg1, width1, mem_val)\n" content += " reg2_val = reg_read(emu, reg2, width2)\n" content += " emu.mem_write(mem_addr, i2b(reg2_val, width2))\n" content += "" content += " add_4_pc(emu)\n" content += " return True\n" content += "\n" content += "def emulate_casa_casl(emu):\n" content += " insn = get_insn(emu)\n" content += " if insn.mnemonic not in ['casa', 'casl']:\n" content += " return False\n" content += " m = re.search(r'([xw]\\d+), ([xw]\\d+), \\[(x\\d+)\\]', insn.op_str)\n" content += " if not m:\n" content += " return False\n" content += "" content += " reg1, width1 = get_reg_and_width(m, 1)\n" content += " reg2, width2 = get_reg_and_width(m, 2)\n" content += " reg3, _ = get_reg_and_width(m, 3)\n" content += "" content += " mem_addr = emu.reg_read(reg3)\n" content += " mem_val = emu.mem_read(mem_addr, 8)\n" content += "" content += " reg1_val = reg_read(emu, reg1, width1)\n" content += " reg2_val = reg_read(emu, reg2, width2)\n" content += " if reg1_val == b2i(mem_val, width1):\n" content += " emu.mem_write(mem_addr, i2b(reg2_val, width2))\n" content += " reg_write(emu, reg1, width1, mem_val)\n" content += "" content += " add_4_pc(emu)\n" content += " return True\n" content += "\n" # print function content += "def print_regs(emu, regs):\n" content += " if only_insns:\n" content += " return\n" content += " for i, r in enumerate(regs):\n" content += " if r.startswith('$xmm'):\n" content += " fmt = '{{:7s}} = {{:#0{:d}x}} '\n".format(32 + 2) content += " print(fmt.format(r, emu.reg_read(regs[r])), end='')\n" content += " if (i % 2 == 1) or (i == len(regs) - 1):\n" content += " print('')\n" content += " else:\n" content += " fmt = '{{:7s}} = {{:#0{:d}x}} '\n".format(current_arch.ptrsize * 2 + 2) content += " print(fmt.format(r, emu.reg_read(regs[r])), end='')\n" content += " if (i % 4 == 3) or (i == len(regs) - 1):\n" content += " print('')\n" content += " return\n" content += "\n" content += "def print_mems(emu):\n" content += " if only_insns:\n" content += " return\n" content += " aligned_addrs = set([x & ~0xf for x in changed_mem.keys()])\n" content += " for aligned_addr in aligned_addrs:\n" content += " for pad_addr in range(aligned_addr, aligned_addr + 0x10):\n" content += " if pad_addr in changed_mem:\n" content += " pass\n" content += " else:\n" content += " changed_mem[pad_addr] = {}\n" content += " changed_mem[pad_addr]['before'] = emu.mem_read(pad_addr, 1)[0]\n" content += " changed_mem[pad_addr]['after'] = emu.mem_read(pad_addr, 1)[0]\n" content += " changed_mem[pad_addr]['type'] = None\n" content += " sorted_data = sorted(changed_mem.items())\n" content += " sliced = [sorted_data[i:i + 16] for i in range(0, len(sorted_data), 16)]\n" content += " prev_address = None\n" content += " for chunk in sliced:\n" content += " address = chunk[0][0]\n" content += " prefix = '{{:#0{:d}x}}'.format(address)\n".format(current_arch.ptrsize * 2 + 2) content += " before = ''\n" content += " after = ''\n" content += " for i in range({:d}):\n".format(16 // current_arch.ptrsize) content += " atmp = []\n" content += " btmp = []\n" content += " for j in range({:d}):\n".format(current_arch.ptrsize) content += " idx = i * {:d} + j\n".format(current_arch.ptrsize) content += " a = chunk[idx][1]['after']\n" content += " b = chunk[idx][1]['before']\n" content += " if a == b:\n" content += " if chunk[idx][1]['type'] is None:\n" content += " btmp.append('{:02x}'.format(b))\n" content += " atmp.append('{:02x}'.format(a))\n" content += " else:\n" content += " btmp.append('\\033[2m{:02x}\\033[0m'.format(b))\n" content += " atmp.append('\\033[2m{:02x}\\033[0m'.format(a))\n" content += " else:\n" content += " btmp.append('\\033[2m\\033[1m{:02x}\\033[0m'.format(b))\n" content += " atmp.append('\\033[2m\\033[1m{:02x}\\033[0m'.format(a))\n" content += " before += '0x' + ''.join(btmp[::-1]) + ' '\n" content += " after += '0x' + ''.join(atmp[::-1]) + ' '\n" content += " line = '{:s} | {:s}| {:s}|'.format(prefix, before, after)\n" content += " if prev_address is not None and prev_address + 0x10 != address:\n" content += " print('*')\n" content += " print(line)\n" content += " prev_address = address\n" content += " print('\\033[2m00\\033[0m: write accessed, ', end='')\n" content += " print('\\033[2m\\033[1m00\\033[0m: value changes')\n" content += " return\n" content += "\n" # TLS if is_x86(): content += "# need to handle segmentation (and pagination) via MSR\n" content += "# from https://github.com/unicorn-engine/unicorn/blob/master/tests/regress/x86_64_msr.py\n" content += "SCRATCH_ADDR = 0xf000\n" content += "def set_msr(emu, msr, value, scratch=SCRATCH_ADDR):\n" content += " buf = b'\\x0f\\x30' # x86: wrmsr\n" content += " emu.mem_map(scratch, 0x1000)\n" content += " emu.mem_write(scratch, buf)\n" if is_x86_64(): content += " emu.reg_write(unicorn.x86_const.UC_X86_REG_RAX, value & 0xffff_ffff)\n" content += " emu.reg_write(unicorn.x86_const.UC_X86_REG_RDX, (value >> 32) & 0xffff_ffff)\n" content += " emu.reg_write(unicorn.x86_const.UC_X86_REG_RCX, msr & 0xffff_ffff)\n" else: content += " emu.reg_write(unicorn.x86_const.UC_X86_REG_EAX, value & 0xffff_ffff)\n" content += " emu.reg_write(unicorn.x86_const.UC_X86_REG_EDX, (value >> 32) & 0xffff_ffff)\n" content += " emu.reg_write(unicorn.x86_const.UC_X86_REG_ECX, msr & 0xffff_ffff)\n" content += " emu.emu_start(scratch, scratch + len(buf), count=1)\n" content += " emu.mem_unmap(scratch, 0x1000)\n" content += " return\n" content += "\n" content += "def set_tls(emu, addr):\n" if is_x86_64(): content += " FS_GS_MSR = 0xc0000100 # MSR_FS_BASE\n" else: content += " FS_GS_MSR = 0xc0000101 # MSR_GS_BASE\n" content += " return set_msr(emu, FS_GS_MSR, addr)\n" content += "\n" # setup registers content += "def reset_regs(emu):\n" # special register (TLS) if is_x86(): # If TLS is not initialized, the fixed address 0x2000 is used. content += " set_tls(emu, {:#x})\n".format(current_arch.get_tls() or 0x2000) if is_arm32() or is_arm64(): content += " # need first. because other register values may be broken when $cpsr is set.\n" cpsr = get_register("$cpsr") content += " emu.reg_write({:s}, {:#x})\n".format(unicorn_registers["$cpsr"], cpsr) if is_arm64(): tpidr = get_register("$TPIDR_EL0") if tpidr is None: tpidr = get_register("$tpidr") content += " emu.reg_write({:s}, {:#x})\n".format(unicorn_registers["$tpidr_el0"], tpidr) if is_arm32(): tls = current_arch.get_tls() content += " emu.reg_write({:s}, {:#x})\n".format(unicorn_registers["$c13_c0_3"], tls) # special register (XMM) if kwargs["add_sse"]: lines = Color.remove_color(gdb.execute("xmm", to_string=True)) for reg in ["$xmm{:d}".format(i) for i in range(16)]: r = re.findall("\\" + reg + r" +: (0x\S+)", lines) if r: regvalue = int(r[0], 16) content += " emu.reg_write({:s}, {:#x})\n".format(unicorn_registers[reg], regvalue) # general register for reg in current_arch.all_registers: if is_x86_64() and reg == "$fs": continue # On x86, writing to the segment register somehow fails, so skip it. if is_x86_32() and reg in X86.special_registers: continue if (is_arm32() or is_arm64()) and reg == "$cpsr": continue regvalue = get_register(reg) content += " emu.reg_write({:s}, {:#x})\n".format(unicorn_registers[reg], regvalue) content += " return\n" content += "\n" # memory dump content += "def reset_memories(emu):\n" for sect in vmmap: if sect.permission == Permission.NONE: continue content += " # Mapping {:s}: {:#x}-{:#x} [{!s}]\n".format( sect.path, sect.page_start, sect.page_end, sect.permission, ) content += " emu.mem_map({:#x}, {:#x}, {})\n".format( sect.page_start, sect.size, oct(sect.permission.value), ) if sect.permission & Permission.READ: code = read_memory(sect.page_start, sect.size) loc = os.path.join(kwargs["dloc"], "{:s}-{:#x}.raw".format(filename, sect.page_start)) open(loc, "wb").write(bytes(code)) content += " emu.mem_write({:#x}, open('{:s}', 'rb').read())\n".format(sect.page_start, loc) # memory patch to avoid avx/neon optimized function if kwargs["patch_got"] and (is_x86_64() or is_arm32()): if is_x86_64(): RE_OPT = re.compile(r"^(?:\*ABS\*|__str|__mem|str|mem).* \| .+ \| (0x\S+) \| (0x\S+) <(__(\w+)_avx2?.*)>") elif is_arm32(): RE_OPT = re.compile(r"^(?:\*ABS\*|__str|__mem|str|mem).* \| .+ \| (0x\S+) \| (0x\S+) <(__(\w+)_neon.*)>") res = gdb.execute("got-all --no-pager", to_string=True) content += " # memory patch to avoid avx/neon optimized function\n" for line in res.splitlines(): # search avx/neon optimized functions line = Color.remove_color(line) m = RE_OPT.search(line) if not m: continue try: got = int(m.group(1), 16) _current_func_addr = int(m.group(2), 16) current_func_name = m.group(3) base_func_name = m.group(4) except ValueError: continue # get original function (e.g., __memmove_avx_unaligned_erms -> __memmove) try: base_func_addr = int(gdb.parse_and_eval("&{:s}".format(base_func_name))) except (gdb.error, ValueError): try: base_func_name = "__" + base_func_name # e.g., __memmove_chk base_func_addr = int(gdb.parse_and_eval("&{:s}".format(base_func_name))) except (gdb.error, ValueError): continue content += " emu.mem_write({:#x}, ({:#x}).to_bytes({:d}, byteorder='little')) # {:s} -> {:s}\n".format( got, base_func_addr, current_arch.ptrsize, current_func_name, base_func_name ) content += " return\n" content += "\n" # reset, emulate functions content += "def reset():\n" content += " emu = unicorn.Uc({:s}, {:s})\n".format(arch, mode) content += " reset_regs(emu)\n" content += " reset_memories(emu)\n" content += " # setup hook functions\n" content += " emu.hook_add(unicorn.UC_HOOK_CODE, code_hook)\n" content += " emu.hook_add(unicorn.UC_HOOK_INTR, intr_hook)\n" if is_x86_64(): content += " emu.hook_add(unicorn.UC_HOOK_INSN, syscall_hook, None, 1, 0, unicorn.x86_const.UC_X86_INS_SYSCALL)\n" content += " emu.hook_add(unicorn.UC_HOOK_MEM_READ_INVALID | unicorn.UC_HOOK_MEM_WRITE_INVALID, mem_invalid_hook)\n" content += " emu.hook_add(unicorn.UC_HOOK_MEM_WRITE, mem_write_hook)\n" content += " return emu\n" content += "\n" content += "def emulate(emu, start_addr, end_addr, count):\n" content += " if not only_insns:\n" content += " print('========================= Initial registers =========================')\n" content += " print_regs(emu, registers)\n" content += "\n" content += " if not only_insns:\n" content += " print('========================= Starting emulation =========================')\n" content += " try:\n" content += " emu.emu_start(start_addr, end_addr, count=count)\n" content += " except Exception:\n" content += " emu.emu_stop()\n" content += " print('========================= Emulation failed =========================')\n" content += " traceback.print_exc(file=sys.stdout)\n" content += "\n" content += " if not only_insns:\n" content += " print('========================= Final registers =========================')\n" content += " print_regs(emu, registers)\n" content += " print('========================= Modified memories (before | after) =========================')\n" content += " print_mems(emu)\n" content += " return\n" content += "\n" content += "if __name__ == '__main__':\n" content += " uc = reset()\n" content += " emulate(uc, {:#x}, {:#x}, {:#x})\n".format( kwargs["start_insn"], kwargs["end_insn"], kwargs["nb_gadget"] or -1, ) return content def run_unicorn(self, content, kwargs): tmp_fd, tmp_filename = GefUtil.mkstemp(prefix="unicorn-emulate", suffix=".py", dt=kwargs["dt"]) os.fdopen(tmp_fd, "w").write(content) if kwargs["skip_emulation"]: info("Unicorn script generated as '{:s}'".format(tmp_filename)) os.chmod(tmp_filename, 0o600) return if kwargs["nb_gadget"] is None: ok("Starting emulation: {:#x} -> {:#x}".format( kwargs["start_insn"], kwargs["end_insn"], )) else: ok("Starting emulation: {:#x} -> after {:d} instructions are executed".format( kwargs["start_insn"], kwargs["nb_gadget"], )) try: res = GefUtil.gef_execute_external([GefUtil.which("python3"), tmp_filename], as_list=True) gef_print("\n".join(res)) except subprocess.CalledProcessError as e: gef_print(e.output.decode("utf-8").rstrip()) os.unlink(tmp_filename) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @ModuleLoader.load_capstone @ModuleLoader.load_unicorn @require_arch_set def do_invoke(self, args): if current_arch.unicorn_support is False: warn("This command is not supported on this architecture") return # start_insn start_insn = args.from_location if start_insn is None: start_insn = current_arch.pc # ng_badget if (args.to_location, args.nb_insn, args.nb_gadget) == (None, None, None): nb_gadget = 10 else: nb_gadget = args.nb_gadget # end_insn if nb_gadget is not None: end_insn = 0 elif args.nb_insn is not None: end_insn = self.get_unicorn_end_addr(start_insn, args.nb_insn) else: end_insn = args.to_location if is_arm32() and end_insn: end_insn &= ~1 # kwargs dt = GefUtil.now_str() kwargs = { "start_insn": start_insn, "end_insn": end_insn, "nb_gadget": nb_gadget, "add_sse": is_x86() and args.add_sse, "verbose": args.verbose, "quiet": args.quiet, "only_insns": args.only_insns, "skip_emulation": args.skip_emulation, "dt": dt, # datetime "dloc": os.path.join(GEF_TEMP_DIR, "unicorn-emulate-" + dt), # memory dump directory "patch_got": args.avoid_avx_neon_opt_func, "emulate_mmap": args.emulate_mmap, "emulate_insn": args.emulate_insn, } os.mkdir(kwargs["dloc"]) # thread locking sched_lock = gdb.parameter("scheduler-locking") gdb.execute("set scheduler-locking on", to_string=True) # generate script = self.make_script(kwargs) # revert gdb.execute("set scheduler-locking {:s}".format(sched_lock), to_string=True) # run self.run_unicorn(script, kwargs) # cleanup if not kwargs["skip_emulation"]: GefUtil.rmdir(kwargs["dloc"]) return @register_command class AngrCommand(GenericCommand): """Use angr to find simple constraints.""" _cmdline_ = "angr" _category_ = "01-h. Debugging Support - Emulation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--find", action="append", default=[], type=AddressUtil.parse_address, help="to find addresses.") parser.add_argument("-a", "--avoid", action="append", default=[], type=AddressUtil.parse_address, help="to avoid addresses.") parser.add_argument("-s", "--sym", nargs=2, action="append", metavar=("LOCATION", "SIZE"), default=[], type=AddressUtil.parse_address, help="make memory symbolic.") parser.add_argument("-t", "--type", action="append", default=[], help="symbolic variable type. (A:A-Z, a:a-z, 0:0-9, s:0x20-0x7e, ?:0x00-0xff, z:0x00)") parser.add_argument("-S", "--skip-execution", action="store_true", help="do not execute.") parser.add_argument("-H", "--hook-stack-chk-fail-by-direct-return", action="store_true", help="hook `__stack_chk_fail@plt` by just `return`.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -f 0x400607 -a 0x400613 -s $rdi 30", "{0:s} -f 0x400607 -a 0x400613 -s $rdi 30 -t Aa0 # sym0:[A-Za-z0-9]+", "{0:s} -f 0x400607 -a 0x400613 -s $rdi 30 -t ? -s $rdx 20 -t Az # sym0:[0x00-0xff]+, sym1:[A-Z\\0]+", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "If there is a stack canary check, angr may fail to find the solution.", "This occurs when execution begins inside a function but terminates outside of it.", "To avoid it, you need to replace these instructions (e.g., `sub rdx, fs:28h; jnz loc_XXX`) with `nop`s.", "Please patch memory or make other appropriate modifications before running the `angr` command.", "", "The -H option is designed to handle this issue automatically.", "But it assumes that there is a `ret` after `call __stack_chk_fail@plt`.", "Note that it will fail if there is a `ret` before `call __stack_chk_fail@plt`.", ] _note_ = "\n".join(_note_) def get_valid_plt(self): """Parse and return a dictionary of valid PLT entries from `got` command output.""" res = gdb.execute("got --quiet --no-pager", to_string=True) res = Color.remove_color(res) valid_plt = {} for line in res.splitlines(): func_name, plt, *_ = line.split(" | ") if plt.startswith("Not found"): continue valid_plt[func_name.strip()] = int(plt, 16) return valid_plt def save_memories(self, dt): """Dump all readable memory regions to files and yield their section info and file paths.""" filename = Path.get_filename() vmmap = ProcessMap.get_process_maps_exclude_special_regions(allow_vdso=True, allow_vsyscall=True) dloc = os.path.join(GEF_TEMP_DIR, "angr-" + dt) os.mkdir(dloc) for sect in vmmap: if sect.permission & Permission.READ: code = read_memory(sect.page_start, sect.size) loc = os.path.join(dloc, "{:s}-{:#x}.raw".format(filename, sect.page_start)) open(loc, "wb").write(bytes(code)) yield sect, loc return None def make_angr_script(self, dt): """Generate an angr analysis script with memory, register, and symbolic constraints setup.""" # initialize content = "#!{:s}\n".format(GefUtil.which("python3")) content += "import angr\n" content += "import claripy\n" content += "import time\n" content += "\n" content += "start_time_real = time.perf_counter()\n" content += "start_time_proc = time.process_time()\n" content += "\n" content += "# initialize\n" content += "proj = angr.Project(\n" content += " {!r},\n".format(Path.get_filepath()) content += " auto_load_libs=False,\n" content += ")\n" content += "\n" content += "state = proj.factory.blank_state(\n" content += " add_options={\n" content += " angr.options.ZERO_FILL_UNCONSTRAINED_REGISTERS,\n" content += " angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY,\n" content += " #angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS,\n" content += " #angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,\n" content += " angr.options.CONSTRAINT_TRACKING_IN_SOLVER,\n" content += " }\n" content += ")\n" content += "\n" # load memories content += "# load memories\n" mems = self.save_memories(dt) for sect, loc in mems: content += "# {!r} {!s}\n".format(sect.path or "", sect.permission) content += "state.memory.store({:#x}, open('{:s}', 'rb').read())\n".format(sect.page_start, loc) content += "\n" # set registers content += "# set regs\n" content += "def set_register(reg, addr):\n" content += " try:\n" content += " setattr(state.regs, reg, addr)\n" content += " except:\n" content += " pass\n" # e.g., ARM32 cpsr content += "\n" if hasattr(current_arch, "general_registers"): target_registers = current_arch.general_registers else: target_registers = current_arch.all_registers for reg in target_registers: reg_value = get_register(reg) if is_arm32(): if reg == "$pc": if current_arch.is_thumb(): reg_value |= 1 content += "set_register({!r}, {:#x})\n".format(reg.replace("$", ""), reg_value) content += "\n" # hook plt content += "# hook plt\n" content += "valid_plt = {\n" valid_plt = self.get_valid_plt() for func_name, plt in valid_plt.items(): content += ' "{:s}": {:#x},\n'.format(func_name, plt) content += "}\n" content += "\n" if self.args.hook_stack_chk_fail_by_direct_return: content += "class StackChkFail(angr.SimProcedure):\n" content += " def run(self):\n" content += " pass # do nothing\n" content += "\n" content += "for func_name, addr in valid_plt.items():\n" if self.args.hook_stack_chk_fail_by_direct_return: content += ' if func_name == "__stack_chk_fail":\n' content += " proj.hook(addr, StackChkFail())\n" content += " continue\n" content += ' if func_name in angr.SIM_PROCEDURES["libc"]:\n' content += ' proj.hook(addr, angr.SIM_PROCEDURES["libc"][func_name]())\n' content += "\n" # symboled memory content += "# symboled memories\n" for i, (sym, sym_sz) in enumerate(self.args.sym): content += "sym_mem{:d} = claripy.BVS('sym_mem{:d}', {:d} * 8)\n".format(i, i, sym_sz) if self.args.type: args_type_i = sorted(set(self.args.type[i])) if "?" in args_type_i: continue content += "for i in range({:d}):\n".format(sym_sz) content += " byte = sym_mem{:d}.get_byte(i)\n".format(i) content += " state.add_constraints(claripy.Or(\n" if "s" in args_type_i: content += " claripy.And(0x20 <= byte, byte < 0x7f),\n" if "z" in args_type_i: content += " byte == 0x00,\n" if "0" in args_type_i: content += " claripy.And(ord('0') <= byte, byte <= ord('9')),\n" if "A" in args_type_i: content += " claripy.And(ord('A') <= byte, byte <= ord('Z')),\n" if "a" in args_type_i: content += " claripy.And(ord('a') <= byte, byte <= ord('z')),\n" content += " ))\n" content += "state.memory.store({:#x}, sym_mem{:d})\n".format(sym, i) content += "\n" # search content += "# search\n" content += "FIND_ADDR = [{:s}]\n".format(",".join([hex(x) for x in self.args.find])) content += "AVOID_ADDR = [{:s}]\n".format(",".join([hex(x) for x in self.args.avoid])) content += "simgr = proj.factory.simulation_manager(state)\n" content += "simgr.explore(find=FIND_ADDR, avoid=AVOID_ADDR)\n" content += "\n" content += "if simgr.found:\n" content += " found_state = simgr.found[0]\n" for i in range(len(self.args.sym)): content += " solution = found_state.solver.eval(sym_mem{:d}, cast_to=bytes)\n".format(i) content += ' print("\\033[1msym{:d}\\033[0m:")\n'.format(i) content += ' print(" raw:", repr(solution))\n' content += ' print(" hex:", solution.hex())\n' content += "else:\n" content += ' print("No solution found")\n' content += 'print("")\n' content += "\n" # result perf content += "end_time_real = time.perf_counter()\n" content += "end_time_proc = time.process_time()\n" content += "\n" content += "real = int(end_time_real - start_time_real)\n" content += "cpu = int(end_time_proc - start_time_proc)\n" content += 'print("Real: {:d}m{:d}s".format(real // 60, real % 60))\n' content += 'print("CPU : {:d}m{:d}s".format(cpu // 60, cpu % 60))\n' return content def run_angr(self): """Generate, save, and optionally execute an angr analysis script, displaying the results.""" # make script dt = GefUtil.now_str() content = self.make_angr_script(dt) # write it tmp_fd, tmp_filename = GefUtil.mkstemp(prefix="angr", suffix=".py", dt=dt) os.fdopen(tmp_fd, "w").write(content) info(tmp_filename) if self.args.skip_execution: return # run it try: res = GefUtil.gef_execute_external([GefUtil.which("python3"), tmp_filename], as_list=True) gef_print("\n".join(res)) except subprocess.CalledProcessError as e: gef_print(e.output.decode("utf-8").rstrip()) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @ModuleLoader.load_angr def do_invoke(self, args): if not args.find or not args.sym: self.usage() return if args.type: if len(args.type) != len(args.sym): err("The --type option must be specified for all symbols") return for t in args.type: for tc in t: if tc not in "Aa0s?z": err("Invalid symbolic type: {:s}".format(tc)) return self.run_angr() return class StubBreakpoint(gdb.Breakpoint): """Create a breakpoint to permanently disable a call (fork/alarm/signal/etc.).""" def __init__(self, func, retval): super().__init__(func, gdb.BP_BREAKPOINT, internal=False) self.func = func self.retval = retval m = "All calls to '{:s}' will be skipped".format(self.func) if self.retval is not None: m += " (with return value set to {:#x})".format(self.retval) info(m) return def stop(self): m = "Ignoring call to '{:s}' ".format(self.func) m += "(setting return value to {:#x})".format(self.retval) gdb.execute("return (unsigned int){:#x}".format(self.retval)) ok(m) return False @register_command class StubCommand(GenericCommand): """Stub out the specified function to skip it. (e.g., fork)""" _cmdline_ = "stub" _category_ = "03-d. Memory - Patch" _aliases_ = ["deactivate"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--retval", type=int, default=0, help="the return value from stub. (default: %(default)s)") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="address/symbol to stub out.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -r 0 fork", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running def do_invoke(self, args): loc = "*{:#x}".format(args.location) StubBreakpoint(loc, args.retval) return @register_command class CapstoneDisassembleCommand(GenericCommand): """Use capstone disassembly framework to disassemble code.""" _cmdline_ = "capstone-disassemble" _category_ = "01-e. Debugging Support - Assemble" _repeat_ = True _aliases_ = ["cs-dis", "pdisas", "nearpc"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address to disassemble. (default: current_arch.pc)") parser.add_argument("-l", "--length", type=AddressUtil.parse_address, help="the length to disassemble. (default: context.nb_lines_code)") parser.add_argument("args", metavar="ARGS", nargs="*", help="arguments for capstone. see following example.") _syntax_ = parser.format_help() valid_arch_modes = { "ARM" : ["ARM", "THUMB"], "ARM64" : ["ARM"], "MIPS" : ["32", "64"], "PPC" : ["32", "64"], "SPARC" : ["32", "32PLUS", "64"], "X86" : ["16", "32", "64"], } _example_ = [ "{0:s} -l 50 $pc # dump from $pc up to 50 lines later", "{0:s} -l 50 $pc arch=ARM mode=ARM endian=1 # specify arch, mode and endian (1:big endian)", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Available architectures and modes:" ] for arch in valid_arch_modes: _note_.append(" - {:8s} {}".format(arch, " / ".join(valid_arch_modes[arch]))) _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) self.add_setting("nb_lines_code_default", 50, "Number of instruction if no length is specified.") return @parse_args @only_if_gdb_running @ModuleLoader.load_capstone @require_arch_set def do_invoke(self, args): kwargs = {} for arg in args.args: if "=" in arg: key, value = arg.split("=", 1) kwargs[key] = value else: err("ARGS must be KEY=VALUE style") return length = args.length or Config.get_gef_setting("capstone_disassemble.nb_lines_code_default") location = args.location or current_arch.pc try: skip = length * self.repeat_count for insn in Disasm.capstone_disassemble(location, length, skip=skip, **kwargs): if insn.address == current_arch.pc: msg = " -> {:s}".format(insn.colored_text(10, highlight=True)) else: msg = " {:s}".format(insn.colored_text(10, highlight=False)) gef_print(msg) except AttributeError: err("Maybe unsupported architecture") except gdb.error: pass return @register_command class GlibcHeapCommand(GenericCommand): """The base command to get information about the Glibc heap structure.""" _cmdline_ = "heap" _category_ = "05-a. Heap - Glibc" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("arena") subparsers.add_parser("arenas") subparsers.add_parser("bins") subparsers.add_parser("bins-simple") subparsers.add_parser("chunk") subparsers.add_parser("chunks") subparsers.add_parser("top") subparsers.add_parser("try-free") subparsers.add_parser("try-malloc") subparsers.add_parser("try-realloc") subparsers.add_parser("try-calloc") subparsers.add_parser("tcache-index-helper") subparsers.add_parser("find-fake-fast") subparsers.add_parser("extract-heap-addr") subparsers.add_parser("calc-protected-fd") subparsers.add_parser("visual-heap") subparsers.add_parser("dump-image") subparsers.add_parser("tracer") subparsers.add_parser("parse") subparsers.add_parser("snapshot") subparsers.add_parser("snapshot-compare") _syntax_ = parser.format_help() _note_ = [ "Supports up to glibc 2.43.", "- 2.15+: GEF treats malloc_par.pagesize as absent (always None).", "- 2.19+: malloc_state.next_free is handled.", "- 2.23+: malloc_state.attached_threads is handled.", "- 2.24+: GEF treats malloc_par.max_total_mem as absent (always None).", "- 2.26: tcache is introduced.", "- 2.26+: MALLOC_ALIGNMENT changes for x86_32/riscv32/ppc32 affect NFASTBINS and bin-size tables.", "- 2.27+: malloc_state layout handling changes (have_fastchunks/fastbins offsets).", "- 2.30: tcache_perthread_struct.counts element size changes 1->2 bytes.", "- 2.32: Safe-Linking (pointer mangling) for tcache/fastbins fd is supported.", "- 2.34+: GEF no longer uses the __malloc_hook-based strategy to locate main_arena.", "- 2.35: heap_info.pagesize is handled.", "- 2.35: malloc_par.{thp_pagesize,hp_pagesize,hp_flags} are handled.", "- 2.42: TCACHE_MAX_BINS 64->76 (+12 large bins, arch-dependent size ranges).", "- 2.42: tcache_perthread_struct.counts changes to num_slots.", "- 2.43: fastbins are removed.", "- 2.43: TCACHE_FILL_COUNT 7->16.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(prefix=True) self.add_setting("tcache_max_count", -1, "Max chunks per tcache bin (if configured by GLIBC_TUNABLES)") return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): self.usage() return @register_command class GlibcHeapTopCommand(GenericCommand): """Display heap top chunk.""" _cmdline_ = "heap top" _category_ = "05-a. Heap - Glibc" _aliases_ = ["top-chunk"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return # get top if args.arena_addr: res = gdb.execute("heap arena --no-pager --arena-addr {:#x}".format(args.arena_addr), to_string=True) else: res = gdb.execute("heap arena --no-pager", to_string=True) m = re.search(r"top = (0x\S+),", Color.remove_color(res)) if not m: err("Could not find top address") return top = int(m.group(1), 16) info("arena.top: {:#x}".format(top)) top += current_arch.ptrsize * 2 gef_print(GlibcHeap.GlibcChunk(arena, top).psprint()) return @register_command class GlibcHeapArenasCommand(GenericCommand): """List heap arenas.""" _cmdline_ = "heap arenas" _category_ = "05-a. Heap - Glibc" _aliases_ = ["arenas"] parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # main_arena arena = GlibcHeap.get_main_arena() if arena is None: err("Could not find glibc main arena") return gef_print(titlify("main_arena")) gef_print("{}".format(arena)) # thread arena gef_print(titlify("thread_arena")) arena = arena.get_next() if arena is None: gef_print("Not found") while arena: gef_print("{}".format(arena)) arena = arena.get_next() return @register_command class GlibcHeapArenaCommand(GenericCommand, BufferingOutput): """Display information on a heap arena.""" _cmdline_ = "heap arena" _category_ = "05-a. Heap - Glibc" _aliases_ = ["arena"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def parse_arena(self, arena): """Parse and append a formatted representation of the given arena's structure to the output list.""" try: cmd = "p ((struct malloc_state*) {:#x})[0]".format(arena.addr) title = titlify("[arena - parsed via debuginfo] --- {:s}".format(cmd)) result = gdb.execute(cmd, to_string=True) self.out.append(title) self.out.extend(result.splitlines()) except gdb.error: title = titlify("[arena - parsed heuristically] --- {:#x}".format(arena.addr)) self.out.append(title) self.out.append("$1 = {") self.out.append(" mutex = {:#x},".format(int(arena.mutex))) self.out.append(" flags = {:#x},".format(int(arena.flags))) if get_libc_version() >= (2, 27) and get_libc_version() < (2, 43): self.out.append(" have_fastchunks = {:#x},".format(int(arena.have_fastchunks))) if get_libc_version() < (2, 43): self.out.append(" fastbinsY = {") for i in range(int(arena.num_fastbins)): self.out.append(" [{:#x}] = {:#x},".format(i, int(arena.fastbinsY[i]))) self.out.append(" },") self.out.append(" top = {:#x},".format(int(arena.top))) self.out.append(" last_remainder = {:#x},".format(int(arena.last_remainder))) self.out.append(" bins = {") for i in range(int(arena.num_bins)): self.out.append(" [{:#x}] = {:#x},".format(i, int(arena.bins[i]))) self.out.append(" },") self.out.append(" binmap = {") for i in range(int(arena.num_binmap)): self.out.append(" [{:#x}] = {:#x},".format(i, int(arena.binmap[i]))) self.out.append(" },") self.out.append(" next = {:#x},".format(int(arena.next))) self.out.append(" next_free = {:#x},".format(int(arena.next_free))) self.out.append(" attached_threads = {:#x},".format(int(arena.attached_threads))) self.out.append(" system_mem = {:#x},".format(int(arena.system_mem))) self.out.append(" max_system_mem = {:#x},".format(int(arena.max_system_mem))) self.out.append("}") return def parse_mp(self): """Parse and append a formatted representation of the malloc_par (mp_) structure to the output list.""" try: mp = AddressUtil.parse_address("&mp_") except gdb.error: mp = GlibcHeap.search_for_mp_() if mp is None: self.out.append(titlify("[mp_]")) self.out.append("Could not find &mp_") return try: cmd = "p ((struct malloc_par*) {:#x})[0]".format(mp) title = titlify("[mp_ - parsed via debuginfo] --- {:s}".format(cmd)) result = gdb.execute(cmd, to_string=True) self.out.append(title) self.out.extend(result.splitlines()) except gdb.error: mp = GlibcHeap.MallocPar(mp) self.out.append(titlify("[mp_ - parsed heuristically] --- {:#x}".format(mp.addr))) self.out.append("$1 = {") self.out.append(" trim_threshold = {:#x},".format(int(mp.trim_threshold))) self.out.append(" top_pad = {:#x},".format(int(mp.top_pad))) self.out.append(" mmap_threshold = {:#x},".format(int(mp.mmap_threshold))) self.out.append(" arena_test = {:#x},".format(int(mp.arena_test))) self.out.append(" arena_max = {:#x},".format(int(mp.arena_max))) if get_libc_version() >= (2, 35): self.out.append(" thp_pagesize = {:#x},".format(int(mp.thp_pagesize))) self.out.append(" hp_pagesize = {:#x},".format(int(mp.hp_pagesize))) self.out.append(" hp_flags = {:#x},".format(int(mp.hp_flags))) self.out.append(" n_mmaps = {:#x},".format(int(mp.n_mmaps))) self.out.append(" n_mmaps_max = {:#x},".format(int(mp.n_mmaps_max))) self.out.append(" max_n_mmaps = {:#x},".format(int(mp.max_n_mmaps))) self.out.append(" no_dyn_threshold = {:#x},".format(int(mp.no_dyn_threshold))) if get_libc_version() < (2, 15): self.out.append(" pagesize = {:#x},".format(int(mp.pagesize))) self.out.append(" mmapped_mem = {:#x},".format(int(mp.mmapped_mem))) self.out.append(" max_mmapped_mem = {:#x},".format(int(mp.max_mmapped_mem))) if get_libc_version() < (2, 24): self.out.append(" max_total_mem = {:#x},".format(int(mp.max_total_mem))) self.out.append(" sbrk_base = {:#x},".format(int(mp.sbrk_base))) if get_libc_version() >= (2, 26): if get_libc_version() < (2, 42): self.out.append(" tcache_bins = {:#x},".format(int(mp.tcache_bins))) else: self.out.append(" tcache_small_bins = {:#x},".format(int(mp.tcache_bins))) self.out.append(" tcache_max_bytes = {:#x},".format(int(mp.tcache_max_bytes))) self.out.append(" tcache_unsorted_limit = {:#x},".format(int(mp.tcache_unsorted_limit))) self.out.append("}") return def parse_heap_info(self, arena): """Parse and append a formatted representation of the _heap_info structure for the given arena to the output list.""" if arena.is_main_arena: self.out.append(titlify("[heap_info]")) self.out.append("Not thread arena") return heap_info = arena.addr & get_pagesize_mask_high() try: cmd = "p ((struct _heap_info*) {:#x})[0]".format(heap_info) title = titlify("[heap_info - parsed via debuginfo] --- {:s}".format(cmd)) result = gdb.execute(cmd, to_string=True) self.out.append(title) self.out.extend(result.splitlines()) except gdb.error: heap_info = GlibcHeap.HeapInfo(heap_info) self.out.append(titlify("[heap_info - parsed heuristically] --- {:#x}".format(heap_info.addr))) self.out.append("$1 = {") self.out.append(" ar_ptr = {:#x},".format(int(heap_info.ar_ptr))) self.out.append(" prev = {:#x},".format(int(heap_info.prev))) self.out.append(" size = {:#x},".format(int(heap_info.size))) self.out.append(" mprotect_size = {:#x},".format(int(heap_info.mprotect_size))) if get_libc_version() >= (2, 35): self.out.append(" pagesize = {:#x},".format(int(heap_info.pagesize))) self.out.append(" pad = {},".format(heap_info.pad)) self.out.append("}") return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return # dump self.out = [] self.parse_arena(arena) self.parse_mp() self.parse_heap_info(arena) # colorize if not Color.disable_color(): for i in range(len(self.out)): self.out[i] = re.sub(" ([a-zA-Z_]+) =", " \033[36m\\1\033[0m =", self.out[i]) self.out[i] = re.sub(" = (0x[0-9a-f]+)", " = \033[34m\\1\033[0m", self.out[i]) self.print_output() return @register_command class GlibcHeapChunkCommand(GenericCommand): """Display information on a heap chunk.""" _cmdline_ = "heap chunk" _category_ = "05-a. Heap - Glibc" _aliases_ = ["chunk"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address to interpret as a chunk.") parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-b", "--as-base", action="store_true", help="use LOCATION as chunk base address (chunk_base_address = chunk_address - ptrsize * 2).") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return # get chunk if args.as_base: chunk = GlibcHeap.GlibcChunk(arena, args.location, from_base=True) else: chunk = GlibcHeap.GlibcChunk(arena, args.location) # dump try: gef_print(chunk.psprint()) except gdb.MemoryError: err("Invalid address") return # extra information info = [] info.extend(arena.get_bins_info(chunk, skip_top=True)) if chunk.chunk_base_address == arena.top: info.append("top") if info: freelist_hint_color = Config.get_gef_setting("theme.heap_freelist_hint") gef_print(" Found freelist/top: {:s}".format(Color.colorify(", ".join(info), freelist_hint_color))) else: gef_print(" Found freelist/top: None") return @register_command class GlibcHeapChunksCommand(GenericCommand, BufferingOutput): """Display information on all heap chunks.""" _cmdline_ = "heap chunks" _category_ = "05-a. Heap - Glibc" _aliases_ = ["chunks"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address interpreted as the beginning of a contiguous chunk. (default: arena.heap_base)") parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-b", "--nb-byte", type=AddressUtil.parse_address, help="temporarily override `heap_chunks.peek_nb_byte`.") parser.add_argument("-o", "--peek-offset", type=AddressUtil.parse_address, default=0, help="temporarily override `heap_chunks.peek_offset`.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} -a 0x7ffff0000020", "{0:s} -a 1", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "about the annotation:", ' - "tcache[idx=7,sz=0x90][1/2]"', " - idx: 0-origin index.", " - sz : the size of the chunk including metadata.", " - 1/ : a position in the free-list.", " - /2: parsed free-list length including corrupted chunks.", " NOT the value of tcache_perthread_struct.count[idx], be careful!", ' - "largebins[idx=98,sz=0x1000-0x1200][8/8]"', " - idx: 0-origin index that `i-th idx` means `bins[i*2 : (i+1)*2]`.", " - sz : the size range of the chunk including metadata.", " - 8/ : a position in the free-list. largebins are FIFO, so the last chunk will be used first.", " - /8: parsed free-list length including corrupted chunks.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) self.add_setting("peek_nb_byte", 0, "Hexdump N first byte(s) inside the chunk data (0 to disable)") self.add_setting("peek_offset", 0, "the offset to start dumping from when using peek_nb_byte") return def print_heap_chunks(self, arena, dump_start, peek_nb, peek_offset): """Iterate and display heap chunks in the given arena, with corruption checks and optional memory peeking.""" # Do not show if top is broken, as it affects exit conditions. if is_32bit() and arena.top % 0x08: self.err_add_out("arena.top is corrupted") return elif is_64bit() and arena.top % 0x10: self.err_add_out("arena.top is corrupted") return # It continues even if last_remainder is broken because it doesn't affect the exit condition. if is_32bit() and arena.last_remainder % 0x08: self.warn_add_out("arena.last_remainder is corrupted") elif is_64bit() and arena.last_remainder % 0x10: self.warn_add_out("arena.last_remainder is corrupted") freelist_hint_color = Config.get_gef_setting("theme.heap_freelist_hint") current_chunk = GlibcHeap.GlibcChunk(arena, dump_start, from_base=True) while True: if current_chunk.chunk_base_address == arena.top: top_str = Color.colorify(" <- top", freelist_hint_color) self.out.append("{!s} {:s}".format(current_chunk, top_str)) break if current_chunk.chunk_base_address > arena.top: self.err_add_out("Corrupted: chunk > top") break if current_chunk.size == 0: # EOF break line = str(current_chunk) # in or not in free-list info = arena.get_bins_info(current_chunk) if info: freelist_hint = Color.colorify(" <- {:s}".format(", ".join(info)), freelist_hint_color) line += freelist_hint self.out.append(line) # peek nbyte if peek_nb: peek_addr = current_chunk.chunk_base_address + peek_offset peeked_data = read_memory(peek_addr, peek_nb) h = hexdump(peeked_data, 0x10, base=peek_addr) self.out.append(h) # goto next next_chunk = current_chunk.get_next_chunk() if next_chunk is None: break if not is_valid_addr(next_chunk.address): self.err_add_out("Corrupted: next_chunk_address is invalid") break current_chunk = next_chunk return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.location is None: dump_start = arena.heap_base # specific pattern if arena.is_main_arena: if (is_x86_32() or is_riscv32() or is_ppc32()) and get_libc_version() >= (2, 26): dump_start += 8 else: dump_start = args.location if args.nb_byte is not None: peek_nb = args.nb_byte else: peek_nb = Config.get_gef_setting("heap_chunks.peek_nb_byte") if args.peek_offset is not None: peek_offset = args.peek_offset else: peek_offset = Config.get_gef_setting("heap_chunks.peek_offset") self.out = [] self.print_heap_chunks(arena, dump_start, peek_nb, peek_offset) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapParseCommand(GenericCommand, BufferingOutput): """Display information on all heap chunks as Pwngdb style.""" _cmdline_ = "heap parse" _category_ = "05-a. Heap - Glibc" _aliases_ = ["parseheap"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} -a 0x7ffff0000020", "{0:s} -a 1", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = GlibcHeapChunksCommand._note_ def make_line(self, arena, chunk): width = 14 if is_64bit() else 10 prev_width = 20 if is_64bit() else 12 info = arena.get_bins_info(chunk) hint = ", ".join(info) if arena.is_chunk_in_freelists(chunk): chunk_freed_color = Config.get_gef_setting("theme.heap_chunk_address_freed") chunk_base_addr_str = Color.colorify("{:<#{:d}x}".format(chunk.chunk_base_address, width), chunk_freed_color) used_str = Color.colorify("{:{:d}s}".format("Freed", width), chunk_freed_color) if arena.is_chunk_in_tcache(chunk) or arena.is_chunk_in_fastbins(chunk): fd_str = "{:<#{:d}x}".format(chunk.get_fwd_ptr(True), width) bk_str = "{:<{:d}s}".format("-", width) else: fd_str = "{:<#{:d}x}".format(chunk.fd, width) bk_str = "{:<#{:d}x}".format(chunk.bk, width) else: chunk_used_color = Config.get_gef_setting("theme.heap_chunk_address_used") chunk_base_addr_str = Color.colorify("{:<#{:d}x}".format(chunk.chunk_base_address, width), chunk_used_color) used_str = Color.colorify("{:{:d}s}".format("Used", width), chunk_used_color) fd_str = "{:<{:d}s}".format("-", width) bk_str = "{:<{:d}s}".format("-", width) if chunk.has_p_bit(): prev_size_str = "({:#x})".format(chunk.get_prev_chunk_size()) else: prev_size_str = "{:#x}".format(chunk.get_prev_chunk_size()) return "{:s} {:<{:d}s} {:<#{:d}x} {:s} {:s} {:s} {:s}".format( chunk_base_addr_str, prev_size_str, prev_width, chunk.size, width, used_str, fd_str, bk_str, hint, ) def parse_heap(self, arena, dump_start): # Do not show if top is broken, as it affects exit conditions. if is_32bit() and arena.top % 0x08: self.err_add_out("arena.top is corrupted") return elif is_64bit() and arena.top % 0x10: self.err_add_out("arena.top is corrupted") return # It continues even if last_remainder is broken because it doesn't affect the exit condition. if is_32bit() and arena.last_remainder % 0x08: self.warn_add_out("arena.last_remainder is corrupted") elif is_64bit() and arena.last_remainder % 0x10: self.warn_add_out("arena.last_remainder is corrupted") width = 14 if is_64bit() else 10 prev_width = 20 if is_64bit() else 12 fmt = "{:{:d}s} {:{:d}s} {:{:d}s} {:{:d}s} {:{:d}s} {:{:d}s} {:s}" legend = ["addr", width, "prev_size", prev_width, "size", width, "status", width, "fd", width, "bk", width, "hint"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) current_chunk = GlibcHeap.GlibcChunk(arena, dump_start, from_base=True) while True: if current_chunk.chunk_base_address == arena.top: self.out.append(self.make_line(arena, current_chunk)) break if current_chunk.chunk_base_address > arena.top: self.err_add_out("Corrupted: chunk > top") break if current_chunk.size == 0: # EOF break line = self.make_line(arena, current_chunk) self.out.append(line) # goto next next_chunk = current_chunk.get_next_chunk() if next_chunk is None: break if not is_valid_addr(next_chunk.address): self.err_add_out("Corrupted: next_chunk_address is invalid") break current_chunk = next_chunk return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return dump_start = arena.heap_base # specific pattern if arena.is_main_arena: if (is_x86_32() or is_riscv32() or is_ppc32()) and get_libc_version() >= (2, 26): dump_start += 8 self.out = [] self.parse_heap(arena, dump_start) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapBinsSimpleCommand(GenericCommand): """Simply display information on the bins of an arena.""" _cmdline_ = "heap bins-simple" _category_ = "05-a. Heap - Glibc" _aliases_ = ["bs", "heapinfo"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-s", "--skip-size", action="store_true", help="skip size information.") parser.add_argument("-v", "--verbose", action="store_true", help="display empty bins.") parser.add_argument("--all", action="store_true", help="dump all arenas.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} -a 0x7ffff0000020 -v", "{0:s} -a 1 -v", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "The meaning of the tcache expression:", " e.g.; 0x80 [6] (1): 0x55555557e480", " 0x80: size; [6]: tcache index; (1): tcache_perthread_struct.count[i]", ] _note_ = "\n".join(_note_) def bins_simple(self, arenas): def get_size(arena, c, from_base=True): if self.args.skip_size: return "" try: sz = GlibcHeap.GlibcChunk(arena, c, from_base=from_base).size return " (sz:{:#x})".format(sz) except gdb.MemoryError: return "" corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") libc_version = get_libc_version() # iterate arena ------------------------------------------------------------------------------------------------------ for arena in arenas: gef_print(titlify("arena: {:#x}{:s}".format( arena.addr, Symbol.get_symbol_string(arena.addr)), color="bold", msg_color="bold"), ) # tcache --------------------------------------------------------------------------------------------------------- TCACHE_SMALL_BINS = arena.TCACHE_SMALL_BINS() TCACHE_FILL_COUNT = arena.TCACHE_FILL_COUNT() gef_print(titlify("tcache")) if libc_version < (2, 26): info("No tcache in this version of libc") else: for i, chunks in arena.get_tcache_list().items(): m = [] for c in chunks: if isinstance(c, str): m.append(Color.colorify(c, corrupted_msg_color)) elif libc_version < (2, 42) or i < TCACHE_SMALL_BINS: m.append("{!s}{:s}".format( ProcessMap.lookup_address(c), Symbol.get_symbol_string(c), )) else: m.append("{!s}{:s}{:s}".format( ProcessMap.lookup_address(c), Symbol.get_symbol_string(c), get_size(arena, c, from_base=False), )) if m or self.args.verbose: count = arena.tcachebins_i_count(i) if "size" in GlibcHeap.get_binsize_table()["tcache"][i]: size = GlibcHeap.get_binsize_table()["tcache"][i]["size"] gef_print("{:#x} [{:d}] ({:d}/{:d}): {:s}".format( size, i, count, TCACHE_FILL_COUNT, " -> ".join(m), ).rstrip()) else: size_min = GlibcHeap.get_binsize_table()["tcache"][i]["size_min"] size_max = GlibcHeap.get_binsize_table()["tcache"][i]["size_max"] gef_print("{:#x}-{:#x} [{:d}] ({:d}/{:d}): {:s}".format( size_min, size_max, i, count, TCACHE_FILL_COUNT, " -> ".join(m), ).rstrip()) # fastbins ------------------------------------------------------------------------------------------------------- gef_print(titlify("fastbins")) if libc_version < (2, 43): for i, chunks in arena.get_fastbins_list().items(): m = [] for c in chunks: if isinstance(c, str): m.append(Color.colorify(c, corrupted_msg_color)) else: m.append("{!s}{:s}".format( ProcessMap.lookup_address(c), Symbol.get_symbol_string(c), )) if m or self.args.verbose: size = GlibcHeap.get_binsize_table()["fastbins"][i]["size"] gef_print("{:#x} [{:d}]: {:s}".format(size, i, " -> ".join(m)).rstrip()) else: info("No fastbins in this version of libc") # unsorted bin --------------------------------------------------------------------------------------------------- gef_print(titlify("unsorted bin")) for _, chunks in arena.get_unsortedbin_list().items(): m = [] for c in chunks: if isinstance(c, str): m.append(Color.colorify(c, corrupted_msg_color)) else: m.append("{!s}{:s}{:s}".format( ProcessMap.lookup_address(c), Symbol.get_symbol_string(c), get_size(arena, c), )) if m or self.args.verbose: gef_print("any [0]: {:s}".format(" <-> ".join(m)).rstrip()) # small bins ----------------------------------------------------------------------------------------------------- gef_print(titlify("small bins")) for i, chunks in arena.get_smallbins_list().items(): m = [] for c in chunks: if isinstance(c, str): m.append(Color.colorify(c, corrupted_msg_color)) else: m.append("{!s}{:s}".format( ProcessMap.lookup_address(c), Symbol.get_symbol_string(c), )) if m or self.args.verbose: size = GlibcHeap.get_binsize_table()["small_bins"][i]["size"] gef_print("{:#x} [{:d}]: {:s}".format(size, i, " <-> ".join(m)).rstrip()) # large bins ----------------------------------------------------------------------------------------------------- gef_print(titlify("large bins")) for i, chunks in arena.get_largebins_list().items(): m = [] for c in chunks: if isinstance(c, str): m.append(Color.colorify(c, corrupted_msg_color)) else: m.append("{!s}{:s}{:s}".format( ProcessMap.lookup_address(c), Symbol.get_symbol_string(c), get_size(arena, c), )) if m or self.args.verbose: size_min = GlibcHeap.get_binsize_table()["large_bins"][i]["size_min"] size_max = GlibcHeap.get_binsize_table()["large_bins"][i]["size_max"] gef_print("{:#x}-{:#x} [{:d}]: {:s}".format(size_min, size_max, i, " <-> ".join(m)).rstrip()) gef_print(titlify("arena")) # top ------------------------------------------------------------------------------------------------------------ top = arena.top gef_print("top: {!s}{:s}{:s}".format( ProcessMap.lookup_address(top), Symbol.get_symbol_string(top), get_size(arena, top), )) # last_remainder ------------------------------------------------------------------------------------------------- lm = arena.last_remainder gef_print("last_remainder: {!s}{:s}{:s}".format( ProcessMap.lookup_address(lm), Symbol.get_symbol_string(lm), get_size(arena, lm), )) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.all: arenas = GlibcHeap.get_all_arenas() else: arenas = [arena] self.bins_simple(arenas) return class GlibcHeapBinsDump: """Manage glibc heap bins dumper.""" def print_tcache(self, arena, verbose=False, index_filter=None): """Pretty-print tcache bins for the given arena, detecting loops and chunk corruption.""" if get_libc_version() < (2, 26): return self.out.append(titlify("tcache (&tcache_perthread_struct: {:#x})".format( arena.tcache_perthread_struct, ))) corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") nb_chunk = 0 for i in range(arena.TCACHE_MAX_BINS()): # index filter if index_filter is not None: if i != index_filter: continue chunk = arena.get_tcachebins_i(i) chunks = [] m = [] # Only print the entry if there are valid chunks. Don't trust count while True: if chunk is None: break try: m.append(" -> {!s}".format(chunk)) if chunk.address in chunks: m.append(Color.colorify( " -> {:#x} [Loop detected]".format(chunk.address), corrupted_msg_color, )) break chunks.append(chunk.address) nb_chunk += 1 next_chunk = chunk.get_fwd_ptr(True) if next_chunk == 0 or next_chunk is None: break chunk = GlibcHeap.GlibcChunk(arena, next_chunk) except gdb.MemoryError: m.append(Color.colorify( " -> {:#x} [Corrupted chunk]".format(chunk.address), corrupted_msg_color, )) break if m or verbose: count = arena.tcachebins_i_count(i) bins_addr = ProcessMap.lookup_address(arena.addrof_tcachebins_i(i)) fd = ProcessMap.lookup_address(read_int_from_memory(bins_addr.value)) if "size" in GlibcHeap.get_binsize_table()["tcache"][i]: size = GlibcHeap.get_binsize_table()["tcache"][i]["size"] self.out.append("tcachebins[idx={:d}, size={:#x}, @{!s}]: fd={!s} count={:d}".format( i, size, bins_addr, fd, count, )) else: size_min = GlibcHeap.get_binsize_table()["tcache"][i]["size_min"] size_max = GlibcHeap.get_binsize_table()["tcache"][i]["size_max"] self.out.append("tcachebins[idx={:d}, size={:#x}-{:#x}, @{!s}]: fd={!s} count={:d}".format( i, size_min, size_max, bins_addr, fd, count, )) if m: self.out.extend(m) self.info_add_out("Found {:d} valid chunks in tcache".format(nb_chunk)) return def print_fastbin(self, arena, verbose=False, index_filter=None): """Pretty-print fastbin lists for the given arena, checking for loops and incorrect indices.""" if get_libc_version() >= (2, 43): return def fastbin_index(sz): return (sz >> 4) - 2 if SIZE_SZ == 8 else (sz >> 3) - 2 SIZE_SZ = current_arch.ptrsize MAX_FAST_SIZE = 80 * SIZE_SZ // 4 NFASTBINS = fastbin_index(MAX_FAST_SIZE) - 1 self.out.append(titlify("fastbins")) corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") nb_chunk = 0 for i in range(NFASTBINS): # index filter if index_filter is not None: if i != index_filter: continue chunk = arena.get_fastbins_i(i) chunks = [] m = [] while True: if chunk is None: break try: m.append(" -> {!s}".format(chunk)) if chunk.address in chunks: m.append(Color.colorify( " -> {:#x} [Loop detected]".format(chunk.chunk_base_address), corrupted_msg_color, )) break if fastbin_index(chunk.get_chunk_size()) != i: m.append(Color.colorify("[Incorrect fastbin_index]", corrupted_msg_color)) chunks.append(chunk.address) nb_chunk += 1 next_chunk = chunk.get_fwd_ptr(True) if next_chunk == 0 or next_chunk is None: break chunk = GlibcHeap.GlibcChunk(arena, next_chunk, from_base=True) except gdb.MemoryError: m.append(Color.colorify( " -> {:#x} [Corrupted chunk]".format(chunk.chunk_base_address), corrupted_msg_color, )) break if m or verbose: bin_table = GlibcHeap.get_binsize_table()["fastbins"] if i in bin_table: size = bin_table[i]["size"] bins_addr = ProcessMap.lookup_address(arena.addrof_fastbins_i(i)) fd = ProcessMap.lookup_address(read_int_from_memory(bins_addr.value)) self.out.append("fastbins[idx={:d}, size={:#x}, @{!s}]: fd={!s}".format( i, size, bins_addr, fd, )) if m: self.out.extend(m) self.info_add_out("Found {:d} valid chunks in fastbins".format(nb_chunk)) return def pprint_bin(self, arena, index, bin_name, verbose=False): """Pretty-print the contents of a heap bin, following forward and backward links and checking for corruption.""" fw, bk = arena.get_bins_i(index) if bk == 0 and fw == 0: warn("Invalid backward and forward bin pointers(fd==bk==NULL)") return -1 bins_addr = arena.addrof_bins_i(index) head = bins_addr - current_arch.ptrsize * 2 if fw == head and not verbose: return 0 bin_table = GlibcHeap.get_binsize_table()[bin_name] if index not in bin_table: return 0 bin_info = bin_table[index] if "size" in bin_info: size_str = "{:#x}".format(bin_info["size"]) elif "size_min" in bin_info and "size_max" in bin_info: size_str = "{:#x}-{:#x}".format(bin_info["size_min"], bin_info["size_max"]) else: size_str = "any" corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") corrupted = False # follow the link backward mb = [] seen_bk = [] nb_chunk = 0 while bk != head: chunk = GlibcHeap.GlibcChunk(arena, bk, from_base=True) if chunk.address in seen_bk: mb.append(Color.colorify( " -> {:#x} [Loop detected]".format(chunk.chunk_base_address), corrupted_msg_color, )) corrupted = True break seen_bk.append(chunk.address) try: mb.append(" -> {!s}".format(chunk)) except gdb.MemoryError: mb.append(Color.colorify( " -> {:#x} [Corrupted chunk]".format(chunk.chunk_base_address), corrupted_msg_color, )) corrupted = True break bk = chunk.bck nb_chunk += 1 if corrupted: # follow the link forward mf = [] seen_fw = [] while fw != head: chunk = GlibcHeap.GlibcChunk(arena, fw, from_base=True) if chunk.address in seen_bk: break if chunk.address in seen_fw: mf.append(Color.colorify( " -> {:#x} [Loop detected]".format(chunk.chunk_base_address), corrupted_msg_color, )) break seen_fw.append(chunk.address) try: mf.append(" -> {!s}".format(chunk)) except gdb.MemoryError: mf.append(Color.colorify( " -> {:#x} [Corrupted chunk]".format(chunk.chunk_base_address), corrupted_msg_color, )) break fw = chunk.fwd # concat m = [] m.append("{:s}[idx={:d}, size={:s}, @{!s}]: fd={!s}, bk={!s}".format( bin_name, index, size_str, ProcessMap.lookup_address(bins_addr), ProcessMap.lookup_address(fw), ProcessMap.lookup_address(bk), )) if corrupted and mf: m += mf m += mb[::-1] self.out.extend(m) return nb_chunk @register_command class GlibcHeapBinsCommand(GenericCommand, GlibcHeapBinsDump, BufferingOutput): """Display information about the bins of an arena.""" _cmdline_ = "heap bins" _category_ = "05-a. Heap - Glibc" _aliases_ = ["bins"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-v", "--verbose", action="store_true", help="display empty bins.") parser.add_argument("--all", action="store_true", help="dump all arenas.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} -a 0x7ffff0000020 -v", "{0:s} -a 1 -v", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=True) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.all: arenas = GlibcHeap.get_all_arenas() else: arenas = [arena] # doit self.out = [] for arena in arenas: self.out.append(titlify("arena: {:#x}{:s}".format( arena.addr, Symbol.get_symbol_string(arena.addr)), color="bold", msg_color="bold"), ) # tcache self.print_tcache(arena, args.verbose) # fastbins self.print_fastbin(arena, args.verbose) # unsorted bin self.out.append(titlify("unsorted bin")) nb_chunk = self.pprint_bin(arena, 0, "unsorted_bin", args.verbose) self.info_add_out("Found {:d} valid chunks in unsorted bin (when traced from `bk`)".format(nb_chunk)) # small bins self.out.append(titlify("small bins")) bins = {} for i in range(1, 63): nb_chunk = self.pprint_bin(arena, i, "small_bins", args.verbose) if nb_chunk < 0: break if nb_chunk > 0: bins[i] = nb_chunk self.info_add_out("Found {:d} valid chunks in {:d} small bins (when traced from `bk`)".format( sum(bins.values()), len(bins), )) # large bins self.out.append(titlify("large bins")) bins = {} for i in range(63, 126): nb_chunk = self.pprint_bin(arena, i, "large_bins", args.verbose) if nb_chunk < 0: break if nb_chunk > 0: bins[i] = nb_chunk self.info_add_out("Found {:d} valid chunks in {:d} large bins (when traced from `bk`)".format( sum(bins.values()), len(bins), )) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapTcachebinsCommand(GenericCommand, GlibcHeapBinsDump, BufferingOutput): """Display information about the Tcache of an arena.""" _cmdline_ = "heap bins tcache" _category_ = "05-a. Heap - Glibc" _aliases_ = ["tcachebins"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-i", "--index-filter", type=AddressUtil.parse_address, help="filter by tcache index.") parser.add_argument("-v", "--verbose", action="store_true", help="display empty bins.") parser.add_argument("--all", action="store_true", help="dump all arenas.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # Determine if we are using libc with tcache built in (2.26+) if get_libc_version() < (2, 26): info("No tcache in this version of libc") return # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.all: arenas = GlibcHeap.get_all_arenas() else: arenas = [arena] # doit self.out = [] for arena in arenas: self.out.append(titlify("arena: {:#x}{:s}".format( arena.addr, Symbol.get_symbol_string(arena.addr)), color="bold", msg_color="bold"), ) self.print_tcache(arena, args.verbose, args.index_filter) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapFastbinsYCommand(GenericCommand, GlibcHeapBinsDump, BufferingOutput): """Display information about the fastbinsY of an arena.""" _cmdline_ = "heap bins fast" _category_ = "05-a. Heap - Glibc" _aliases_ = ["fastbins"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-i", "--index-filter", type=AddressUtil.parse_address, help="filter by fastbins index.") parser.add_argument("-v", "--verbose", action="store_true", help="display empty bins.") parser.add_argument("--all", action="store_true", help="dump all arenas.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.all: arenas = GlibcHeap.get_all_arenas() else: arenas = [arena] # doit self.out = [] for arena in arenas: self.out.append(titlify("arena: {:#x}{:s}".format( arena.addr, Symbol.get_symbol_string(arena.addr)), color="bold", msg_color="bold"), ) self.print_fastbin(arena, args.verbose, args.index_filter) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapUnsortedBinsCommand(GenericCommand, GlibcHeapBinsDump, BufferingOutput): """Display information about the Unsorted Bins of an arena.""" _cmdline_ = "heap bins unsorted" _category_ = "05-a. Heap - Glibc" _aliases_ = ["unsortedbin"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-v", "--verbose", action="store_true", help="display empty bins.") parser.add_argument("--all", action="store_true", help="dump all arenas.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.all: arenas = GlibcHeap.get_all_arenas() else: arenas = [arena] # doit self.out = [] for arena in arenas: self.out.append(titlify("arena: {:#x}{:s}".format( arena.addr, Symbol.get_symbol_string(arena.addr)), color="bold", msg_color="bold"), ) self.out.append(titlify("unsorted bin")) nb_chunk = self.pprint_bin(arena, 0, "unsorted_bin", args.verbose) self.info_add_out("Found {:d} valid chunks in unsorted bin (when traced from `bk`)".format(nb_chunk)) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapSmallBinsCommand(GenericCommand, GlibcHeapBinsDump, BufferingOutput): """Display information about the Small Bins of an arena.""" _cmdline_ = "heap bins small" _category_ = "05-a. Heap - Glibc" _aliases_ = ["smallbins"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-i", "--index-filter", type=AddressUtil.parse_address, help="filter by smallbins index.") parser.add_argument("-v", "--verbose", action="store_true", help="display empty bins.") parser.add_argument("--all", action="store_true", help="dump all arenas.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.all: arenas = GlibcHeap.get_all_arenas() else: arenas = [arena] # doit self.out = [] for arena in arenas: self.out.append(titlify("arena: {:#x}{:s}".format( arena.addr, Symbol.get_symbol_string(arena.addr)), color="bold", msg_color="bold"), ) self.out.append(titlify("small bins")) bins = {} for i in range(1, 63): # index filter if args.index_filter is not None: if i != args.index_filter: continue # print nb_chunk = self.pprint_bin(arena, i, "small_bins", args.verbose) if nb_chunk < 0: break if nb_chunk > 0: bins[i] = nb_chunk self.info_add_out("Found {:d} valid chunks in {:d} small bins (when traced from `bk`)".format( sum(bins.values()), len(bins), )) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapLargeBinsCommand(GenericCommand, GlibcHeapBinsDump, BufferingOutput): """Display information about the Large Bins of an arena.""" _cmdline_ = "heap bins large" _category_ = "05-a. Heap - Glibc" _aliases_ = ["largebins"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-i", "--index-filter", type=AddressUtil.parse_address, help="filter by largebins index.") parser.add_argument("-v", "--verbose", action="store_true", help="display empty bins.") parser.add_argument("--all", action="store_true", help="dump all arenas.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.all: arenas = GlibcHeap.get_all_arenas() else: arenas = [arena] # doit self.out = [] for arena in arenas: self.out.append(titlify("arena: {:#x}{:s}".format( arena.addr, Symbol.get_symbol_string(arena.addr)), color="bold", msg_color="bold"), ) self.out.append(titlify("large bins")) bins = {} for i in range(63, 126): # index filter if args.index_filter is not None: if i != args.index_filter: continue # print nb_chunk = self.pprint_bin(arena, i, "large_bins", args.verbose) if nb_chunk < 0: break if nb_chunk > 0: bins[i] = nb_chunk self.info_add_out("Found {:d} valid chunks in {:d} large bins (when traced from `bk`)".format( sum(bins.values()), len(bins), )) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapTryFreeCommand(GenericCommand): """Emulate with unicorn to check errors when freeing a chunk.""" _cmdline_ = "heap try-free" _category_ = "05-a. Heap - Glibc" _aliases_ = ["try-free"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="the memory address to be freed.") parser.add_argument("-a", "--free-addr", dest="caller_address", type=AddressUtil.parse_address, help="the memory address of free().") parser.add_argument("-s", "--skip-emulation", "--save", action="store_true", help="do not run, just save the script.") parser.add_argument("-c", "--command", action="append", default=[], help="command to be executed after emulation succeeds, with the memory state temporarily reflected.") parser.add_argument("-v", "--verbose", action="store_true", help="show internal state.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x555555579930", "{0:s} -a 0x7ffff7cadd30 0x555555579930 # need free address when no symbol", '{0:s} -c "visual-heap" 0x555555579930 # execute visual-heap', ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "It may work even if NOT Glibc (untested).", "It may be detected as a failure even though it actually succeeded.", " - Any system call was called", " - Any interrupt was raised", " - An instruction that unicorn does not support was executed", "They are emulated to the best extent possible, but the emulation may be incomplete.", " - The address returned by mmap can differ from the actual one; this is an emulation limitation.", "The failure message may not be detected because it is searched for heuristically.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def get_caller_address(self, name): """Resolve and return the caller address for a given symbol name, considering user input, PLT, and real addresses.""" # user specified address if self.args.caller_address is not None: return self.args.caller_address # searching through symbols caller_address = None # PLT pattern (e.g., &'malloc@plt') try: caller_address = int(gdb.parse_and_eval("&'{:s}@plt'".format(name))) except gdb.error: pass if caller_address is not None: # If you use PLT, only userland binary's PLT is valid (libc PLT is invalid) x = ProcessMap.lookup_address(caller_address) if x and x.section and x.section.path == Path.get_filepath(append_proc_root_prefix=False): return caller_address # real address (not PLT) try: caller_address = int(gdb.parse_and_eval("&{:s}".format(name))) except gdb.error: pass return caller_address def make_patch_info(self, caller_address, arg1, arg2): """Prior to running unicorn-emulate, generate a patch to be applied.""" patches = {} if is_x86_64(): regs_new = {"$rdi": arg1, "$rsi": arg2, "$rax": caller_address} patches[current_arch.pc] = bytes.fromhex("ffd0") # call rax stop_address = current_arch.pc + 2 elif is_x86_32(): regs_new = {"$edi": arg1, "$esi": arg2, "$eax": caller_address} patches[current_arch.pc] = bytes.fromhex("5657ffd0") # push esi; push edi; call eax stop_address = current_arch.pc + 4 elif is_arm32(): # Check if caller_address is thumb2 # The reason for checking only 3 instructions is that xxx@plt is 3 instructions. res = gdb.execute("x/3i {:#x}".format(caller_address), to_string=True) # pattern 1 (thumb2) # 0x4085fdb0 : @ instruction: 0xe7fd4778 # 0x4085fdb4 : add r12, pc, #0, 12 # 0x4085fdb8 : add r12, r12, #240, 20 @ 0xf0000 # in fact: # 0x4085fdb1 : bx pc if "UNDEFINED" in res.splitlines()[0]: caller_address += 1 else: # pattern 2 (thumb2) # 0x408abb60 <__GI___libc_malloc>: push {r3, r4, r5, r6, r7, lr} # 0x408abb62 <__GI___libc_malloc+2>: mov r6, r0 # 0x408abb64 <__GI___libc_malloc+4>: ldr r3, [pc, #548] addrs = [int(x.strip().split()[0], 16) for x in res.splitlines()] if any(a % 4 == 2 for a in addrs): caller_address += 1 else: # pattern 3 (ARM) # 0x4085fdc0 : add r12, pc, #0, 12 # 0x4085fdc4 : add r12, r12, #240, 20 @ 0xf0000 # 0x4085fdc8 : ldr pc, [r12, #152]! @ 0x98 pass # Check current $pc is thumb2 if current_arch.is_thumb(): regs_new = {"$r0": arg1, "$r1": arg2, "$r2": caller_address} patches[current_arch.pc & ~1] = bytes.fromhex("9047") # blx r2 stop_address = (current_arch.pc & ~1) + 2 else: regs_new = {"$r0": arg1, "$r1": arg2, "$r2": caller_address} patches[current_arch.pc] = bytes.fromhex("32ff2fe1") # blx r2 stop_address = current_arch.pc + 4 elif is_arm64(): regs_new = {"$x0": arg1, "$x1": arg2, "$x2": caller_address} patches[current_arch.pc] = bytes.fromhex("40003fd6") # blr x2 stop_address = current_arch.pc + 4 # create namedtuple dic = {} dic["regs_new"] = {r: v for r, v in regs_new.items() if v is not None} dic["regs_old"] = {r: get_register(r) for r, v in regs_new.items() if v is not None} dic["patches"] = patches dic["stop_address"] = stop_address Info = collections.namedtuple("Info", dic.keys()) return Info(*dic.values()) def get_reason(self, res): """From the results of unicorn-emulate, get the reason why the run failed.""" if "UC_ERR_READ_UNMAPPED" in res: return "Memory read error" if "UC_ERR_WRITE_UNMAPPED" in res: return "Memory write error" # heuristic search if "Modified memories (before | after)" in res: # In cases where emulation fails, an interrupt or system call has occurred. # When execution stops, any memory that has been modified up to this point # will likely contain a pointer to the error message. # e.g., # ========================= Modified memories (before | after) ========================= # 0x00007fffffffd6f0 | 0x00007fffffffd724 0x00007fff00000038 | 0x00007fffffffd724 0x00007ffff7c9094e | # 0x00007fffffffd700 | 0x00007fff015c0adc 0x00007ffff7fc5860 | 0x00007fffffffd730 0x0000000000000000 | # 0x00007fffffffd710 | 0x000000005702b738 0x00007ffff7fc5bac | 0x000000005702b738 0x00007fff00000010 | # 0x00007fffffffd720 | 0x00000000f7fbd160 0x0000000000000000 | 0x00007fffffffd820 0x00007fffffffd7b0 | modified_memories = res[res.find("Modified memories (before | after)"):] modified_memories = Color.remove_color(modified_memories) message_strings = set() for line in modified_memories.splitlines(): # search pointer of `after` if line.count("|") != 3: continue after_memories = line.split("|")[-2] for x in after_memories.strip().split(): addr = int(x, 16) s = read_cstring_from_memory(addr) if not s: continue if len(s) <= current_arch.ptrsize: # too short continue if " " not in s or "(" not in s or ")" not in s: # wrong message continue message_strings.add(s) if message_strings: if len(message_strings) == 1: return list(message_strings)[0] return str(list(message_strings)) if "UC_ERR_INSN_INVALID" in res: return "Maybe try to execute unicorn unsupported instruction" return "???" def get_syscall(self, res): """From the results of unicorn-emulate, get information about the system calls that were called.""" # syscall=N is detected and displayed by unicorn-emulate. m = re.search(r"syscall=(\d+)", res) if not m: return None # match against syscall_table syscall_num = int(m.group(1)) syscall_table = get_syscall_table() syscall_entry = syscall_table.nr_table.get(syscall_num, None) if syscall_entry: syscall_name = syscall_entry.name else: syscall_name = "???" return syscall_num, syscall_name def get_allocated_address(self, res): """From the results of unicorn-emulate, get the allocated address.""" final_registers = res[res.find("Final registers"):] if is_x86_64(): m = re.search(r"\$rax\s+=\s+(0x\S+)", final_registers) elif is_x86_32(): m = re.search(r"\$eax\s+=\s+(0x\S+)", final_registers) elif is_arm32(): m = re.search(r"\$r0\s+=\s+(0x\S+)", final_registers) elif is_arm64(): m = re.search(r"\$x0\s+=\s+(0x\S+)", final_registers) allocated_address = int(m.group(1), 16) return allocated_address def print_result(self, name, res): """Print the result of an emulation run, displaying errors or success messages based on system call outcomes.""" if "Emulation failed" in res: # fail success = False # The system call that could not be emulated syscall = self.get_syscall(res) if syscall: err("Trace failed: system call emulation error: {:d} (={:s})".format(syscall[0], syscall[1])) # If write* system call was called, it is assumed that an abort was called. # By investigating the changed memory address, the reason may be found. if not syscall or "write" in syscall[1]: reason = self.get_reason(res) err("{:s} failed: {:s}".format(name, Color.boldify(reason))) else: # success success = True # The system call that could be emulated syscall = self.get_syscall(res) if syscall: info("System call emulated: {:d} (={:s})".format(syscall[0], syscall[1])) if name == "free": ok("{:s} succeeded".format(name)) else: allocated_address = self.get_allocated_address(res) ok("{:s} succeeded: {:s}".format(name, Color.colorify_hex(allocated_address, "bold"))) return success def make_patch_info_from_emulation_result(self, res): """Parse emulation output and create a dictionary of memory patches from modified memory regions.""" patches = {} if "Modified memories (before | after)" in res: modified_memories = res[res.find("Modified memories (before | after)"):] modified_memories = Color.remove_color(modified_memories) for line in modified_memories.splitlines(): # search pointer of `after` if line.count("|") != 3: continue addr = int(line.split("|")[0], 16) after_memories = line.split("|")[-2] values = [int(x, 16) for x in after_memories.strip().split()] values = [x.to_bytes(current_arch.ptrsize, "little") for x in values] patches[addr] = b"".join(values) return patches def doit(self, name, arg1, arg2): """For each of free, malloc, realloc, and calloc, generate a patch to memory, emulate the execution, collect and interpret the output, and then undoe the patch.""" caller_address = self.get_caller_address(name) if caller_address is None: err("Could not find `{:s}`".format(name)) return # make patch info # The arguments of free, malloc, realloc, and calloc are at most 2 patch_info = self.make_patch_info(caller_address, arg1, arg2) # modify (registers, memories) for regname, regvalue in patch_info.regs_new.items(): if self.args.verbose: info("set {:s}={:#x}".format(regname, regvalue)) gdb.execute("set {:s}={:#x}".format(regname, regvalue)) tag = PatchCommand.PatchInfo.get_unique_tag() for patch_addr, patch_code in patch_info.patches.items(): if self.args.verbose: info("patch hex {:#x} {:s}".format(patch_addr, patch_code.hex())) PatchCommand.PatchInfo(patch_addr, patch_code, tag=tag).patch(silent=not self.args.verbose) # execute option = ["-t", hex(patch_info.stop_address), "-A", "-E", "-I"] if self.args.skip_emulation: option.append("-s") res = "" try: if self.args.verbose: info("unicorn-emulate {:s}".format(" ".join(option))) res = gdb.execute("unicorn-emulate {:s}".format(" ".join(option)), to_string=True) if self.args.verbose or self.args.skip_emulation: gef_print(res.rstrip()) except Exception: pass # print if res and not self.args.skip_emulation: success = self.print_result(name, res) # temporarily execute command if success and self.args.command: # temporarily reflects changes in memory patches = self.make_patch_info_from_emulation_result(res) for addr, value in patches.items(): PatchCommand.PatchInfo(addr, value, tag=tag).patch(silent=not self.args.verbose) # do command for cmd in self.args.command: try: gef_print(titlify(cmd, color="bold", msg_color="bold")) gdb.execute(cmd) except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() gef_print(exc_value) # revert (registers, memories, thread locking) for regname, regvalue in patch_info.regs_old.items(): if self.args.verbose: info("set {:s}={:#x}".format(regname, regvalue)) gdb.execute("set {:s}={:#x}".format(regname, regvalue)) PatchCommand.PatchInfo.revert_to_tag(tag, silent=not self.args.verbose) if self.args.verbose: info("patch revert ok") return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): self.doit("free", args.address, None) return @register_command class GlibcHeapTryMallocCommand(GlibcHeapTryFreeCommand): """Emulate with unicorn to check errors when allocating a chunk.""" _cmdline_ = "heap try-malloc" _category_ = "05-a. Heap - Glibc" _aliases_ = ["try-malloc"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size to be allocated.") parser.add_argument("-a", "--malloc-addr", dest="caller_address", type=AddressUtil.parse_address, help="the memory address of malloc().") parser.add_argument("-s", "--skip-emulation", "--save", action="store_true", help="do not run, just save the script.") parser.add_argument("-c", "--command", action="append", default=[], help="command to be executed after emulation succeeds, with the memory state temporarily reflected.") parser.add_argument("-v", "--verbose", action="store_true", help="show internal state.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x120", "{0:s} -a 0x7ffff7cad650 0x120 # need malloc address when no symbol", '{0:s} -c "visual-heap" 0x120 # execute visual-heap', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): self.doit("malloc", args.size, None) return @register_command class GlibcHeapTryReallocCommand(GlibcHeapTryFreeCommand): """Emulate with unicorn to check errors when re-allocating a chunk.""" _cmdline_ = "heap try-realloc" _category_ = "05-a. Heap - Glibc" _aliases_ = ["try-realloc"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="the memory address to be re-allocated.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size to be re-allocated.") parser.add_argument("-a", "--realloc-addr", dest="caller_address", type=AddressUtil.parse_address, help="the memory address of realloc().") parser.add_argument("-s", "--skip-emulation", "--save", action="store_true", help="do not run, just save the script.") parser.add_argument("-c", "--command", action="append", default=[], help="command to be executed after emulation succeeds, with the memory state temporarily reflected.") parser.add_argument("-v", "--verbose", action="store_true", help="show internal state.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x555555579930 0x120", "{0:s} -a 0x7ffff7cae0a0 0x555555579930 0x120 # need realloc address when no symbol", '{0:s} -c "visual-heap" 0x555555579930 0x120 # execute visual-heap', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): self.doit("realloc", args.address, args.size) return @register_command class GlibcHeapTryCallocCommand(GlibcHeapTryFreeCommand): """Emulate with unicorn to check errors when allocating a zero-initialized chunk.""" _cmdline_ = "heap try-calloc" _category_ = "05-a. Heap - Glibc" _aliases_ = ["try-calloc"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size to be allocated.") parser.add_argument("nmemb", metavar="NMEMB", type=AddressUtil.parse_address, help="the number of blocks.") parser.add_argument("-a", "--calloc-addr", dest="caller_address", type=AddressUtil.parse_address, help="the memory address of calloc().") parser.add_argument("-s", "--skip-emulation", "--save", action="store_true", help="do not run, just save the script.") parser.add_argument("-c", "--command", action="append", default=[], help="command to be executed after emulation succeeds, with the memory state temporarily reflected.") parser.add_argument("-v", "--verbose", action="store_true", help="show internal state.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x10 1", "{0:s} -a 0x7ffff7cae7a0 0x10 1 # need calloc address when no symbol", '{0:s} -c "visual-heap" 0x10 1 # execute visual-heap', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): self.doit("calloc", args.size, args.nmemb) return @register_command class GlibcHeapTcacheIndexHelperCommand(GenericCommand): """Helper for calculating tcache index etc.""" _cmdline_ = "heap tcache-index-helper" _category_ = "05-a. Heap - Glibc" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-i", "--index", type=AddressUtil.parse_address, help="the index of tcache entry (0 ~ 63 (~glibc 2.41) or 75 (glibc 2.42~)).") parser.add_argument("-c", "--count-addr", type=AddressUtil.parse_address, help="the address of &tcache.counts[i].") parser.add_argument("-e", "--entry-addr", type=AddressUtil.parse_address, help="the address of &tcache.entries[i].") _syntax_ = parser.format_help() def print_tcache_info(self, arena, index): """Print addresses of tcache count and entry for the specified bin index in the given arena.""" if index < 0: err("Invalid index (< 0)") return if index >= arena.TCACHE_MAX_BINS(): err("Invalid index (>= TCACHE_MAX_BINS)") return # counts / num_slots if get_libc_version() < (2, 42): count_addr = arena.addrof_tcachebins_i_count(index) info("&tcache.counts[{:d}] = {!s}".format(index, ProcessMap.lookup_address(count_addr))) else: num_slots_addr = arena.addrof_tcachebins_i_count(index) info("&tcache.num_slots[{:d}] = {!s}".format(index, ProcessMap.lookup_address(num_slots_addr))) # entries entry_addr = arena.addrof_tcachebins_i(index) info("&tcache.entries[{:d}] = {!s}".format(index, ProcessMap.lookup_address(entry_addr))) return @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): # Determine if we are using libc with tcache built in (2.26+) if get_libc_version() < (2, 26): err("No tcache in this version of libc") return # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return # doit info("heap_base: {!s}".format(ProcessMap.lookup_address(arena.heap_base))) info("tcache: {!s} (tcache_perthread_struct)".format(ProcessMap.lookup_address(arena.tcache_perthread_struct))) if (args.index, args.count_addr, args.entry_addr) == (None, None, None): self.print_tcache_info(arena, 0) return if args.index is not None: self.print_tcache_info(arena, args.index) if args.count_addr is not None: if get_libc_version() < (2, 30): index = args.count_addr - arena.addrof_tcachebins_i_count(0) else: if args.count_addr % 2 == 1: err("Invalid address (count_addr % 2 == 1)") return index = (args.count_addr - arena.addrof_tcachebins_i_count(0)) // 2 self.print_tcache_info(arena, index) if args.entry_addr is not None: index = (args.entry_addr - arena.addrof_tcachebins_i(0)) // current_arch.ptrsize self.print_tcache_info(arena, index) return @register_command class GlibcHeapFindFakeFastCommand(GenericCommand, BufferingOutput): """Find candidate fake fast chunks from RW memory.""" _cmdline_ = "heap find-fake-fast" _category_ = "05-a. Heap - Glibc" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--include-heap", action="store_true", help="heap is also included in the search target.") parser.add_argument("--aligned", action="store_true", help="search only aligned chunks.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="search target size.") parser.add_argument("--region-size-threshold", type=AddressUtil.parse_address, default=0x0200_0000, help="threshold for region size to skip search. (default: %(default)#x)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = [ "It is not possible to find candidates that straddle the two regions.", ] _note_ = "\n".join(_note_) def print_result(self, m, pos, size_candidate): """Display the result of a memory search, including address, flags, and a memory dump with colored highlights.""" path = "unknown" if m.path == "" else m.path address = ProcessMap.lookup_address(m.page_start + pos) self.info_add_out("Found at {!s} in {!r} [{!s}]".format(address, path, m.permission)) # flag with coloring flag = [] color = "" if size_candidate & 0b100: color = Config.get_gef_setting("theme.heap_chunk_flag_non_main_arena") flag += [Color.colorify("NON_MAIN_ARENA", color)] color = "" if size_candidate & 0b10: color = Config.get_gef_setting("theme.heap_chunk_flag_is_mmapped") flag += [Color.colorify("IS_MMAPED", color)] color = "" if size_candidate & 0b1: color = Config.get_gef_setting("theme.heap_chunk_flag_prev_inuse") flag += [Color.colorify("PREV_INUSE", color)] self.out.append(" [{:s}]".format(" ".join(flag))) # dump try: if is_64bit(): res = gdb.execute("x/6xg {:#x}".format(address.value), to_string=True) else: res = gdb.execute("x/6xw {:#x}".format(address.value), to_string=True) truncated_flag = False except Exception: if is_64bit(): res = gdb.execute("x/2xg {:#x}".format(address.value), to_string=True) else: res = gdb.execute("x/2xw {:#x}".format(address.value), to_string=True) truncated_flag = True for line in res.splitlines(): self.out.append(" {:s}".format(line)) if truncated_flag: self.warn_add_out("Areas from {!s} are inaccessible and display truncated.".format(address)) return def find_fake_fast(self, target_size): """Scan memory regions for fake fastbin chunks matching the target size and display findings.""" if is_64bit(): mask = ~0xf unpack = u64 else: mask = ~0x7 unpack = u32 if self.args.aligned: unit = 0x10 else: unit = 0x1 ZERO_PAGE = b"\0" * get_pagesize() target_size &= mask vmmap = ProcessMap.get_process_maps_exclude_special_regions() for m in vmmap: # RW permission required if not (m.permission & Permission.READ): continue if not (m.permission & Permission.WRITE): continue # ignore "[heap]" or not if m.path.startswith("[heap]"): if not self.args.include_heap: continue # skip if too large region if m.size >= self.args.region_size_threshold: path = "unknown" if m.path == "" else m.path self.info_add_out("{!r} is skipped since too large ({:#x} >= {:#x})".format( path, m.size, self.args.region_size_threshold, )) continue data = read_memory(m.page_start, m.size) # Scanning page-by-page pos = 0 while pos < m.size: if (pos & get_pagesize_mask_low()) == 0: # fast check for all zero, because there may be huge mmap-ed memory if data[pos:pos + get_pagesize()] == ZERO_PAGE: pos += get_pagesize() continue pos_of_size_start = pos + current_arch.ptrsize pos_of_size_end = pos + current_arch.ptrsize * 2 size_candidate = data[pos_of_size_start:pos_of_size_end] # even if it is found near the end of region, it cannot be used. if len(size_candidate) != current_arch.ptrsize: break # check if it's the size you want size_candidate = unpack(size_candidate) if (size_candidate & mask) != target_size: pos += unit continue # found self.print_result(m, pos, size_candidate) pos += unit return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): MIN_SIZE = GlibcHeap.HeapInfo.MIN_SIZE() if args.size < MIN_SIZE: err("Wrong size") return self.out = [] self.find_fake_fast(args.size) self.print_output(check_terminal_size=True) return @register_command class GlibcHeapExtractHeapAddrCommand(GenericCommand): """Extract heap address from protected `fd` pointer of single linked-list (glibc 2.32~).""" _cmdline_ = "heap extract-heap-addr" _category_ = "05-a. Heap - Glibc" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("value", metavar="VALUE", nargs="?", type=AddressUtil.parse_address, help="the value to extract.") group.add_argument("--source", action="store_true", help="shows the source instead of displaying extracted value.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x000055500000C7F9", ] _example_ = "\n".join(_example_).format(_cmdline_) def reveal(self, fd): # https://smallkirby.hatenablog.com/entry/safeunlinking L = fd >> 36 for i in range(3): temp = (fd >> (36 - (i + 1) * 8)) & 0xff element = ((L >> 4) ^ temp) & 0xff L = (L << 8) + element return L << 12 @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): if args.source: s = GefUtil.get_source(GlibcHeapExtractHeapAddrCommand.reveal) gef_print(s) return extracted_ptr = self.reveal(args.value) extracted_ptr = ProcessMap.lookup_address(extracted_ptr) gef_print("Protected fd pointer: {:#x}".format(args.value)) gef_print(" -> Extracted heap address: {!s} (=fd & ~0xfff)".format(extracted_ptr)) return @register_command class GlibcHeapCalcProtectedFdCommand(GenericCommand): """Calculate a valid value as protected `fd` pointer of single linked-list (glibc 2.32~).""" _cmdline_ = "heap calc-protected-fd" _category_ = "05-a. Heap - Glibc" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("fd", type=AddressUtil.parse_address, help="the fd value.") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address to interpret as a chunk.") parser.add_argument("-b", "--as-base", action="store_true", help="use LOCATION as chunk base address (chunk_base_address = chunk_address - ptrsize * 2).") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0 0x5555555594e0", "{0:s} 0 0x5555555594e0 -b", ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): loc = args.location if args.as_base: loc -= current_arch.ptrsize * 2 ptr = (loc >> 12) ^ args.fd gef_print("Protected fd pointer: {:#x}".format(ptr)) return @register_command class GlibcHeapVisualHeapCommand(GenericCommand, BufferingOutput): """Visualize chunks on a heap.""" _cmdline_ = "heap visual-heap" _category_ = "05-a. Heap - Glibc" _aliases_ = ["visual-heap"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address interpreted as the beginning of a contiguous chunk. (default: arena.heap_base)") parser.add_argument("-a", dest="arena_addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-c", dest="max_count", type=AddressUtil.parse_address, help="maximum number of chunks to parse; use when the number of chunks is very large.") parser.add_argument("-f", "--full", action="store_true", help="display the same line without omitting.") parser.add_argument("-d", "--dark-color", action="store_true", help="use the dark color if chunk is allocated.") parser.add_argument("-s", "--safe-linking-decode", action="store_true", help="decode safe-linking encoded pointer if tcache or fastbins.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() normal_colors = [ Color.redify, Color.greenify, Color.blueify, Color.yellowify, ] dark_colors = [ lambda x: Color.colorify(x, "bright_black"), lambda x: Color.colorify(x, "graphite"), ] def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def generate_visual_chunk(self, chunk, idx): """Generate a visual, colorized representation of a heap chunk's contents, grouping repeated rows and annotating bin info.""" unpack = u32 if current_arch.ptrsize == 4 else u64 data = slicer(chunk.data, current_arch.ptrsize * 2) group_line_threshold = 8 arena = chunk.arena addr = chunk.chunk_base_address width = current_arch.ptrsize * 2 + 2 exceed_top = False has_bins_info = False out_tmp = [] # Group rows to display rows with the same value together. for blk, blks in itertools.groupby(data): repeat_count = len(list(blks)) d1, d2 = unpack(blk[:current_arch.ptrsize]), unpack(blk[current_arch.ptrsize:]) dascii = "".join([chr(x) if 0x20 <= x < 0x7f else "." for x in blk]) if self.args.full or repeat_count < group_line_threshold: # non-collapsed line for _ in range(repeat_count): bins_info = arena.get_bins_info(addr) if bins_info: bins_info = " <- {:s}".format(", ".join(bins_info)) has_bins_info = True else: bins_info = "" if self.args.safe_linking_decode: if chunk.address == addr and ("tcache" in bins_info or "fastbins" in bins_info): d1 = chunk.get_fwd_ptr(True) offset1 = addr - chunk.chunk_base_address offset2 = addr - arena.heap_base out_tmp.append("{:#x}|{:+#08x}|{:+#08x}: {:#0{:d}x} {:#0{:d}x} | {:s} | {:s}".format( addr, offset1, offset2, d1, width, d2, width, dascii, bins_info, ).rstrip()) addr += current_arch.ptrsize * 2 if addr > arena.top + current_arch.ptrsize * 4: exceed_top = True break else: # collapsed line bins_info = arena.get_bins_info(addr) if bins_info: bins_info = " <- {:s}".format(", ".join(bins_info)) has_bins_info = True else: bins_info = "" offset1 = addr - chunk.chunk_base_address offset2 = addr - arena.heap_base out_tmp.append("{:#x}|{:+#08x}|{:+#08x}: {:#0{:d}x} {:#0{:d}x} | {:s} | {:s}".format( addr, offset1, offset2, d1, width, d2, width, dascii, bins_info, ).rstrip()) addr += current_arch.ptrsize * 2 * repeat_count out_tmp.append("* {:#d} lines, {:#x} bytes".format( repeat_count - 1, (repeat_count - 1) * current_arch.ptrsize * 2, )) if exceed_top: break # coloring if self.args.dark_color and not has_bins_info: color_func = self.dark_colors[idx % len(self.dark_colors)] else: color_func = self.normal_colors[idx % len(self.normal_colors)] self.out.append("\n".join(map(color_func, out_tmp))) # corrupted case if exceed_top: self.out.append(Color.boldify("...")) return def generate_visual_heap(self, arena, dump_start, max_count): """Generate a visual representation of the heap by iterating over chunks, handling corruption and optional progress display.""" sect = ProcessMap.process_lookup_address(dump_start) if sect: end = sect.page_end else: # If qemu-user 8.1 or higher, the `process_lookup_address` to obtain the section list # uses `info proc mappings` internally. # This is fast, but does not return an accurate list in some cases. # For example, sparc64 may not include the heap area. # So it detects the end of the page from arena.top. end = arena.top + GlibcHeap.GlibcChunk(arena, arena.top, from_base=True).size try: from tqdm import tqdm except ImportError: tqdm = None if tqdm: pbar = tqdm(total=end - dump_start, leave=False) addr = dump_start i = 0 while addr < end: chunk = GlibcHeap.GlibcChunk(arena, addr + current_arch.ptrsize * 2) # corrupt check if chunk.size == 0: msg = "{} Corrupted (chunk.size == 0)".format(Color.colorify("[!]", "bold red")) self.out.append(msg) chunk.data = read_memory(addr, max(arena.top - addr + 0x10, 0)) self.generate_visual_chunk(chunk, i) break elif addr != arena.top and addr + chunk.size > arena.top: msg = "{} Corrupted (addr + chunk.size > arena.top)".format(Color.colorify("[!]", "bold red")) self.out.append(msg) chunk.data = read_memory(addr, max(arena.top - addr + 0x10, 0)) self.generate_visual_chunk(chunk, i) break elif addr + chunk.size > end: msg = "{} Corrupted (addr + chunk.size > sect.page_end)".format(Color.colorify("[!]", "bold red")) self.out.append(msg) chunk.data = read_memory(addr, max(arena.top - addr + 0x10, 0)) self.generate_visual_chunk(chunk, i) break # maybe not corrupted try: chunk.data = read_memory(addr, chunk.size) except gdb.MemoryError: break self.generate_visual_chunk(chunk, i) addr += chunk.size i += 1 if tqdm: pbar.update(chunk.size) if max_count and max_count <= i: break if tqdm: pbar.close() return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.location is None: dump_start = arena.heap_base # specific pattern if arena.is_main_arena: if (is_x86_32() or is_riscv32() or is_ppc32()) and get_libc_version() >= (2, 26): dump_start += 8 else: dump_start = args.location self.out = [] self.generate_visual_heap(arena, dump_start, args.max_count) self.print_output() return @register_command class GlibcHeapDumpImageCommand(GenericCommand): """Visualize chunks on a heap as composition image.""" _cmdline_ = "heap dump-image" _category_ = "05-a. Heap - Glibc" _aliases_ = ["dump-image"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address interpreted as the beginning of a contiguous chunk. (default: arena.heap_base)") parser.add_argument("-a", dest="arena_addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("-c", dest="max_count", type=AddressUtil.parse_address, help="maximum number of chunks to parse; use when the number of chunks is very large.") parser.add_argument("-t", "--include-top", action="store_true", help="include top chunk.") parser.add_argument("-s", "--save-as-png", action="store_true", help="save as png.") parser.add_argument("-S", "--scale", type=float, default=1.0, help="magnification to enlarge or reduce the image.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = [ Color.colorify("In-use chunks", "underline") + " are displayed alternately in " + \ Color.colorify("dark gray", "gray") + " and " + Color.colorify("light gray", "cloud") + ".", Color.colorify("Freed chunks", "underline") + " are displayed alternately in " + \ Color.colorify("muted red", "orange") + " and " + Color.colorify("muted yellow", "lemon_yellow") + ".", "In both cases, the color is determined by whether the chunk's position from the beginning", "is odd-numbered or even-numbered.", "", "The `convert` command limits height to 32000px; output may shrink based on heap size.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def collect_chunks(self, arena, dump_start, max_count): sect = ProcessMap.process_lookup_address(dump_start) if sect: end = sect.page_end else: # If qemu-user 8.1 or higher, the `process_lookup_address` to obtain the section list # uses `info proc mappings` internally. # This is fast, but does not return an accurate list in some cases. # For example, sparc64 may not include the heap area. # So it detects the end of the page from arena.top. end = arena.top + GlibcHeap.GlibcChunk(arena, arena.top, from_base=True).size try: from tqdm import tqdm except ImportError: tqdm = None if tqdm: pbar = tqdm(total=end - dump_start, leave=False) chunks = [] err_msg = None addr = dump_start i = 0 while addr < end: chunk = GlibcHeap.GlibcChunk(arena, addr + current_arch.ptrsize * 2) # corrupt check if chunk.size == 0: err_msg = "{} Corrupted (chunk.size == 0)".format(Color.colorify("[!]", "bold red")) chunk.data = read_memory(addr, max(arena.top - addr + 0x10, 0)) chunks.append(chunk) break elif addr != arena.top and addr + chunk.size > arena.top: err_msg = "{} Corrupted (addr + chunk.size > arena.top)".format(Color.colorify("[!]", "bold red")) chunk.data = read_memory(addr, max(arena.top - addr + 0x10, 0)) chunks.append(chunk) break elif addr + chunk.size > end: err_msg = "{} Corrupted (addr + chunk.size > sect.page_end)".format(Color.colorify("[!]", "bold red")) chunk.data = read_memory(addr, max(arena.top - addr + 0x10, 0)) chunks.append(chunk) break # maybe not corrupted try: chunk.data = read_memory(addr, chunk.size) except gdb.MemoryError: break if chunk.is_top(): if not self.args.include_top: break chunks.append(chunk) addr += chunk.size i += 1 if tqdm: pbar.update(chunk.size) if max_count and max_count <= i: break if tqdm: pbar.close() return chunks, err_msg def generate_image(self, chunks): MIN_SIZE = GlibcHeap.HeapInfo.MIN_SIZE() MALLOC_ALIGNMENT = GlibcHeap.HeapInfo.MALLOC_ALIGNMENT() def chunk_size_to_line_number(chunk): line_num = ((chunk.size - MIN_SIZE) // MALLOC_ALIGNMENT) + 1 return max(line_num, 1) line_nums = [chunk_size_to_line_number(c) for c in chunks] used_or_freed = [c.is_real_used() for c in chunks] total = sum(line_nums) if total <= 0: return None used_cols = [ b"\xb3\xb3\xb3", # 70% gray (179) b"\x4d\x4d\x4d", # 30% gray (77) ] freed_cols = [ b"\xc6\x6b\x5b", # muted red b"\xd8\xb4\x5a", # muted yellow ] target_h = 30000 # limit of convert command target_w = 1 # assign to target_h by ratio (rounding error is absorbed by accumulator) data_parts = [] acc = 0 # error accumulation (molecule side) for i, (h, u) in enumerate(zip(line_nums, used_or_freed)): if u: color = used_cols[i & 1] else: color = freed_cols[i & 1] acc += h * target_h px = acc // total acc = acc % total px = int(px) if px <= 0: continue data_parts.append(color * (px * target_w)) data = b"".join(data_parts) tmp_fd, tmp_path = GefUtil.mkstemp(prefix="heap-dump-image", suffix=".raw") os.fdopen(tmp_fd, "wb").write(data) return tmp_path def make_command_line(self, image_path): img_height = os.path.getsize(image_path) // 3 # RGB img_width = 1 command_options = [ "-size {:d}x{:d}".format(img_width, img_height), "-depth 8", ] # terminal size (number of characters) term_height, term_width = GefUtil.get_terminal_size() # it's too tight, so make it slightly smaller. term_width *= 0.95 term_height *= 0.95 # number of pixels per character font_width_px = 6 font_height_px = 12 # pixel dimensions of the terminal term_width_px = int(term_width * font_width_px * self.args.scale) term_height_px = int(term_height * font_height_px * self.args.scale) # convert option command_options.extend([ "-filter Box", "-resize {:d}x{:d}!".format(term_width_px, term_height_px), ]) if self.args.save_as_png: cmd = "{!r} {:s} rgb:{!r} PNG:{!r}".format( GefUtil.which("convert"), " ".join(command_options), image_path, image_path[:-4] + ".png" ) else: cmd = "{!r} {:s} rgb:{!r} sixel:-".format( GefUtil.which("convert"), " ".join(command_options), image_path, ) return cmd @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): try: GefUtil.which("convert") # imagemagick except FileNotFoundError as e: err("{}".format(e)) return # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.location is None: dump_start = arena.heap_base # specific pattern if arena.is_main_arena: if (is_x86_32() or is_riscv32() or is_ppc32()) and get_libc_version() >= (2, 26): dump_start += 8 else: dump_start = args.location # parse chunks chunks, err_msg = self.collect_chunks(arena, dump_start, args.max_count) # make image image_path = self.generate_image(chunks) if not image_path: return # show cmd = self.make_command_line(image_path) os.system(cmd) os.unlink(image_path) if args.save_as_png: info("Saved as {!r}".format(image_path[:-4] + ".png")) return @register_command class GlibcHeapSnapshotCommand(GenericCommand): """Take a snapshot of heap.""" _cmdline_ = "heap snapshot" _category_ = "05-a. Heap - Glibc" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("--all", action="store_true", help="dump all arenas.") _syntax_ = parser.format_help() @staticmethod def dump_heap(arena): # data try: section = ProcessMap.lookup_address(arena.heap_base).section page_start = section.page_start region_size = section.size except Exception: err("Failed to get memory range") return None try: raw = read_memory(page_start, region_size) except gdb.MemoryError: err("Failed to dump memory") return None # info try: heap_base = arena.heap_base dump_start = heap_base if arena.is_main_arena: if (is_x86_32() or is_riscv32() or is_ppc32()) and get_libc_version() >= (2, 26): dump_start += 8 info = { "heap_base": arena.heap_base, "dump_start": dump_start, "top": arena.top, } chunks = [] current_chunk = GlibcHeap.GlibcChunk(arena, dump_start, from_base=True) while True: """ 0x555555fa9500|+0x00000: 0x0000000000000020 0x0000000000000041 <- start_offset 0x555555fa9510|+0x00010: 0x726f7272652d736c 0x2d746f6e6e61632d 0x555555fa9520|+0x00020: 0x7269642d6e65706f 0x622d79726f746365 0x555555fa9530|+0x00030: 0x72637365642d6461 0x696c2f726f747069 <- end_offset """ start_offset = current_chunk.chunk_base_address - heap_base chunks.append({ "start_offset": start_offset, "size": current_chunk.size, "end_offset": start_offset + current_chunk.size - (current_arch.ptrsize * 2), "used": current_chunk.is_real_used(), "extra": "", }) if current_chunk.chunk_base_address > arena.top: break if current_chunk.size == 0: break chunks[-1]["extra"] = ",".join(arena.get_bins_info(current_chunk)) if current_chunk.chunk_base_address == arena.top: break next_chunk = current_chunk.get_next_chunk() if next_chunk is None: break if not is_valid_addr(next_chunk.address): break current_chunk = next_chunk info["chunks"] = chunks except Exception: return None return raw, info @staticmethod def take_snapshot(arena, arena_index): ret = GlibcHeapSnapshotCommand.dump_heap(arena) if ret is None: return None raw, info = ret raw_fd, raw_filepath = GefUtil.mkstemp( prefix="heap-ss-arena{:d}".format(arena_index), dt=datetime.datetime.now().strftime("%H%M%S"), suffix=".raw", ) os.fdopen(raw_fd, "wb").write(raw) base, _ = os.path.splitext(raw_filepath) json.dump(info, open(base + ".json", "w")) GlibcHeapSnapshotCommand.last_dumped_filepath = raw_filepath return raw_filepath @staticmethod def read_snapshot(filepath): if os.path.basename(filepath) == filepath: filepath = os.path.join(GEF_TEMP_DIR, filepath) base, _ = os.path.splitext(filepath) raw_path = base + ".raw" json_path = base + ".json" if not os.path.exists(raw_path) or os.path.getsize(raw_path) == 0: err("Invalid file path (.raw)") return None if not os.path.exists(json_path) or os.path.getsize(json_path) == 0: err("Invalid file path (.json)") return None try: raw = open(raw_path, "rb").read() except gdb.MemoryError: return None try: info = json.loads(open(json_path).read()) except Exception: return None return raw, info @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return if args.all: arenas = GlibcHeap.get_all_arenas() else: arenas = [arena] # doit for i, arena in enumerate(arenas): path = self.take_snapshot(arena, i) if path: info("Snapshot successful: {:s}".format(path)) return @register_command class GlibcHeapSnapshotCompareCommand(GenericCommand, BufferingOutput): """Compare current heap with a previously saved heap-snapshot.""" _cmdline_ = "heap snapshot-compare" _category_ = "05-a. Heap - Glibc" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--arena-addr", type=AddressUtil.parse_address, help="the address or number to interpret as an arena. (default: main_arena)") parser.add_argument("file_path", metavar="FILE_PATH", nargs="?", help="the filepath to compare (default: last dumped file).") parser.add_argument("file_path2", metavar="FILE_PATH2", nargs="?", help="the filepath to compare.") parser.add_argument("-e", "--extra", action="store_true", help="display extra chunk info.") parser.add_argument("-f", "--full", action="store_true", help="display after `top` chunk.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() _example_ = [ "{0:s} /path/to/snapshot1 # compare the current memory and file1", "{0:s} /path/to/snapshot1 /path/to/snapshot2 # compare file1 and file2", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Please specify the file obtained by the `heap snapshot` command.", "Usually, it is saved in /tmp/gef/heap-snashot-arenaN-...", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_FILENAME) return class LightRangeDict: def __init__(self): self.starts = [] self.items = [] return def add(self, start, stop, value): import bisect if not (start < stop): raise ValueError("start must be < stop") i = bisect.bisect_left(self.starts, start) if 0 < i and start < self.items[i - 1][1]: raise ValueError("overlapping range") if i < len(self.items) and self.items[i][0] < stop: raise ValueError("overlapping range") self.starts.insert(i, start) self.items.insert(i, (start, stop, value)) return None def __getitem__(self, key): import bisect i = bisect.bisect_right(self.starts, key) - 1 if 0 <= i: start, stop, value = self.items[i] if key < stop: return value if hasattr(self, "default"): return self.default raise KeyError(key) def setdefault(self, default): self.default = default return def compare(self, raw1, info1, raw2, info2): ptrsize = current_arch.ptrsize assert len(raw1) % ptrsize == 0 assert len(raw2) % ptrsize == 0 double_ptrsize = ptrsize * 2 hex_width = double_ptrsize + 2 if self.args.full: max_size = max(len(raw1), len(raw2)) else: max_size = max( info1["top"] + double_ptrsize - info1["heap_base"], info2["top"] + double_ptrsize - info2["heap_base"], ) color_dict = { # same, is_size, is_underline, is_used (True, True, True, True): "underline magenta", (True, True, True, False): "underline magenta", (True, True, False, True): "magenta", (True, True, False, False): "magenta", (True, False, True, True): "underline graphite", (True, False, True, False): "underline", (True, False, False, True): "graphite", (True, False, False, False): "", (False, True, True, True): "bold underline magenta", (False, True, True, False): "bold underline magenta", (False, True, False, True): "bold magenta", (False, True, False, False): "bold magenta", (False, False, True, True): "bold underline graphite", (False, False, True, False): "bold underline", (False, False, False, True): "bold graphite", (False, False, False, False): "bold", } start_offset_list1 = {c["start_offset"] for c in info1["chunks"]} start_offset_list2 = {c["start_offset"] for c in info2["chunks"]} end_offset_list1 = {c["end_offset"] for c in info1["chunks"]} end_offset_list2 = {c["end_offset"] for c in info2["chunks"]} if self.args.extra: prefix_blank = " " * (AddressUtil.get_format_address_width() + 18) fwidth = (double_ptrsize + 2) * 2 + 7 + double_ptrsize extra_dict1 = {c["start_offset"]: c["extra"] for c in info1["chunks"]} extra_dict2 = {c["start_offset"]: c["extra"] for c in info2["chunks"]} used_dict1 = self.LightRangeDict() used_dict1.setdefault(False) for ch in info1["chunks"]: used_dict1.add(ch["start_offset"], ch["start_offset"] + ch["size"], ch["used"]) used_dict2 = self.LightRangeDict() used_dict2.setdefault(False) for ch in info2["chunks"]: used_dict2.add(ch["start_offset"], ch["start_offset"] + ch["size"], ch["used"]) def to_ascii(v): s = "" for i in range(ptrsize): c = (v >> (8 * i)) & 0xff s += chr(c) if 0x20 <= c < 0x7f else "." return s # process block tqdm = GefUtil.get_tqdm(not self.args.quiet) for pos in tqdm(range(0, max_size, double_ptrsize), leave=False): # skip or not raw16_1 = raw1[pos : pos + double_ptrsize] raw16_2 = raw2[pos : pos + double_ptrsize] # coloring hex_1 = [] hex_2 = [] ascii_1 = [] ascii_2 = [] is_line_same = True # unpack values1 = slice_unpack(raw16_1, ptrsize) values2 = slice_unpack(raw16_2, ptrsize) # check size, underline, used is_size1 = pos in start_offset_list1 is_size2 = pos in start_offset_list2 is_underline1 = pos in end_offset_list1 is_underline2 = pos in end_offset_list2 is_used1 = used_dict1[pos] is_used2 = used_dict2[pos] # cmp for i in range(2): try: v1 = values1[i] h1 = "{:#0{:d}x}".format(v1, hex_width) a1 = to_ascii(v1) except IndexError: v1 = None h1 = " " * hex_width a1 = " " * ptrsize try: v2 = values2[i] h2 = "{:#0{:d}x}".format(v2, hex_width) a2 = to_ascii(v2) except IndexError: v2 = None h2 = " " * hex_width a2 = " " * ptrsize # element coloring is_same = (v1 is None) or (v2 is None) or v1 == v2 is_line_same &= is_same is_size1_e = is_size1 & (i == 1) is_size2_e = is_size2 & (i == 1) hex_1.append(Color.colorify(h1, color_dict[is_same, is_size1_e, is_underline1, is_used1])) ascii_1.append(Color.colorify(a1, color_dict[is_same, is_size1_e, is_underline1, is_used1])) hex_2.append(Color.colorify(h2, color_dict[is_same, is_size2_e, is_underline2, is_used2])) ascii_2.append(Color.colorify(a2, color_dict[is_same, is_size2_e, is_underline2, is_used2])) # blank coloring sep1 = Color.colorify(" ", " ".join(color_dict[True, False, is_underline1, is_used1])) sep2 = Color.colorify(" ", " ".join(color_dict[True, False, is_underline2, is_used2])) hex_1_joined = sep1.join(hex_1) hex_2_joined = sep2.join(hex_2) ascii_1_joined = sep1.join(ascii_1) ascii_2_joined = sep2.join(ascii_2) if self.args.extra: # make extra line extra1 = extra_dict1.get(pos, "") extra2 = extra_dict2.get(pos, "") if extra1 or extra2: line = "{:s} {:{:d}s} {:{:d}s}".format(prefix_blank, extra1, fwidth, extra2, fwidth) self.out.append(line.rstrip()) # make line addr = ProcessMap.lookup_address(info1["dump_start"] + pos) line = "{:s}{!s}|{:+#08x}|{:+06d}: {:s} | {:s} | {:s} | {:s} |".format( " " if is_line_same else "+", addr, pos, pos // double_ptrsize, hex_1_joined, ascii_1_joined, hex_2_joined, ascii_2_joined, ) self.out.append(line) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): if args.file_path is not None and args.file_path2 is not None: ret1 = GlibcHeapSnapshotCommand.read_snapshot(self.args.file_path) ret2 = GlibcHeapSnapshotCommand.read_snapshot(self.args.file_path2) file_path1 = os.path.basename(self.args.file_path) file_path2 = os.path.basename(self.args.file_path2) else: # parse arena arena = GlibcHeap.get_arena(args.arena_addr) if arena is None: err("No valid arena") return if arena.heap_base is None or not is_valid_addr(arena.heap_base): err("Heap is not initialized") return ret1 = GlibcHeapSnapshotCommand.dump_heap(arena) file_path1 = "Current memory" if args.file_path is None: if not hasattr(GlibcHeapSnapshotCommand, "last_dumped_filepath"): err("Invalid filepath") return ret2 = GlibcHeapSnapshotCommand.read_snapshot(GlibcHeapSnapshotCommand.last_dumped_filepath) file_path2 = os.path.basename(GlibcHeapSnapshotCommand.last_dumped_filepath) else: ret2 = GlibcHeapSnapshotCommand.read_snapshot(self.args.file_path) file_path2 = os.path.basename(self.args.file_path) if ret1 is None or ret2 is None: return raw1, info1 = ret1 raw2, info2 = ret2 # legend self.out = [] fwidth = (current_arch.ptrsize * 2 + 2) * 2 + 7 + (current_arch.ptrsize * 2) fmt = " {:{:d}s} {:8s} {:6s} {:{:d}s} {:{:d}s}" legend = [ "Address", AddressUtil.get_format_address_width(), "Offset", "Line", file_path1, fwidth, file_path2, fwidth, ] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # doit self.compare(memoryview(raw1), info1, memoryview(raw2), info2) self.print_output(check_terminal_size=True) return @register_command class RegistersCommand(GenericCommand): """Display many or all register values from current architecture.""" _cmdline_ = "registers" _category_ = "01-a. Debugging Support - Context" _aliases_ = ["regs"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("registers", metavar="REGISTERS", nargs="*", type=lambda x: x if x.startswith("$") else "$" + x, help="An array of registers. (default: current_arch.all_registers)") parser.add_argument("-s", "--simple", action="store_true", help="skip dereference.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} $eax $eip $esp", ] _example_ = "\n".join(_example_).format(_cmdline_) def get_all_registers(self): if is_x86(): all_registers = current_arch.all_registers + current_arch.virtual_registers else: all_registers = current_arch.all_registers return all_registers def check_unavailable_regs(self): """Detect and record unavailable registers for later display handling.""" if hasattr(self, "regs_to_check_unavailable"): return self.regs_to_check_unavailable = [] for regname in self.get_all_registers(): try: reg = gdb.parse_and_eval(regname) except gdb.error: # older qemu cannot resolve `fs_base` etc. continue if reg.type.code == gdb.TYPE_CODE_VOID: continue if str(reg) == "": self.regs_to_check_unavailable.append(regname) return def get_regname_color(self, regname, regvalue): """Return the appropriate color for a register name based on whether its value has changed.""" unchanged_color = Config.get_gef_setting("theme.registers_register_name") changed_color = Config.get_gef_setting("theme.registers_value_changed") old_value = ContextRegistersCommand.old_registers.get(regname, 0) if regvalue == old_value: color = unchanged_color else: color = changed_color return color def dump_seg_reg_x86_16(self): """Format and return x86 16-bit segment register values with color and dereferencing.""" lines = [] for regname, (seg, reg) in current_arch.seg_extended_registers.items(): segval = get_register(seg) & 0xffff regval = get_register(reg) & 0xffff value = current_arch.real2phys(segval, regval) # colorling color = self.get_regname_color(regname, value) # reg name line = "{}:".format(Color.colorify(regname, color)) # dereference values value_s = AddressUtil.format_address(value, memalign_size=2.5) line += " {:04x}:{:04x}: {:s} -> ".format(segval, regval, value_s) line += AddressUtil.recursive_dereference_to_string(value, skip_idx=1) lines.append(line) return lines def dump_regs(self, target_regs): """Format and return register values for display, with color, alignment, and optional dereferencing.""" aliased_registers = current_arch.get_aliased_registers() widest = current_arch.get_aliased_registers_name_max() special_line = "" flag_line = "" lines = [] for regname in target_regs: try: reg = gdb.parse_and_eval(regname) except gdb.error: # invalid register continue if reg.type.code == gdb.TYPE_CODE_VOID: continue # str(reg) is slow, so skip if unneeded if regname in self.regs_to_check_unavailable: # for qiling framework, fs_base/gs_base (x86), cpsr/fpsr/fpcr (Aarch64) are unavailable if str(reg) == "": padreg = aliased_registers.get(regname, regname).ljust(widest, " ") line = "{:s}: {:s}".format( Color.colorify(padreg, Config.get_gef_setting("theme.registers_register_name")), Color.colorify("", "yellow underline"), ) lines.append(line) continue # value try: if hasattr(reg, "bytes"): reg_len = len(reg.bytes) else: reg_len = current_arch.ptrsize except gdb.error: # In the qiling framework, it may fail just by doing hasattr (e.g., bndstatus) continue value = AddressUtil.align_address(int(reg), memalign_size=reg_len) # colorling color = self.get_regname_color(regname, value) # special (e.g., segment) registers go on their own line if current_arch.special_registers and regname in current_arch.special_registers: special_line += "{:s}: {:#06x} ".format( Color.colorify(regname, color), get_register(regname), ) continue # reg name padreg = aliased_registers.get(regname, regname).ljust(widest, " ") # flag register if current_arch.flag_register and regname == current_arch.flag_register: flag_line += "{:s}: {:s}".format( Color.colorify(padreg, color), current_arch.flag_register_to_human(), ) continue # make one line line = "{:s}: ".format(Color.colorify(padreg, color)) if self.args.simple: # not dereference line += "{:s} ".format(ProcessMap.lookup_address(value).long_fmt()) else: # dereference values if is_x86_16(): line += AddressUtil.format_address(value, memalign_size=4) derefs = AddressUtil.recursive_dereference_to_string(value, skip_idx=1) if derefs: line += " -> {:s}".format(derefs) elif is_mipsn32(): line += AddressUtil.format_address(value, memalign_size=8, long_fmt=True) derefs = AddressUtil.recursive_dereference_to_string(value, skip_idx=1) if derefs: line += " -> {:s}".format(derefs) else: line += AddressUtil.recursive_dereference_to_string(value) lines.append(line) if self.args.simple: one_width = widest + 5 + current_arch.ptrsize * 2 nb = GefUtil.get_terminal_size()[1] // one_width lines = ["".join(r) for r in slicer(lines, nb)] if flag_line: lines.append(flag_line) if special_line: lines.append(special_line.rstrip()) if not self.args.simple: if is_x86_16(): lines += self.dump_seg_reg_x86_16() return lines @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.check_unavailable_regs() if args.registers: target_regs = args.registers else: target_regs = self.get_all_registers() out = self.dump_regs(target_regs) if out: gef_print("\n".join(out)) return @register_command class RopperCommand(GenericCommand): """Invoke ropper to search rop gadgets.""" _cmdline_ = "ropper" _category_ = "01-i. Debugging Support - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("args", metavar="ROPPER_OPTIONS", nargs="*", help="An array of arguments to pass as is to the ropper command. (default: %(default)s)") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} -h # show detail of options", '{0:s} --jmp "rax,rcx" # filter by jmp registers', '{0:s} --search "pop r?x" # filter by pop registers', ] _example_ = "\n".join(_example_).format(_cmdline_) _help_ = None _help_examples_ = None def print_help(self): self.usage() ropper_bin = GefUtil.which("ropper") if self._help_ is None: self._help_ = subprocess.check_output([ropper_bin, "--help"]).decode("utf-8") if self._help_examples_ is None: self._help_examples_ = subprocess.check_output([ropper_bin, "--help-examples"]).decode("utf-8") help_text = titlify("gef --help") help_text += self._help_ help_text += titlify("gef --help-examples") help_text += self._help_examples_ gef_print(help_text, less=True) return # Need not @parse_args because argparse can't stop interpreting options for ropper. @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @ModuleLoader.load_ropper @require_arch_set def do_invoke(self, argv): if "-h" in argv or "--help" in argv: self.print_help() return if "--file" not in argv: filepath = Path.get_filepath() if filepath is None: err("Missing info about file. Please set: `file /path/to/target_binary`") return argv.extend(["--file", filepath]) else: try: filepath = argv[argv.index("--file") + 1] except IndexError: self.print_help() return if not os.path.isfile(filepath): err("Invalid filepath") return # ropper set up own autocompleter after which gdb/gef autocomplete don't work # due to fork/waitpid, child will be broken but parent will not change gef_print(titlify(filepath)) pid = os.fork() if pid == 0: # Reorder GdbRemoveReadlineFinder in child processes so readline can be loaded. finder = None for x in list(sys.meta_path): if type(x).__name__ == "GdbRemoveReadlineFinder": finder = x sys.meta_path.remove(x) break if finder is not None: sys.meta_path.append(finder) # doit try: ropper = sys.modules["ropper"] ropper.start(argv) except (Exception, SystemExit): pass os._exit(0) else: os.waitpid(pid, 0) return @register_command class RpCommand(GenericCommand, BufferingOutput): """Invoke rp++ (v2) command to search rop gadgets (x64/x86 only).""" _cmdline_ = "rp" _category_ = "01-i. Debugging Support - Other" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--bin", action="store_true", help="apply rp++ to binary itself.") group.add_argument("--libc", action="store_true", help="apply rp++ to libc.so searched from vmmap.") group.add_argument("--file", help="apply rp++ to specified file.") group.add_argument("--kernel", action="store_true", help="dump kernel, then apply vmlinux-to-elf and rp++.") parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="REGEXP filter.") parser.add_argument("-r", "--rop", dest="rop_N", type=int, default=3, help="the max length of rop gadget. (default: %(default)s)") parser.add_argument("-a", "--allow-branches", action="store_true", help="enable --allow-branches.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("--no-print", action="store_true", help="run rp, create a temporary file, but don't display it.") _syntax_ = parser.format_help() _example_ = [ '{0:s} --bin -f "pop r[abcd]x"', '{0:s} --libc -f "(xchg|mov) [re]sp, \\\\w+" -f "ret"', "{0:s} --bin -a # show more gadgets", "{0:s} --kernel # only under qemu-system", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_FILENAME) return def exec_rp(self, rp, ropN, allow_branches, path): """Run rp++ to search for ROP gadgets, saving output to a file and returning its path.""" astr = "ab" if allow_branches else "" output_file = "rp{:d}{:s}_{:s}.txt".format(ropN, astr, os.path.basename(path)) output_path = os.path.join(GEF_TEMP_DIR, output_file) aops = "--allow-branches " if allow_branches else "" cmd = "{!r} --file={!r} --rop={:d} {:s}--unique > {!r}".format(rp, path, ropN, aops, output_path) gef_print(titlify(cmd)) if not os.path.exists(output_path): os.system(cmd) return output_path def apply_filter(self, rp_output_path, base_address): """Apply regex filters to rp++ output, adjust gadget addresses, and store matching lines.""" if not os.path.exists(rp_output_path): err("Could not find {!r}".format(rp_output_path)) return lines = open(rp_output_path, "r").read() for line in lines.splitlines(): line = Color.remove_color(line) match = True for re_pattern in self.args.filter: if not re_pattern.search(line): match = False break if match: if line.startswith("0x"): x = line.split(":") addr, gadget = int(x[0], 16), ":".join(x[1:]) addr -= base_address # fix address x = Color.redify("{:#08x}".format(addr)) + ":" + gadget # repaint color else: x = line self.out.append(x) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): try: rp = GefUtil.which("rp-lin") except FileNotFoundError as e: err("{}".format(e)) return if args.kernel: try: nm = GefUtil.which(Config.get_gef_setting("gef.nm_command")) grep = GefUtil.which("grep") except FileNotFoundError as e: err("{}".format(e)) return base_address = 0 if args.libc: libc_targets = ("libc-2.", "libc.so.6", "libuClibc-") libc = ProcessMap.process_lookup_path(libc_targets) if libc is None: err("Could not find the libc") return path = libc.path elif args.bin: binary = Path.get_filepath() if binary is None: err("Could not find the binary") return path = binary elif args.file: if not os.path.exists(args.file): err("Could not find {}".format(args.file)) return path = args.file elif args.kernel: if not is_qemu_system(): err("--kernel are supported under qemu-system only") return info("Wait for memory scan") # dump kernel then apply vmlinux-to-elf symboled_vmlinux_file = VmlinuxToElfApplyCommand.dump_kernel_elf() if symboled_vmlinux_file is None: err("Failed to create kernel ELF") return path = symboled_vmlinux_file cmd = "{!r} {!r} | {!r} ' _stext$'".format(nm, symboled_vmlinux_file, grep) out = GefUtil.gef_execute_external(cmd, as_list=True, shell=True) if len(out) != 1: err("Failed to resolve _stext") return base_address = int(out[0].split()[0], 16) # invoke rp++ rp_output_path = self.exec_rp(rp, args.rop_N, args.allow_branches, path) if args.no_print: return # filtering self.out = [] self.apply_filter(rp_output_path, base_address) # print self.print_output(check_terminal_size=True) return @register_command class AssembleCommand(GenericCommand): """Assemble inline code using Keystone.""" _cmdline_ = "asm" _category_ = "01-e. Debugging Support - Assemble" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", dest="arch", help="specify the architecture. (default: current_arch.arch)") parser.add_argument("-m", dest="mode", help="specify the mode. (default: current_arch.mode)") parser.add_argument("-e", dest="big_endian", action="store_true", help="use big-endian.") parser.add_argument("-s", dest="as_shellcode", action="store_true", help="output like shellcode style.") parser.add_argument("-l", dest="overwrite_location", metavar="LOCATION", type=AddressUtil.parse_address, help="write to memory address.") parser.add_argument("-H", "--hex", action="store_true", help="show in hex style.") parser.add_argument("instruction", metavar="INSTRUCTION", nargs="+", help="the code to assemble.") _syntax_ = parser.format_help() _example_ = [ '{0:s} -a X86 -m 64 "mov rax, qword ptr [rax] ; inc rax ;"', '{0:s} -a X86 -m 32 "mov eax, dword ptr [eax] ; inc eax ;"', '{0:s} -a X86 -m 16 "mov ax, word ptr [ax] ; inc ax"', '{0:s} -a ARM -m ARM "sub r1, r2, r3"', '{0:s} -a ARM -m ARM -e "sub r1, r2, r3"', '{0:s} -a ARM -m THUMB "movs r4, #0xf0"', '{0:s} -a ARM -m THUMB -e "movs r4, #0xf0"', '{0:s} -a ARM64 -m ARM "ldr w1, [sp, #0x8]"', '{0:s} -a MIPS -m 32 "and $9, $6, $7"', '{0:s} -a MIPS -m 32 -e "and $9, $6, $7"', '{0:s} -a MIPS -m 64 "and $9, $6, $7"', '{0:s} -a MIPS -m 64 -e "and $9, $6, $7"', '{0:s} -a PPC -m 32 -e "add 1, 2, 3"', '{0:s} -a PPC -m 64 "add 1, 2, 3"', '{0:s} -a PPC -m 64 -e "add 1, 2, 3"', '{0:s} -a SPARC -m 32 -e "add %g1, %g2, %g3"', '{0:s} -a SPARC -m 32PLUS -e "add %g1, %g2, %g3"', '{0:s} -a SPARC -m 64 -e "add %g1, %g2, %g3"', '{0:s} -a S390X -m 64 -e "a %r0, 4095(%r15,%r1)"', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @ModuleLoader.load_keystone def do_invoke(self, args): if (args.arch, args.mode) == (None, None): if is_alive() and current_arch: arch, mode = UnicornKeystoneCapstone.get_keystone_arch( arch=current_arch.arch, mode=current_arch.mode, endian=Endian.is_big_endian(), ) arch_mode_s = ":".join([current_arch.arch, current_arch.mode]) endian_s = "big" if Endian.is_big_endian() else "little" else: # if not alive, defaults to x86-64 arch, mode = UnicornKeystoneCapstone.get_keystone_arch(arch="X86", mode="64", endian=False) arch_mode_s = "X86:64" endian_s = "little" elif not args.arch: err("An architecture (-a) must be provided") return elif not args.mode: # keystone gives no error so check here err("A mode (-m) must be provided") return elif args.arch in ["SPARC", "S390X"] and args.big_endian is False: # keystone gives no error so check here err("A big endian flag (-e) must be provided") return else: try: arch, mode = UnicornKeystoneCapstone.get_keystone_arch( arch=args.arch, mode=args.mode, endian=args.big_endian, ) arch_mode_s = ":".join([args.arch, args.mode]) endian_s = "big" if args.big_endian else "little" except AttributeError: self.usage() return insns = " ".join(args.instruction) insns = [x.strip() for x in insns.split(";") if x is not None and x.strip() != ""] info("Assembling {:d} instruction{:s} for {:s} ({:s} endian)".format( len(insns), "s" if len(insns) > 1 else "", arch_mode_s, endian_s, )) if args.as_shellcode: gef_print('sc = ""') raw = b"" for insn in insns: res = UnicornKeystoneCapstone.keystone_assemble(insn, arch, mode, raw=True) if not res: gef_print("(Invalid)") continue if args.overwrite_location is not None: raw += res continue s = binascii.hexlify(res) if args.hex: res = String.bytes2str(s) else: res = b"\\x" + b"\\x".join([s[i:i + 2] for i in range(0, len(s), 2)]) res = res.decode("utf-8") if args.as_shellcode: res = 'sc += "{:s}"'.format(res) gef_print("{:60s} # {:s}".format(res, insn)) if args.overwrite_location is not None: hex_code = binascii.hexlify(raw).decode() gdb.execute("patch hex {:#x} {:s}".format(args.overwrite_location, hex_code)) return @register_command class DisassembleCommand(GenericCommand): """Disassemble inline code using Capstone.""" _cmdline_ = "dasm" _category_ = "01-e. Debugging Support - Assemble" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", dest="arch", help="specify the architecture. (default: current_arch.arch)") parser.add_argument("-m", dest="mode", help="specify the mode. (default: current_arch.mode)") parser.add_argument("-e", dest="big_endian", action="store_true", help="use big-endian.") parser.add_argument("hex_code", metavar="HEX_CODE", nargs="+", help="the hex code to disassemble.") _syntax_ = parser.format_help() _example_ = [ '{0:s} -a X86 -m 64 "488b00 48ffc0"', '{0:s} -a X86 -m 32 "8b00 40"', '{0:s} -a X86 -m 16 "8b00 40"', '{0:s} -a ARM -m ARM "031042e0"', '{0:s} -a ARM -m ARM -e "e0421003"', '{0:s} -a ARM -m THUMB "f024"', '{0:s} -a ARM -m THUMB -e "24f0"', '{0:s} -a ARM64 -m ARM "e10b40b9"', '{0:s} -a MIPS -m 32 "2448c700"', '{0:s} -a MIPS -m 32 -e "00c74824"', '{0:s} -a MIPS -m 64 "2448c700"', '{0:s} -a MIPS -m 64 -e "00c74824"', '{0:s} -a PPC -m 32 -e "7c221a14"', '{0:s} -a PPC -m 64 "141a227c"', '{0:s} -a PPC -m 64 -e "7c221a14"', '{0:s} -a SPARC -m 32 -e "86004002"', '{0:s} -a SPARC -m 32PLUS -e "86004002"', '{0:s} -a SPARC -m 64 -e "86004002"', '{0:s} -a RISCV -m 32 "97c10600"', '{0:s} -a RISCV -m 64 "97c10600"', '{0:s} -a S390X -m 64 -e "5a0f1fff"', '{0:s} -a M68K -m 32 -e "9dce"', '{0:s} -a LOONGARCH -m 64 "89001500" # capstone v6.x~', '{0:s} -a LOONGARCH -m 32 "89001500" # capstone v6.x~', '{0:s} -a ALPHA -m 64 "0b00bd27" # capstone v6.x~', '{0:s} -a ALPHA -m 64 -e "27bd000b" # capstone v6.x~', '{0:s} -a HPPA -m 32 -e "0fc01299" # capstone v6.x~', '{0:s} -a HPPA -m 64 -e "0fc01299" # capstone v6.x~', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @ModuleLoader.load_capstone def do_invoke(self, args): if (args.arch, args.mode) == (None, None): if is_alive() and current_arch: arch, mode = UnicornKeystoneCapstone.get_capstone_arch( arch=current_arch.arch, mode=current_arch.mode, endian=Endian.is_big_endian(), ) arch_mode_s = ":".join([current_arch.arch, current_arch.mode]) endian_s = "big" if Endian.is_big_endian() else "little" else: # if not alive, defaults to x86-64 arch, mode = UnicornKeystoneCapstone.get_capstone_arch( arch="X86", mode="64", endian=False, ) arch_mode_s = "X86:64" endian_s = "little" elif not args.arch: err("An architecture (-a) must be provided") return elif not args.mode: err("A mode (-m) must be provided") return elif args.arch in ["SPARC", "S390X", "M68K", "HPPA"] and args.big_endian is False: # capstone gives no error so check here err("A big endian flag (-e) must be provided") return else: try: arch, mode = UnicornKeystoneCapstone.get_capstone_arch( arch=args.arch, mode=args.mode, endian=args.big_endian, ) arch_mode_s = ":".join([args.arch, args.mode]) endian_s = "big" if args.big_endian else "little" except AttributeError: self.usage() return insns = " ".join(args.hex_code) insns = insns.replace(" ", "").replace("\t", "") try: insns = binascii.unhexlify(insns) except binascii.Error: err("Invalid format") return info("Disassembling {:d} bytes for {:s} ({:s} endian)".format( len(insns), arch_mode_s, endian_s, )) capstone = sys.modules["capstone"] try: cs = capstone.Cs(arch, mode) except capstone.CsError: err("CsError") return cs.detail = True # noqa for insn in cs.disasm(insns, 0x0): b = binascii.hexlify(insn.bytes).decode("utf-8") gef_print("{:>#6x}:\t{:<10s}\t{:s}\t{:s}".format(insn.address, b, insn.mnemonic, insn.op_str)) return @register_command class AsmListCommand(GenericCommand): """List general instructions by capstone (x64/x86 only).""" _cmdline_ = "asm-list" _category_ = "01-e. Debugging Support - Assemble" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", dest="arch", help="specify the architecture. (default: current_arch.arch)") parser.add_argument("-m", dest="mode", help="specify the mode. (default: current_arch.mode)") parser.add_argument("-e", dest="big_endian", action="store_true", help="use big-endian.") parser.add_argument("-b", dest="nbyte", type=int, help="filter by the length of asm byte.") parser.add_argument("-f", dest="include", action="append", help="filter by specified string.") parser.add_argument("-v", dest="exclude", action="append", help="filter by specified string.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -a X86 -m 64", "{0:s} -a X86 -m 32", "{0:s} -a X86 -m 16", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "- F0 (LOCK prefix) is ignored", "- F2/F3 (REPNE/REP prefix) are ignored", "- 2E/36/3E/26/64/65 (CS/SS/DS/ES/FS/GS override prefix) are ignored", "- 2E/3E (branch hint prefix) are ignored", "- 66 (operand size prefix) is included", "- 67 (address size prefix) is ignored", "- 40-4F (REX prefix) are ignored", "- C4/C5 (VEX prefix) are ignored", "- 8F (XOP prefix) is ignored", "- 62 (EVEX prefix) is ignored", ] _note_ = "\n".join(_note_) cache = None def listup_x86(self, arch, mode): if self.cache: return self.cache DISP64 = "1122334455667788" DISP32 = "11223344" DISP16 = "1122" DISP8 = "11" @Cache.cache_this_session def get_typical_bytecodes_modrm(reg_value): mod_list = range(4) assert 0 <= reg_value <= 7 reg_list = [reg_value] rm_list = [0, 0b100] # The correct value is range(8), but it is reduced for speed. sib_list = [0, 0b01001001] # The correct value is range(256), but it is reduced for speed. bytecodes = [] for mod, reg, rm in itertools.product(mod_list, reg_list, rm_list): modrm = "{:02X}".format((mod << 6) | (reg << 3) | rm) if mod == 0b00: if rm == 0b101: # special case; [REG + disp32] bytecode = modrm + DISP32 elif rm == 0b100: # use sib; [INDEX * SCALE + BASE] for sib in sib_list: bytecode = modrm + "{:02X}".format(sib) else: # [REG] bytecode = modrm elif mod == 0b01: if rm == 0b100: # use sib; [INDEX * SCALE + BASE + disp8] bytecode = [] for sib in sib_list: b = modrm + "{:02X}".format(sib) + DISP8 bytecode.append(b) else: # [REG + disp8] bytecode = modrm + DISP8 elif mod == 0b10: if rm == 0b100: # use sib; [INDEX * SCALE + BASE + disp32] bytecode = [] for sib in sib_list: b = modrm + "{:02X}".format(sib) + DISP32 bytecode.append(b) else: # [REG + disp32] bytecode = modrm + DISP32 elif mod == 0b11: # REG bytecode = modrm if isinstance(bytecode, list): bytecodes.extend(bytecode) else: bytecodes.append(bytecode) return bytecodes def get_typical_bytecodes(opcodes): bytecodes = [] for operand in opcodes.split(): if operand in ["ib", "cb"]: bytecode = [DISP8] elif operand in ["iw", "cw"]: bytecode = [DISP16] elif operand in ["id", "cd"]: bytecode = [DISP32] elif operand in ["iq"]: bytecode = [DISP64] elif operand in ["/0", "/1", "/2", "/3", "/4", "/5", "/6", "/7"]: bytecode = get_typical_bytecodes_modrm(int(operand[1])) elif operand == "/r": bytecode = get_typical_bytecodes_modrm(0) elif operand.endswith(("+r", "+i")): b = int(operand.split("+")[0], 16) bytecode = ["{:02X}".format(b + x) for x in range(8)] else: bytecode = [operand] bytecodes.append(bytecode) return ["".join(b) for b in itertools.product(*bytecodes)] def load_x86_json(): x86data_js = os.path.join(GEF_TEMP_DIR, "x86data.js") if os.path.exists(x86data_js) and os.path.getsize(x86data_js) > 0: x86 = open(x86data_js, "rb").read() else: url = "https://raw.githubusercontent.com/bata24/gef/dev/asmdb/x86data.js" x86 = http_get(url) if x86 is None: err("Connection timed out: {:s}".format(url)) return None open(x86data_js, "wb").write(x86) x86 = x86.split(b"// ${JSON:BEGIN}")[1].split(b"// ${JSON:END}")[0] return json.loads(x86) # load capstone capstone = sys.modules["capstone"] try: cs = capstone.Cs(arch, mode) except capstone.CsError: err("CsError") return None # default instruction set x86 = load_x86_json() # manually added x86_insns = x86["instructions"] # [opcode_str, unused, unused, opcodes, attr] x86_insns.append(["icebp", "", "", "F1", "Undocumented"]) x86_insns.append(["salc", "", "", "D6", "Undocumented"]) #x86_insns.append(["umov", "", "", "0F 10 /r", "Undocumented"]) # used by another opcode #x86_insns.append(["umov", "", "", "0F 11 /r", "Undocumented"]) # used by another opcode #x86_insns.append(["umov", "", "", "0F 12 /r", "Undocumented"]) # used by another opcode #x86_insns.append(["umov", "", "", "0F 13 /r", "Undocumented"]) # used by another opcode #x86_insns.append(["loadall", "", "", "0F 05", "Undocumented"]) # used by another opcode #x86_insns.append(["loadall", "", "", "0F 07", "Undocumented"]) # used by another opcode #x86_insns.append(["xbts", "", "", "0F A6", "Undocumented"]) # removed now #x86_insns.append(["ibts", "", "", "0F A7", "Undocumented"]) # removed now # parse it valid_patterns = [] seen_patterns = [] for insn in x86_insns: opcodes = insn[3] attr = insn[4].split() # filter ignore prefix pattern if "REX.W" in opcodes.split(): continue if "VEX" in opcodes.split()[0].split("."): continue if "EVEX" in opcodes.split()[0].split("."): continue if "XOP" in opcodes.split()[0].split("."): continue # e.g., "FF /2" -> ["FF10", "FF5011", ...] bytecodes = get_typical_bytecodes(opcodes) # check it is valid or not for hex_code in bytecodes: # dup check if hex_code in seen_patterns: continue # disasm code = bytes.fromhex(hex_code) try: asm = cs.disasm(code, 0).__next__() except StopIteration: continue opstr = asm.mnemonic + " " + asm.op_str # add valid_patterns.append([hex_code, opstr, opcodes, attr]) seen_patterns.append(hex_code) self.cache = valid_patterns return valid_patterns @parse_args @ModuleLoader.load_capstone @require_arch_set def do_invoke(self, args): if (args.arch, args.mode) == (None, None): if is_alive(): arch, mode = UnicornKeystoneCapstone.get_capstone_arch( arch=current_arch.arch, mode=current_arch.mode, endian=Endian.is_big_endian(), ) arch_mode_s = ":".join([current_arch.arch, current_arch.mode]) endian_s = "big" if Endian.is_big_endian() else "little" else: # if not alive, defaults to x86-64 arch, mode = UnicornKeystoneCapstone.get_capstone_arch(arch="X86", mode="64", endian=False) arch_mode_s = "X86:64" endian_s = "little" elif not args.arch: err("An architecture (-a) must be provided") return elif not args.mode: err("A mode (-m) must be provided") return elif args.arch in ["SPARC", "S390X", "M68K"] and args.big_endian is False: # capstone gives no error so check here err("A big endian flag (-e) must be provided") return else: try: arch, mode = UnicornKeystoneCapstone.get_capstone_arch( arch=args.arch, mode=args.mode, endian=args.big_endian, ) arch_mode_s = ":".join([args.arch, args.mode]) endian_s = "big" if args.big_endian else "little" except AttributeError: self.usage() return # list bytecode pattern if arch_mode_s.startswith("X86:"): if endian_s == "big": err("X86 is not big endian") return patterns = self.listup_x86(arch, mode) else: err("Unsupported other than x86/x64") return if patterns is None: err("Failed to list entries") return # filter and print self.out = [] fmt = "{:22s} {:70s} {:22s} {!s}" legend = ["Hex code", "Assembly code", "Opcode", "Attributes"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for hex_code, opstr, opcodes, attr in patterns: # byte length filter if args.nbyte is not None and args.nbyte * 2 != len(hex_code): continue # keyword filter line = "{:22s} {:70s} {:22s} {!s}".format(hex_code, opstr, opcodes, ",".join(attr)) if args.include and any(f not in line for f in args.include): continue if args.exclude and any(f in line for f in args.exclude): continue # not filtered self.out.append(line) gef_print("\n".join(self.out), less=not args.no_pager) return @register_command class ProcessSearchCommand(GenericCommand, BufferingOutput): """Display a smart list of processes.""" _cmdline_ = "ps" _category_ = "02-a. Process Information - General" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("pattern", metavar="REGEX_PATTERN", nargs="?", help="filter by regex.") parser.add_argument("-a", "--attach", type=int, help="attach it.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="include kernel thread, socat, grep, gdb, sshd, bash, systemd, etc.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} ./a.out", ] _example_ = "\n".join(_example_).format(_cmdline_) def get_processes(self): output = GefUtil.gef_execute_external([GefUtil.which("ps"), "auxww"], as_list=True) names = [x.lower().replace("%", "") for x in output[0].split()] for line in output[1:]: fields = line.split() t = {} for i, name in enumerate(names): if i == len(names) - 1: t[name] = " ".join(fields[i:]) else: t[name] = fields[i] yield t return @parse_args @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @require_arch_set def do_invoke(self, args): if args.pattern: pattern = re.compile(args.pattern) else: pattern = re.compile("^.*$") self.out = [] for process in self.get_processes(): pid = int(process["pid"]) command = process["command"] process["user"] = process["user"].ljust(8) if not re.search(pattern, command): continue if not args.verbose: if command.startswith("[") and command.endswith("]"): # kernel thread continue skip_list = [ # common "socat ", "grep ", "gdb ", "gdb-multiarch", "-bash", "sshd:", "ssh-agent ", # VMware tools "vmhgfs-fuse", "vmware-vmblock-fuse", "fusermount3", # system service "avahi-daemon:", "@dbus-daemon", "(sd-pam)", "gjs ", "gdm-session-worker", "cupsd ", "cups-browsed ", # common path ("/bin/", "/usr/bin/"), ("/sbin/", "/usr/sbin/"), ("/lib/", "/usr/lib/"), "/usr/libexec/", "/snap/", "/var/lib/pcp/pmdas", ] if any(command.startswith(x) for x in skip_list): continue if args.attach: if args.attach == pid: ok("Attaching to process='{:s}' pid={:d}".format(process["command"], pid)) gdb.execute("attach {:d}".format(pid)) return line = [process[i] for i in ("pid", "user", "cpu", "mem", "tty", "command")] self.out.append("\t".join(line)) self.print_output() return @register_command class ElfInfoCommand(GenericCommand): """Display a limited subset of ELF header information.""" _cmdline_ = "elf-info" _category_ = "02-a. Process Information - General" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-e", "--use-readelf", action="store_true", help="use readelf.") parser.add_argument("-r", "--remote", action="store_true", help="parse remote binary if download feature is available.") parser.add_argument("-f", "--file", help="the file path to parse.") parser.add_argument("-a", "--address", type=AddressUtil.parse_address, help="the memory address to parse.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="dump the content of each section.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # parse binary itself", "{0:s} -f /bin/ls # parse binary", "{0:s} -f /bin/ls -r # parse remote binary", "{0:s} -a 0x555555554000 # parse memory", "{0:s} -e -f /bin/ls # show `readelf -a FILE | less`", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self, *args, **kwargs): super().__init__(complete=gdb.COMPLETE_FILENAME) return classes = { Elf.ELF_CLASS_NONE : "Unknown", Elf.ELF_32_BITS : "32-bit", Elf.ELF_64_BITS : "64-bit", } endianness = { Elf.ELF_DATA_NONE : "Unknown", Elf.LITTLE_ENDIAN : "Little-Endian", Elf.BIG_ENDIAN : "Big-Endian", } osabis = { Elf.OSABI_SYSTEMV : "UNIX System V ABI", Elf.OSABI_HPUX : "Hewlett-Packard HP-UX", Elf.OSABI_NETBSD : "NetBSD", Elf.OSABI_LINUX : "GNU Linux", Elf.OSABI_HURD : "GNU Hurd", Elf.OSABI_86OPEN : "86Open Common IA32 ABI", Elf.OSABI_SOLARIS : "Sun Solaris", Elf.OSABI_AIX : "IBM AIX", Elf.OSABI_IRIX : "SGI IRIX", Elf.OSABI_FREEBSD : "FreeBSD", Elf.OSABI_TRU64 : "Compaq TRU64 UNIX", Elf.OSABI_MODESTO : "Novell Modesto", Elf.OSABI_OPENBSD : "OpenBSD", Elf.OSABI_OPENVMS : "OpenVMS", Elf.OSABI_NSK : "Hewlett-Packard Non-Stop Kernel", Elf.OSABI_AROS : "Amiga Research OS", Elf.OSABI_FENIXOS : "The FenixOS highly scalable multi-core OS", Elf.OSABI_CLOUDABI : "Nuxi CloudABI", Elf.OSABI_OPENVOS : "Stratus Technologies OpenVOS", Elf.OSABI_ARM_AEABI : "ARM EABI", Elf.OSABI_ARM : "ARM", Elf.OSABI_STANDALONE : "Standalone (embedded) application", } types = { Elf.ET_NONE : "No file type", Elf.ET_REL : "Relocatable", Elf.ET_EXEC : "Executable", Elf.ET_DYN : "Shared", Elf.ET_CORE : "Core", } machines = { Elf.EM_NONE : "No machine", Elf.EM_M32 : "AT&T WE 32100", Elf.EM_SPARC : "SUN SPARC", Elf.EM_386 : "Intel 80386", Elf.EM_68K : "Motorola m68k family", Elf.EM_88K : "Motorola m88k family", Elf.EM_IAMCU : "Intel MCU", Elf.EM_860 : "Intel 80860", Elf.EM_MIPS : "MIPS R3000 big-endian", Elf.EM_S370 : "IBM System/370 Processor", Elf.EM_MIPS_RS3_LE : "MIPS RS3000 Little-endian", Elf.EM_PARISC : "Hewlett-Packard PA-RISC", Elf.EM_VPP500 : "Fujitsu VPP500", Elf.EM_SPARC32PLUS : "Enhanced instruction set SPARC", Elf.EM_960 : "Intel 80960", Elf.EM_PPC : "PowerPC", Elf.EM_PPC64 : "64-bit PowerPC", Elf.EM_S390 : "IBM System/390 Processor", Elf.EM_SPU : "IBM SPU/SPC", Elf.EM_V800 : "NEC V800", Elf.EM_FR20 : "Fujitsu FR20", Elf.EM_RH32 : "TRW RH-32", Elf.EM_RCE : "Motorola RCE", Elf.EM_ARM : "ARM 32-bit architecture (AARCH32)", Elf.EM_ALPHA : "Digital Alpha", Elf.EM_SH : "Hitachi SH", Elf.EM_SPARCV9 : "SPARC Version 9", Elf.EM_TRICORE : "Siemens TriCore embedded processor", Elf.EM_ARC : "Argonaut RISC Core, Argonaut Technologies Inc.", Elf.EM_H8_300 : "Hitachi H8/300", Elf.EM_H8_300H : "Hitachi H8/300H", Elf.EM_H8S : "Hitachi H8S", Elf.EM_H8_500 : "Hitachi H8/500", Elf.EM_IA_64 : "Intel IA-64 processor architecture", Elf.EM_MIPS_X : "Stanford MIPS-X", Elf.EM_COLDFIRE : "Motorola ColdFire", Elf.EM_68HC12 : "Motorola M68HC12", Elf.EM_MMA : "Fujitsu MMA Multimedia Accelerator", Elf.EM_PCP : "Siemens PCP", Elf.EM_NCPU : "Sony nCPU embedded RISC processor", Elf.EM_NDR1 : "Denso NDR1 microprocessor", Elf.EM_STARCORE : "Motorola Star*Core processor", Elf.EM_ME16 : "Toyota ME16 processor", Elf.EM_ST100 : "STMicroelectronics ST100 processor", Elf.EM_TINYJ : "Advanced Logic Corp. TinyJ embedded processor family", Elf.EM_X86_64 : "AMD x86-64 architecture", Elf.EM_PDSP : "Sony DSP Processor", Elf.EM_PDP10 : "Digital Equipment Corp. PDP-10", Elf.EM_PDP11 : "Digital Equipment Corp. PDP-11", Elf.EM_FX66 : "Siemens FX66 microcontroller", Elf.EM_ST9PLUS : "STMicroelectronics ST9+ 8/16 bit microcontroller", Elf.EM_ST7 : "STMicroelectronics ST7 8-bit microcontroller", Elf.EM_68HC16 : "Motorola MC68HC16 Microcontroller", Elf.EM_68HC11 : "Motorola MC68HC11 Microcontroller", Elf.EM_68HC08 : "Motorola MC68HC08 Microcontroller", Elf.EM_68HC05 : "Motorola MC68HC05 Microcontroller", Elf.EM_SVX : "Silicon Graphics SVx", Elf.EM_ST19 : "STMicroelectronics ST19 8-bit microcontroller", Elf.EM_VAX : "Digital VAX", Elf.EM_CRIS : "Axis Communications 32-bit embedded processor", Elf.EM_JAVELIN : "Infineon Technologies 32-bit embedded processor", Elf.EM_FIREPATH : "Element 14 64-bit DSP Processor", Elf.EM_ZSP : "LSI Logic 16-bit DSP Processor", Elf.EM_MMIX : "Donald Knuth's educational 64-bit processor", Elf.EM_HUANY : "Harvard University machine-independent object files", Elf.EM_PRISM : "SiTera Prism", Elf.EM_AVR : "Atmel AVR 8-bit microcontroller", Elf.EM_FR30 : "Fujitsu FR30", Elf.EM_D10V : "Mitsubishi D10V", Elf.EM_D30V : "Mitsubishi D30V", Elf.EM_V850 : "NEC v850", Elf.EM_M32R : "Mitsubishi M32R", Elf.EM_MN10300 : "Matsushita MN10300", Elf.EM_MN10200 : "Matsushita MN10200", Elf.EM_PJ : "picoJava", Elf.EM_OPENRISC : "OpenRISC 32-bit embedded processor", Elf.EM_ARC_COMPACT : "ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5)", Elf.EM_XTENSA : "Tensilica Xtensa Architecture", Elf.EM_VIDEOCORE : "Alphamosaic VideoCore processor", Elf.EM_TMM_GPP : "Thompson Multimedia General Purpose Processor", Elf.EM_NS32K : "National Semiconductor 32000 series", Elf.EM_TPC : "Tenor Network TPC processor", Elf.EM_SNP1K : "Trebia SNP 1000 processor", Elf.EM_ST200 : "STMicroelectronics ST200 microcontroller", Elf.EM_IP2K : "Ubicom IP2xxx microcontroller family", Elf.EM_MAX : "MAX Processor", Elf.EM_CR : "National Semiconductor CompactRISC microprocessor", Elf.EM_F2MC16 : "Fujitsu F2MC16", Elf.EM_MSP430 : "Texas Instruments embedded microcontroller msp430", Elf.EM_BLACKFIN : "Analog Devices Blackfin (DSP) processor", Elf.EM_SE_C33 : "S1C33 Family of Seiko Epson processors", Elf.EM_SEP : "Sharp embedded microprocessor", Elf.EM_ARCA : "Arca RISC Microprocessor", Elf.EM_UNICORE : "Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University", Elf.EM_EXCESS : "eXcess: 16/32/64-bit configurable embedded CPU", Elf.EM_DXP : "Icera Semiconductor Inc. Deep Execution Processor", Elf.EM_ALTERA_NIOS2 : "Altera Nios II soft-core processor", Elf.EM_CRX : "National Semiconductor CompactRISC CRX microprocessor", Elf.EM_XGATE : "Motorola XGATE embedded processor", Elf.EM_C166 : "Infineon C16x/XC16x processor", Elf.EM_M16C : "Renesas M16C series microprocessors", Elf.EM_DSPIC30F : "Microchip Technology dsPIC30F Digital Signal Controller", Elf.EM_CE : "Freescale Communication Engine RISC core", Elf.EM_M32C : "Renesas M32C series microprocessors", Elf.EM_TSK3000 : "Altium TSK3000 core", Elf.EM_RS08 : "Freescale RS08 embedded processor", Elf.EM_SHARC : "Analog Devices SHARC family of 32-bit DSP processors", Elf.EM_ECOG2 : "Cyan Technology eCOG2 microprocessor", Elf.EM_SCORE7 : "Sunplus S+core7 RISC processor", Elf.EM_DSP24 : "New Japan Radio (NJR) 24-bit DSP Processor", Elf.EM_VIDEOCORE3 : "Broadcom VideoCore III processor", Elf.EM_LATTICEMICO32 : "RISC processor for Lattice FPGA architecture", Elf.EM_SE_C17 : "Seiko Epson C17 family", Elf.EM_TI_C6000 : "The Texas Instruments TMS320C6000 DSP family", Elf.EM_TI_C2000 : "The Texas Instruments TMS320C2000 DSP family", Elf.EM_TI_C5500 : "The Texas Instruments TMS320C55x DSP family", Elf.EM_TI_ARP32 : "Texas Instruments Application Specific RISC Processor, 32bit fetch", Elf.EM_TI_PRU : "Texas Instruments Programmable Realtime Unit", Elf.EM_MMDSP_PLUS : "STMicroelectronics 64bit VLIW Data Signal Processor", Elf.EM_CYPRESS_M8C : "Cypress M8C microprocessor", Elf.EM_R32C : "Renesas R32C series microprocessors", Elf.EM_TRIMEDIA : "NXP Semiconductors TriMedia architecture family", Elf.EM_QDSP6 : "QUALCOMM DSP6 Processor", Elf.EM_8051 : "Intel 8051 and variants", Elf.EM_STXP7X : "STMicroelectronics STxP7x family of configurable and extensible RISC processors", Elf.EM_NDS32 : "Andes Technology compact code size embedded RISC processor family", Elf.EM_ECOG1 : "Cyan Technology eCOG1X family", Elf.EM_ECOG1X : "Cyan Technology eCOG1X family", Elf.EM_MAXQ30 : "Dallas Semiconductor MAXQ30 Core Micro-controllers", Elf.EM_XIMO16 : "New Japan Radio (NJR) 16-bit DSP Processor", Elf.EM_MANIK : "M2000 Reconfigurable RISC Microprocessor", Elf.EM_CRAYNV2 : "Cray Inc. NV2 vector architecture", Elf.EM_RX : "Renesas RX family", Elf.EM_METAG : "Imagination Technologies META processor architecture", Elf.EM_MCST_ELBRUS : "MCST Elbrus general purpose hardware architecture", Elf.EM_ECOG16 : "Cyan Technology eCOG16 family", Elf.EM_CR16 : "National Semiconductor CompactRISC CR16 16-bit microprocessor", Elf.EM_ETPU : "Freescale Extended Time Processing Unit", Elf.EM_SLE9X : "Infineon Technologies SLE9X core", Elf.EM_L10M : "Intel L10M", Elf.EM_K10M : "Intel K10M", 182 : "Reserved for future Intel use", Elf.EM_AARCH64 : "ARM 64-bit architecture (AARCH64)", 184 : "Reserved for future ARM use", Elf.EM_AVR32 : "Atmel Corporation 32-bit microprocessor family", Elf.EM_STM8 : "STMicroeletronics STM8 8-bit microcontroller", Elf.EM_TILE64 : "Tilera TILE64 multicore architecture family", Elf.EM_TILEPRO : "Tilera TILEPro multicore architecture family", Elf.EM_MICROBLAZE : "Xilinx MicroBlaze 32-bit RISC soft processor core", Elf.EM_CUDA : "NVIDIA CUDA architecture", Elf.EM_TILEGX : "Tilera TILE-Gx multicore architecture family", Elf.EM_CLOUDSHIELD : "CloudShield architecture family", Elf.EM_COREA_1ST : "KIPO-KAIST Core-A 1st generation processor family", Elf.EM_COREA_2ND : "KIPO-KAIST Core-A 2nd generation processor family", Elf.EM_ARCV2 : "Synopsys ARCompact V2", # codespell:ignore Elf.EM_OPEN8 : "Open8 8-bit RISC soft processor core", Elf.EM_RL78 : "Renesas RL78 family", Elf.EM_VIDEOCORE5 : "Broadcom VideoCore V processor", Elf.EM_78KOR : "Renesas 78KOR family", Elf.EM_56800EX : "Freescale 56800EX Digital Signal Controller (DSC)", Elf.EM_BA1 : "Beyond BA1 CPU architecture", Elf.EM_BA2 : "Beyond BA2 CPU architecture", Elf.EM_XCORE : "XMOS xCORE processor family", Elf.EM_MCHP_PIC : "Microchip 8-bit PIC(r) family", Elf.EM_INTELGT : "Intel Graphics Technology", Elf.EM_INTEL206 : "Reserved by Intel", Elf.EM_INTEL207 : "Reserved by Intel", Elf.EM_INTEL208 : "Reserved by Intel", Elf.EM_INTEL209 : "Reserved by Intel", Elf.EM_KM32 : "KM211 KM32 32-bit processor", Elf.EM_KMX32 : "KM211 KMX32 32-bit processor", Elf.EM_KMX16 : "KM211 KMX16 16-bit processor", Elf.EM_KMX8 : "KM211 KMX8 8-bit processor", Elf.EM_KVARC : "KM211 KVARC processor", Elf.EM_CDP : "Paneve CDP architecture family", Elf.EM_COGE : "Cognitive Smart Memory Processor", Elf.EM_COOL : "Bluechip Systems CoolEngine", Elf.EM_NORC : "Nanoradio Optimized RISC", Elf.EM_CSR_KALIMBA : "CSR Kalimba architecture family", Elf.EM_Z80 : "Zilog Z80", Elf.EM_VISIUM : "Controls and Data Services VISIUMcore processor", Elf.EM_FT32 : "FTDI Chip FT32 high performance 32-bit RISC architecture", Elf.EM_MOXIE : "Moxie processor family", Elf.EM_AMDGPU : "AMD GPU architecture", Elf.EM_RISCV : "RISC-V", Elf.EM_LANAI : "Lanai 32-bit processor", Elf.EM_CEVA : "CEVA Processor Architecture Family", Elf.EM_CEVA_X2 : "CEVA X2 Processor Family", Elf.EM_BPF : "Linux BPF - in-kernel virtual machine", Elf.EM_GRAPHCORE_IPU : "Graphcore Intelligent Processing Unit", Elf.EM_IMG1 : "Imagination Technologies", Elf.EM_NFP : "Netronome Flow Processor", Elf.EM_VE : "NEC Vector Engine", Elf.EM_CSKY : "C-SKY processor family", Elf.EM_ARC_COMPACT3_64 : "Synopsys ARCv2.3 64-bit", # codespell:ignore Elf.EM_MCS6502 : "MOS Technology MCS 6502 processor", Elf.EM_ARC_COMPACT3 : "Synopsys ARCv2.3 32-bit", # codespell:ignore Elf.EM_KVX : "Kalray VLIW core of the MPPA processor family", Elf.EM_65816 : "WDC 65816/65C816", Elf.EM_LOONGARCH : "LoongArch", Elf.EM_KF32 : "ChipON KungFu32", Elf.EM_U16_U8CORE : "LAPIS nX-U16/U8", Elf.EM_TACHYUM : "Tachyum", Elf.EM_56800EF : "NXP 56800EF Digital Signal Controller (DSC)", Elf.EM_AVR_UNOFFICIAL : "AVR (unofficial)", Elf.EM_MSP430_UNOFFICIAL : "MSP430 (unofficial)", Elf.EM_EPIPHANY_UNOFFICIAL : "Adapteva Epiphany (unofficial)", Elf.EM_AVR32_UNOFFICIAL : "Atmel AVR32 (unofficial)", Elf.EM_MT_UNOFFICIAL : "Morpho MT (unofficial)", Elf.EM_FR30_UNOFFICIAL : "FR30 (unofficial)", Elf.EM_OPENRISC_OLD : "OpenRISC (obsolete)", Elf.EM_WEBASSEMBLY : "Web Assembly binaries (unofficial)", Elf.EM_C166_UNOFFICIAL : "Infineon C166 (unofficial)", Elf.EM_S12Z : "Freescale S12Z", Elf.EM_FRV_UNOFFICIAL : "Cygnus FR-V (unofficial)", Elf.EM_DLX_UNOFFICIAL : "DLX (unofficial)", Elf.EM_D10V_UNOFFICIAL : "Cygnus D10V (unofficial)", Elf.EM_D30V_UNOFFICIAL : "Cygnus D30V (unofficial)", Elf.EM_IP2K_UNOFFICIAL : "Ubicom IP2xxx (unofficial)", Elf.EM_OPENRISC_OLD2 : "OpenRISC (obsolete)", Elf.EM_PPC_UNOFFICIAL : "Cygnus PowerPC (unofficial)", Elf.EM_ALPHA_UNOFFICIAL : "Digital Alpha (unofficial)", Elf.EM_M32R_UNOFFICIAL : "Cygnus M32R (unofficial)", Elf.EM_V850_UNOFFICIAL : "Cygnus V859 (unofficial)", Elf.EM_S390_OLD : "IBM S/390 (obsolete)", Elf.EM_XTENSA_UNOFFICIAL : "Old Xtensa (unofficial)", Elf.EM_XSTORMY_UNOFFICIAL : "xstormy16 (unofficial)", Elf.EM_MICROBLAZE_UNOFFICIAL : "Old MicroBlaze (unofficial)", Elf.EM_MN10300_UNOFFICIAL : "Cygnus MN10300 (unofficial)", Elf.EM_MN10200_UNOFFICIAL : "Cygnus MN10200 (unofficial)", Elf.EM_MEP_UNOFFICIAL : "Toshiba MeP (unofficial)", Elf.EM_M32C_UNOFFICIAL : "Renesas M32C (unofficial)", Elf.EM_IQ2000_UNOFFICIAL : "Vitesse IQ2000 (unofficial)", Elf.EM_NIOS_UNOFFICIAL : "NIOS (unofficial)", Elf.EM_MOXIE_UNOFFICIAL : "Moxie (unofficial)", } versions = { Elf.EV_NONE : "Invalid version", Elf.EV_CURRENT : "Current version", } ptype = { Elf.Phdr.PT_NULL : "NULL", Elf.Phdr.PT_LOAD : "LOAD", Elf.Phdr.PT_DYNAMIC : "DYNAMIC", Elf.Phdr.PT_INTERP : "INTERP", Elf.Phdr.PT_NOTE : "NOTE", Elf.Phdr.PT_SHLIB : "SHLIB", Elf.Phdr.PT_PHDR : "PHDR", Elf.Phdr.PT_TLS : "TLS", Elf.Phdr.PT_GNU_EH_FRAME : "GNU_EH_FLAME", Elf.Phdr.PT_GNU_STACK : "GNU_STACK", Elf.Phdr.PT_GNU_RELRO : "GNU_RELRO", Elf.Phdr.PT_GNU_PROPERTY : "GNU_PROPERTY", Elf.Phdr.PT_GNU_SFRAME : "SFRAME", Elf.Phdr.PT_SUNWBSS : "SUNWBSS", Elf.Phdr.PT_SUNWSTACK : "SUNWSTACK", } pflags = { 0 : "---", Elf.Phdr.PF_X : "--X", Elf.Phdr.PF_W : "-W-", Elf.Phdr.PF_R : "R--", Elf.Phdr.PF_W | Elf.Phdr.PF_X : "-WX", Elf.Phdr.PF_R | Elf.Phdr.PF_X : "R-X", Elf.Phdr.PF_R | Elf.Phdr.PF_W : "RW-", Elf.Phdr.PF_R | Elf.Phdr.PF_W | Elf.Phdr.PF_X : "RWX", } stype = { Elf.Shdr.SHT_NULL : "NULL", Elf.Shdr.SHT_PROGBITS : "PROGBITS", Elf.Shdr.SHT_SYMTAB : "SYMTAB", Elf.Shdr.SHT_STRTAB : "STRTAB", Elf.Shdr.SHT_RELA : "RELA", Elf.Shdr.SHT_HASH : "HASH", Elf.Shdr.SHT_DYNAMIC : "DYNAMIC", Elf.Shdr.SHT_NOTE : "NOTE", Elf.Shdr.SHT_NOBITS : "NOBITS", Elf.Shdr.SHT_REL : "REL", Elf.Shdr.SHT_SHLIB : "SHLIB", Elf.Shdr.SHT_DYNSYM : "DYNSYM", Elf.Shdr.SHT_INIT_ARRAY : "INIT_ARRAY", Elf.Shdr.SHT_FINI_ARRAY : "FINI_ARRAY", Elf.Shdr.SHT_PREINIT_ARRAY : "PREINIT_ARRAY", Elf.Shdr.SHT_GROUP : "GROUP", Elf.Shdr.SHT_SYMTAB_SHNDX : "SYMTAB_SHNDX", Elf.Shdr.SHT_RELR : "RELR", Elf.Shdr.SHT_ANDROID_REL : "ANDROID_REL", Elf.Shdr.SHT_ANDROID_RELA : "ANDROID_RELA", Elf.Shdr.SHT_GNU_INCREMENTAL_INPUTS : "GNU_INCREMENTAL_INPUTS", Elf.Shdr.SHT_LLVM_ODRTAB : "LLVM_ODRTAB", Elf.Shdr.SHT_LLVM_LINKER_OPTIONS : "LLVM_LINKER_OPTIONS", Elf.Shdr.SHT_LLVM_CALL_GRAPH_PROFILE : "LLVM_CALL_GRAPH_PROFILE", Elf.Shdr.SHT_LLVM_ADDRSIG : "LLVM_ADDRSIG", Elf.Shdr.SHT_LLVM_DEPENDENT_LIBRARIES : "LLVM_DEPENDENT_LIBRARIES", Elf.Shdr.SHT_LLVM_SYMPART : "LLVM_SYMPART", Elf.Shdr.SHT_LLVM_PART_EHDR : "LLVM_PART_EHDR", Elf.Shdr.SHT_LLVM_PART_PHDR : "LLVM_PART_PHDR", Elf.Shdr.SHT_LLVM_BB_ADDR_MAP_V0 : "LLVM_BB_ADDR_MAP_V0", Elf.Shdr.SHT_LLVM_CALL_GRAPH_PROFILE : "LLVM_CALL_GRAPH_PROFILE", Elf.Shdr.SHT_LLVM_BB_ADDR_MAP : "LLVM_BB_ADDR_MAP", Elf.Shdr.SHT_LLVM_OFFLOADING : "LLVM_OFFLOADING", Elf.Shdr.SHT_LLVM_LTO : "LLVM_LTO", Elf.Shdr.SHT_ANDROID_RELR : "ANDROID_RELR", Elf.Shdr.SHT_GNU_ATTRIBUTES : "GNU_ATTRIBUTES", Elf.Shdr.SHT_GNU_HASH : "GNU_HASH", Elf.Shdr.SHT_GNU_LIBLIST : "GNU_LIBLIST", Elf.Shdr.SHT_CHECKSUM : "CHECKSUM", Elf.Shdr.SHT_SUNW_move : "SUNW_move", Elf.Shdr.SHT_SUNW_COMDAT : "SUNW_COMDAT", Elf.Shdr.SHT_SUNW_syminfo : "SUNW_syminfo", Elf.Shdr.SHT_GNU_verdef : "GNU_verdef", Elf.Shdr.SHT_GNU_verneed : "GNU_verneed", Elf.Shdr.SHT_GNU_versym : "GNU_versym", } def elf_info(self, elf, orig_filepath=None): if elf.filename: if orig_filepath: filename = "{:s} (remote: {:s})".format(elf.filename, orig_filepath) else: filename = elf.filename elif elf.addr is not None: filename = "{:#x}".format(elf.addr) magic_hex = " ".join(slicer(struct.pack(">I", elf.e_magic).hex(), 2)) if Endian.is_big_endian(): magic_str = repr(p32(elf.e_magic).decode()) else: magic_str = repr(p32(elf.e_magic).decode()[::-1]) data = [ ("Magic", "{:s} ({:s})".format(magic_hex, magic_str)), ("Class", "{:#x} - {:s}".format(elf.e_class, self.classes[elf.e_class])), ("Endianness", "{:#x} - {:s}".format(elf.e_endianness, self.endianness[elf.e_endianness])), ("ELF Version", "{:#x} - {:s}".format(elf.e_eiversion, self.versions[elf.e_eiversion])), ("OS ABI", "{:#x} - {:s}".format(elf.e_osabi, self.osabis[elf.e_osabi])), ("ABI Version", "{:#x}".format(elf.e_abiversion)), ("Type", "{:#x} - {:s}".format(elf.e_type, self.types[elf.e_type])), ("Machine", "{:#x} - {:s}".format(elf.e_machine, self.machines.get(elf.e_machine, "Unknown"))), ("Version", "{:#x} - {:s}".format(elf.e_version, self.versions[elf.e_version])), ("Entry point", "{:s}".format(AddressUtil.format_address(elf.e_entry))), ("Program Header Table", "{:s}".format(AddressUtil.format_address(elf.e_phoff))), ("Program Header Entry Size", "{0:d} ({0:#x})".format(elf.e_phentsize)), ("Number of Program Headers", "{:d}".format(elf.e_phnum)), ("Section Header Table", "{:s}".format(AddressUtil.format_address(elf.e_shoff))), ("Section Header Entry Size", "{0:d} ({0:#x})".format(elf.e_shentsize)), ("Number of Section Headers", "{:d}".format(elf.e_shnum)), ("ELF Header Size", "{0:d} ({0:#x})".format(elf.e_ehsize)), ("Section Header String Table Index", "{0:d} ({0:#x})".format(elf.e_shstrndx)), ("Processor Specific Flags", "{:#x}".format(elf.e_flags)), ] self.out.append(titlify("ELF Header - {:s}".format(filename))) for title, content in data: self.out.append("{:<34s}: {}".format(title, content)) self.out.append(titlify("Program Header - {:s}".format(filename))) self.phdr_info(elf) self.out.append(titlify("Section Header - {:s}".format(filename))) self.shdr_info(elf) return def phdr_info(self, elf): name_width = max([len(self.ptype.get(p.p_type, "UNKNOWN")) for p in elf.phdrs]) fmt = "[{:>2s}] {:{:d}s} {:>12s} {:>12s} {:>12s} {:>12s} {:>12s} {:5s} {:>8s}" legend = [ "#", "Type", name_width, "Offset", "Virtaddr", "Physaddr", "FileSiz", "MemSiz", "Flags", "Align", ] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for i, p in enumerate(elf.phdrs): p_type = self.ptype.get(p.p_type, "UNKNOWN") p_flags = self.pflags.get(p.p_flags, "???") fmt = "[{:2d}] {:{:d}s} {:#12x} {:#12x} {:#12x} {:#12x} {:#12x} {:5s} {:#8x}" args = [ i, p_type, name_width, p.p_offset, p.p_vaddr, p.p_paddr, p.p_filesz, p.p_memsz, p_flags, p.p_align, ] self.out.append(fmt.format(*args)) return def shdr_info(self, elf): if not elf.shdrs: self.out.append("Not loaded") return name_width = max([len(s.sh_name) for s in elf.shdrs]) fmt = "[{:>2s}] {:{:d}s} {:>15s} {:>12s} {:>12s} {:>12s} {:>12s} {:>5s} {:>5s} {:>5s} {:>8s}" legend = ["#", "Name", name_width, "Type", "Address", "Offset", "Size", "EntSiz", "Flags", "Link", "Info", "Align"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for i, s in enumerate(elf.shdrs): sh_type = self.stype.get(s.sh_type, "UNKNOWN") sh_flags = "" if s.sh_flags & Elf.Shdr.SHF_WRITE: sh_flags += "W" if s.sh_flags & Elf.Shdr.SHF_ALLOC: sh_flags += "A" if s.sh_flags & Elf.Shdr.SHF_EXECINSTR: sh_flags += "X" if s.sh_flags & Elf.Shdr.SHF_MERGE: sh_flags += "M" if s.sh_flags & Elf.Shdr.SHF_STRINGS: sh_flags += "S" if s.sh_flags & Elf.Shdr.SHF_INFO_LINK: sh_flags += "I" if s.sh_flags & Elf.Shdr.SHF_LINK_ORDER: sh_flags += "L" if s.sh_flags & Elf.Shdr.SHF_OS_NONCONFORMING: sh_flags += "O" if s.sh_flags & Elf.Shdr.SHF_GROUP: sh_flags += "G" if s.sh_flags & Elf.Shdr.SHF_TLS: sh_flags += "T" if s.sh_flags & Elf.Shdr.SHF_EXCLUDE: sh_flags += "E" if s.sh_flags & Elf.Shdr.SHF_COMPRESSED: sh_flags += "C" fmt = "[{:2d}] {:{:d}s} {:>15s} {:#12x} {:#12x} {:#12x} {:#12x} {:5s} {:#5x} {:#5x} {:#8x}" args = [ i, s.sh_name, name_width, sh_type, s.sh_addr, s.sh_offset, s.sh_size, s.sh_entsize, sh_flags, s.sh_link, s.sh_info, s.sh_addralign, ] self.out.append(fmt.format(*args)) if self.args.verbose: if s.sh_size > 0x1000: # heuristic value self.out.append("Skip because too large ({:#x} > 0x1000)".format(s.sh_size)) else: fd = open(elf.filename, "rb") fd.seek(s.sh_offset, 0) section_data = fd.read(s.sh_size) self.out.append(hexdump(section_data, show_symbol=False, base=s.sh_offset)) return @parse_args def do_invoke(self, args): local_filepath = None remote_filepath = None tmp_filepath = None self.out = [] # memory parse pattern if args.address is not None: try: elf = Elf.get_elf(args.address) except gdb.MemoryError: err("Memory read error") return if elf is None or not elf.is_valid(): err("Failed to parse ELF") else: self.elf_info(elf) gef_print("\n".join(self.out), less=not args.no_pager) return # file parse pattern if args.remote: if not is_remote_debug(): err("-r option is allowed only remote debug") return if is_qemu_system(): err("-r option is unsupported under qemu-system") return if args.file: remote_filepath = args.file # if specified, assume it is remote elif gdb.current_progspace().filename: f = gdb.current_progspace().filename if f.startswith("target:"): # gdbserver f = f[7:] remote_filepath = f elif Pid.get_pid(remote=True): remote_filepath = "/proc/{:d}/exe".format(Pid.get_pid(remote=True)) else: err("File name could not be determined") return data = Path.read_remote_file(remote_filepath, as_byte=True) # qemu-user is failed here, it is ok if not data: err("Failed to read remote filepath") return tmp_fd, tmp_filepath = GefUtil.mkstemp(prefix="elf-info", suffix=".elf") os.fdopen(tmp_fd, "wb").write(data) local_filepath = tmp_filepath del data elif args.file: local_filepath = args.file elif args.file is None: if is_qemu_system(): err("Argument-less calls are unsupported under qemu-system") return local_filepath = Path.get_filepath() if local_filepath is None: err("File name could not be determined") return # readelf pattern if args.use_readelf: try: readelf = GefUtil.which(Config.get_gef_setting("gef.readelf_command")) except FileNotFoundError: err("Could not find readelf") return try: less = GefUtil.which("less") except FileNotFoundError: less = False if args.no_pager or not less: os.system("LANG=C {!r} -a --wide {!r}".format(readelf, local_filepath)) else: os.system("LANG=C {!r} -a --wide {!r} | {!r}".format(readelf, local_filepath, less)) if tmp_filepath and os.path.exists(tmp_filepath): os.unlink(tmp_filepath) return # self parse pattern elf = Elf.get_elf(local_filepath) if elf is None or not elf.is_valid(): err("Failed to parse ELF") else: data = open(local_filepath, "rb").read() self.out.append("size: {:d} bytes, sha1: {:s}".format(len(data), hashlib.sha1(data).hexdigest())) self.elf_info(elf, remote_filepath) gef_print("\n".join(self.out), less=not args.no_pager) if tmp_filepath and os.path.exists(tmp_filepath): os.unlink(tmp_filepath) return @register_command class ChecksecCommand(GenericCommand): """Check the security properties of the current executable or passed as argument.""" _cmdline_ = "checksec" _category_ = "02-f. Process Information - Security" _aliases_ = ["cs"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--remote", action="store_true", help="parse remote binary if download feature is available.") parser.add_argument("-f", "--file", help="the file path to parse.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -f /bin/ls", "{0:s} -r", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_FILENAME) return def check_CET_SHSTK(self, sec): # Intel CET SHSTK flags via Ehdr if "CET SHSTK flag" not in sec: # ELF is not x86_64 return if sec["CET SHSTK flag"]: gef_print("{:<40s}: {:s}".format("CET SHSTK feature flag (via Ehdr)", Color.colorify("Found", "bold green"))) else: gef_print("{:<40s}: {:s}".format("CET SHSTK feature flag (via Ehdr)", Color.colorify("Not found", "bold red"))) # gdb mode check if not is_x86(): return if not is_alive(): return if is_rr(): return # Intel CET SHSTK status via arch_prctl if is_pin(): # Intel SDE implements userspace CET SHSTK but old interface r = Checksec.get_cet_status_old_interface() if r is None: msg = Color.colorify("Disabled", "bold red") + " (kernel does not support; Intel SDE has no `-cet` option)" gef_print("{:<40s}: {:s}".format("CET IBT status (via old arch_prctl IF)", msg)) else: if r & 0b10: msg = Color.colorify("Enabled", "bold green") + " (kernel supports; Intel SDE has `-cet` option)" gef_print("{:<40s}: {:s}".format("CET SHSTK status (via old arch_prctl IF)", msg)) else: msg = Color.colorify("Disabled", "bold red") + " (kernel supports but disabled; Intel SDE has `-cet` option)" gef_print("{:<40s}: {:s}".format("CET SHSTK status (via old arch_prctl IF)", msg)) else: # kernel 6.6 or after supports userspace CET SHSTK r = Checksec.get_cet_status_new_interface() if r is None: msg = Color.colorify("Unimplemented", "bold red") + " (kernel does not support; kernel supports it from 6.6)" gef_print("{:<40s}: {:s}".format("CET SHSTK status (via new arch_prctl IF)", msg)) else: if r & 0b01: msg = Color.colorify("Enabled", "bold green") + " (kernel supports and enabled)" gef_print("{:<40s}: {:s}".format("CET SHSTK status (via new arch_prctl IF)", msg)) else: msg = Color.colorify("Disabled", "bold red") + " (kernel supports but disabled)" gef_print("{:<40s}: {:s}".format("CET SHSTK status (via new arch_prctl IF)", msg)) # Intel CET SHSTK status via procfs r = Checksec.get_cet_status_via_procfs() if r is None: msg = Color.grayify("Unknown") + " (failed to open /proc/PID/status)" gef_print("{:<40s}: {:s}".format("CET SHSTK status (via procfs)", msg)) gef_print("{:<40s}: {:s}".format("CET SHSTK Lock status (via procfs)", msg)) elif r is False: msg = Color.colorify("Unimplemented", "bold red") + " (kernel does not support; kernel supports it from 6.6)" gef_print("{:<40s}: {:s}".format("CET SHSTK status (via procfs)", msg)) gef_print("{:<40s}: {:s}".format("CET SHSTK Lock status (via procfs)", msg)) else: if r["shstk"]: gef_print("{:<40s}: {:s}".format("CET SHSTK status (via procfs)", Color.colorify("Enabled", "bold green"))) else: msg = Color.colorify("Disabled", "bold red") + " (kernel supports but disabled)" gef_print("{:<40s}: {:s}".format("CET SHSTK status (via procfs)", msg)) if r["shstk lock"]: gef_print("{:<40s}: {:s}".format("CET SHSTK Lock status (via procfs)", Color.colorify("Enabled", "bold green"))) else: msg = Color.colorify("Disabled", "bold red") + " (kernel supports but no locked)" gef_print("{:<40s}: {:s}".format("CET SHSTK Lock status (via procfs)", msg)) return def check_CET_IBT(self, sec): # Intel CET IBT flags via Ehdr if "CET IBT flag" not in sec: # ELF is not x86_64 return if sec["CET IBT flag"]: gef_print("{:<40s}: {:s}".format("CET IBT feature flag (via Ehdr)", Color.colorify("Found", "bold green"))) else: gef_print("{:<40s}: {:s}".format("CET IBT feature flag (via Ehdr)", Color.colorify("Not found", "bold red"))) # gdb mode check if not is_x86(): return if not is_alive(): return if is_rr(): return # Intel CET IBT status via arch_prctl if is_pin(): # Intel SDE implements userspace CET IBT but old interface r = Checksec.get_cet_status_old_interface() if r is None: msg = Color.colorify("Disabled", "bold red") + " (kernel does not support; Intel SDE has no `-cet` option)" gef_print("{:<40s}: {:s}".format("CET IBT status (via old arch_prctl IF)", msg)) else: if r & 0b01: msg = Color.colorify("Enabled", "bold green") + " (kernel supports; Intel SDE has `-cet` option)" gef_print("{:<40s}: {:s}".format("CET IBT status (via old arch_prctl IF)", msg)) else: msg = Color.colorify("Disabled", "bold red") + " (kernel supports but disabled; Intel SDE has `-cet` option)" gef_print("{:<40s}: {:s}".format("CET IBT status (via old arch_prctl IF)", msg)) else: # kernel does not support userspace CET IBT yet, only supports kernel space CET IBT. # https://lwn.net/Articles/889475/ (2022/3/31) msg = Color.colorify("Unimplemented", "bold red") + " (at least kernel 6.6 does not support userspace IBT)" gef_print("{:<40s}: {:s}".format("CET IBT status", msg)) return def check_PAC(self, sec): # PAC opcode if "PAC" not in sec: # ELF is not ARM64 return if sec["PAC"] is None: gef_print("{:<40s}: {:s}".format("PAC opcode", Color.grayify("Unknown"))) elif sec["PAC"]: gef_print("{:<40s}: {:s}".format("PAC opcode", Color.colorify("Found", "bold green"))) else: gef_print("{:<40s}: {:s}".format("PAC opcode", Color.colorify("Not found", "bold red"))) # gdb mode check if not is_arm64(): return if not is_alive(): return if is_rr(): return # PAC status r = Checksec.get_pac_status() if r is None: msg = Color.colorify("Disabled", "bold red") + " (kernel does not support PAC)" gef_print("{:<40s}: {:s}".format("PAC", msg)) elif r < 0: msg = Color.grayify("Unknown") + " (kernel supports PAC but does not support PR_PAC_GET_ENABLED_KEYS prctl option)" gef_print("{:<40s}: {:s}".format("PAC", msg)) elif r == 0: msg = Color.colorify("Disabled", "bold red") + " (kernel supports PAC but no keys are enabled)" gef_print("{:<40s}: {:s}".format("PAC", msg)) elif r > 0: keys = [] if r & 0b00001: keys.append("APIAKEY") if r & 0b00010: keys.append("APIBKEY") if r & 0b00100: keys.append("APDAKEY") if r & 0b01000: keys.append("APDBKEY") if r & 0b10000: keys.append("APGAKEY") keys = ", ".join(keys) msg = Color.colorify("Enabled", "bold green") + " (enabled keys: {:s})".format(keys) gef_print("{:<40s}: {:s}".format("PAC", msg)) return def check_MTE(self, sec): # gdb mode check if not is_arm64(): return if not is_alive(): return if is_rr(): return # MTE status r = Checksec.get_mte_status() if r is None: msg = Color.colorify("Disabled", "bold red") + " (kernel does not support MTE)" gef_print("{:<40s}: {:s}".format("MTE", msg)) elif r < 0: msg = Color.grayify("Unknown") + " (kernel supports MTE but does not support PR_SET_TAGGED_ADDR_CTRL)" gef_print("{:<40s}: {:s}".format("MTE", msg)) elif (r & 0b1) == 0: msg = Color.colorify("Disabled", "bold red") + " (kernel supports MTE but disabled)" gef_print("{:<40s}: {:s}".format("MTE", msg)) elif (r & 0b1) == 1 and (r & 0b110) == 0: msg = Color.colorify("Disabled", "bold red") + " (MTE is enabled, but fault is ignored)" gef_print("{:<40s}: {:s}".format("MTE", msg)) else: keys = [] if r & 0b010: keys.append("PR_MTE_TCF_SYNC") if r & 0b100: keys.append("PR_MTE_TCF_ASYNC") keys = ", ".join(keys) msg = Color.colorify("Enabled", "bold green") + " (MTE is enabled as: {:s})".format(keys) gef_print("{:<40s}: {:s}".format("MTE", msg)) return def check_system_ASLR(self): if is_remote_debug(): msg = Color.grayify("Unknown") gef_print("{:<40s}: {:s} (remote process)".format("System-ASLR", msg)) else: try: system_aslr = int(open("/proc/sys/kernel/randomize_va_space").read()) if system_aslr == 0: msg = Color.colorify("Disabled", "bold red") gef_print("{:<40s}: {:s} (randomize_va_space: 0)".format("System ASLR", msg)) elif system_aslr == 1: msg = Color.colorify("Partially Enabled", "bold yellow") gef_print("{:<40s}: {:s} (randomize_va_space: 1)".format("System ASLR", msg)) elif system_aslr == 2: msg = Color.colorify("Enabled", "bold green") gef_print("{:<40s}: {:s} (randomize_va_space: 2)".format("System ASLR", msg)) except (FileNotFoundError, OSError): msg = Color.grayify("Unknown") gef_print("{:<40s}: {:s} (randomize_va_space: error)".format("System-ASLR", msg)) return def check_gdb_ASLR(self): if is_attach() or is_remote_debug(): msg = Color.grayify("Ignored") gef_print("{:<40s}: {:s} (attached or remote process)".format("GDB ASLR setting", msg)) else: ret = gdb.parameter("disable-randomization") if ret is True: msg = Color.colorify("Disabled", "bold red") gef_print("{:<40s}: {:s} (disable-randomization: on)".format("GDB ASLR setting", msg)) elif ret is False: msg = Color.colorify("Enabled", "bold green") gef_print("{:<40s}: {:s} (disable-randomization: off)".format("GDB ASLR setting", msg)) else: msg = Color.grayify("Unknown") gef_print("{:<40s}: {:s}".format("GDB ASLR setting", msg)) return def get_colored_msg(self, val): if val is True: msg = Color.greenify(Color.boldify("Enabled")) elif val is False: msg = Color.redify(Color.boldify("Disabled")) elif val is None: msg = Color.grayify("Unknown") return msg def print_security_properties(self, filename): elf = Elf.get_elf(filename) if elf is None or not elf.is_valid(): err("checksec is failed") return sec = elf.checksec() if sec is False: err("checksec is failed") return gef_print(titlify("Basic information")) # Canary msg = self.get_colored_msg(sec["Canary"]) if sec["Canary"] is True and is_alive(): res = CanaryCommand.gef_read_canary() if not res: msg += " (Could not get the canary value)" else: msg += " (value: {:#x})".format(res[0]) gef_print("{:<40s}: {:s}".format("Canary", msg)) # NX gef_print("{:<40s}: {:s}".format("NX", self.get_colored_msg(sec["NX"]))) # PIE if sec["PIE"]: gef_print("{:<40s}: {:s}".format("PIE", self.get_colored_msg(sec["PIE"]))) else: vaddr = min([p.p_vaddr for p in elf.phdrs if p.p_type == Elf.Phdr.PT_LOAD]) gef_print("{:<40s}: {:s} ({:#x})".format("PIE", self.get_colored_msg(sec["PIE"]), vaddr)) # RELRO if sec["Full RELRO"]: # -Wl,-z,relro -Wl,-z,now gef_print("{:<40s}: {:s}".format("RELRO", Color.colorify("Full RELRO", "bold green"))) elif sec["Partial RELRO"]: # -Wl,-z,relro -Wl,-z,lazy gef_print("{:<40s}: {:s}".format("RELRO", Color.colorify("Partial RELRO", "bold yellow"))) else: # -Wl,-z,norelro gef_print("{:<40s}: {:s}".format("RELRO", Color.colorify("No RELRO", "bold red"))) # Fortify if sec["Fortify"]: gef_print("{:<40s}: {:s}".format("Fortify", Color.colorify("Found", "bold green"))) else: gef_print("{:<40s}: {:s}".format("Fortify", Color.colorify("Not found", "bold red"))) gef_print(titlify("Additional information")) # Static if sec["Static"]: if sec["PIE"]: gef_print("{:<40s}: {:s}".format("Static/Dynamic", "Static-PIE")) else: gef_print("{:<40s}: {:s}".format("Static/Dynamic", "Static")) else: gef_print("{:<40s}: {:s}".format("Static/Dynamic", "Dynamic")) # Symbol if sec["Symbol"]: gef_print("{:<40s}: {:s}".format("Symbol", Color.colorify("Found", "bold red"))) else: gef_print("{:<40s}: {:s}".format("Symbol", Color.colorify("Stripped", "bold green"))) # Debug information if sec["Debuginfo"]: gef_print("{:<40s}: {:s}".format("Debuginfo", Color.colorify("Found", "bold red"))) else: gef_print("{:<40s}: {:s}".format("Debuginfo", Color.colorify("Stripped", "bold green"))) # Intel CET self.check_CET_SHSTK(sec) self.check_CET_IBT(sec) # ARM64 PAC/MTE self.check_PAC(sec) self.check_MTE(sec) # RPATH if sec["RPATH"]: gef_print("{:<40s}: {:s}".format("RPATH", Color.colorify("Found", "bold red"))) # RUNPATH if sec["RUNPATH"]: gef_print("{:<40s}: {:s}".format("RUNPATH", Color.colorify("Found", "bold red"))) # Clang CFI if sec["Clang CFI"]: gef_print("{:<40s}: {:s}".format("Clang CFI", self.get_colored_msg(sec["Clang CFI"]))) # Clang SafeStack if sec["Clang SafeStack"]: gef_print("{:<40s}: {:s}".format("Clang SafeStack", self.get_colored_msg(sec["Clang SafeStack"]))) # ASLR self.check_system_ASLR() self.check_gdb_ASLR() return @parse_args @exclude_specific_gdb_mode(mode=("wine", "kgdb")) @require_arch_set def do_invoke(self, args): if is_qemu_system() or is_vmware(): info("Redirect to kchecksec") gdb.execute("kchecksec") return local_filepath = None remote_filepath = None tmp_filepath = None if args.remote: if not is_remote_debug(): err("-r option is allowed only remote debug") return if args.file: remote_filepath = args.file # if specified, assume it is remote elif gdb.current_progspace().filename: f = gdb.current_progspace().filename if f.startswith("target:"): # gdbserver f = f[7:] remote_filepath = f elif Pid.get_pid(remote=True): remote_filepath = "/proc/{:d}/exe".format(Pid.get_pid(remote=True)) else: err("File name could not be determined") return data = Path.read_remote_file(remote_filepath, as_byte=True) # qemu-user is failed here, it is ok if not data: err("Failed to read remote filepath") return tmp_fd, tmp_filepath = GefUtil.mkstemp(prefix="checksec", suffix=".elf") os.fdopen(tmp_fd, "wb").write(data) local_filepath = tmp_filepath del data elif args.file: local_filepath = args.file elif args.file is None: if is_qemu_system(): err("Argument-less calls are unsupported under qemu-system") return local_filepath = Path.get_filepath() if local_filepath is None: err("File name could not be determined") return self.print_security_properties(local_filepath) if tmp_filepath and os.path.exists(tmp_filepath): os.unlink(tmp_filepath) return @register_command class KernelChecksecCommand(GenericCommand): """Check the security properties of the current kernel.""" _cmdline_ = "kchecksec" _category_ = "06-c. Qemu-system/KGDB Cooperation - Linux Basic" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def check_basic_information(self): kcmdline = Kernel.kernel_cmdline() if kcmdline is None or kcmdline.cmdline is None: gef_print("{:<40s}: {:s}".format("Kernel cmdline", "Not found")) else: gef_print("{:<40s}: {:s}".format("Kernel cmdline", kcmdline.cmdline.strip())) text_base = Kernel.get_kernel_base() if text_base is None: gef_print("{:<40s}: {:s}".format("Kernel base (heuristic)", "Not found")) else: gef_print("{:<40s}: {:#x}".format("Kernel base (heuristic)", text_base)) stext = Symbol.get_ksymaddr("_stext") if stext is None: gef_print("{:<40s}: {:s}".format("Kernel base (_stext from kallsyms)", "Not found")) else: gef_print("{:<40s}: {:#x}".format("Kernel base (_stext from kallsyms)", stext)) return def x86_specific(self): if not is_x86(): return cr0 = get_register("cr0", use_monitor=True) cr4 = get_register("cr4", use_monitor=True) # WP if (cr0 >> 16) & 1: gef_print("{:<40s}: {:s}".format("Write Protection (CR0 bit 16)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s}".format("Write Protection (CR0 bit 16)", Color.colorify("Disabled", "bold red"))) # PAE if (cr4 >> 5) & 1: gef_print("{:<40s}: {:s} (NX is supported)".format("PAE (CR4 bit 5)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s} (NX is unsupported)".format("PAE (CR4 bit 5)", Color.colorify("Disabled", "bold red"))) # SMEP if (cr4 >> 20) & 1: gef_print("{:<40s}: {:s}".format("SMEP (CR4 bit 20)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s}".format("SMEP (CR4 bit 20)", Color.colorify("Disabled", "bold red"))) # SMAP if (cr4 >> 21) & 1: gef_print("{:<40s}: {:s}".format("SMAP (CR4 bit 21)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s}".format("SMAP (CR4 bit 21)", Color.colorify("Disabled", "bold red"))) # CET if (cr4 >> 23) & 1: gef_print("{:<40s}: {:s}".format("CET (CR4 bit 23)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s}".format("CET (CR4 bit 23)", Color.colorify("Disabled", "bold red"))) # CET MSR if (cr4 >> 23) & 1: if is_kvm_enabled(): additional = "for more precisely, use `msr MSR_IA32_S_CET` without --enable-kvm" gef_print("{:<40s}: {:s} ({:s})".format("CET SHSTK (MSR_IA32_S_CET bit 0)", Color.grayify("Unknown"), additional)) gef_print("{:<40s}: {:s} ({:s})".format("CET IBT (MSR_IA32_S_CET bit 2)", Color.grayify("Unknown"), additional)) else: ret = gdb.execute("msr --quiet MSR_IA32_S_CET", to_string=True) MSR_IA32_S_CET = int(ret, 16) if MSR_IA32_S_CET & 1: gef_print("{:<40s}: {:s}".format("CET SHSTK (MSR_IA32_S_CET bit 0)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s}".format("CET SHSTK (MSR_IA32_S_CET bit 0)", Color.colorify("Disabled", "bold red"))) if (MSR_IA32_S_CET >> 2) & 1: gef_print("{:<40s}: {:s}".format("CET IBT (MSR_IA32_S_CET bit 2)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s}".format("CET IBT (MSR_IA32_S_CET bit 2)", Color.colorify("Disabled", "bold red"))) return def arm32_specific(self): if not is_arm32(): return # PXN ID_MMFR0 = get_register("$ID_MMFR0") ID_MMFR0_S = get_register("$ID_MMFR0_S") if ID_MMFR0 is not None and (ID_MMFR0 >> 2) & 1: gef_print("{:<40s}: {:s}".format("PXN (ID_MMFR0 bit 2)", Color.colorify("Enabled", "bold green"))) elif ID_MMFR0_S is not None and (ID_MMFR0_S >> 2) & 1: gef_print("{:<40s}: {:s}".format("PXN (ID_MMFR0 bit 2)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s}".format("PXN (ID_MMFR0 bit 2)", Color.colorify("Disabled", "bold red"))) # PAN gef_print("{:<40s}: {:s} (all ARMv7 is unsupported)".format("PAN", Color.colorify("Disabled", "bold red"))) return def arm64_specific(self): if not is_arm64(): return # PXN gef_print("{:<40s}: {:s} (all ARMv8~ is supported)".format("PXN", Color.colorify("Enabled", "bold green"))) # PAN ID_AA64MMFR1_EL1 = get_register("$ID_AA64MMFR1_EL1", use_mbed_exec=True) if ID_AA64MMFR1_EL1 is not None and ((ID_AA64MMFR1_EL1 >> 20) & 0b1111) != 0b0000: gef_print("{:<40s}: {:s}".format("PAN (ID_AA64MMFR1_EL1 bit 23-20)", Color.colorify("Enabled", "bold green"))) else: gef_print("{:<40s}: {:s}".format("PAN (ID_AA64MMFR1_EL1 bit 23-20)", Color.colorify("Disabled", "bold red"))) return def check_kaslr(self): cfg = "CONFIG_RANDOMIZE_BASE (KASLR)" kcmdline = Kernel.kernel_cmdline() ksym_ret = gdb.execute("ksymaddr-remote --quiet --no-pager kaslr_", to_string=True) if not ksym_ret: additional = "`kaslr_*`: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return if kcmdline and "nokaslr" in kcmdline.cmdline: additional = "nokaslr is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: additional = "`kaslr_*`: Found, nokaslr is not in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_fgkaslr(self): if not is_x86_64(): return # https://github.com/alobakin/linux/pull/3 cfg = "CONFIG_FG_KASLR (FGKASLR)" kversion = Kernel.kernel_version() if kversion < "5.5": # https://lore.kernel.org/kernel-hardening/20200205223950.1212394-1-kristen@linux.intel.com/ additional = "FGKASLR was first proposed in 5.5.0-rc7" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) cfg = "CONFIG_MODULE_FG_KASLR (FGKASLR)" gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("Unsupported", "bold red"))) return if "6.14" <= kversion: # As of 6.14, the following detection logic is no longer available. # It has not yet been incorporated into the mainline, and no samples are available. # Therefore, this check is disabled. gef_print("{:<40s}: {:s}".format(cfg, Color.grayify("Unknown"))) cfg = "CONFIG_MODULE_FG_KASLR (FGKASLR)" gef_print("{:<40s}: {:s}".format(cfg, Color.grayify("Unknown"))) return swapgs_restore_regs_and_return_to_usermode = Symbol.get_ksymaddr("swapgs_restore_regs_and_return_to_usermode") commit_creds = Symbol.get_ksymaddr("commit_creds") # something is wrong if not swapgs_restore_regs_and_return_to_usermode or not commit_creds: gef_print("{:<40s}: {:s}".format(cfg, Color.grayify("Unknown"))) cfg = "CONFIG_MODULE_FG_KASLR (FGKASLR)" gef_print("{:<40s}: {:s}".format(cfg, Color.grayify("Unknown"))) return # swapgs_restore_regs_and_return_to_usermode is in a fixed location. # commit_creds are placed dynamically. if swapgs_restore_regs_and_return_to_usermode < commit_creds: # For some reason this works fine kcmdline = Kernel.kernel_cmdline() if kcmdline and "nokaslr" in kcmdline.cmdline: additional = "nokaslr is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif kcmdline and "nofgkaslr" in kcmdline.cmdline: additional = "nofgkaslr is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif kcmdline and "fgkaslr=off" in kcmdline.cmdline: additional = "fgkaslr=off is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: additional = "swapgs_restore_regs_and_return_to_usermode < commit_creds" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) # Could not build detection logic for CONFIG_MODULE_FG_KASLR. # But there is no way to disable it except at build time. # It's included in the patch that introduces FGKASLR, so it is assumed to be always enabled. cfg = "CONFIG_MODULE_FG_KASLR (FGKASLR)" gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("Enabled (maybe)", "bold green"))) else: additional = "swapgs_restore_regs_and_return_to_usermode > commit_creds" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) cfg = "CONFIG_MODULE_FG_KASLR (FGKASLR)" gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("Unsupported", "bold red"))) return def check_kpti(self): kversion = Kernel.kernel_version() if "6.9" <= kversion: cfg = "CONFIG_MITIGATION_PAGE_TABLE_ISOLATION (KPTI)" else: cfg = "CONFIG_PAGE_TABLE_ISOLATION (KPTI)" kcmdline = Kernel.kernel_cmdline() if is_x86(): pti_init = Symbol.get_ksymaddr("pti_init") if pti_init is None: additional = "pti_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) elif kcmdline and "nopti" in kcmdline.cmdline: additional = "nopti is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif kcmdline and "pti=off" in kcmdline.cmdline: additional = "pti=off is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif kcmdline and "mitigations=off" in kcmdline.cmdline: additional = "mitigations=off is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif kcmdline and "pti=on" in kcmdline.cmdline: additional = "pti=on is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) elif is_in_kernel(): lines = PageMap.get_page_maps_by_pagewalk("pagewalk --quiet --no-pager --simple --disable-color").splitlines() for line in lines: if "USER" in line and "R-X" in line: # If the qemu startup option does not include `-cpu kvm64`, # isolation will not occur even if KPTI is enabled. additional = "USER memory has R-X permission in kernel context" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return else: additional = "USER memory has no R-X permission in kernel context" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled (maybe)", "bold green"), additional)) else: gef_print("{:<40s}: {:s}".format(cfg, Color.grayify("Unknown"))) if is_arm32(): gef_print("{:<40s}: {:s} (ARMv7 is unsupported)".format(cfg, Color.colorify("Unsupported", "bold red"))) if is_arm64(): pti_init = Symbol.get_ksymaddr("pti_init") if pti_init is None: additional = "pti_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) elif kcmdline and "kpti=0" in kcmdline.cmdline: additional = "kpti=0 is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif kcmdline and "mitigations=off" in kcmdline.cmdline and "nokaslr" in kcmdline.cmdline: additional = "mitigations=off and nokaslr are in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif kcmdline and "mitigations=off" in kcmdline.cmdline and "nokaslr" not in kcmdline.cmdline: additional = "mitigations=off is in cmdline, nokaslr is not in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) elif kcmdline and "kpti=1" in kcmdline.cmdline: additional = "kpti=1 is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("Enabled (maybe)", "bold green"))) return def check_rwx_page(self): cfg = "RWX kernel page" kinfo = Kernel.get_kernel_layout() for m in kinfo.maps: if m[2] == "RWX": gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("Found", "bold red"))) return gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("Not found", "bold green"))) return def check_secure_world(self): if not is_arm32() and not is_arm64(): return mtree_ret = gdb.execute("monitor info mtree -f", to_string=True) if ".secure-ram" in mtree_ret: gef_print("{:<40s}: {:s}".format("Secure world", "Found")) else: gef_print("{:<40s}: {:s}".format("Secure world", "Not found")) return def check_CONFIG_SLAB_FREELIST_HARDENED(self): cfg = "CONFIG_SLAB_FREELIST_HARDENED" slab_cache_names = " ".join("kmalloc-{:d}".format(n) for n in [8, 16, 32, 64, 96, 128, 192, 256, 512]) slub_dump_ret = gdb.execute("slub-dump --quiet --no-pager {:s}".format(slab_cache_names), to_string=True) if slub_dump_ret.count("Corrupted") >= 2: # Destruction of up to one SLUB freelist is allowed. gef_print("{:<40s}: {:s}".format(cfg, Color.grayify("Unknown"))) return slub_dump_ret = gdb.execute("slub-dump --meta", to_string=True) r = re.search(r"offsetof\(kmem_cache, random\): (0x\S+)", slub_dump_ret) if r: additional = "offsetof(kmem_cache, random): {:s}".format(r.group(1)) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("Disabled", "bold red"))) return def check_CONFIG_SLAB_VIRTUAL(self): cfg = "CONFIG_SLAB_VIRTUAL" # https://patchwork.kernel.org/project/linux-mm/patch/20230915105933.495735-12-matteorizzo@google.com/#25548022 stw = "slub_tlbflush_worker" if not Symbol.get_ksymaddr(stw): additional = "{:s}: {:s}".format(stw, "Not found") gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return kversion = Kernel.kernel_version() if kversion < "6.6": additional = "{:s}: Found (kernel version < 6.6)".format(stw) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: # If version is >= 6.6, vmlinux can switch `slab_virtual` status with boot-parameter kcmdline = Kernel.kernel_cmdline() r = re.search(r"slab_virtual=(\d+)", kcmdline.cmdline) if r: additional = "{:s}: Found, {:s} is in cmdline".format(stw, r.group(0)) if r.group(1) == "0": gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: additional = "{:s}: Found, slab_virtual is NOT in cmdline".format(stw) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return def check_selinux(self): cfg = "SELinux" # SELinux does not support being built as a kernel module. # Therefore, only symbols in the kernel can be used to determine whether SELinux is supported or not. selinux_init = Symbol.get_ksymaddr("selinux_init") if selinux_init is None: additional = "selinux_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return kversion = Kernel.kernel_version() if kversion < "4.17": selinux_enabled_addr = Symbol.get_ksymaddr("selinux_enabled") selinux_enforcing_addr = Symbol.get_ksymaddr("selinux_enforcing") if selinux_enabled_addr is None: additional = "selinux_init: Found, selinux_enabled: Not detected" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return if selinux_enforcing_addr is None: additional = "selinux_init: Found, seliux_enforcing: Not detected" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return selinux_enabled = read_int32_from_memory(selinux_enabled_addr) selinux_enforcing = read_int32_from_memory(selinux_enforcing_addr) additional = "selinux_init: Found, selinux_enabled: {:d}, selinux_enforcing: {:d}".format( selinux_enabled, selinux_enforcing, ) if selinux_enabled == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif selinux_enforcing == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Permissive", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enforcing", "bold green"), additional)) return # 4.17 <= kernel """ struct selinux_state { #ifdef CONFIG_SECURITY_SELINUX_DISABLE abool disabled; #endif #ifdef CONFIG_SECURITY_SELINUX_DEVELOP abool enforcing; #endif abool checkreqprot; abool initialized; abool policycap[__POLICYDB_CAP_MAX]; # __POLICYDB_CAP_MAX:6 ~ 8 bytes astruct page *status_page; astruct mutex status_lock; astruct selinux_avc *avc; astruct selinux_policy __rcu *policy; astruct mutex policy_mutex; } __randomize_layout; x64 sample gef> x/16xg 0xffffffff8ba20740 0xffffffff8ba20740: 0x0100010101010001 0x0000000000000001 0xffffffff8ba20750: 0xffffe3bc00215140 0x0000000000000000 0xffffffff8ba20760: 0x0000000000000000 0xffffffff8ba20768 0xffffffff8ba20770: 0xffffffff8ba20768 0xffffffff8ba1ef20 0xffffffff8ba20780: 0xffff8ecc7fe62800 0x0000000000000000 """ selinux_state = KernelAddressHeuristicFinder.get_selinux_state() if selinux_state is None: additional = "selinux_init: Found, selinux_state: Not detected" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return if read_int64_from_memory(selinux_state) == 0: additional = "selinux_init: Found, selinux_state: Not initialized" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return selinux_disable = Symbol.get_ksymaddr("selinux_disable") CONFIG_SECURITY_SELINUX_DISABLE = selinux_disable is not None enforcing_setup = Symbol.get_ksymaddr("enforcing_setup") CONFIG_SECURITY_SELINUX_DEVELOP = enforcing_setup is not None # selinux_state.disabled if CONFIG_SECURITY_SELINUX_DISABLE: selinux_disabled = read_int8_from_memory(selinux_state) additional = "selinux_init: Found, selinux_state.disable: {:d}".format(selinux_disabled) else: selinux_disabled = None additional = "selinux_init: Found, selinux_state.disable: Not found" # selinux_state.enforcing if CONFIG_SECURITY_SELINUX_DEVELOP and CONFIG_SECURITY_SELINUX_DISABLE: selinux_enforcing = read_int8_from_memory(selinux_state + 1) additional += ", selinux_state.enforcing: {:d}".format(selinux_enforcing) elif CONFIG_SECURITY_SELINUX_DEVELOP and not CONFIG_SECURITY_SELINUX_DISABLE: selinux_enforcing = read_int8_from_memory(selinux_state) additional += ", selinux_state.enforcing: {:d}".format(selinux_enforcing) else: selinux_enforcing = True additional += ", selinux_state.enforcing: Not found" if selinux_disabled: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif not selinux_enforcing: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Permissive", "bold red"), additional)) elif selinux_enforcing: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enforcing", "bold green"), additional)) return def check_smack(self): cfg = "SMACK" smack_init = Symbol.get_ksymaddr("smack_init") if smack_init is None: additional = "smack_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return kfilesystems_ret = gdb.execute("kfilesystems --quiet --no-pager --skip-mount-path", to_string=True) if not kfilesystems_ret: additional = "smack_init: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return if "smackfs" in kfilesystems_ret: additional = "smack_init: Found, smackfs: Mounted" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: additional = "smack_init: Found, smackfs: Not mounted" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return def check_apparmor(self): cfg = "AppArmor" apparmor_init = Symbol.get_ksymaddr("apparmor_init") if apparmor_init is None: additional = "apparmor_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return apparmor_enabled_addr = KernelAddressHeuristicFinder.get_apparmor_enabled() apparmor_initialized_addr = KernelAddressHeuristicFinder.get_apparmor_initialized() if apparmor_enabled_addr is None: additional = "apparmor_init: Found, apparmor_enabled: Not detected" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return if apparmor_initialized_addr is None: additional = "apparmor_init: Found, apparmor_initialized: Not detected" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return kversion = Kernel.kernel_version() if kversion < "5.1": apparmor_enabled = read_int8_from_memory(apparmor_enabled_addr) # bool else: apparmor_enabled = read_int32_from_memory(apparmor_enabled_addr) # int apparmor_initialized = read_int32_from_memory(apparmor_initialized_addr) if apparmor_enabled not in [0, 1]: additional = "apparmor_init: Found, apparmor_enabled: {:#x}".format(apparmor_enabled) gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return if apparmor_initialized not in [0, 1]: additional = "apparmor_init: Found, apparmor_initialized: {:#x}".format(apparmor_initialized) gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return additional = "apparmor_init: Found" additional += ", apparmor_initialized: {:d}".format(apparmor_initialized) additional += ", apparmor_enabled: {:d}".format(apparmor_enabled) if apparmor_enabled == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif apparmor_initialized == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_tomoyo(self): cfg = "TOMOYO" tomoyo_init = Symbol.get_ksymaddr("tomoyo_init") if tomoyo_init is None: additional = "tomoyo_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return tomoyo_enabled_addr = KernelAddressHeuristicFinder.get_tomoyo_enabled() if tomoyo_enabled_addr is None: additional = "tomoyo_init: Found, tomoyo_enabled: Not detected" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return tomoyo_enabled = read_int32_from_memory(tomoyo_enabled_addr) additional = "tomoyo_init: Found, tomoyo_enabled: {:d}".format(tomoyo_enabled) if tomoyo_enabled == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_yama(self): cfg = "Yama (ptrace_scope)" yama_init = Symbol.get_ksymaddr("yama_init") if yama_init is None: additional = "yama_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return ptrace_scope_addr = KernelAddressHeuristicFinder.get_ptrace_scope() if ptrace_scope_addr is None: additional = "yama_init: Found, kernel.yama.ptrace_scope: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return ptrace_scope = read_int32_from_memory(ptrace_scope_addr) additional = "yama_init: Found, kernel.yama.ptrace_scope: {:d}".format(ptrace_scope) if ptrace_scope == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_integrity(self): cfg = "Integrity (IMA/EVM)" integrity_iintcache_init = Symbol.get_ksymaddr("integrity_iintcache_init") if integrity_iintcache_init is None: additional = "integrity_iintcache_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return kcmdline = Kernel.kernel_cmdline() if kcmdline and "ima_appraise=enforce" in kcmdline.cmdline: additional = "integrity_iintcache_init: Found, ima_appraise=enforce is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) elif kcmdline and "ima_appraise=off" in kcmdline.cmdline: additional = "integrity_iintcache_init: Found, ima_appraise=off is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) elif kcmdline and "ima_appraise=log" in kcmdline.cmdline: additional = "integrity_iintcache_init: Found, ima_appraise=log is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Permissive", "bold red"), additional)) elif kcmdline and "ima_appraise=fix" in kcmdline.cmdline: additional = "integrity_iintcache_init: Found, ima_appraise=fix is in cmdline" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Permissive", "bold red"), additional)) else: additional = "integrity_iintcache_init: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return def check_loadpin(self): cfg = "LoadPin" loadpin_init = Symbol.get_ksymaddr("loadpin_init") if loadpin_init is None: additional = "loadpin_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return kversion = Kernel.kernel_version() if kversion < "4.20": loadpin_cfg_name = "kernel.loadpin.enabled" loadpin_cfg_addr = KernelAddressHeuristicFinder.get_loadpin_enabled() else: loadpin_cfg_name = "kernel.loadpin.enforce" loadpin_cfg_addr = KernelAddressHeuristicFinder.get_loadpin_enforce() if loadpin_cfg_addr is None: additional = "loadpin_init: Found, {:s}: Not found".format(loadpin_cfg_name) gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return loadpin_status = read_int32_from_memory(loadpin_cfg_addr) additional = "loadpin_init: Found, {:s}: {:d}".format(loadpin_cfg_name, loadpin_status) if loadpin_status == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_safe_setid(self): cfg = "SafeSetID" safesetid_security_init = Symbol.get_ksymaddr("safesetid_security_init") if safesetid_security_init is None: additional = "safesetid_security_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return additional = "safesetid_security_init: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return def check_lockdown(self): cfg = "Lockdown" kversion = Kernel.kernel_version() if kversion < "5.4": additional = "{:s}: implemented from linux 5.4".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unimplemented", "bold red"), additional)) return lockdown_lsm_init = Symbol.get_ksymaddr("lockdown_lsm_init") if lockdown_lsm_init is None: additional = "lockdown_lsm_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return kernel_locked_down = KernelAddressHeuristicFinder.get_kernel_locked_down() if kernel_locked_down is None: additional = "lockdown_lsm_init: Found, kernel_locked_down: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return try: val = read_int32_from_memory(kernel_locked_down) except gdb.MemoryError: additional = "lockdown_lsm_init: Found, kernel_locked_down: Memory read error" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return if val == 0: additional = "lockdown_lsm_init: Found, kernel_locked_down: 0 (none)" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: if val == 1: additional = "lockdown_lsm_init: Found, kernel_locked_down: 1 (integrity)" elif val == 2: additional = "lockdown_lsm_init: Found, kernel_locked_down: 2 (confidentiality)" else: additional = "lockdown_lsm_init: Found, kernel_locked_down: {:d}".format(val) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_bpf(self): cfg = "BPF" bpf_lsm_init = Symbol.get_ksymaddr("bpf_lsm_init") if bpf_lsm_init is None: additional = "bpf_lsm_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return additional = "bpf_lsm_init: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return def check_landlock(self): cfg = "Landlock" landlock_init = Symbol.get_ksymaddr("landlock_init") if landlock_init is None: additional = "landlock_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unsupported", "bold red"), additional)) return additional = "landlock_init: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, "Supported", additional)) return def check_lkrg(self): cfg = "Linux Kernel Runtime Guard (LKRG)" kmod_ret = gdb.execute("kmod --quiet --no-pager", to_string=True) if "Not found" in kmod_ret: additional = "kmod is failed" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return if ": lkrg " in kmod_ret: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), "Loaded")) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), "Not loaded")) return def check_unprivileged_userfaultfd(self): cfg = "vm.unprivileged_userfaultfd" stv_uff_ret = gdb.execute("syscall-table-view -f userfaultfd --quiet --no-pager", to_string=True) if "userfaultfd" not in stv_uff_ret: additional = "userfaultfd syscall: Unimplemented" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Syscall unsupported", "bold green"), additional)) return if "invalid userfaultfd" in stv_uff_ret: additional = "userfaultfd syscall: Disabled" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Syscall unsupported", "bold green"), additional)) return kversion = Kernel.kernel_version() if kversion < "5.2": additional = "userfaultfd syscall: Enabled, but without {:s} restriction! (implemented from linux 5.2)".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unimplemented", "bold red"), additional)) return sysctl_unprivileged_userfaultfd = KernelAddressHeuristicFinder.get_sysctl_unprivileged_userfaultfd() if sysctl_unprivileged_userfaultfd is None: additional = "{:s}: Not found".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return v = read_int32_from_memory(sysctl_unprivileged_userfaultfd) additional = "{:s}: {:d}".format(cfg, v) if v == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold green"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold red"), additional)) return def check_unprivileged_bpf_disabled(self): cfg = "kernel.unprivileged_bpf_disabled" stv_bpf_ret = gdb.execute("syscall-table-view -f bpf --quiet --no-pager", to_string=True) if "bpf" not in stv_bpf_ret: additional = "bpf syscall: Unimplemented" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Syscall unsupported", "bold green"), additional)) return if "invalid bpf" in stv_bpf_ret: additional = "bpf syscall: Disabled" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Syscall unsupported", "bold green"), additional)) return kversion = Kernel.kernel_version() if kversion < "4.4": additional = "bpf syscall: Enabled, without {:s}, but it needs CAP_SYS_ADMIN (implemented from linux 4.4)".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unimplemented", "bold green"), additional)) return sysctl_unprivileged_bpf_disabled = KernelAddressHeuristicFinder.get_sysctl_unprivileged_bpf_disabled() if sysctl_unprivileged_bpf_disabled is None: additional = "{:s}: Not found".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return v = read_int32_from_memory(sysctl_unprivileged_bpf_disabled) additional = "{:s}: {:d}".format(cfg, v) if v == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_kexec_load_disabled(self): cfg = "kernel.kexec_load_disabled" kversion = Kernel.kernel_version() if kversion < "3.14": additional = "{:s}: implemented from linux 3.14".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unimplemented", "bold red"), additional)) return r1 = gdb.execute("syscall-table-view -f kexec_load --quiet --no-pager", to_string=True) r2 = gdb.execute("syscall-table-view -f kexec_file_load --quiet --no-pager", to_string=True) if ("kexec_load" not in r1 or "invalid kexec_load" in r1) and \ ("kexec_file_load" not in r2 or "invalid kexec_file_load" in r2): additional = "" if "kexec_load" not in r1: additional = "kexec_load syscall: Unimplemented" elif "invalid kexec_load" in r1: additional = "kexec_load syscall: Disabled" if "kexec_file_load" not in r2: additional += ", " + "kexec_file_load syscall: Unimplemented" elif "invalid kexec_file_load" in r2: additional += ", " + "kexec_file_load syscall: Disabled" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Syscall unsupported", "bold green"), additional)) return kexec_load_disabled = KernelAddressHeuristicFinder.get_kexec_load_disabled() if kexec_load_disabled is None: additional = "{:s}: Not found".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return v1 = read_int32_from_memory(kexec_load_disabled) additional = "{:s}: {:d}".format(cfg, v1) if v1 == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_namespaces(self): kversion = Kernel.kernel_version() ksysctl_ret = Kernel.get_ksysctl("kernel.version") cfgs = [ ["4.9", "user.max_user_namespaces"], ["4.9", "user.max_pid_namespaces"], ["4.9", "user.max_uts_namespaces"], ["4.9", "user.max_ipc_namespaces"], ["4.9", "user.max_net_namespaces"], ["4.9", "user.max_mnt_namespaces"], ["4.9", "user.max_cgroup_namespaces"], ["5.6", "user.max_time_namespaces"], ] prev_fail = False for kv, cfg in cfgs: if kversion < kv: additional = "{:s}: implemented from linux {:s}".format(cfg, kv) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unimplemented", "bold red"), additional)) continue if not ksysctl_ret: # maybe CONFIG_RANDSTRUCT=y additional = "{:s}: Not found".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) continue if prev_fail: # Kernel.get_ksysctl is very slow, so skip if previous Kernel.get_ksysctl() was failed additional = "{:s}: Not found".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) continue addr = Kernel.get_ksysctl(cfg) # very slow if addr is None: additional = "{:s}: Not found".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) prev_fail = True continue val = read_int32_from_memory(addr) if val: gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("{:d}".format(val), "bold red"))) else: gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("{:d}".format(val), "bold green"))) return def check_unprivileged_userns_clone(self): cfg = "kernel.unprivileged_userns_clone" addr = Kernel.get_ksysctl(cfg) if addr is None: additional = "{:s}: Not found, Only present in debian-based environments".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return val = read_int32_from_memory(addr) additional = "{:s}: {:d}, Only present in debian-based environments".format(cfg, val) if val: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold green"), additional)) return def check_userns_restrict(self): cfg = "kernel.userns_restrict" addr = Kernel.get_ksysctl(cfg) if addr is None: additional = "{:s}: Not found, Only present in ALT-linux-based environments".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return val = read_int32_from_memory(addr) additional = "{:s}: {:d}, Only present in ALT-linux-based environments".format(cfg, val) if val: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return def check_CONFIG_KALLSYMS_ALL(self): cfg = "CONFIG_KALLSYMS_ALL" modprobe_path = Symbol.get_ksymaddr("modprobe_path") if modprobe_path: additional = "modprobe_path: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold red"), additional)) else: additional = "modprobe_path: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold green"), additional)) return def check_CONFIG_IKCONFIG(self): cfg = "CONFIG_IKCONFIG" ikconfig_init = Symbol.get_ksymaddr("ikconfig_init") if ikconfig_init: additional = "ikconfig_init: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold red"), additional)) else: additional = "ikconfig_init: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold green"), additional)) return def check_CONFIG_DEBUG_INFO_BTF(self): cfg = "CONFIG_DEBUG_INFO_BTF" __start_BTF = Symbol.get_ksymaddr("__start_BTF") if __start_BTF: additional = "__start_BTF: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold red"), additional)) else: additional = "__start_BTF: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold green"), additional)) return def check_CONFIG_RANDSTRUCT(self): cfg = "CONFIG_RANDSTRUCT" # In cases where kallsyms could be resolved, but ksysctl could not be resolved correctly, # it is assumed that the structure is strange. # Each structure parsed by ksysctl has no difference among kernel versions, except for `struct ctl_dir.inodes`. # Additionally, the first member of struct ctl_table is a *char procname, which will almost certainly # readable something. If this fails, it can be determined that the randstruct is used. ksysctl_ret = Kernel.get_ksysctl("kernel.version") if not ksysctl_ret: additional = "ksysctl was failed" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) warn(Color.boldify("With `CONFIG_RANDSTRUCT=y`, identifying structure members may be unreliable.")) warn(Color.boldify("As a result, many GEF commands will not work correctly.")) else: additional = "ksysctl was successful" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return def check_CONFIG_STATIC_USERMODEHELPER(self): def get_permission(addr): maps = Kernel.get_maps() if not maps: return None for vaddr, size, perm in maps: if vaddr <= addr < vaddr + size: return perm return None cfg = "CONFIG_STATIC_USERMODEHELPER" kversion = Kernel.kernel_version() if kversion < "4.11": additional = "{:s}: implemented from linux 4.11".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Unimplemented", "bold red"), additional)) return call_usermodehelper_setup = Symbol.get_ksymaddr("call_usermodehelper_setup") if call_usermodehelper_setup is None: additional = "call_usermodehelper_setup: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return res = gdb.execute("x/50i {:#x}".format(call_usermodehelper_setup), to_string=True) use_static = False if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_any_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_any_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: if not is_valid_addr(x): continue # default value of CONFIG_STATIC_USERMODEHELPER_PATH is "/sbin/usermode-helper". if read_memory(x, 5) == b"/sbin": use_static = True break # sometimes CONFIG_STATIC_USERMODEHELPER_PATH is set to "". # If CONFIG_STATIC_USERMODEHELPER_PATH is "", one NUL should be stored. # In many cases, another string seems to start being stored at the next address of NUL. # It is rare for two consecutive NULs to occur, and we use this in the detection logic. if read_memory(x, 1) == b"\x00" and read_memory(x + 1, 1) != b"\x00": # check if the address is read-only or not if get_permission(x) == "R--": use_static = True break if use_static: additional = "call_usermodehelper_setup uses static path" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: additional = "call_usermodehelper_setup uses dynamic path" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return def check_CONFIG_STACKPROTECTOR(self): cfg = "CONFIG_STACKPROTECTOR" ktask_ret = gdb.execute("ktask --meta", to_string=True) r = re.search(r"offsetof\(task_struct, stack_canary\): (0x\S+)", ktask_ret) if r: additional = "offsetof(task_struct, stack_canary): {:s}".format(r.group(1)) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return if "stack_canary" in ktask_ret: gef_print("{:<40s}: {:s}".format(cfg, Color.colorify("Disabled", "bold red"))) else: additional = "ktask was failed" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return def check_CONFIG_SHADOW_CALL_STACK(self): if not is_arm64(): return cfg = "CONFIG_SHADOW_CALL_STACK (Clang ARM64)" scs_alloc = Symbol.get_ksymaddr("scs_alloc") if scs_alloc: additional = "scs_alloc: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: additional = "scs_alloc: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return def check_CONFIG_HARDENED_USERCOPY(self): cfg = "CONFIG_HARDENED_USERCOPY" __check_heap_object = Symbol.get_ksymaddr("__check_heap_object") if __check_heap_object: additional = "__check_heap_object: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) else: additional = "__check_heap_object: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) return def check_CONFIG_FUSE_FS(self): cfg = "CONFIG_FUSE_FS" fuse_do_open = Symbol.get_ksymaddr("fuse_do_open") if fuse_do_open: additional = "fuse_do_open: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold red"), additional)) else: ret = gdb.execute("kmod --filter fuse --quiet", to_string=True) if ret: additional = "fuse module: Found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold red"), additional)) else: additional = "fuse module: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold green"), additional)) return def check_kadr_kallsyms(self): cfg = "KADR (kallsyms)" kversion = Kernel.kernel_version() if kversion < "4.15": kptr_restrict = KernelAddressHeuristicFinder.get_kptr_restrict() if kptr_restrict is None: additional = "kernel.kptr_restrict: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return v1 = read_int32_from_memory(kptr_restrict) additional = "kernel.kptr_restrict: {:d}".format(v1) if v1 == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return kptr_restrict = KernelAddressHeuristicFinder.get_kptr_restrict() sysctl_perf_event_paranoid = KernelAddressHeuristicFinder.get_sysctl_perf_event_paranoid() if kptr_restrict is None: additional = "kernel.kptr_restrict: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return if sysctl_perf_event_paranoid is None: additional = "kernel.perf_event_paranoid: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return v1 = read_int32_from_memory(kptr_restrict) v2 = u32(read_memory(sysctl_perf_event_paranoid, 4), s=True) additional = "kernel.kptr_restrict: {:d}, kernel.perf_event_paranoid: {:d}".format(v1, v2) if v1 == 0 and v2 <= 1: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_kadr_dmesg(self): dmesg_restrict = KernelAddressHeuristicFinder.get_dmesg_restrict() cfg = "KADR (dmesg)" if dmesg_restrict is None: additional = "kernel.dmesg_restrict: Not found" gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return v1 = read_int32_from_memory(dmesg_restrict) additional = "kernel.dmesg_restrict: {:d}".format(v1) if v1 == 0: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Disabled", "bold red"), additional)) else: gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.colorify("Enabled", "bold green"), additional)) return def check_mmap_min_addr(self): cfg = "vm.mmap_min_addr" mmap_min_addr = KernelAddressHeuristicFinder.get_mmap_min_addr() if mmap_min_addr is None: additional = "{:s}: Not found".format(cfg) gef_print("{:<40s}: {:s} ({:s})".format(cfg, Color.grayify("Unknown"), additional)) return val = read_int_from_memory(mmap_min_addr) if val: gef_print("{:<40s}: {:s}".format(cfg, Color.colorify_hex(val, "bold green"))) else: gef_print("{:<40s}: {:s}".format(cfg, Color.colorify_hex(val, "bold red"))) return def check_supported_syscall(self): cfg = "Supported system call" supported_syscall = [] if is_x86_32(): if KernelAddressHeuristicFinder.get_sys_call_table_x86(): supported_syscall.append("x86(native)") elif is_x86_64(): if KernelAddressHeuristicFinder.get_sys_call_table_x64(): supported_syscall.append("x64") if KernelAddressHeuristicFinder.get_sys_call_table_x86(): supported_syscall.append("x86(compat)") elif Symbol.get_ksymaddr("ia32_sys_call"): # 6.6.26~ supported_syscall.append("x86(compat)") if KernelAddressHeuristicFinder.get_sys_call_table_x32(): supported_syscall.append("x32") elif Symbol.get_ksymaddr("x32_sys_call"): # 6.6.26~ supported_syscall.append("x32") elif is_arm32(): if KernelAddressHeuristicFinder.get_sys_call_table_arm32(): supported_syscall.append("arm32(native)") elif is_arm64(): if KernelAddressHeuristicFinder.get_sys_call_table_arm64(): supported_syscall.append("arm64") if KernelAddressHeuristicFinder.get_sys_call_table_arm64_compat(): supported_syscall.append("arm32(compat)") if supported_syscall: gef_print("{:<40s}: {:s}".format(cfg, ", ".join(supported_syscall))) else: gef_print("{:<40s}: {:s}".format(cfg, "???")) return def print_security_properties_qemu_system(self): gef_print(titlify("Kernel information")) kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return gef_print("{:<40s}: {:d}.{:d}.{:d}".format("Kernel version", *kversion.version_tuple)) self.check_basic_information() gef_print(titlify("Register settings")) self.x86_specific() self.arm32_specific() self.arm64_specific() if Symbol.get_ksymaddr("_stext") is None: err("ksymaddr-remote is failed") return gef_print(titlify("Memory settings")) self.check_kaslr() self.check_fgkaslr() self.check_kpti() self.check_rwx_page() self.check_secure_world() gef_print(titlify("Allocator")) allocator = Kernel.get_slab_type() gef_print("{:<40s}: {:s}".format("Allocator", allocator)) if allocator == "SLUB": self.check_CONFIG_SLAB_FREELIST_HARDENED() self.check_CONFIG_SLAB_VIRTUAL() gef_print(titlify("Security Module")) self.check_selinux() self.check_smack() self.check_apparmor() self.check_tomoyo() self.check_yama() self.check_integrity() self.check_loadpin() self.check_safe_setid() self.check_lockdown() self.check_bpf() self.check_landlock() self.check_lkrg() gef_print(titlify("Dangerous system call")) self.check_unprivileged_userfaultfd() self.check_unprivileged_bpf_disabled() self.check_kexec_load_disabled() gef_print(titlify("namespaces")) self.check_namespaces() self.check_unprivileged_userns_clone() self.check_userns_restrict() gef_print(titlify("Other")) self.check_CONFIG_KALLSYMS_ALL() self.check_CONFIG_IKCONFIG() self.check_CONFIG_DEBUG_INFO_BTF() self.check_CONFIG_RANDSTRUCT() self.check_CONFIG_STATIC_USERMODEHELPER() self.check_CONFIG_STACKPROTECTOR() self.check_CONFIG_SHADOW_CALL_STACK() self.check_CONFIG_HARDENED_USERCOPY() self.check_CONFIG_FUSE_FS() self.check_kadr_kallsyms() self.check_kadr_dmesg() self.check_mmap_min_addr() self.check_supported_syscall() return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.print_security_properties_qemu_system() return @register_command class DwarfExceptionHandlerInfoCommand(GenericCommand, BufferingOutput): """Dump the DWARF exception handler information with the byte code itself.""" _cmdline_ = "dwarf-exception-handler" _category_ = "02-e. Process Information - Complex Structure Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-f", "--file", help="the file path to parse.") parser.add_argument("-r", "--remote", action="store_true", help="parse remote binary if download feature is available.") parser.add_argument("-x", "--hexdump", action="store_true", help="with hexdump.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # parse loaded binary", "{0:s} -r # parse remote binary", "{0:s} -f /usr/bin/apt # parse specified binary", "{0:s} -x # with hexdump", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Simplified DWARF exception structure:", "", "[OLD IMPLEMENTATION]", " libgcc_s.so bss area ELF Program Header (for .eh_frame_hdr)", "+-----------------------+ +-->+----------------+", "| ... | | | p_type |", "| frame_hdr_cache_head |---+ | | p_flags |", "+-frame_hdr_cache_entry-+<--+ | | p_offset |", "| pc_low | | | p_vaddr |----+", "| pc_high | | | p_paddr | |", "| load_base | | | p_filesz | |", "| p_eh_frame_hdr |------+ | p_memsz | |", "| p_dynamic | | p_align | | [NEW IMPLEMENTATION]", "| link |---+ +----------------+ | _dlfo_main@ld.so rodata area", "+-frame_hdr_cache_entry-+<--+ | _dlfo_nodelete_mappings@ld.so rodata area", "| pc_low | | +-------------+", "| pc_high | | | map_start |", "| load_base | | | map_end |", "| p_eh_frame_hdr | | | map |", "| p_dynamic | |<--------| eh_frame |", "| link | | | (eh_dbase) |", "+-----------------------+ | | (eh_count) |", "The frame_hdr_cache_head and frame_hdr_cache_entry are | +-------------+", "initialized the first time they are called. |", " |", " +-----------------------------+", " |", ".eh_frame_hdr | .eh_frame .gcc_except_table", "+----------------------+<--+ +-->+-CIE-------------------+<--+ +-->+-LSDA-----------------+", "| version | | | length | | | | lpstart_enc |", "| eh_frame_ptr_enc | | | cie_id (=0) | | | | ttype_enc |", "| fde_count_enc | | | version | | | | ttype_off |", "| table_enc | | | augmentation_string | | | | call_site_encoding |", "| eh_frame_ptr |------+ | code_alignment_factor | | | | call_site_table_len |", "| fde_count | | data_alignment_factor | | | |+-CallSite-----------+|", "| Table[0] initial_loc | | retaddr_register | | | || call_site_start || try_start", "| Table[0] fde |---+ | augmentation_len | | | || call_site_length || try_end", "| Table[1] initial_loc | | | augmentation_data[0] | | | || landing_pad || catch_start", "| Table[1] fde | | | ... |-(augmentation=='P')-+ | || action ||---+", "| ... | | | ... | | | | |+-CallSite-----------+| |", "| Table[N] initial_loc | | | augmentation_data[N] | | | | || ... || |", "| Table[N] fde | | | program | | | | |+-ActionTable--------+|<--+", "+----------------------+ +----->+-FDE-------------------+ | | | || ar_filter ||---+", " | length | | | | || ar_disp || |", " | cie_pointer (!=0) |---+ | | |+-ActionTable--------+| |", " | pc_begin | try_catch_base | | || ... || |", " | pc_range | | | |+-TTypeTable---------+| |", " | augmentation_len | | | || ...(stored upward) || |", " | augmentation_data[0] | | | |+-TTypeTable---------+|<--+", " | ... |-(augmentation=='L')-|-+ || ttype ||---> type_info", " | augmentation_data[N] | | |+--------------------+|", " | program | | +-LSDA-----------------+", " +-CIE-------------------+ +-----------------+ | ... |", " | ... | | +----------------------+", " +-FDE-------------------+ |", " | ... | |", " +-----------------------+ |", " +----> personality_routine(=__gxx_personality_v0@libstdc++.so)", ] _note_ = "\n".join(_note_) class ErrorEntry: def __init__(self, *args): self.tag = "error" assert len(args) == 2 self.msg1 = args[0] self.msg2 = args[1] return def __str__(self): msg = "[!] {:s}\n{:s}".format(self.msg1, self.msg2) return msg class SeparatorEntry: def __init__(self, *args): self.tag = "separator" assert 2 <= len(args) <= 3 self.pos = args[0] self.name = args[1] if len(args) == 3 and args[2] is not None: self.extra = args[2] else: self.extra = "" return def __str__(self): if self.extra: extra_s = " | {:s}".format(self.extra) else: extra_s = "" msg = "[{:#06x}] {:4s}{:s}".format(self.pos, self.name, extra_s) msg = titlify(msg, color="red", msg_color="red") return msg class DataEntry: def __init__(self, *args): self.tag = "data" assert 3 <= len(args) <= 5 self.pos = args[0] self.raw_data = args[1] self.name = args[2] if len(args) >= 4 and args[3] is not None: self.value = args[3] else: self.value = "" if len(args) == 5 and args[4] is not None: self.extra = args[4] else: self.extra = "" return def __str__(self): pos_s = "[{:#08x}|+{:#06x}]".format(self.sec.offset + self.pos, self.pos) if self.raw_data is None: raw_data_s = "" elif isinstance(self.raw_data, int): raw_data_s = "{:02x}".format(self.raw_data) elif isinstance(self.raw_data, bytes): raw_data_s = " ".join(["{:02x}".format(x) for x in self.raw_data]) else: raise if isinstance(self.value, str): value_s = self.value elif isinstance(self.value, int): value_s = "{:#018x}".format(self.value) elif isinstance(self.value, list): value_s = " ".join(["{:#018x}".format(x) for x in self.value]) else: raise if self.extra: extra_s = " | {:s}".format(self.extra) else: extra_s = "" if self.value is not None: msg = "{:s} {:<23s} {:<30s}: {:<18s}{:s}".format( pos_s, raw_data_s, self.name, value_s, extra_s, ) else: msg = "{:s} {:<23s} {:<50s}{:s}".format( pos_s, raw_data_s, self.name, extra_s, ) return msg def add_sec(self, sec): self.sec = sec return def format_entry(self, sec, entries): out = [] out.append(titlify(sec.name)) # hexdump if self.args.hexdump: out.append(hexdump(sec.data, show_symbol=False, base=sec.offset)) # print details fmt = "[{:<8}|+{:<6}] {:<23s} {:<30s}: {:<18s} | {:s}" legend = ["FileOff", "Offset", "Raw bytes", "Name", "Value", "Extra Information"] out.append(GefUtil.make_legend(fmt.format(*legend))) # print each entries for entry in entries: if entry.tag == "data": entry.add_sec(sec) out.append(str(entry)) return out def get_uleb128(self, data, pos): acc = 0 i = 0 while True: if i == 10: return pos, 0xffff_ffff_ffff_ffff pos, b = self.read_1ubyte(data, pos) acc |= (b & 0x7f) << (i * 7) if (b & 0x80) == 0: return pos, acc i += 1 def get_sleb128(self, data, pos): orig_pos = pos pos, acc = self.get_uleb128(data, pos) length = pos - orig_pos sleb_sign_mask = 1 << (length * 7 - 1) if (acc & sleb_sign_mask) == 0: return pos, acc else: sleb_value_mask = sleb_sign_mask - 1 sleb_value = acc & sleb_value_mask bit_len = len("{:b}".format(sleb_value)) real_sign_mask = 1 << bit_len real_value_mask = real_sign_mask - 1 return pos, -1 * (((~sleb_value) & real_value_mask) + 1) def read_1ubyte(self, data, pos): acc = data[pos] return pos + 1, acc def read_1sbyte(self, data, pos): pB = lambda a: struct.pack("= 4: new_pos, ptr_size = self.read_1ubyte(data, pos) entries.append(self.DataEntry(pos, data[pos:new_pos], "ptr_size", ptr_size)) pos = new_pos new_pos, segment_size = self.read_1ubyte(data, pos) entries.append(self.DataEntry(pos, data[pos:new_pos], "segment_size", segment_size)) pos = new_pos # parse code/data alignment factor new_pos, code_alignment_factor = self.get_uleb128(data, pos) entries.append(self.DataEntry( pos, data[pos:new_pos], "code_alignment_factor", code_alignment_factor, )) pos = new_pos new_pos, data_alignment_factor = self.get_sleb128(data, pos) entries.append(self.DataEntry( pos, data[pos:new_pos], "data_alignment_factor", data_alignment_factor, )) pos = new_pos # parse augmentation data if augmentation == "eh": if self.elf.e_class == Elf.ELF_32_BITS: new_pos, adjust = self.read_4ubyte(data, pos) else: new_pos, adjust = self.read_8ubyte(data, pos) entries.append(self.DataEntry(pos, data[pos:new_pos], "eh_data", adjust)) pos = new_pos if version == 1: new_pos, return_address_register = self.read_1ubyte(data, pos) ra_reg_name = self.get_register_name(return_address_register) extra_s = "Reg: {:s}".format(ra_reg_name) entries.append(self.DataEntry( pos, data[pos:new_pos], "return_address_register", return_address_register, extra_s, )) pos = new_pos else: new_pos, return_address_register = self.get_uleb128(data, pos) ra_reg_name = self.get_register_name(return_address_register) extra_s = "Reg: {:s}".format(ra_reg_name) entries.append(self.DataEntry( pos, data[pos:new_pos], "return_address_register", return_address_register, extra_s, )) pos = new_pos if augmentation[0] == "z": new_pos, augmentation_len = self.get_uleb128(data, pos) entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_len", augmentation_len, )) pos = new_pos for cp in augmentation[1:]: if cp == "R": new_pos, fde_encoding = self.read_1ubyte(data, pos) encoding_str = self.get_encoding_str(fde_encoding) extra_s = "FDE address encoding: {:s}".format(encoding_str) entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_data(R)", fde_encoding, extra_s, )) pos = new_pos elif cp == "L": new_pos, lsda_encoding = self.read_1ubyte(data, pos) encoding_str = self.get_encoding_str(lsda_encoding) extra_s = "LSDA pointer encoding: {:s}".format(encoding_str) entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_data(L)", lsda_encoding, extra_s, )) pos = new_pos elif cp == "P": new_pos, p_encoding = self.read_1ubyte(data, pos) encoding_str = self.get_encoding_str(p_encoding) extra_s = "Personality pointer encoding: {:s}".format(encoding_str) entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_data(P)", p_encoding, extra_s, )) pos = new_pos new_pos, p_addr = self.read_encoded(p_encoding, data, pos) if (p_encoding & 0x70) == self.DW_EH_PE_pcrel: p_addr += shdr.sh_offset + pos if self.elf.is_pie(): extra_s = "Personality pointer address: $codebase+{:#x}".format( load_base + p_addr, ) else: extra_s = "Personality pointer address: {:#x}".format( load_base + p_addr, ) else: extra_s = "Personality pointer address" entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_data(P)", p_addr, extra_s, )) pos = new_pos else: # unknown new_pos, x = self.read_1ubyte(data, pos) entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_data({:s})".format(cp), x, )) pos = new_pos if ptr_size == 4 or ptr_size == 8: cie = {} cie["cie_offset"] = offset cie["augmentation"] = augmentation cie["fde_encoding"] = fde_encoding cie["lsda_encoding"] = lsda_encoding cie["address_size"] = ptr_size cie["code_alignment_factor"] = code_alignment_factor cie["data_alignment_factor"] = data_alignment_factor Cie = collections.namedtuple("Cie", cie.keys()) cie = Cie(*cie.values()) cies.append(cie) else: # FDE parsing cie = [x for x in cies if start - cie_id == x.cie_offset][0] entries.append(self.SeparatorEntry(offset, "FDE")) entries += tmp_entries # unit_length, cie_pointer tmp_entries = [] ptr_size = self.encoded_ptr_size(cie.fde_encoding, cie.address_size) base = pos # parse pc_begin if ptr_size == 4: new_pos, initial_location = self.read_4ubyte(data, pos) elif ptr_size == 8: new_pos, initial_location = self.read_8ubyte(data, pos) if (cie.fde_encoding & 0x70) == self.DW_EH_PE_pcrel: vma_base = shdr.sh_offset + base + initial_location if ptr_size == 4: vma_base &= 0xffff_ffff elif ptr_size == 8: vma_base &= 0xffff_ffff_ffff_ffff if self.elf.is_pie(): extra_s = "pc_begin vma: $codebase+{:#x}".format(load_base + vma_base) else: extra_s = "pc_begin vma: {:#x}".format(load_base + vma_base) entries.append(self.DataEntry( pos, data[pos:new_pos], "pc_begin", vma_base, extra_s, )) else: entries.append(self.DataEntry( pos, data[pos:new_pos], "pc_begin", initial_location, )) pos = new_pos # parse pc_range if ptr_size == 4: new_pos, pc_range = self.read_4ubyte(data, pos) elif ptr_size == 8: new_pos, pc_range = self.read_8ubyte(data, pos) if (cie.fde_encoding & 0x70) == self.DW_EH_PE_pcrel: end_off = vma_base + pc_range else: end_off = initial_location + pc_range if ptr_size == 4: end_off &= 0xffff_ffff elif ptr_size == 8: end_off &= 0xffff_ffff_ffff_ffff if self.elf.is_pie(): extra_s = "pc_end vma: $codebase+{:#x}".format(load_base + end_off) else: extra_s = "pc_end vma: {:#x}".format(load_base + end_off) entries.append(self.DataEntry( pos, data[pos:new_pos], "pc_range", pc_range, extra_s, )) pos = new_pos # parse augmentation if cie.augmentation[0] == "z": new_pos, augmentation_len = self.get_uleb128(data, pos) entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_len", augmentation_len, )) pos = new_pos aug_end = pos + augmentation_len if augmentation_len: for cp in cie.augmentation[1:]: if cp == "L": new_pos, lsda_pointer = self.read_encoded(cie.lsda_encoding, data, pos) if (cie.lsda_encoding & 0x70) == self.DW_EH_PE_pcrel: lsda_pointer += shdr.sh_offset + pos if self.elf.is_pie(): extra_s = "LSDA pointer vma: $codebase+{:#x}".format( load_base + lsda_pointer, ) else: extra_s = "LSDA pointer vma: {:#x}".format( load_base + lsda_pointer, ) entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_data(L)", lsda_pointer, extra_s, )) else: entries.append(self.DataEntry( pos, data[pos:new_pos], "augmentation_data(L)", lsda_pointer, "LSDA pointer", )) pos = new_pos if pos < aug_end: entries.append(self.DataEntry(pos, data[pos:aug_end], "?")) pos = aug_end # common entries += self.parse_cfa_program(data, pos, cie_end, vma_base, version, cie) pos = cie_end except (IndexError, ValueError): _exc_type, exc_value, _exc_traceback = sys.exc_info() entries.append(self.ErrorEntry("Parse Error", exc_value)) return entries DW_CFA_advance_loc = 0x40 DW_CFA_offset = 0x80 DW_CFA_restore = 0xc0 DW_CFA_nop = 0x00 DW_CFA_set_loc = 0x01 DW_CFA_advance_loc1 = 0x02 DW_CFA_advance_loc2 = 0x03 DW_CFA_advance_loc4 = 0x04 DW_CFA_offset_extended = 0x05 DW_CFA_restore_extended = 0x06 DW_CFA_undefined = 0x07 DW_CFA_same_value = 0x08 DW_CFA_register = 0x09 DW_CFA_remember_state = 0x0a DW_CFA_restore_state = 0x0b DW_CFA_def_cfa = 0x0c DW_CFA_def_cfa_register = 0x0d DW_CFA_def_cfa_offset = 0x0e DW_CFA_def_cfa_expression = 0x0f DW_CFA_expression = 0x10 DW_CFA_offset_extended_sf = 0x11 DW_CFA_def_cfa_sf = 0x12 DW_CFA_def_cfa_offset_sf = 0x13 DW_CFA_val_offset = 0x14 DW_CFA_val_offset_sf = 0x15 DW_CFA_val_expression = 0x16 DW_CFA_low_user = 0x1c # noqa: F841 DW_CFA_MIPS_advance_loc8 = 0x1d DW_CFA_GNU_window_save = 0x2d # dup DW_CFA_AARCH64_negate_ra_state = 0x2d # noqa: F841 DW_CFA_GNU_args_size = 0x2e DW_CFA_GNU_negative_offset_extended = 0x2f # noqa: F841 DW_CFA_high_user = 0x3f # noqa: F841 def get_register_name(self, reg): if self.elf.e_machine == Elf.EM_X86_64: REG_LIST = [ "rax", "rdx", "rcx", "rbx", "rsi", "rdi", "rbp", "rsp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "rip", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7", "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "rflags", "es", "cs", "ss", "ds", "fs", "gs", "???", "???", "fs.base", "gs.base", "???", "???", "tr", "ldtr", "mxcsr", "fcw", "fsw", ] elif self.elf.e_machine == Elf.EM_386: REG_LIST = [ "eax", "ecx", "edx", "rbx", "esp", "ebp", "esi", "edi", "eip", "eflags", "trapno", "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7", "???", "???", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "fctrl", "fstat", "mxcsr", "es", "cs", "ss", "ds", "fs", "gs", ] elif self.elf.e_machine == Elf.EM_ARM: REG_LIST = [ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", ] + ["???"] * 40 + [ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "wcgr0", "wcgr1", "wcgr2", "wcgr3", "wcgr4", "wcgr5", "wcgr6", "wcgr7", "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7", "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15", "spsr", "spsr_fiq", "spsr_irq", "spsr_abt", "spsr_und", "spsr_svc", ] + ["???"] * 10 + [ "r8_usr", "r9_usr", "r10_usr", "r11_usr", "r12_usr", "r13_usr", "r14_usr", "r8_fiq", "r9_fiq", "r10_fiq", "r11_fiq", "r12_fiq", "r13_fiq", "r14_fiq", "r13_irq", "r14_irq", "r13_abt", "r14_abt", "r13_und", "r14_und", "r13_svc", "r14_svc", ] + ["???"] * 26 + [ "wc0", "wc1", "wc2", "wc3", "wc4", "wc5", "wc6", "wc7", ] elif self.elf.e_machine == Elf.EM_AARCH64: REG_LIST = [ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp", "???", "elr", ] + ["???"] * 30 + [ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", ] else: # other arch is unimplemented return "r{:d}".format(reg) if reg < len(REG_LIST): return REG_LIST[reg] return "???" def parse_cfa_program(self, data, pos, pos_end, vma_base, version, cie): encoding = cie.fde_encoding ptr_size = cie.address_size code_align = cie.code_alignment_factor data_align = cie.data_alignment_factor pc = vma_base indent = " " * 4 entries = [] entries.append(self.DataEntry(pos, None, "program")) try: while pos < pos_end: new_pos, opcode = self.read_1ubyte(data, pos) if opcode < self.DW_CFA_advance_loc: if opcode == self.DW_CFA_nop: entries.append(self.DataEntry(pos, data[pos:new_pos], indent + "nop")) elif opcode == self.DW_CFA_set_loc: new_pos, op1 = self.read_encoded(encoding, data, new_pos) pc = vma_base + op1 entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "set_loc {:#x} to {:#x}".format(op1, pc), )) elif opcode == self.DW_CFA_advance_loc1: op1 = data[new_pos] new_pos += 1 pc += op1 * code_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "advance_loc1 {:#x} to {:#x}".format(op1, pc), )) elif opcode == self.DW_CFA_advance_loc2: new_pos, op1 = self.read_2ubyte(data, new_pos) pc += op1 * code_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "advance_loc2 {:#x} to {:#x}".format(op1, pc), )) elif opcode == self.DW_CFA_advance_loc4: new_pos, op1 = self.read_4ubyte(data, new_pos) pc += op1 * code_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "advance_loc4 {:#x} to {:#x}".format(op1, pc), )) elif opcode == self.DW_CFA_offset_extended: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) off = op2 * data_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "offset_extended r{:d} ({:s}) at cfa{:+#x}".format(op1, regname, off), )) elif opcode == self.DW_CFA_restore_extended: new_pos, op1 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "restore_extended r{:d} ({:s})".fomart(op1, regname), )) elif opcode == self.DW_CFA_undefined: new_pos, op1 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "undefined r{:d} ({:s})".format(op1, regname), )) elif opcode == self.DW_CFA_same_value: new_pos, op1 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "same_value r{:d} ({:s})".format(op1, regname), )) elif opcode == self.DW_CFA_register: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) regname1 = self.get_register_name(op1) regname2 = self.get_register_name(op2) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "register r{:d} ({:s}) in r{:d} ({:s})".format(op1, regname1, op2, regname2), )) elif opcode == self.DW_CFA_remember_state: entries.append(self.DataEntry(pos, data[pos:new_pos], indent + "remember_state")) elif opcode == self.DW_CFA_restore_state: entries.append(self.DataEntry(pos, data[pos:new_pos], indent + "restore_state")) elif opcode == self.DW_CFA_def_cfa: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "def_cfa r{:d} ({:s}) at offset {:#x}".format(op1, regname, op2), )) elif opcode == self.DW_CFA_def_cfa_register: new_pos, op1 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "def_cfa_register r{:d} ({:s})".format(op1, regname), )) elif opcode == self.DW_CFA_def_cfa_offset: new_pos, op1 = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "def_cfa_offset {:#x}".format(op1), )) elif opcode == self.DW_CFA_def_cfa_expression: new_pos, op1 = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "def_cfa_expression {:#x}".format(op1), )) entries += self.parse_ops(version, ptr_size, op1, data, new_pos) new_pos += op1 elif opcode == self.DW_CFA_expression: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "expression r{:d} ({:s})".format(op1, regname), )) entries += self.parse_ops(version, ptr_size, op2, data, new_pos) new_pos += op2 elif opcode == self.DW_CFA_offset_extended_sf: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) off = op2 * data_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "offset_extended_sf r{:d} ({:s}) at cfa{:+#x}".format(op1, regname, off), )) elif opcode == self.DW_CFA_def_cfa_sf: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) off = op2 * data_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "def_cfa_sf r{:d} ({:s}) at offset {:#x}".format(op1, regname, off), )) elif opcode == self.DW_CFA_def_cfa_offset_sf: new_pos, op1 = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "def_cfa_offset_sf {:#x}".format(op1 * data_align), )) elif opcode == self.DW_CFA_val_offset: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) off = op2 * data_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "val_offset {:#x} at offset {:#x}".format(op1, off), )) elif opcode == self.DW_CFA_val_offset_sf: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) off = op2 * data_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "val_offset_sf {:#x} at offset {:#x}".format(op1, off), )) elif opcode == self.DW_CFA_val_expression: new_pos, op1 = self.get_uleb128(data, new_pos) new_pos, op2 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "val_expression r{:d} ({:s})".format(op1, regname), )) entries += self.parse_ops(version, ptr_size, op2, data, new_pos) new_pos += op2 elif opcode == self.DW_CFA_MIPS_advance_loc8: new_pos, op1 = self.read_8ubyte(data, new_pos) pc += op1 * code_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "MIPS_advance_loc8 {:#x} to {:#x}".format(op1, pc), )) elif opcode == self.DW_CFA_GNU_window_save: if self.elf.e_machine == Elf.EM_AARCH64: entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "AARCH64_negate_ra_state", )) else: entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "GNU_window_save", )) elif opcode == self.DW_CFA_GNU_args_size: new_pos, op1 = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "args_size {:#x}".format(op1), )) else: entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "??? {:#x}".format(opcode), )) elif opcode < self.DW_CFA_offset: op1 = opcode & 0x3f pc += op1 * code_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "advance_loc {:d} to {:#x}".format(op1, pc), )) elif opcode < self.DW_CFA_restore: op1 = opcode & 0x3f new_pos, op2 = self.get_uleb128(data, new_pos) regname = self.get_register_name(op1) off = op2 * data_align entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "offset r{:d} ({:s}) at cfa{:+#x}".format(op1, regname, off), )) else: op1 = opcode & 0x3f regname = self.get_register_name(op1) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "restore r{:d}".format(op1), )) pos = new_pos except (IndexError, ValueError): _exc_type, exc_value, _exc_traceback = sys.exc_info() entries.append(self.ErrorEntry("Parse Error", exc_value)) return entries DW_OP_addr = 0x03 # Constant address DW_OP_deref = 0x06 # DW_OP_const1u = 0x08 # Unsigned 1-byte constant DW_OP_const1s = 0x09 # Signed 1-byte constant DW_OP_const2u = 0x0a # Unsigned 2-byte constant DW_OP_const2s = 0x0b # Signed 2-byte constant DW_OP_const4u = 0x0c # Unsigned 4-byte constant DW_OP_const4s = 0x0d # Signed 4-byte constant DW_OP_const8u = 0x0e # Unsigned 8-byte constant DW_OP_const8s = 0x0f # Signed 8-byte constant DW_OP_constu = 0x10 # Unsigned LEB128 constant DW_OP_consts = 0x11 # Signed LEB128 constant DW_OP_dup = 0x12 # DW_OP_drop = 0x13 # DW_OP_over = 0x14 # DW_OP_pick = 0x15 # 1-byte stack index DW_OP_swap = 0x16 # DW_OP_rot = 0x17 # DW_OP_xderef = 0x18 # DW_OP_abs = 0x19 # DW_OP_and = 0x1a # DW_OP_div = 0x1b # DW_OP_minus = 0x1c # DW_OP_mod = 0x1d # DW_OP_mul = 0x1e # DW_OP_neg = 0x1f # DW_OP_not = 0x20 # DW_OP_or = 0x21 # DW_OP_plus = 0x22 # DW_OP_plus_uconst = 0x23 # Unsigned LEB128 addend DW_OP_shl = 0x24 # DW_OP_shr = 0x25 # DW_OP_shra = 0x26 # DW_OP_xor = 0x27 # DW_OP_bra = 0x28 # Signed 2-byte constant DW_OP_eq = 0x29 # DW_OP_ge = 0x2a # DW_OP_gt = 0x2b # DW_OP_le = 0x2c # DW_OP_lt = 0x2d # DW_OP_ne = 0x2e # DW_OP_skip = 0x2f # Signed 2-byte constant DW_OP_lit0 = 0x30 # Literal 0 DW_OP_lit1 = 0x31 # Literal 1 DW_OP_lit2 = 0x32 # Literal 2 DW_OP_lit3 = 0x33 # Literal 3 DW_OP_lit4 = 0x34 # Literal 4 DW_OP_lit5 = 0x35 # Literal 5 DW_OP_lit6 = 0x36 # Literal 6 DW_OP_lit7 = 0x37 # Literal 7 DW_OP_lit8 = 0x38 # Literal 8 DW_OP_lit9 = 0x39 # Literal 9 DW_OP_lit10 = 0x3a # Literal 10 DW_OP_lit11 = 0x3b # Literal 11 DW_OP_lit12 = 0x3c # Literal 12 DW_OP_lit13 = 0x3d # Literal 13 DW_OP_lit14 = 0x3e # Literal 14 DW_OP_lit15 = 0x3f # Literal 15 DW_OP_lit16 = 0x40 # Literal 16 DW_OP_lit17 = 0x41 # Literal 17 DW_OP_lit18 = 0x42 # Literal 18 DW_OP_lit19 = 0x43 # Literal 19 DW_OP_lit20 = 0x44 # Literal 20 DW_OP_lit21 = 0x45 # Literal 21 DW_OP_lit22 = 0x46 # Literal 22 DW_OP_lit23 = 0x47 # Literal 23 DW_OP_lit24 = 0x48 # Literal 24 DW_OP_lit25 = 0x49 # Literal 25 DW_OP_lit26 = 0x4a # Literal 26 DW_OP_lit27 = 0x4b # Literal 27 DW_OP_lit28 = 0x4c # Literal 28 DW_OP_lit29 = 0x4d # Literal 29 DW_OP_lit30 = 0x4e # Literal 30 DW_OP_lit31 = 0x4f # Literal 31 DW_OP_reg0 = 0x50 # Register 0 DW_OP_reg1 = 0x51 # Register 1 DW_OP_reg2 = 0x52 # Register 2 DW_OP_reg3 = 0x53 # Register 3 DW_OP_reg4 = 0x54 # Register 4 DW_OP_reg5 = 0x55 # Register 5 DW_OP_reg6 = 0x56 # Register 6 DW_OP_reg7 = 0x57 # Register 7 DW_OP_reg8 = 0x58 # Register 8 DW_OP_reg9 = 0x59 # Register 9 DW_OP_reg10 = 0x5a # Register 10 DW_OP_reg11 = 0x5b # Register 11 DW_OP_reg12 = 0x5c # Register 12 DW_OP_reg13 = 0x5d # Register 13 DW_OP_reg14 = 0x5e # Register 14 DW_OP_reg15 = 0x5f # Register 15 DW_OP_reg16 = 0x60 # Register 16 DW_OP_reg17 = 0x61 # Register 17 DW_OP_reg18 = 0x62 # Register 18 DW_OP_reg19 = 0x63 # Register 19 DW_OP_reg20 = 0x64 # Register 20 DW_OP_reg21 = 0x65 # Register 21 DW_OP_reg22 = 0x66 # Register 22 DW_OP_reg23 = 0x67 # Register 24 DW_OP_reg24 = 0x68 # Register 24 DW_OP_reg25 = 0x69 # Register 25 DW_OP_reg26 = 0x6a # Register 26 DW_OP_reg27 = 0x6b # Register 27 DW_OP_reg28 = 0x6c # Register 28 DW_OP_reg29 = 0x6d # Register 29 DW_OP_reg30 = 0x6e # Register 30 DW_OP_reg31 = 0x6f # Register 31 DW_OP_breg0 = 0x70 # Base register 0 DW_OP_breg1 = 0x71 # Base register 1 DW_OP_breg2 = 0x72 # Base register 2 DW_OP_breg3 = 0x73 # Base register 3 DW_OP_breg4 = 0x74 # Base register 4 DW_OP_breg5 = 0x75 # Base register 5 DW_OP_breg6 = 0x76 # Base register 6 DW_OP_breg7 = 0x77 # Base register 7 DW_OP_breg8 = 0x78 # Base register 8 DW_OP_breg9 = 0x79 # Base register 9 DW_OP_breg10 = 0x7a # Base register 10 DW_OP_breg11 = 0x7b # Base register 11 DW_OP_breg12 = 0x7c # Base register 12 DW_OP_breg13 = 0x7d # Base register 13 DW_OP_breg14 = 0x7e # Base register 14 DW_OP_breg15 = 0x7f # Base register 15 DW_OP_breg16 = 0x80 # Base register 16 DW_OP_breg17 = 0x81 # Base register 17 DW_OP_breg18 = 0x82 # Base register 18 DW_OP_breg19 = 0x83 # Base register 19 DW_OP_breg20 = 0x84 # Base register 20 DW_OP_breg21 = 0x85 # Base register 21 DW_OP_breg22 = 0x86 # Base register 22 DW_OP_breg23 = 0x87 # Base register 23 DW_OP_breg24 = 0x88 # Base register 24 DW_OP_breg25 = 0x89 # Base register 25 DW_OP_breg26 = 0x8a # Base register 26 DW_OP_breg27 = 0x8b # Base register 27 DW_OP_breg28 = 0x8c # Base register 28 DW_OP_breg29 = 0x8d # Base register 29 DW_OP_breg30 = 0x8e # Base register 30 DW_OP_breg31 = 0x8f # Base register 31 DW_OP_regx = 0x90 # Unsigned LEB128 register DW_OP_fbreg = 0x91 # Signed LEB128 offset DW_OP_bregx = 0x92 # ULEB128 register followed by SLEB128 off DW_OP_piece = 0x93 # ULEB128 size of piece addressed DW_OP_deref_size = 0x94 # 1-byte size of data retrieved DW_OP_xderef_size = 0x95 # 1-byte size of data retrieved DW_OP_nop = 0x96 # DW_OP_push_object_address = 0x97 # DW_OP_call2 = 0x98 # DW_OP_call4 = 0x99 # DW_OP_call_ref = 0x9a # DW_OP_form_tls_address = 0x9b # TLS offset to address in current thread DW_OP_call_frame_cfa = 0x9c # CFA as determined by CFI DW_OP_bit_piece = 0x9d # ULEB128 size and ULEB128 offset in bits DW_OP_implicit_value = 0x9e # DW_FORM_block follows opcode DW_OP_stack_value = 0x9f # No operands, special like DW_OP_piece # DW_OP_implicit_pointer = 0xa0 # DW_OP_addrx = 0xa1 # DW_OP_constx = 0xa2 # DW_OP_entry_value = 0xa3 # DW_OP_const_type = 0xa4 # DW_OP_regval_type = 0xa5 # DW_OP_deref_type = 0xa6 # DW_OP_xderef_type = 0xa7 # DW_OP_convert = 0xa8 # DW_OP_reinterpret = 0xa9 # # GNU extensions DW_OP_GNU_push_tls_address = 0xe0 # DW_OP_GNU_uninit = 0xf0 # DW_OP_GNU_encoded_addr = 0xf1 # DW_OP_GNU_implicit_pointer = 0xf2 # DW_OP_GNU_entry_value = 0xf3 # DW_OP_GNU_const_type = 0xf4 # DW_OP_GNU_regval_type = 0xf5 # DW_OP_GNU_deref_type = 0xf6 # DW_OP_GNU_convert = 0xf7 # DW_OP_GNU_reinterpret = 0xf9 # DW_OP_GNU_parameter_ref = 0xfa # # GNU Debug Fission extensions DW_OP_GNU_addr_index = 0xfb # DW_OP_GNU_const_index = 0xfc # DW_OP_GNU_variable_value = 0xfd # DW_OP_lo_user = 0xe0 # Implementation-defined range start DW_OP_hi_user = 0xff # Implementation-defined range end # noqa: F841 DWARF_ONE_KNOWN_DW_OP = { DW_OP_GNU_addr_index : "GNU_addr_index", DW_OP_GNU_const_index : "GNU_const_index", DW_OP_GNU_const_type : "GNU_const_type", DW_OP_GNU_convert : "GNU_convert", DW_OP_GNU_deref_type : "GNU_deref_type", DW_OP_GNU_encoded_addr : "GNU_encoded_addr", DW_OP_GNU_entry_value : "GNU_entry_value", DW_OP_GNU_implicit_pointer : "GNU_implicit_pointer", DW_OP_GNU_parameter_ref : "GNU_parameter_ref", DW_OP_GNU_push_tls_address : "GNU_push_tls_address", DW_OP_GNU_regval_type : "GNU_regval_type", DW_OP_GNU_reinterpret : "GNU_reinterpret", DW_OP_GNU_uninit : "GNU_uninit", DW_OP_GNU_variable_value : "GNU_variable_value", DW_OP_abs : "abs", DW_OP_addr : "addr", DW_OP_addrx : "addrx", DW_OP_and : "and", DW_OP_bit_piece : "bit_piece", DW_OP_bra : "bra", DW_OP_breg0 : "breg0", DW_OP_breg1 : "breg1", DW_OP_breg2 : "breg2", DW_OP_breg3 : "breg3", DW_OP_breg4 : "breg4", DW_OP_breg5 : "breg5", DW_OP_breg6 : "breg6", DW_OP_breg7 : "breg7", DW_OP_breg8 : "breg8", DW_OP_breg9 : "breg9", DW_OP_breg10 : "breg10", DW_OP_breg11 : "breg11", DW_OP_breg12 : "breg12", DW_OP_breg13 : "breg13", DW_OP_breg14 : "breg14", DW_OP_breg15 : "breg15", DW_OP_breg16 : "breg16", DW_OP_breg17 : "breg17", DW_OP_breg18 : "breg18", DW_OP_breg19 : "breg19", DW_OP_breg20 : "breg20", DW_OP_breg21 : "breg21", DW_OP_breg22 : "breg22", DW_OP_breg23 : "breg23", DW_OP_breg24 : "breg24", DW_OP_breg25 : "breg25", DW_OP_breg26 : "breg26", DW_OP_breg27 : "breg27", DW_OP_breg28 : "breg28", DW_OP_breg29 : "breg29", DW_OP_breg30 : "breg30", DW_OP_breg31 : "breg31", DW_OP_bregx : "bregx", DW_OP_call2 : "call2", DW_OP_call4 : "call4", DW_OP_call_frame_cfa : "call_frame_cfa", DW_OP_call_ref : "call_ref", DW_OP_const1s : "const1s", DW_OP_const1u : "const1u", DW_OP_const2s : "const2s", DW_OP_const2u : "const2u", DW_OP_const4s : "const4s", DW_OP_const4u : "const4u", DW_OP_const8s : "const8s", DW_OP_const8u : "const8u", DW_OP_const_type : "const_type", DW_OP_consts : "consts", DW_OP_constu : "constu", DW_OP_constx : "constx", DW_OP_convert : "convert", DW_OP_deref : "deref", DW_OP_deref_size : "deref_size", DW_OP_deref_type : "deref_type", DW_OP_div : "div", DW_OP_drop : "drop", DW_OP_dup : "dup", DW_OP_entry_value : "entry_value", DW_OP_eq : "eq", DW_OP_fbreg : "fbreg", DW_OP_form_tls_address : "form_tls_address", DW_OP_ge : "ge", DW_OP_gt : "gt", DW_OP_implicit_pointer : "implicit_pointer", DW_OP_implicit_value : "implicit_value", DW_OP_le : "le", DW_OP_lit0 : "lit0", DW_OP_lit1 : "lit1", DW_OP_lit2 : "lit2", DW_OP_lit3 : "lit3", DW_OP_lit4 : "lit4", DW_OP_lit5 : "lit5", DW_OP_lit6 : "lit6", DW_OP_lit7 : "lit7", DW_OP_lit8 : "lit8", DW_OP_lit9 : "lit9", DW_OP_lit10 : "lit10", DW_OP_lit11 : "lit11", DW_OP_lit12 : "lit12", DW_OP_lit13 : "lit13", DW_OP_lit14 : "lit14", DW_OP_lit15 : "lit15", DW_OP_lit16 : "lit16", DW_OP_lit17 : "lit17", DW_OP_lit18 : "lit18", DW_OP_lit19 : "lit19", DW_OP_lit20 : "lit20", DW_OP_lit21 : "lit21", DW_OP_lit22 : "lit22", DW_OP_lit23 : "lit23", DW_OP_lit24 : "lit24", DW_OP_lit25 : "lit25", DW_OP_lit26 : "lit26", DW_OP_lit27 : "lit27", DW_OP_lit28 : "lit28", DW_OP_lit29 : "lit29", DW_OP_lit30 : "lit30", DW_OP_lit31 : "lit31", DW_OP_lt : "lt", DW_OP_minus : "minus", DW_OP_mod : "mod", DW_OP_mul : "mul", DW_OP_ne : "ne", DW_OP_neg : "neg", DW_OP_nop : "nop", DW_OP_not : "not", DW_OP_or : "or", DW_OP_over : "over", DW_OP_pick : "pick", DW_OP_piece : "piece", DW_OP_plus : "plus", DW_OP_plus_uconst : "plus_uconst", DW_OP_push_object_address : "push_object_address", DW_OP_reg0 : "reg0", DW_OP_reg1 : "reg1", DW_OP_reg2 : "reg2", DW_OP_reg3 : "reg3", DW_OP_reg4 : "reg4", DW_OP_reg5 : "reg5", DW_OP_reg6 : "reg6", DW_OP_reg7 : "reg7", DW_OP_reg8 : "reg8", DW_OP_reg9 : "reg9", DW_OP_reg10 : "reg10", DW_OP_reg11 : "reg11", DW_OP_reg12 : "reg12", DW_OP_reg13 : "reg13", DW_OP_reg14 : "reg14", DW_OP_reg15 : "reg15", DW_OP_reg16 : "reg16", DW_OP_reg17 : "reg17", DW_OP_reg18 : "reg18", DW_OP_reg19 : "reg19", DW_OP_reg20 : "reg20", DW_OP_reg21 : "reg21", DW_OP_reg22 : "reg22", DW_OP_reg23 : "reg23", DW_OP_reg24 : "reg24", DW_OP_reg25 : "reg25", DW_OP_reg26 : "reg26", DW_OP_reg27 : "reg27", DW_OP_reg28 : "reg28", DW_OP_reg29 : "reg29", DW_OP_reg30 : "reg30", DW_OP_reg31 : "reg31", DW_OP_regval_type : "regval_type", DW_OP_regx : "regx", DW_OP_reinterpret : "reinterpret", DW_OP_rot : "rot", DW_OP_shl : "shl", DW_OP_shr : "shr", DW_OP_shra : "shra", DW_OP_skip : "skip", DW_OP_stack_value : "stack_value", DW_OP_swap : "swap", DW_OP_xderef : "xderef", DW_OP_xderef_size : "xderef_size", DW_OP_xderef_type : "xderef_type", DW_OP_xor : "xor", } def dwarf_locexpr_opcode_string(self, code): if code in self.DWARF_ONE_KNOWN_DW_OP: return self.DWARF_ONE_KNOWN_DW_OP[code] elif code >= self.DW_OP_lo_user: return "lo_user+{:#x}".format(code - self.DW_OP_lo_user) else: return "??? ({:#x})".format(code) def parse_ops(self, vers, addrsize, length, data, pos, indent_n=0): indent = " " * ((indent_n + 2) * 4) entries = [] ref_size = addrsize if vers < 3 else 0 if length == 0: entries.append(self.DataEntry(pos, None, indent + "(empty)")) return entries offset = 0 try: while length: new_pos, op = self.read_1ubyte(data, pos) op_name = self.dwarf_locexpr_opcode_string(op) if op in [self.DW_OP_addr]: if addrsize == 4: new_pos, d = self.read_4ubyte(data, new_pos) elif addrsize == 8: new_pos, d = self.read_8ubyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_call_ref, self.DW_OP_GNU_variable_value]: if ref_size == 4: new_pos, d = self.read_4ubyte(data, new_pos) else: new_pos, d = self.read_8ubyte(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), )) elif op in [self.DW_OP_deref]: typ = {4: "uint", 8: "ulong"}[addrsize] extra_s = "pop; push *({:s}*)popped_value".format(typ) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) elif op in [self.DW_OP_xderef]: typ = {4: "uint", 8: "ulong"}[addrsize] extra_s = "pop; pop; push *({:s}*)(popped_value2_as_segment:popped_value1)".format(typ) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) elif op in [self.DW_OP_deref_size]: new_pos, d = self.read_1ubyte(data, new_pos) typ = {1: "uchar", 2: "ushort", 4: "uint", 8: "ulong"}[d] extra_s = "pop; push *({:s}*)popped_value".format(typ) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_xderef_size]: new_pos, d = self.read_1ubyte(data, new_pos) typ = {1: "uchar", 2: "ushort", 4: "uint", 8: "ulong"}[d] extra_s = "pop; pop; push *({:s}*)(popped_value2_as_segment:popped_value1)".forma(typ) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_pick]: new_pos, d = self.read_1ubyte(data, new_pos) extra_s = "push stack[{:d}]".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_const1u]: new_pos, d = self.read_1ubyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_const2u]: new_pos, d = self.read_2ubyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_const4u]: new_pos, d = self.read_4ubyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_const8u]: new_pos, d = self.read_8ubyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_const1s]: new_pos, d = self.read_1sbyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_const2u]: new_pos, d = self.read_2sbyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_const4s]: new_pos, d = self.read_4sbyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_const8s]: new_pos, d = self.read_8sbyte(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_piece, self.DW_OP_regx, self.DW_OP_plus_uconst]: new_pos, d = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), )) elif op in [self.DW_OP_constu]: new_pos, d = self.get_uleb128(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_consts]: new_pos, d = self.get_sleb128(data, new_pos) extra_s = "push {:#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_addrx, self.DW_OP_GNU_addr_index, self.DW_OP_constx, self.DW_OP_GNU_const_index]: new_pos, d = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}]".format(offset, op_name, d), )) elif op in [self.DW_OP_bit_piece]: new_pos, d1 = self.get_uleb128(data, new_pos) new_pos, d2 = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x},{:#x}".format(offset, op_name, d1, d2), )) elif op in [self.DW_OP_lit0, self.DW_OP_lit1, self.DW_OP_lit2, self.DW_OP_lit3, self.DW_OP_lit4, self.DW_OP_lit5, self.DW_OP_lit6, self.DW_OP_lit7, self.DW_OP_lit8, self.DW_OP_lit9, self.DW_OP_lit10, self.DW_OP_lit11, self.DW_OP_lit12, self.DW_OP_lit13, self.DW_OP_lit14, self.DW_OP_lit15, self.DW_OP_lit16, self.DW_OP_lit17, self.DW_OP_lit18, self.DW_OP_lit19, self.DW_OP_lit20, self.DW_OP_lit21, self.DW_OP_lit22, self.DW_OP_lit23, self.DW_OP_lit24, self.DW_OP_lit25, self.DW_OP_lit26, self.DW_OP_lit27, self.DW_OP_lit28, self.DW_OP_lit29, self.DW_OP_lit30, self.DW_OP_lit31]: extra_s = "push {:#x}".format(op - 0x30) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) elif op in [self.DW_OP_reg0, self.DW_OP_reg1, self.DW_OP_reg2, self.DW_OP_reg3, self.DW_OP_reg4, self.DW_OP_reg5, self.DW_OP_reg6, self.DW_OP_reg7, self.DW_OP_reg8, self.DW_OP_reg9, self.DW_OP_reg10, self.DW_OP_reg11, self.DW_OP_reg12, self.DW_OP_reg13, self.DW_OP_reg14, self.DW_OP_reg15, self.DW_OP_reg16, self.DW_OP_reg17, self.DW_OP_reg18, self.DW_OP_reg19, self.DW_OP_reg20, self.DW_OP_reg21, self.DW_OP_reg22, self.DW_OP_reg23, self.DW_OP_reg24, self.DW_OP_reg25, self.DW_OP_reg26, self.DW_OP_reg27, self.DW_OP_reg28, self.DW_OP_reg29, self.DW_OP_reg30, self.DW_OP_reg31]: regname = self.get_register_name(op - 0x50) extra_s = "push {:s}".format(regname) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) elif op in [self.DW_OP_breg0, self.DW_OP_breg1, self.DW_OP_breg2, self.DW_OP_breg3, self.DW_OP_breg4, self.DW_OP_breg5, self.DW_OP_breg6, self.DW_OP_breg7, self.DW_OP_breg8, self.DW_OP_breg9, self.DW_OP_breg10, self.DW_OP_breg11, self.DW_OP_breg12, self.DW_OP_breg13, self.DW_OP_breg14, self.DW_OP_breg15, self.DW_OP_breg16, self.DW_OP_breg17, self.DW_OP_breg18, self.DW_OP_breg19, self.DW_OP_breg20, self.DW_OP_breg21, self.DW_OP_breg22, self.DW_OP_breg23, self.DW_OP_breg24, self.DW_OP_breg25, self.DW_OP_breg26, self.DW_OP_breg27, self.DW_OP_breg28, self.DW_OP_breg29, self.DW_OP_breg30, self.DW_OP_breg31]: new_pos, d = self.get_sleb128(data, new_pos) regname = self.get_register_name(op - 0x70) extra_s = "push {:s}{:+#x}".format(regname, d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_fbreg]: new_pos, d = self.get_sleb128(data, new_pos) regname = "push frame_base{:*#x}".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x}".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_bregx]: new_pos, d1 = self.get_uleb128(data, new_pos) new_pos, d2 = self.get_sleb128(data, new_pos) regname = self.get_register_name(d1) extra_s = "push {:s}{:+#x}".format(regname, d2) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x},{:#x}".format(offset, op_name, d1, d2), None, extra_s, )) elif op in [self.DW_OP_call2]: new_pos, d = self.read_2ubyte(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}]".format(offset, op_name, d), )) elif op in [self.DW_OP_call4]: new_pos, d = self.read_4ubyte(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}]".format(offset, op_name, d), )) elif op in [self.DW_OP_bra]: new_pos, d = self.read_2sbyte(data, new_pos) d += offset + 3 extra_s = "pop; jmp to [{:#x}] if popped_value != 0".format(d) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}]".format(offset, op_name, d), None, extra_s, )) elif op in [self.DW_OP_skip]: new_pos, d = self.read_2sbyte(data, new_pos) d += offset + 3 entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}]".format(offset, op_name, d), )) elif op in [self.DW_OP_implicit_value]: new_pos, d = self.get_uleb128(data, new_pos) block_s = " ".join(["{:02x}".format(x) for x in data[new_pos:new_pos + d]]) new_pos += d entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:s}".format(offset, op_name, block_s), )) elif op in [self.DW_OP_implicit_pointer, self.DW_OP_GNU_implicit_pointer]: if ref_size == 4: new_pos, d1 = self.read_4ubyte(data, new_pos) elif ref_size == 8: new_pos, d1 = self.read_8ubyte(data, new_pos) new_pos, d2 = self.get_sleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}] {:+#x}".format(offset, op_name, d1, d2), )) elif op in [self.DW_OP_entry_value, self.DW_OP_GNU_entry_value]: new_pos, d = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), )) entries += self.parse_ops(vers, addrsize, d, data, new_pos, indent=indent + 1) new_pos += d elif op in [self.DW_OP_const_type, self.DW_OP_GNU_const_type]: new_pos, d1 = self.get_uleb128(data, new_pos) new_pos, d2 = self.read_1ubyte(data, new_pos) block_s = " ".join(["{:02x}".format(x) for x in data[new_pos:new_pos + d2]]) new_pos += d2 entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}] {:s}".format(offset, op_name, d1, block_s), )) elif op in [self.DW_OP_regval_type, self.DW_OP_GNU_regval_type]: new_pos, d1 = self.get_uleb128(data, new_pos) new_pos, d2 = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x} [{:#x}]".format(offset, op_name, d1, d2), )) elif op in [self.DW_OP_deref_type, self.DW_OP_GNU_deref_type]: new_pos, d1 = self.read_1ubyte(data, new_pos) new_pos, d2 = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x} [{:#x}]".format(offset, op_name, d1, d2), )) elif op in [self.DW_OP_xderef_type]: new_pos, d1 = self.read_1ubyte(data, new_pos) new_pos, d2 = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} {:#x} [{:#x}]".format(offset, op_name, d1, d2), )) elif op in [self.DW_OP_convert, self.DW_OP_GNU_convert, self.DW_OP_reinterpret, self.DW_OP_GNU_reinterpret]: new_pos, d = self.get_uleb128(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}]".format(offset, op_name, d), )) elif op in [self.DW_OP_GNU_parameter_ref]: new_pos, d = self.read_4ubyte(data, new_pos) entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s} [{:#x}]".format(offset, op_name, d), )) elif op in [self.DW_OP_drop]: extra_s = "pop" entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) elif op in [self.DW_OP_dup]: extra_s = "push stack[0]" entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) elif op in [self.DW_OP_over]: extra_s = "push stack[1]" entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) elif op in [self.DW_OP_swap]: extra_s = "stack[0],stack[1] = stack[1],stack[0]" entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) elif op in [self.DW_OP_rot]: extra_s = "stack[0],stack[1],stack[2] = stack[1],stack[2],stack[0]" entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), None, extra_s, )) else: entries.append(self.DataEntry( pos, data[pos:new_pos], indent + "[{:#04x}] {:s}".format(offset, op_name), )) length -= new_pos - pos offset += new_pos - pos pos = new_pos except (KeyError, IndexError, ValueError): _exc_type, exc_value, _exc_traceback = sys.exc_info() entries.append(self.ErrorEntry("Parse Error", exc_value)) return entries def parse_gcc_except_table(self, gcc_except_table, eh_frame_entries): def get_lsda_info(eh_frame_entries): dic = {} is_fde = False for entry in eh_frame_entries: if entry.tag != "data": continue if entry.name == "cie_pointer": is_fde = True continue if entry.name == "cie_id": is_fde = False continue if is_fde: if entry.name == "pc_begin": pc_begin = entry.value continue if entry.name != "augmentation_data(L)": continue dic[entry.value - load_base] = pc_begin + load_base return dic section_base = gcc_except_table.offset data = gcc_except_table.data shdr = self.elf.get_shdr(".gcc_except_table") load_base = self.elf.get_phdr(Elf.Phdr.PT_LOAD).p_vaddr ptr_size = 4 if self.elf.e_class == Elf.ELF_32_BITS else 8 lsda_pos_info = get_lsda_info(eh_frame_entries) entries = [] pos = 0 lsda_table_cnt = 0 try: lsda_pos_padding = 0 while data[pos:]: # search LSDA start address if (section_base + pos) not in lsda_pos_info: lsda_pos_padding += 1 pos += 1 continue # Found if lsda_pos_padding: entries.append(self.SeparatorEntry(pos - lsda_pos_padding, "Padding")) entries.append(self.DataEntry( pos - lsda_pos_padding, data[pos - lsda_pos_padding:pos], "padding", )) lsda_pos_padding = 0 entries.append(self.SeparatorEntry(pos, "LSDA Table[{:4d}]".format(lsda_table_cnt))) lpstart = lsda_pos_info[section_base + pos] # parse lpstart_encoding new_pos, lpstart_encoding = self.read_1ubyte(data, pos) encoding_str = self.get_encoding_str(lpstart_encoding) entries.append(self.DataEntry( pos, data[pos:new_pos], "landing_pad_start_encoding", lpstart_encoding, "encoding: {:s}".format(encoding_str), )) pos = new_pos # parse lpstart if lpstart_encoding != self.DW_EH_PE_omit: new_pos, lpstart = self.read_encoded(lpstart_encoding, data, pos) # overwrite lpstart entries.append(self.DataEntry( pos, data[pos:new_pos], "landing_pad_start", lpstart, )) pos = new_pos # parse ttype_encoding new_pos, ttype_encoding = self.read_1ubyte(data, pos) encoding_str = self.get_encoding_str(ttype_encoding) entries.append(self.DataEntry( pos, data[pos:new_pos], "ttype_encoding", ttype_encoding, "encoding: {:s}".format(encoding_str), )) pos = new_pos # parse ttype_base_offset ttype_base = None if ttype_encoding != self.DW_EH_PE_omit: new_pos, ttype_base_offset = self.get_uleb128(data, pos) ttype_base = new_pos + ttype_base_offset entries.append(self.DataEntry( pos, data[pos:new_pos], "ttype_base_offset", ttype_base_offset, "ttype_base: {:#x}".format(ttype_base), )) pos = new_pos # parse call_site_encoding new_pos, call_site_encoding = self.read_1ubyte(data, pos) encoding_str = self.get_encoding_str(call_site_encoding) entries.append(self.DataEntry( pos, data[pos:new_pos], "call_site_encoding", call_site_encoding, "encoding: {:s}".format(encoding_str), )) pos = new_pos # parse call_site_table_len new_pos, call_site_table_len = self.get_uleb128(data, pos) entries.append(self.DataEntry( pos, data[pos:new_pos], "call_site_table_len", call_site_table_len, )) pos = new_pos # parse call_site_table action_table_pos = pos + call_site_table_len table_cnt = 0 max_action = 0 while pos < action_table_pos: entries.append(self.SeparatorEntry(pos, "Call site table[{:4d}]".format(table_cnt))) new_pos, call_site_start = self.read_encoded(call_site_encoding, data, pos) if self.elf.is_pie(): extra_s = "try-start vma: $codebase+{:#x}".format(lpstart + call_site_start) else: extra_s = "try-start vma: {:#x}".format(lpstart + call_site_start) entries.append(self.DataEntry( pos, data[pos:new_pos], "call_site_start", call_site_start, extra_s, )) pos = new_pos new_pos, call_site_length = self.read_encoded(call_site_encoding, data, pos) if self.elf.is_pie(): extra_s = "try-end vma: $codebase+{:#x}".format(lpstart + call_site_start + call_site_length) else: extra_s = "try-end vma: {:#x}".format(lpstart + call_site_start + call_site_length) entries.append(self.DataEntry( pos, data[pos:new_pos], "call_site_length", call_site_length, extra_s, )) pos = new_pos new_pos, call_site_lpad = self.read_encoded(call_site_encoding, data, pos) if call_site_lpad == 0: extra_s = "" elif self.elf.is_pie(): extra_s = "catch vma: $codebase+{:#x}".format(lpstart + call_site_lpad) else: extra_s = "catch vma: {:#x}".format(lpstart + call_site_lpad) entries.append(self.DataEntry( pos, data[pos:new_pos], "landing_pad", call_site_lpad, extra_s, )) pos = new_pos new_pos, action = self.get_uleb128(data, pos) max_action = max(action, max_action) if action == 0: extra_s = "no action" else: extra_s = "action: {:#x}".format(action_table_pos + action - 1) entries.append(self.DataEntry( pos, data[pos:new_pos], "action", action, extra_s, )) pos = new_pos table_cnt += 1 # parse action_table max_ar_filter = 0 table_cnt = 0 if max_action: action_table_end_pos = action_table_pos + max_action + 1 while pos < action_table_end_pos: entries.append(self.SeparatorEntry(pos, "Action table[{:4d}]".format(table_cnt))) new_pos, ar_filter = self.get_sleb128(data, pos) if ar_filter == 0: extra_s = "cleanup" else: enc_size = self.encoded_ptr_size(ttype_encoding, ptr_size) extra_s = "catch typeinfo: {:#x}".format(ttype_base - ar_filter * enc_size) entries.append(self.DataEntry( pos, data[pos:new_pos], "action_record_filter", ar_filter, extra_s, )) max_ar_filter = max(ar_filter, max_ar_filter) pos = new_pos new_pos, ar_disp = self.get_sleb128(data, pos) if ar_disp & 1: extra_s = "-> Action Table[{:4d}]".format(table_cnt + (ar_disp + 1) // 2) elif ar_disp: extra_s = "-> ???" else: extra_s = "list end" entries.append(self.DataEntry( pos, data[pos:new_pos], "action_record_next", ar_disp, extra_s, )) pos = new_pos table_cnt += 1 # parse ttype_table if max_ar_filter > 0 and ttype_base is not None: enc_size = self.encoded_ptr_size(ttype_encoding, ptr_size) new_pos = ttype_base - max_ar_filter * enc_size if pos != new_pos: entries.append(self.SeparatorEntry(pos, "Padding")) entries.append(self.DataEntry(pos, data[pos:new_pos], "padding")) pos = new_pos current_ar_filter = max_ar_filter while pos < ttype_base: entries.append(self.SeparatorEntry(pos, "TType table[{:4d}]".format(current_ar_filter))) new_pos, ttype = self.read_encoded(ttype_encoding, data, pos) if (ttype_encoding & 0x70) == self.DW_EH_PE_pcrel: ttype_pointer = shdr.sh_offset + pos + ttype if ttype: if self.elf.is_pie(): extra_s = "TType pointer vma: $codebase+{:#x}".format(load_base + ttype_pointer) else: extra_s = "TType pointer vma: {:#x}".format(load_base + ttype_pointer) else: extra_s = "" entries.append(self.DataEntry(pos, data[pos:new_pos], "ttype", ttype, extra_s)) else: entries.append(self.DataEntry(pos, data[pos:new_pos], "ttype", ttype, "TType pointer")) pos = new_pos current_ar_filter -= 1 if ttype_base is not None: entries.append(self.SeparatorEntry(ttype_base, "TType table base (Stored upwards)")) # next LSDA if ttype_base is None: pass else: pos = ttype_base lsda_table_cnt += 1 except (KeyError, IndexError, ValueError): _exc_type, exc_value, _exc_traceback = sys.exc_info() entries.append(self.ErrorEntry("Parse Error", exc_value)) return entries def read_section(self, section_name): shdr = self.elf.get_shdr(section_name) if shdr is None: err("Could not find {} section".format(section_name)) return None f = open(self.elf.filename, "rb") f.seek(shdr.sh_offset) data = f.read(shdr.sh_size) f.close() info("Found {} section".format(section_name)) dic = {"name": section_name, "offset": shdr.sh_offset, "data": data} Section = collections.namedtuple("Section", dic.keys()) return Section(*dic.values()) @parse_args @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): local_filepath = None remote_filepath = None tmp_filepath = None if args.remote: if not is_remote_debug(): err("-r option is allowed only remote debug") return if args.file: remote_filepath = args.file # if specified, assume it is remote elif gdb.current_progspace().filename: f = gdb.current_progspace().filename if f.startswith("target:"): # gdbserver f = f[7:] remote_filepath = f elif Pid.get_pid(remote=True): remote_filepath = "/proc/{:d}/exe".format(Pid.get_pid(remote=True)) else: err("File name could not be determined") return data = Path.read_remote_file(remote_filepath, as_byte=True) # qemu-user is failed here, it is ok if not data: err("Failed to read remote filepath") return tmp_fd, tmp_filepath = GefUtil.mkstemp(prefix="dwarf-exception-handler", suffix=".elf") os.fdopen(tmp_fd, "wb").write(data) local_filepath = tmp_filepath del data elif args.file: local_filepath = args.file elif args.file is None: if is_qemu_system(): err("Argument-less calls are unsupported under qemu-system") return local_filepath = Path.get_filepath() if local_filepath is None: err("File name could not be determined") return def unlink_tmp_filepath(tmp_filepath): if tmp_filepath and os.path.exists(tmp_filepath): os.unlink(tmp_filepath) return self.elf = Elf.get_elf(local_filepath) if self.elf is None or not self.elf.is_valid(): err("Failed to parse ELF") unlink_tmp_filepath(tmp_filepath) return # read section eh_frame_hdr = self.read_section(".eh_frame_hdr") if eh_frame_hdr is None: unlink_tmp_filepath(tmp_filepath) return eh_frame = self.read_section(".eh_frame") if eh_frame is None: unlink_tmp_filepath(tmp_filepath) return gcc_except_table = self.read_section(".gcc_except_table") if gcc_except_table is None: unlink_tmp_filepath(tmp_filepath) return # parse section self.out = [] entries1 = self.parse_eh_frame_hdr(eh_frame_hdr) self.out += self.format_entry(eh_frame_hdr, entries1) entries2 = self.parse_eh_frame(eh_frame) self.out += self.format_entry(eh_frame, entries2) entries3 = self.parse_gcc_except_table(gcc_except_table, entries2) self.out += self.format_entry(gcc_except_table, entries3) # print self.print_output() unlink_tmp_filepath(tmp_filepath) return @register_command class MultiBreakCommand(GenericCommand): """Set multiple breakpoints easily.""" _cmdline_ = "multi-break" _category_ = "01-b. Debugging Support - Breakpoint" parser = argparse.ArgumentParser(prog=_cmdline_, add_help=False) parser.add_argument("location", metavar="LOCATION", nargs="+", type=AddressUtil.parse_address, help="the address(es) to set breakpoint.") _syntax_ = parser.format_help() _note_ = [ "This command is intended to improve the readability of history", "by allowing you to set multiple breakpoints on a single line.", ] _note_ = "\n".join(_note_) @parse_args def do_invoke(self, args): for bp in args.location: gdb.execute("b *{:#x}".format(bp)) return @register_command class MainBreakCommand(GenericCommand): """Set a breakpoint at the beginning of main with or without symbols, then continue.""" _cmdline_ = "main-break" _category_ = "01-b. Debugging Support - Breakpoint" parser = argparse.ArgumentParser(prog=_cmdline_, add_help=False) _syntax_ = parser.format_help() def get_libc_start_main(self): try: return AddressUtil.parse_address("__libc_start_main") except gdb.error: pass ret = gdb.execute("got --no-pager --quiet __libc_start_main", to_string=True) ret = ret.strip() if not ret: err("Failed to resolve __libc_start_main") return None elem = Color.remove_color(ret).splitlines()[0].split() if elem[-1].endswith(">"): return int(elem[-2], 16) else: return int(elem[-1], 16) def search_main(self): libc_start_main = self.get_libc_start_main() if libc_start_main is None: return None if libc_start_main == 0: elf = Elf.get_elf() if elf is None or not elf.is_valid(): return None entry = elf.e_entry if elf.is_pie(): codebase = ProcessMap.get_codebase() if codebase is None: return None entry += codebase EntryBreakBreakpoint("*{:#x}".format(entry)) ContextCommand.hide_context() gdb.execute("continue") # do not use c wrapper ContextCommand.unhide_context() libc_start_main = self.get_libc_start_main() if libc_start_main == 0: # something is wrong return None EntryBreakBreakpoint("*{:#x}".format(libc_start_main)) ContextCommand.hide_context() gdb.execute("continue") # do not use c wrapper ContextCommand.unhide_context() # get first arg when break at __libc_start_main _, val = current_arch.get_ith_parameter(0) return val @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): try: main_address = AddressUtil.parse_address("main") except gdb.error: main_address = self.search_main() if main_address is None: err("Failed to set a breakpoint to main") return EntryBreakBreakpoint("*{:#x}".format(main_address)) ContextCommand.hide_context() try: gdb.execute("continue") # do not use c wrapper except gdb.error as e: err(str(e)) ContextCommand.unhide_context() return ContextCommand.unhide_context() gdb.execute("context") return @register_command class LoadBreakCommand(GenericCommand): """Break if something is loaded (wrapper of `set stop-on-solib-events 1`).""" _cmdline_ = "load-break" _category_ = "01-b. Debugging Support - Breakpoint" _repeat_ = True parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running def do_invoke(self, args): if gdb.parameter("stop-on-solib-events"): err("stop-on-solib-events is already 1") return before = gdb.execute("vmmap --quiet --no-pager", to_string=True) gdb.execute("set stop-on-solib-events 1") gdb.execute("continue") if not is_alive(): return gdb.execute("set stop-on-solib-events 0") after = gdb.execute("vmmap --quiet --no-pager", to_string=True) import difflib res = difflib.ndiff(before.splitlines(), after.splitlines()) res = [line for line in res if line.startswith(("+", "-"))] if res: gef_print(titlify("Memory map diff")) gef_print("\n".join(res)) return class EntryBreakBreakpoint(gdb.Breakpoint): """Breakpoint used internally to stop execution at the most convenient entry point.""" def __init__(self, location): super().__init__(location, gdb.BP_BREAKPOINT, internal=True, temporary=True) self.silent = True return def stop(self): EventHandler.__gef_check_disabled_bp__ = True self.enabled = False Cache.reset_gef_caches() return True @register_command class EntryBreakCommand(GenericCommand): """Try to find best entry point and set a temporary breakpoint on it.""" _cmdline_ = "entry-break" _category_ = "01-b. Debugging Support - Breakpoint" _aliases_ = ["start"] parser = argparse.ArgumentParser(prog=_cmdline_, add_help=False) _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): super().__init__(complete=gdb.COMPLETE_FILENAME) self.add_setting( "entrypoint_symbols", " ".join([ "main", # glibc "__libc_start_main", # glibc "__uClibc_main", # uClibc "_start", # glibc "__start", # used by MIPS "'main.main'", # Golang "'start._start'", # zig ]), "Possible symbols for entry points", ) return @staticmethod def stop_callback(_): # unhook EventHooking.gef_on_new_unhook(EntryBreakCommand.stop_callback) ContextCommand.unhide_context() # get section fpath = Path.get_filepath() executable_section = ProcessMap.process_lookup_path(fpath, perm_mask=Permission.EXECUTE) if executable_section is None: # for context.disable_vmmap next_insn = get_insn_next(current_arch.pc) info("Breaking at: {:#x}".format(next_insn.address)) EntryBreakBreakpoint("*{:#x}".format(next_insn.address)) elif executable_section.page_start <= current_arch.pc < executable_section.page_end: # already stopped around entry point. # However, it automatically resumes execution, so we need a breakpoint. next_insn = get_insn_next(current_arch.pc) info("Breaking at: {:#x}".format(next_insn.address)) EntryBreakBreakpoint("*{:#x}".format(next_insn.address)) else: # stopped in ld, so continue to entry-point. base_address = ProcessMap.process_lookup_path(fpath).page_start entry_address = base_address + Elf.get_elf(fpath).e_entry info("Breaking at entry-point: {:#x}".format(entry_address)) EntryBreakBreakpoint("*{:#x}".format(entry_address)) # automatically continue return # Need not @parse_args because argparse can't stop interpreting argument for start. @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, argv): if is_alive(): if is_remote_debug(): err("Unsupported gdb mode") return gdb.execute("kill", to_string=True) fpath = Path.get_filepath() if fpath is None: warn("No executable to debug, use `file` to load a binary") return if not os.access(fpath, os.X_OK): warn("The file {!r} is not executable".format(fpath)) return elf = Elf.get_elf(fpath) if elf is None or not elf.is_valid(): warn("Invalid ELF") return # use symbol if loaded entrypoints = Config.get_gef_setting("entry_break.entrypoint_symbols").split() for sym in entrypoints: try: value = AddressUtil.parse_address(sym) except gdb.error: continue # symbol found info("Breaking at {:#x} ({:s})".format(value, sym)) EntryBreakBreakpoint(sym) gdb.execute("run {:s}".format(" ".join(argv))) return # no symbols. use elf entry point # non-PIE if not elf.is_pie(): entry = elf.e_entry info("Breaking at entry-point: {:#x}".format(entry)) EntryBreakBreakpoint("*{:#x}".format(entry)) gdb.execute("run {}".format(" ".join(argv))) return # PIE warn("PIC binary detected, retrieving text base address") # Some ELF does not use ld. (e.g., ELF built by zig) # So use gef_on_new_hook (use gdb.events.new_objfile internally), # instead of `set stop-on-solib-events 1` because shared object are never loaded. # At least gdb 10.1 (Ubuntu 18.04) supports gdb.events.new_objfile. ContextCommand.hide_context() EventHooking.gef_on_new_hook(EntryBreakCommand.stop_callback) gdb.execute("run {}".format(" ".join(argv))) return class CommandBreakBreakpoint(gdb.Breakpoint): """Breakpoint which executes user-defined command silently and continue.""" def __init__(self, loc, cmd): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=False, temporary=False) self.cmd = cmd return def stop(self): Cache.reset_gef_caches() gdb.execute(self.cmd) return False @register_command class CommandBreakCommand(GenericCommand): """Set a breakpoint which executes user-defined command silently and continue, if hit.""" _cmdline_ = "command-break" _category_ = "01-b. Debugging Support - Breakpoint" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address to set a breakpoint. (default: current_arch.pc)") parser.add_argument("command", metavar="COMMAND", type=str, help="the command executed if breakpoint is hit.") _syntax_ = parser.format_help() _example_ = [ '{0:s} 0x55555555aab9 "hexdump -n $sp+0x120"', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args def do_invoke(self, args): location = args.location if location is None: location = current_arch.pc CommandBreakBreakpoint(location, args.command) return class RegisterDumpBreakBreakpoint(gdb.Breakpoint): """Breakpoint which dump registers silently and continue.""" def __init__(self, loc, tag, regs): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=False, temporary=False) self.loc = loc self.tag = tag self.regs = regs return def stop(self): Cache.reset_gef_caches() out = [] for r in self.regs: try: v = get_register(r) except gdb.error: continue out.append("{:s}={:#x}".format(r, v)) colored_addr = Color.colorify_hex(self.loc, "bold yellow") tag = "" if self.tag: tag = "{:s}: ".format(self.tag) gef_print("{:s}: {:s}{:s}".format(colored_addr, tag, ", ".join(out))) return False @register_command class RegisterDumpBreakCommand(GenericCommand): """Set a breakpoint which dumps registers silently and continue, if hit.""" _cmdline_ = "regdump-break" _category_ = "01-b. Debugging Support - Breakpoint" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address to set a breakpoint. (default: current_arch.pc)") parser.add_argument("-t", "--tag", help="the tag if breakpoint is hit.") parser.add_argument("-r", "--regs", action="append", help="the register name dumped if breakpoint is hit.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x55555555aab9 -r rax", '{0:s} 0x55555555aab9 -t "state changed" -r rax', ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @require_arch_set def do_invoke(self, args): if not args.regs: self.usage() return location = args.location if location is None: location = current_arch.pc RegisterDumpBreakBreakpoint(location, args.tag, args.regs) return class TakenOrNotBreakpoint(gdb.Breakpoint): """Breakpoint which only branch is taken or not.""" def __init__(self, loc, taken, is_hwbp): if is_hwbp: bp_type = gdb.BP_HARDWARE_BREAKPOINT else: bp_type = gdb.BP_BREAKPOINT super().__init__("*{:#x}".format(loc), bp_type, internal=False) self.loc = loc self.taken = taken return def stop(self): Cache.reset_gef_caches() insn = get_insn() if not current_arch.is_conditional_branch(insn): return False # continue taken, _ = current_arch.is_branch_taken(insn) if self.taken: return taken else: return not taken @register_command class BreakIfTakenCommand(GenericCommand): """Set a breakpoint which breaks if branch is taken.""" _cmdline_ = "break-if-taken" _category_ = "01-b. Debugging Support - Breakpoint" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address to set breakpoint.") parser.add_argument("--hw", action="store_true", help="use hardware breakpoint.") _syntax_ = parser.format_help() @parse_args @require_arch_set def do_invoke(self, args): TakenOrNotBreakpoint(args.location, True, args.hw) return @register_command class BreakIfNotTakenCommand(GenericCommand): """Set a breakpoint which breaks if branch is not taken.""" _cmdline_ = "break-if-not-taken" _category_ = "01-b. Debugging Support - Breakpoint" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address to set breakpoint.") parser.add_argument("--hw", action="store_true", help="use hardware breakpoint.") _syntax_ = parser.format_help() @parse_args @require_arch_set def do_invoke(self, args): TakenOrNotBreakpoint(args.location, False, args.hw) return @register_command class ContextCommand(GenericCommand): """Display various information every time GDB hits a breakpoint.""" _cmdline_ = "context" _category_ = "01-a. Debugging Support - Context" _aliases_ = ["ctx"] parser = argparse.ArgumentParser(prog=_cmdline_) commands = [ [], "legend", "regs", "stack", "code", "mem_access", "args", "source", "mem_watch", "trace", "threads", "extra", "on", "off", ] parser.add_argument("commands", nargs="*", choices=commands, default=[], metavar="{legend,regs,stack,code,mem_access,args,source,mem_watch,trace,threads,extra}|{on,off}", help="invoke each pane individually, or temporarily control the output.") parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() _note_ = [ 'If "on" or "off" is specified, that operation takes precedence.', "`context XXX YYY` invokes the `context-XXX` command and then the `context-YYY` command, in that order.", "There are various configuration options that modify the behavior of context. You can list them with gef config context.", ] _note_ = "\n".join(_note_) context_hidden = False @staticmethod def hide_context(): ContextCommand.context_hidden = True return @staticmethod def unhide_context(): ContextCommand.context_hidden = False return @staticmethod def is_hide(): if ContextCommand.context_hidden: return True enabled = Config.get_gef_setting("context.enable") if not enabled: return True return False def __init__(self): super().__init__(complete="use_user_complete") self.add_setting("enable", True, "Enable/disable printing the context when breaking") self.add_setting("nb_max_string_length", 0x40, "Number of bytes of strings to show") self.add_setting("clear_screen", True, "Clear the screen before printing the context") default_legend = "legend regs stack code mem_access args source mem_watch threads trace extra" self.add_setting("layout", default_legend, "Order of sections (add '-' to skip: trace -> -trace)") self.add_setting("smart_cpp_function_name", False, "Print cpp function name without args if demangled") self.add_setting("enable_auto_switch_for_i8086", True, "Enable auto architecture switching for i8086 <-> x86-32") self.add_setting("disable_vmmap", False, "Disable memory map generation to speed up (e.g., for firmware debugging)") self.add_setting("disable_auxv", False, "Disable scanning auxv from memory to speed up (e.g., for firmware debugging)") self.add_setting("redirect", "", "Default target tty name to redirect `context` to") EventHooking.gef_on_continue_hook(ContextRegistersCommand.update_registers) EventHooking.gef_on_continue_hook(ContextExtraCommand.empty_extra_messages) return def complete(self, text, word): # noqa if text == "": # no prefix return [s for s in self.commands if ((word is None) or (s and word in s))] if text.strip().split()[-1] in self.commands: # already matched, but repeat again return [s for s in self.commands if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.commands if s and s.startswith(text.strip().split()[-1])] @staticmethod @Cache.cache_this_session def get_redirect(section, ignore_redirect): if ignore_redirect: return None redirect = Config.get_gef_setting("context_{:s}.redirect".format(section)) if redirect: return redirect redirect = Config.get_gef_setting("context.redirect") if redirect: return redirect return None @staticmethod def context_title(m, redirect=None): line_color = Config.get_gef_setting("theme.context_title_line") msg_color = Config.get_gef_setting("theme.context_title_message") HORIZONTAL_LINE = "-" _, tty_columns = GefUtil.get_terminal_size(redirect=redirect) if not m: title = Color.colorify(HORIZONTAL_LINE * tty_columns, line_color) else: trail_len = len(m) + 6 width = max(tty_columns - trail_len, 0) title = "" title += Color.colorify(HORIZONTAL_LINE * width + " ", line_color) title += Color.colorify(m, msg_color) title += Color.colorify(" " + HORIZONTAL_LINE * 4, line_color) gef_print(title, redirect=redirect) return @staticmethod def execute_command(cmd, redirect=None): if redirect: res = gdb.execute(cmd, to_string=True) gef_print(res.rstrip(), redirect=redirect) else: gdb.execute(cmd) return def i386_auto_switch(self): if not Config.get_gef_setting("context.enable_auto_switch_for_i8086"): return # check whether protected mode or not. # even if `CR0.PE=1`, it will not switch until `ljmp`. # so it is better to judge whether `$cs=0x8` or not. # https://wiki.osdev.org/Protected_Mode cs = get_register("$cs") if cs is None or cs == 8: set_arch("x86") else: set_arch("i8086") return @Cache.cache_this_session def get_order_in_each_pane(self, current_layout, ignore_redirect): order_in_pane = {} for section in current_layout: # target layout is disabled if section[0] == "-": continue redirect = ContextCommand.get_redirect(section, ignore_redirect) order_in_pane[redirect] = order_in_pane.get(redirect, []) + [section] order_info = {} for redirect, order in order_in_pane.items(): for i, section in enumerate(order): is_head = i == 0 is_tail = i == len(order) - 1 order_info[section] = [is_head, is_tail, redirect] return order_info def clear_screen(self, redirect): if not Config.get_gef_setting("context.clear_screen"): return if redirect: gef_print("\x1b[H\x1b[2J", end="", redirect=redirect) return if len(self.args.commands) == 0: # this is more faster than executing "shell clear -x" print("\x1b[H\x1b[2J", end="") return @parse_args def do_invoke(self, args): # check on/off if "off" in args.commands: if len(args.commands) > 1: self.usage() return ContextCommand.hide_context() return if "on" in args.commands: if len(args.commands) > 1: self.usage() return ContextCommand.unhide_context() return # check config if ContextCommand.is_hide(): return # check running or not if not is_alive(): warn("No debugging session active") return if gdb.selected_thread().is_running(): # If the thread is running, do nothing (just to be safe) return # check layout if len(args.commands) > 0: current_layout = args.commands else: current_layout = Config.get_gef_setting("context.layout").strip().split() if not current_layout: return # get info for clear_screen and last line order_info = self.get_order_in_each_pane(tuple(current_layout), args.ignore_redirect) # for create command if args.ignore_redirect: opts = "-i" else: opts = "" # i386 auto switch if is_qemu_system() and get_arch() == "i8086": self.i386_auto_switch() # do each layout for section in current_layout: # If a process is terminated while the context command is executing, # the output will be distorted, so it is a good idea to check by every loop. if not is_alive(): break # target layout is disabled if section[0] == "-": continue is_head, is_tail, redirect = order_info[section] # clear screen if is_head: self.clear_screen(redirect) # call each context sub command try: gdb.execute("context-{:s} {:s}".format(section, opts)) except gdb.error: exc_type, exc_value, exc_traceback = sys.exc_info() gef_print(exc_value, redirect=redirect) # for time measurement #from cProfile import Profile #import pstats #pr = Profile() #pr.runcall(lambda: gdb.execute("context-{:s} {:s}".format(section, opts))) #stats = pstats.Stats(pr) #stats.sort_stats("tottime") #stats.print_stats(10) # last line if is_tail: ContextCommand.context_title("", redirect) return @register_command class ContextLegendCommand(GenericCommand): """Context internal command to display the legend.""" _cmdline_ = "context-legend" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context legend` to") return @staticmethod @Cache.cache_this_session def get_context_legend(): if is_qemu_system() or is_kgdb() or is_vmware(): return None if Config.get_gef_setting("gef.disable_color"): return None legend = "[ Legend: {:s} ]".format( " | ".join([ Color.colorify("Modified register", Config.get_gef_setting("theme.registers_value_changed")), Color.colorify("Code", Config.get_gef_setting("theme.address_code")), Color.colorify("Heap", Config.get_gef_setting("theme.address_heap")), Color.colorify("Stack", Config.get_gef_setting("theme.address_stack")), Color.colorify("Writable", Config.get_gef_setting("theme.address_writable")), Color.colorify("ReadOnly", Config.get_gef_setting("theme.address_readonly")), Color.colorify("None", Config.get_gef_setting("theme.address_valid_but_none")), Color.colorify("RWX", Config.get_gef_setting("theme.address_rwx")), Color.colorify("String", Config.get_gef_setting("theme.dereference_string")), ]), ) return legend def context_legend(self, redirect): legend = ContextLegendCommand.get_context_legend() if legend: gef_print(legend, redirect=redirect) return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("legend", args.ignore_redirect) try: self.context_legend(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class ContextRegistersCommand(GenericCommand): """Context internal command to display registers.""" _cmdline_ = "context-regs" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() old_registers = {} previous_extra_regs = {} def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context regs` to") self.add_setting("show_registers_raw", False, "Show the registers pane with raw values (no dereference)") self.add_setting("ignore_registers", "", "Space-separated list of registers not to display (e.g., '$cs $ds $gs')") self.add_setting("show_errno", True, "Show errno after a syscall instruction") self.add_setting("show_mmx_xmm_ymm_fpu", True, "Show MMX/XMM/YMM/FPU registers when stopped at a related instruction") return @staticmethod def update_registers(_event): if current_arch is None: return for reg in current_arch.all_registers: try: ContextRegistersCommand.old_registers[reg] = get_register(reg) except Exception: ContextRegistersCommand.old_registers[reg] = 0 if is_x86(): for reg in current_arch.virtual_registers: try: ContextRegistersCommand.old_registers[reg] = gdb.parse_and_eval(reg) except gdb.error: ContextRegistersCommand.old_registers[reg] = 0 if is_x86_16(): for regname, (seg, reg) in current_arch.seg_extended_registers.items(): segval = get_register(seg) & 0xffff regval = get_register(reg) & 0xffff value = current_arch.real2phys(segval, regval) ContextRegistersCommand.old_registers[regname] = value return RE_SUB_OPERAND1 = re.compile(r"<.*?>") RE_SUB_OPERAND2 = re.compile(r"\[.*?\]") RE_FINDALL_SSE = re.compile(r"(xmm\d+)") RE_FINDALL_AVX = re.compile(r"(ymm\d+)") RE_FINDALL_MMX = re.compile(r"([^xy]mm\d+)") RE_FINDALL_FPU = re.compile(r"(st\(\d\))") def context_regs_extra(self, redirect): if not Config.get_gef_setting("context_regs.show_mmx_xmm_ymm_fpu"): return if not is_x86(): return try: insn = get_insn() insn_prev = get_insn_prev() except gdb.MemoryError: self.previous_extra_regs = {} return if insn is None: return if insn_prev is None: return operands = ", ".join(insn.operands) operands = self.RE_SUB_OPERAND1.sub("", operands) operands = self.RE_SUB_OPERAND2.sub("", operands) if self.previous_extra_regs: if self.previous_extra_regs["pc"] != insn_prev.address: self.previous_extra_regs = {} printed_extra_regs = {"pc": current_arch.pc} # sse register to_save_regs = self.RE_FINDALL_SSE.findall(operands) to_print_regs = to_save_regs + self.previous_extra_regs.get("xmm", []) if to_print_regs: to_print_regs = sorted(set(to_print_regs)) lines = gdb.execute("xmm", to_string=True).splitlines() for reg in to_print_regs: for line in lines: if ("$" + reg) in line.split(":")[0]: gef_print(line, redirect=redirect) if reg in to_save_regs: printed_extra_regs["xmm"] = printed_extra_regs.get("xmm", []) + [reg] break # avx register to_save_regs = self.RE_FINDALL_AVX.findall(operands) to_print_regs = to_save_regs + self.previous_extra_regs.get("ymm", []) if to_print_regs: to_print_regs = sorted(set(to_print_regs)) lines = gdb.execute("ymm", to_string=True).splitlines() for reg in to_print_regs: for line in lines: if ("$" + reg) in line.split(":")[0]: gef_print(line, redirect=redirect) if reg in to_save_regs: printed_extra_regs["ymm"] = printed_extra_regs.get("ymm", []) + [reg] break # mmx register to_save_regs = self.RE_FINDALL_MMX.findall(operands) to_print_regs = to_save_regs + self.previous_extra_regs.get("mmx", []) if to_print_regs: to_print_regs = sorted(set(to_print_regs)) lines = gdb.execute("mmx", to_string=True).splitlines() for reg in to_print_regs: for line in lines: if ("$" + reg) in line.split(":")[0]: gef_print(line, redirect=redirect) if reg in to_save_regs: printed_extra_regs["mmx"] = printed_extra_regs.get("mmx", []) + [reg] break # fpu register if insn.mnemonic[0] == "f": to_save_regs = self.RE_FINDALL_FPU.findall(operands) to_print_regs = to_save_regs + self.previous_extra_regs.get("fpu", []) if to_print_regs: to_print_regs = sorted(set(to_print_regs)) lines = gdb.execute("fpu", to_string=True).splitlines() for reg in to_print_regs: for line in lines: if ("$" + re.sub(r"[()]", reg, "")) in line.split(":")[0]: gef_print(line, redirect=redirect) if reg in to_save_regs: printed_extra_regs["fpu"] = printed_extra_regs.get("fpu", []) + [reg] break self.previous_extra_regs = printed_extra_regs return def context_regs_syscall_errno(self, redirect): if not Config.get_gef_setting("context_regs.show_errno"): return if is_qemu_system() or is_kgdb() or is_kdb() or is_vmware() or is_wine(): return if current_arch is None: return if current_arch.return_register is None: return if current_arch.ptrsize not in [4, 8]: return regvalue = get_register(current_arch.return_register) if not AddressUtil.is_msb_on(regvalue): return try: insn_prev = get_insn_prev() except gdb.MemoryError: return if insn_prev is None: return if not current_arch.is_syscall(insn_prev): return if current_arch.ptrsize == 4: val = struct.unpack(" 3: # e.g., eflags 0x206 [ PF IF ] special_lines.append(line) continue lines.append(s[:2]) if len(lines) <= len(special_lines): # something is wrong ContextCommand.execute_command("info registers", redirect) return while len(lines) % 3: lines.append("") first_half = lines[:len(lines) // 3] second_half = lines[len(lines) // 3:][:len(lines) // 3] third_half = lines[len(lines) // 3:] pipe = Color.cyanify("|") final_lines = [] for f, s, t in zip(first_half, second_half, third_half): final_lines.append("{:10s} {:20s} {:s} {:10s} {:20s} {:s} {:10s} {:20s}".format( *f, pipe, *s, pipe, *t, )) final_lines = "\n".join(final_lines + special_lines) gef_print(final_lines, redirect=redirect) return try: compact_info_registers() except Exception: ContextCommand.execute_command("info registers", redirect) return def context_registers(self, redirect): ContextCommand.context_title("registers", redirect) if current_arch is None: self.context_registers_default(redirect) return target_registers = self.get_target_registers() # exec registers if Config.get_gef_setting("context_regs.show_registers_raw"): opt = "-s" else: opt = "" ContextCommand.execute_command("registers {:s} {:s}".format(opt, target_registers), redirect) # for extra regs self.context_regs_extra(redirect) # for x86 only (xmm, ymm, ...) self.context_regs_syscall_errno(redirect) return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("regs", args.ignore_redirect) try: self.context_registers(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class ContextStackCommand(GenericCommand): """Context internal command to display stack.""" _cmdline_ = "context-stack" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context stack` to") self.add_setting("show_stack_raw", False, "Show the stack pane as raw hexdump (no dereference)") self.add_setting("nb_lines", 8, "Number of line in the stack pane") return def context_stack_default(self, redirect): try: res = gdb.execute("info register $sp", to_string=True) except gdb.error: err("Failed to get value of $SP", redirect=redirect) return try: sp = int(res.split()[1], 0) except (IndexError, ValueError): err("Failed to get value of $SP", redirect=redirect) return try: data = read_memory(sp, 0x40) except gdb.MemoryError: err("Failed to read from $sp", redirect) return unit = AddressUtil.get_memory_alignment() hexdata = hexdump(data, base=sp, unit=unit) gef_print(hexdata, redirect=redirect) return def context_stack(self, redirect): ContextCommand.context_title("stack", redirect) if current_arch is None: self.context_stack_default(redirect) return if current_arch.sp is None: err("Failed to get value of $SP", redirect=redirect) return show_raw = Config.get_gef_setting("context_stack.show_stack_raw") nb_lines = Config.get_gef_setting("context_stack.nb_lines") if show_raw is True: try: mem = read_memory(current_arch.sp, 0x10 * nb_lines) gef_print(hexdump(mem, base=current_arch.sp), redirect=redirect) except gdb.MemoryError: err("Cannot read memory from $SP (corrupted stack pointer?)", redirect=redirect) return if current_arch.stack_grow_down: nb_lines *= -1 ContextCommand.execute_command( "dereference {:#x} {:d} --no-pager".format(current_arch.sp, nb_lines), redirect, ) return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("stack", args.ignore_redirect) try: self.context_stack(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class ContextCodeCommand(GenericCommand): """Context internal command to display code.""" _cmdline_ = "context-code" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() context_comments = {} def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context code` to") self.add_setting("show_opcodes_size", 8, "Number of bytes of opcodes to display next to the disassembly") self.add_setting("show_opcodes_size_x64_x86", 10, "Number of bytes of opcodes to display next to the disassembly") self.add_setting("peek_call", True, "Peek into call opcode") self.add_setting("peek_conditional_branch", True, "Emulates a conditional branch and peeks if it is taken") self.add_setting("peek_jump", True, "Peek into jump opcode") self.add_setting("peek_ret", True, "Peek into ret opcode") self.add_setting("use_native_x_command", False, "Use x/16i instead of Disasm.gef_disassemble") self.add_setting("use_capstone", False, "Use capstone as disassembler in the code pane (instead of GDB)") self.add_setting("nb_lines", 6, "Number of instruction after $pc") self.add_setting("nb_lines_prev", 3, "Number of instruction before $pc") return RE_SUB_BRANCH_ADDR1 = re.compile(r".*# (0x[a-fA-F0-9]+).*") RE_SUB_BRANCH_ADDR2 = re.compile(r".*# (0x[a-fA-F0-9]+).*") RE_SUB_BRANCH_ADDR3 = re.compile(r".* (PTR|ptr) \[(.+?)\].*") RE_SUB_BRANCH_ADDR4 = re.compile(r".* (PTR|ptr) fs:\[?(0x[a-fA-F0-9]+)\]?.*") RE_SUB_BRANCH_ADDR5 = re.compile(r".* (PTR|ptr) gs:\[?(0x[a-fA-F0-9]+)\]?.*") RE_SUB_BRANCH_ADDR6 = re.compile(r"^.*\s+([-0-9]+)$") RE_SUB_BRANCH_ADDR7 = re.compile(r".*(0x[a-fA-F0-9]+).*") @staticmethod def get_branch_addr(insn, to_str=False): ops = " ".join(insn.operands) ops = re.sub(r"<.*?>", "", ops) # is there an evaluated immediate value? # x86/x64 (default): call ... [rip+0x1111] # 0xAABBCCDD if " # 0x" in ops and not is_loongarch64(): addr = ContextCodeCommand.RE_SUB_BRANCH_ADDR1.sub(r"\1", ops) ptr = to_unsigned_long(gdb.parse_and_eval(addr)) try: if to_str: return "{:#x}".format(read_int_from_memory(ptr)) else: return read_int_from_memory(ptr) except gdb.MemoryError: if to_str: return "*{:#x}".format(ptr) else: return None # is there an evaluated immediate value? # loongarch64: bnez $t1, -8 (0x7ffff8) # 0x120000868 if " # 0x" in ops and is_loongarch64(): addr = ContextCodeCommand.RE_SUB_BRANCH_ADDR2.sub(r"\1", ops) ptr = to_unsigned_long(gdb.parse_and_eval(addr)) if to_str: return "{:#x}".format(ptr) else: return ptr # is there a memory reference by register? # x86/x64 (default): call ... PTR [rbx] # x86/x64 (capstone): call ... ptr [rbx] # x64 (capstone): call ... ptr [rip + 0x1111] if is_x86(): if " PTR [" in ops or " ptr [" in ops: addr = ContextCodeCommand.RE_SUB_BRANCH_ADDR3.sub(r"\2", ops) for gr in current_arch.general_registers: addr = addr.replace(gr.replace("$", ""), gr) if is_x86_64(): addr = addr.replace("$rip", "$rip+{:#x}".format(len(insn.opcodes))) try: ptr = to_unsigned_long(gdb.parse_and_eval(addr)) except gdb.error: return None try: if to_str: return "{:#x}".format(read_int_from_memory(ptr)) else: return read_int_from_memory(ptr) except gdb.MemoryError: if to_str: return "*{:#x}".format(ptr) else: return None # is there a segment relative? # x64 (default): call ... PTR fs:0x10 # x64 (capstone): call ... ptr fs:[0x10] if is_x86_64(): if " PTR fs:" in ops or " ptr fs:" in ops: ofs = ContextCodeCommand.RE_SUB_BRANCH_ADDR4.sub(r"\2", ops) ofs = to_unsigned_long(gdb.parse_and_eval(ofs)) fs = current_arch.get_fs() try: if to_str: return "{:#x}".format(read_int_from_memory(fs + ofs)) else: return read_int_from_memory(fs + ofs) except gdb.MemoryError: if to_str: return "*{:#x}".format(fs + ofs) else: return None # is there a segment relative? # x86 (default): call ... PTR gs:0x10 # x86 (capstone): call ... ptr gs:[0x10] if is_x86_32(): if " PTR gs:" in ops or " ptr gs:" in ops: ofs = ContextCodeCommand.RE_SUB_BRANCH_ADDR5.sub(r"\2", ops) ofs = to_unsigned_long(gdb.parse_and_eval(ofs)) gs = current_arch.get_gs() try: if to_str: return "{:#x}".format(read_int_from_memory(gs + ofs)) else: return read_int_from_memory(gs + ofs) except gdb.MemoryError: if to_str: return "*{:#x}".format(gs + ofs) else: return None # is there a relative immediate? # microblaze: brlid r15, -136 # microblaze: bneid r4, -8 // 3ffe9848 if is_microblaze(): addr = ContextCodeCommand.RE_SUB_BRANCH_ADDR6.sub(r"\1", ops.split("//")[0].strip()) try: addr = int(addr) + insn.address if to_str: return "{:#x}".format(addr) else: return addr except Exception: pass # is there a absolute immediate value (with segment)? # x86_16: ljmp 0xf000:0xe05b if is_x86_16() and ":0x" in ops: seg, val = [int(x, 16) for x in ops.split(":")] if ops.startswith("0x"): addr = current_arch.real2phys(seg, val) else: addr = val if to_str: return "{:#x}".format(addr) else: return addr # is there a absolute immediate value? # s390x: bra 0x3ffdfc60 # s390x: brasl %r14, 0x1020b50 # RISCV: jal ra, 0x13894 # RISCV: bgeu t1, a2, 0x10350 if "0x" in ops: addr = ContextCodeCommand.RE_SUB_BRANCH_ADDR7.sub(r"\1", ops) if to_str: return "{:#x}".format(to_unsigned_long(gdb.parse_and_eval(addr))) else: return to_unsigned_long(gdb.parse_and_eval(addr)) # is there register(s)? # x86/x64: call rax # s390x: basr %lr, %r1 # sh4: jsr @r1 # alpha: jmp (t0) if insn.operands[-1].split(): maybe_reg = insn.operands[-1].split()[0] if len(maybe_reg) <= 5 and maybe_reg[0] == "(" and maybe_reg[-1] == ")": maybe_reg = maybe_reg[1:-1] ptr = get_register(maybe_reg) if ptr is not None: if to_str: return "{:#x}".format(ptr) else: return ptr # bctr? # ppc: bctr if is_ppc32() or is_ppc64(): if insn.mnemonic == "bctr": addr = get_register("ctr") if addr is None: return None if to_str: return "{:#x}".format(addr) else: return addr # jirl? # loongarch64: jirl $ra, $ra, 0 if is_loongarch64(): if insn.mnemonic == "jirl": if len(insn.operands) >= 3: reg = insn.operands[1] off = int(insn.operands[2], 0) addr = get_register(reg) + off if to_str: return "{:#x}".format(addr) else: return addr return None def get_breakpoints(self): breakpoints = gdb.breakpoints() if not breakpoints: return [] bp_locations = [] for b in breakpoints: if hasattr(b, "locations"): # gdb 13.1~ for bl in b.locations: if bl and bl.address is not None: bp_locations.append(bl.address) else: # for old gdb if b.location and b.location.startswith("*"): pos = b.location.lstrip("*") try: x = int(pos, 16) bp_locations.append(x) except ValueError: pass return bp_locations def context_code_default(self, redirect): ContextCommand.execute_command("x/8i $pc", redirect) return def context_code(self, redirect): if current_arch is None: ContextCommand.context_title("code", redirect) self.context_code_default(redirect) return use_native_x_command = Config.get_gef_setting("context_code.use_native_x_command") nb_insn = Config.get_gef_setting("context_code.nb_lines") nb_insn_prev = Config.get_gef_setting("context_code.nb_lines_prev") if is_x86(): show_opcodes_size = Config.get_gef_setting("context_code.show_opcodes_size_x64_x86") else: show_opcodes_size = Config.get_gef_setting("context_code.show_opcodes_size") past_lines_color = Config.get_gef_setting("theme.context_code_past") future_lines_color = Config.get_gef_setting("theme.context_code_future") use_capstone = Config.get_gef_setting("context_code.use_capstone") pc = current_arch.pc bp_locations = self.get_breakpoints() try: frame = gdb.selected_frame() arch_name = "{:s}:{:s}".format(current_arch.arch.lower(), current_arch.mode) except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). frame = None arch_name = "{:s}:{:s}".format(current_arch.arch.lower(), "???") if use_native_x_command: arch_name += " (gdb-native)" elif use_capstone: arch_name += " (capstone)" else: arch_name += " (gdb-native)" ContextCommand.context_title("code: {:s}".format(arch_name), redirect) if use_native_x_command: ContextCommand.execute_command("x/16i {:#x}".format(current_arch.pc), redirect) return for insn in Disasm.gef_disassemble(pc, nb_insn, nb_prev=nb_insn_prev): line = "" is_taken = False target = None delay_slot = None # bp prefix if insn.address in bp_locations: bp_prefix = Color.redify("*") else: bp_prefix = " " # insn to string with coloring by address against pc if insn.address < pc: if past_lines_color: text = insn.colored_text(show_opcodes_size, highlight=False, disable_color=True) text = Color.colorify(text, past_lines_color) else: text = insn.colored_text(show_opcodes_size, highlight=False) elif insn.address == pc: text = insn.colored_text(show_opcodes_size, highlight=True) else: if future_lines_color: text = insn.colored_text(show_opcodes_size, highlight=False, disable_color=True) text = Color.colorify(text, future_lines_color) else: text = insn.colored_text(show_opcodes_size, highlight=False) # bp prefix and branch info if insn.address != pc: line += "{:s} {:s}".format(bp_prefix, text) elif insn.address == pc: line += "{:s}-> {:s}".format(bp_prefix, text) # branch info if current_arch.is_conditional_branch(insn): if Config.get_gef_setting("context_code.peek_conditional_branch") is True: is_taken, reason = current_arch.is_branch_taken(insn) if is_taken: target = ContextCodeCommand.get_branch_addr(insn) reason = "[Reason: {:s}]".format(reason) if reason else "" line += "\t" + Color.colorify("TAKEN {:s}".format(reason), "bold green") delay_slot = current_arch.has_delay_slot else: reason = "[Reason: !({:s})]".format(reason) if reason else "" line += "\t" + Color.colorify("NOT taken {:s}".format(reason), "bold red") elif current_arch.is_jump(insn): if Config.get_gef_setting("context_code.peek_jump") is True: target = ContextCodeCommand.get_branch_addr(insn) delay_slot = current_arch.has_delay_slot elif current_arch.is_call(insn): if Config.get_gef_setting("context_code.peek_call") is True: target = ContextCodeCommand.get_branch_addr(insn) delay_slot = current_arch.has_delay_slot elif current_arch.is_ret(insn): if Config.get_gef_setting("context_code.peek_ret") is True: target = current_arch.get_ra(insn, frame) delay_slot = current_arch.has_ret_delay_slot if is_arc32() or is_arc64(): delay_slot = insn.mnemonic.endswith(".d") or insn.mnemonic.endswith(".d.nt") # comment if insn.address in self.context_comments: line += "\t\t" + Color.grayify("// " + "; ".join(self.context_comments[insn.address])) gef_print(line, redirect=redirect) # add extra branch info if target: # for delay slot try: if delay_slot: next_insn = list(Disasm.gef_disassemble(insn.address, 2))[-1] text = "{:s} {:s}\t{:s}".format( bp_prefix, next_insn.colored_text(show_opcodes_size, highlight=False), Color.colorify("Maybe delay-slot", "bold yellow"), ) gef_print(text, redirect=redirect) except Exception: pass # branch target address try: for i, tinsn in enumerate(Disasm.gef_disassemble(target, nb_insn)): text = tinsn.colored_text(show_opcodes_size, highlight=False) if i == 0: gef_print("", redirect=redirect) # need blank line text = " -> {}".format(text) else: text = " {}".format(text) gef_print(text, redirect=redirect) gef_print("", redirect=redirect) # need blank line except Exception: pass return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("code", args.ignore_redirect) try: self.context_code(redirect) except Exception as e: # In ARM64, before and after the transition to EL3, # there are cases where read_memory fails but the x command works. try: ContextCommand.execute_command("x/8i $pc", redirect) except Exception: err(str(e), redirect=redirect) # use first Exception string return @register_command class ContextMemoryAccessCommand(GenericCommand): """Context internal command to display accessing memory.""" _cmdline_ = "context-mem-access" _category_ = "01-a. Debugging Support - Context" _aliases_ = ["context-mem_access"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context mem_access` to") return RE_SUB_OPERAND3 = re.compile(r"<.*?>") RE_FINDALL_SEG1 = re.compile(r"[^:](\[.+?\])") RE_MATCH_REG1 = re.compile(r"r\d+d?") RE_MATCH_REG2 = re.compile(r"r\d+") RE_MATCH_REG3 = re.compile(r"[xw]\d+") def context_memory_access1(self, redirect): if not (is_x86() or is_arm32() or is_arm32_cortex_m() or is_arm64()): return inst_iter = Disasm.gef_disassemble(current_arch.pc, 2) try: insn_here = inst_iter.__next__() except StopIteration: return if insn_here.operands == []: return if insn_here.mnemonic == "nop": return insn = ",".join(insn_here.operands) insn = self.RE_SUB_OPERAND3.sub("", insn) r = self.RE_FINDALL_SEG1.findall(str(insn)) # Unsupported: seg:[reg] if not r: return insn_next = inst_iter.__next__() codesize = insn_next.address - insn_here.address for code in r: code = code[1:-1] # skip "[" and "]" if is_x86(): # add "$" to resiter code = code.replace("+", " + ") code = code.replace("-", " - ") code = code.replace("*", " * ") code = code.replace("eiz", " 0 ") # $eiz is always 0x0 code = code.split() code = ["$" + x if x.isalpha() or self.RE_MATCH_REG1.match(x) else x for x in code] code = "".join(code) # $rip/$eip points next instruction code_orig, code = code, code.replace("$rip", "$rip+{:#x}".format(codesize)) elif is_arm32() or is_arm32_cortex_m(): # add "$" to resiter code = code.replace(" ", "") code = code.replace("#", "") code = code.replace("lsl", "<<") code = code.split(",") code = ["$" + x if x.isalpha() or self.RE_MATCH_REG2.match(x) else x for x in code] if "<<" in code[-1]: code = code[:-2] + ["(" + code[-2] + code[-1] + ")"] code = "+".join(code) # $pc points next next instruction code_orig, code = code, code.replace("$pc", "$pc+{:#x}".format(codesize * 2)) elif is_arm64(): # add "$" to resiter code = code.replace(" ", "") code = code.replace("#", "") code = code.replace("lsl", "<<").replace("sxtw", "<<").replace("uxtw", "<<") code = code.replace("xzr", " 0 ") # $xzr is always 0x0 code = code.replace("wzr", " 0 ") # $wzr is always 0x0 code = code.replace("wsp", " ($sp&0xffff) ") # $wsp is a half of $sp code = code.split(",") code = ["$" + x if x.isalpha() or self.RE_MATCH_REG3.match(x) else x for x in code] if "<<" == code[-1]: code[-1] += "0" if "<<" in code[-1]: code = code[:-2] + ["(" + code[-2] + code[-1] + ")"] code = "+".join(code) # $pc points next next instruction code_orig, code = code, code.replace("$pc", "$pc+{:#x}".format(codesize * 2)) # print try: code = code.replace("$", "(long)$") addr = AddressUtil.parse_address(code) except gdb.error: # some binary fails to resolve "(long)" try: addr = AddressUtil.parse_address(code_orig) except gdb.error: return ContextCommand.context_title("memory access: {:s} = {:#x}".format(code_orig, addr), redirect) self.is_context_title_written = True ContextCommand.execute_command("dereference {:#x} 4 --no-pager".format(addr), redirect) return RE_FINDALL_SEG2 = re.compile(r"((fs|gs):\[?([^,\]]+)\]?)") RE_MATCH_REG4 = re.compile(r"r\d+d?") def context_memory_access2(self, redirect): if not is_x86(): return inst_iter = Disasm.gef_disassemble(current_arch.pc, 2) try: insn_here = inst_iter.__next__() except StopIteration: return if insn_here.operands == []: return insn = ",".join(insn_here.operands) insn = re.sub(r"<.+>", "", insn) try: insn_next = inst_iter.__next__() except StopIteration: return codesize = insn_next.address - insn_here.address r = self.RE_FINDALL_SEG2.findall(str(insn)) if r: code, fsgs, offset = r[0][0], r[0][1], r[0][2] if fsgs == "fs": fsgs_val = current_arch.get_fs() else: fsgs_val = current_arch.get_gs() if fsgs_val is None: return offset = offset.replace("+", " + ") offset = offset.replace("-", " - ") offset = offset.replace("*", " * ") offset = offset.replace("eiz", " 0 ") # $eiz is always 0x0 offset = offset.split() offset = ["$" + x if x.isalpha() or self.RE_MATCH_REG4.match(x) else x for x in offset] offset = "".join(offset) # $rip/$eip points next instruction offset = offset.replace("$rip", "$rip+{:#x}".format(codesize)) offset = AddressUtil.parse_address(offset) addr = AddressUtil.align_address(fsgs_val + offset) ContextCommand.context_title("memory access: {:s} = {:#x}".format(code, addr), redirect) self.is_context_title_written = True ContextCommand.execute_command("dereference {:#x} 4 --no-pager".format(addr), redirect) return RE_FINDALL_SEG3 = re.compile(r"((es|ds|ss|cs):\[?([^,\]]+)\]?)") RE_MATCH_REG5 = re.compile(r"r\d+d?") def context_memory_access3(self, redirect): if not is_x86(): return inst_iter = Disasm.gef_disassemble(current_arch.pc, 1) try: insn_here = inst_iter.__next__() except StopIteration: return if insn_here.operands == []: return insn = ",".join(insn_here.operands) insn = re.sub(r"<.+>", "", insn) r = self.RE_FINDALL_SEG3.findall(str(insn)) for rr in r: code, addr = rr[0], rr[2] addr = addr.replace("+", " + ") addr = addr.replace("-", " - ") addr = addr.replace("*", " * ") addr = addr.replace("eiz", " 0 ") # $eiz is always 0x0 addr = addr.split() addr = ["$" + x if x.isalpha() or self.RE_MATCH_REG5.match(x) else x for x in addr] addr = AddressUtil.parse_address("".join(addr)) ContextCommand.context_title("memory access: {:s} = {:#x}".format(code, addr), redirect) self.is_context_title_written = True ContextCommand.execute_command("dereference {:#x} 4 --no-pager".format(addr), redirect) return def context_memory_access(self, redirect): self.context_memory_access1(redirect) self.context_memory_access2(redirect) # for x86/x64 - fs/gs self.context_memory_access3(redirect) # for x86/x64 - cs/ss/ds/es return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("mem_access", args.ignore_redirect) self.is_context_title_written = False try: self.context_memory_access(redirect) except Exception as e: if not self.is_context_title_written: ContextCommand.context_title("memory access", redirect) err(str(e), redirect=redirect) return @register_command class ContextArgumentsCommand(GenericCommand): """Context internal command to display arguments.""" _cmdline_ = "context-args" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context args` to") self.add_setting("nb_guessed_arguments", 6, "Number to display when guessing functions arguments") return def get_got_value(self, addr): # GOT cannot be detected in kernel mode if is_qemu_system() or is_vmware() or is_kgdb(): return None # GOT Cannot be detected if no file is specified addr_obj = ProcessMap.lookup_address(addr) if not addr_obj: return None if not addr_obj.section: return None if not addr_obj.section.path: return None filepath = addr_obj.section.path if not os.path.exists(filepath): return None # something is wrong ret = Symbol.gdb_get_location(addr) if not ret: return None # check if PLT func_name = ret[0] if not func_name.endswith("@plt"): return None func_name = func_name[:-4] # use `got` command to obtain function address try: ret = gdb.execute("got -q -f {!r} --exact {:s}".format(filepath, func_name), to_string=True) ret = Color.remove_color(ret).splitlines() if len(ret) != 1: return None ret = ret[0] got_value = int(ret.split("|")[-1].split()[0], 16) except Exception: return None return got_value def get_call_destination_function_block(self, insn): # call insn -> destination addr addr = ContextCodeCommand.get_branch_addr(insn) if addr is None: return None # check if addr is PLT or not ret = self.get_got_value(addr) if ret: # use got value addr = ret # Even if the GOT is unresolved in Partial RELRO, there is no problem. # This is because the subsequent gdb.block_for_pc(addr) returns None. # addr -> block block = gdb.block_for_pc(addr) if block and not block.function: return None return block def print_arguments_from_symbol_x86(self, block, redirect): suffix_words = ("_avx2", "_avx", "_avx512", "_sse", "_sse2", "_ssse3", "_sse4_1", "_sse42") inner_words = ("_avx2_", "_avx_", "_avx512_", "_sse2_") if block.function.name.endswith(suffix_words): match = True elif any(x in block.function.name for x in inner_words): match = True else: match = False if match: function_name = "{:#x} <{:s}>".format(block.start, block.function.name) self.print_guessed_arguments(function_name, redirect) return True return False def print_arguments_from_symbol(self, block, redirect): """If symbols were found, parse them and print the argument adequately.""" # setup iterator args_info = [x for x in block if x.is_argument] if len(args_info) == len(block.function.type.fields()): # new implementation; get args information from the block. # we can get both type names and variable names. iterator = args_info title = "arguments (from block)" # special case if len(args_info) == 0: # Some string processing functions are implemented in assembly for speed. # Since there is no type information for these arguments, the number of arguments is always 0. # Here, if a function name matches the blacklist, type information is not used and # print_guessed_arguments is forcibly called to display context_args.nb_guessed_arguments arguments. if is_x86(): if self.print_arguments_from_symbol_x86(block, redirect): return else: # old implementation. # we can get type names, but not variable names iterator = block.function.type.fields() title = "arguments (from fields)" # helper function def get_type_name(t): try: pointer_nest = 0 while t.code == gdb.TYPE_CODE_PTR: pointer_nest += 1 t = t.target() if not t.name: return None return t.name + "*" * pointer_nest except Exception: return None # get each values args = [] for i, f in enumerate(iterator): # value value = current_arch.get_ith_parameter(i, in_func=False)[1] if value is None: break value = AddressUtil.recursive_dereference_to_string(value) # name name = f.name or "var_{:d}".format(i) # type name typ = get_type_name(f.type) if typ is None: typ = {1: "BYTE", 2: "WORD", 4: "DWORD", 8: "QWORD"}[f.type.sizeof] # ok args.append("{:s} {:s} = {:s}".format(typ, name, value)) # output ContextCommand.context_title(title, redirect) gef_print("{:#x} <{:s}> (".format(block.start, block.function.name), redirect=redirect) for a in args: gef_print(" {:s},".format(a), redirect=redirect) gef_print(")", redirect=redirect) return def print_guessed_arguments(self, function_name, redirect): """When no symbol, print six arguments.""" arg_key_color = Config.get_gef_setting("theme.registers_register_name") nb_argument = Config.get_gef_setting("context_args.nb_guessed_arguments") # get each values args = [] for i in range(nb_argument): try: key, value = current_arch.get_ith_parameter(i, in_func=False) value = AddressUtil.recursive_dereference_to_string(value) except Exception: break args.append("{:s} = {:s}".format(Color.colorify(key, arg_key_color), value)) # output ContextCommand.context_title("arguments (guessed)", redirect) gef_print("{:s} (".format(function_name), redirect=redirect) for a in args: gef_print(" {:s},".format(a), redirect=redirect) gef_print(")", redirect=redirect) return def context_args(self, redirect): if current_arch is None: return # get insn try: insn = get_insn() except gdb.MemoryError: return if insn is None: return # syscall case if current_arch.is_syscall(insn): ContextCommand.context_title("arguments", redirect) ContextCommand.execute_command("syscall-args", redirect) return # non-call case if not current_arch.is_call(insn): return # call case (from symbol) block = self.get_call_destination_function_block(insn) if block: # okay, it has symbols and not in blacklist self.print_arguments_from_symbol(block, redirect) return # call case (guessing) # no symbols, try extract target address addr = ContextCodeCommand.get_branch_addr(insn) if addr is not None: function_name = "{:#x}{:s}".format( addr, Symbol.get_symbol_string(addr, nosymbol_string=" "), ) else: # failed, use raw operands function_name = " ".join(insn.operands) self.print_guessed_arguments(function_name, redirect) return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("args", args.ignore_redirect) try: self.context_args(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class ContextSourceCommand(GenericCommand): """Context internal command to display source.""" _cmdline_ = "context-source" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("nb_lines", metavar="NB_LINES", nargs="?", type=AddressUtil.parse_address, help="temporarily overrides context_source.nb_lines.") parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context source` to") self.add_setting("show_source_code_variable_values", True, "Show extra PC context info in the source code") self.add_setting("nb_lines", 6, "Number of source code after $pc") return def get_source_breakpoints(self, file_base_name): breakpoints = gdb.breakpoints() if not breakpoints: return [] bp_locations = [] for b in breakpoints: if hasattr(b, "locations") and b.locations: for bl in b.locations: if bl and bl.source: bp_locations.append("{:s}:{:d}".format(bl.source[0], bl.source[1])) else: # for old gdb bp_locations.append(b.location) return bp_locations def line_has_breakpoint(self, file_name, line_number, bp_locations): if not bp_locations: return False filename_line = "{}:{}".format(file_name, line_number) return any(filename_line in loc for loc in bp_locations) def get_pc_context_info(self, pc, line): try: current_block = gdb.block_for_pc(pc) except gdb.error: return [] if not current_block or not current_block.is_valid(): return [] m = [] seen_symbol = [] while current_block and not current_block.is_static: for sym in current_block: if sym.is_function: continue if re.search(r"\W{}\W".format(sym.name), line): try: val = gdb.parse_and_eval(sym.name) except gdb.error: continue if val.type.code in (gdb.TYPE_CODE_PTR, gdb.TYPE_CODE_ARRAY): if val.address is None: continue addr = int(val.address) val = AddressUtil.recursive_dereference_to_string(addr) elif val.type.code == gdb.TYPE_CODE_INT: try: val = hex(int(val)) except gdb.error: continue else: continue if sym.name not in seen_symbol: seen_symbol.append(sym.name) msg = "{} = {}".format(Color.yellowify(sym.name), val) m.append(msg) current_block = current_block.superblock return m def context_source(self, redirect): if current_arch is None: return try: pc = current_arch.pc symtabline = gdb.find_pc_line(pc) symtab = symtabline.symtab line_num = symtabline.line - 1 # we subtract one because line number returned by gdb start at 1 if not symtab.is_valid(): return fpath = symtab.fullname() with open(fpath, "r") as f: lines = [x.rstrip() for x in f.readlines()] except Exception: return ContextCommand.context_title( "source: {}+{}".format(os.path.normpath(symtab.filename), line_num + 1), redirect, ) if self.args.nb_lines is not None: nb_lines = self.args.nb_lines else: nb_lines = Config.get_gef_setting("context_source.nb_lines") past_lines_color = Config.get_gef_setting("theme.context_code_past") cur_line_color = Config.get_gef_setting("theme.source_current_line") future_lines_color = Config.get_gef_setting("theme.context_code_future") show_extra_info = Config.get_gef_setting("context_source.show_source_code_variable_values") file_base_name = os.path.basename(symtab.filename) bp_locations = self.get_source_breakpoints(file_base_name) for i in range(line_num - nb_lines + 1, line_num + nb_lines): if i < 0: continue if len(lines) <= i: break if self.line_has_breakpoint(file_base_name, i + 1, bp_locations): bp_prefix = Color.redify("*") else: bp_prefix = " " if i < line_num: past_line = "{:4d} {:s}".format(i + 1, lines[i]) past_line = Color.colorify(past_line, past_lines_color) gef_print("{:1s}{:2s}{:s}".format(bp_prefix, "", past_line), redirect=redirect) elif i == line_num: prefix = "{:1s}->{:4d} ".format(bp_prefix, i + 1) leading = len(lines[i]) - len(lines[i].lstrip()) if show_extra_info: extra_info = self.get_pc_context_info(pc, lines[i]) for ext in extra_info: gef_print("{}// {}".format(" " * (len(prefix) + leading), ext), redirect=redirect) gef_print(Color.colorify("{}{:s}".format(prefix, lines[i]), cur_line_color), redirect=redirect) elif i > line_num: future_line = "{:4d} {:s}".format(i + 1, lines[i]) future_line = Color.colorify(future_line, future_lines_color) gef_print("{:1s}{:2s}{:s}".format(bp_prefix, "", future_line), redirect=redirect) return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("source", args.ignore_redirect) try: self.context_source(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class ContextMemoryWatchCommand(GenericCommand): """Context internal command to display watching memory.""" _cmdline_ = "context-mem-watch" _category_ = "01-a. Debugging Support - Context" _aliases_ = ["context-mem_watch"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context mem_watch` to") return def context_memory_watch(self, redirect): if current_arch is None: return for address, opt in sorted(MemoryWatchCommand.mem_watches.items()): count, fmt = opt[0:2] ContextCommand.context_title("memory:{:#x}".format(address), redirect) if fmt == "pointers": cmd = "dereference {:#x} {:d} --no-pager".format(address, count) else: cmd = "hexdump {:s} {:#x} {:d} --no-pager".format(fmt, address, count) ContextCommand.execute_command(cmd, redirect) return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("mem_watch", args.ignore_redirect) try: self.context_memory_watch(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class ContextTraceCommand(GenericCommand): """Context internal command to display backtrace.""" _cmdline_ = "context-trace" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("nb_lines", metavar="NB_LINES", nargs="?", type=AddressUtil.parse_address, help="temporarily overrides context_trace.nb_lines.") parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context trace` to") self.add_setting("nb_lines", 10, "Number of line in the backtrace pane") self.add_setting("nb_lines_before", 2, "Number of line in the backtrace pane before selected frame") return def context_trace(self, redirect): ContextCommand.context_title("trace", redirect) if self.args.nb_lines is not None: nb_lines = self.args.nb_lines else: nb_lines = Config.get_gef_setting("context_trace.nb_lines") if nb_lines <= 0: return try: orig_frame = gdb.selected_frame() current_frame = gdb.newest_frame() except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). err("Failed to get frame information", redirect=redirect) return frames = [current_frame] while current_frame != orig_frame: current_frame = current_frame.older() frames.append(current_frame) nb_lines_before = Config.get_gef_setting("context_trace.nb_lines_before") level = max(len(frames) - nb_lines_before - 1, 0) current_frame = frames[level] while current_frame and nb_lines: current_frame.select() if not current_frame.is_valid(): break # address and symbol pc = current_frame.pc() if is_x86_16(): pc = current_arch.real2phys("$cs", pc) sym = Symbol.get_symbol_string(pc, nosymbol_string=" ") # frame name """ Frame names (=current_frmae.name()) and symbols (=Symbol.get_symbol_string(current_frame.pc())) usually match, but sometimes they don't. This is an example. gef> bt #0 __futex_abstimed_wait_common64 #1 __futex_abstimed_wait_common #2 __GI___futex_abstimed_wait_cancelable64 #3 0x00007f0635e93f1b in __pthread_cond_wait_common #4 ___pthread_cond_timedwait64 gef> context trace [#0] 0x7f0635e9119d <__futex_abstimed_wait_cancelable64+0xed> [#1] 0x7f0635e9119d <__futex_abstimed_wait_cancelable64+0xed> [#2] 0x7f0635e9119d <__futex_abstimed_wait_cancelable64+0xed> [#3] 0x7f0635e93f1b [#4] 0x7f0635e93f1b This likely occurs when each symbol exists but is inlined into a single function by optimization. Therefore, the frame name is also displayed if it differs. """ try: ret = Symbol.gdb_get_location(pc) if ret is None: frame_name = None elif ret[0] == current_frame.name(): frame_name = None else: frame_name = Instruction.smartify_text(current_frame.name()) except (ValueError, gdb.error): frame_name = None # current index coloring if current_frame == orig_frame: idx = Color.colorify("#{:d}".format(level), "bold green") current_frame_symbol = "*" else: idx = Color.colorify("#{:d}".format(level), "bold magenta") current_frame_symbol = " " # print if frame_name: frame_name = Color.colorify(frame_name, "bold yellow") gef_print("[{:s}{:s}] {!s}{:s} (frame name: {:s})".format( current_frame_symbol, idx, ProcessMap.lookup_address(pc), sym, frame_name, ), redirect=redirect) else: gef_print("[{:s}{:s}] {!s}{:s}".format( current_frame_symbol, idx, ProcessMap.lookup_address(pc), sym, ), redirect=redirect) # go next frame try: current_frame = current_frame.older() except gdb.error: break level += 1 nb_lines -= 1 if nb_lines == 0: if current_frame: gef_print("[...]", redirect=redirect) orig_frame.select() return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("trace", args.ignore_redirect) try: self.context_trace(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class ContextThreadsCommand(GenericCommand): """Context internal command to display threads.""" _cmdline_ = "context-threads" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("nb_lines", metavar="NB_LINES", nargs="?", type=AddressUtil.parse_address, help="temporarily overrides context_threads.nb_lines.") parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context threads` to") self.add_setting("nb_lines", 4, "Number of line in the threads pane (-1: infinity)") return def reason(self): try: res = gdb.execute("info program", to_string=True).splitlines() except gdb.error: return "STOPPED" if not res: return "NOT RUNNING" for line in res: line = line.strip() if line.startswith("It stopped with signal "): return line.replace("It stopped with signal ", "").split(",", 1)[0] if line == "The program being debugged is not being run.": return "NOT RUNNING" if line == "It stopped at a breakpoint that has since been deleted.": return "TEMPORARY BREAKPOINT" if line.startswith("It stopped at breakpoint "): return "BREAKPOINT" if line == "It stopped after being stepped.": return "SINGLE STEP" return "STOPPED" def context_threads(self, redirect): if self.args.nb_lines is not None: nb_lines = self.args.nb_lines else: nb_lines = Config.get_gef_setting("context_threads.nb_lines") # get all threads threads = gdb.selected_inferior().threads() # Note that the order of the list returned by gdb.selected_inferior().threads() # may differ depending on the version of gdb. threads = sorted(threads, key=lambda t: t.num) # title if nb_lines < 0: shown_threads = len(threads) else: shown_threads = nb_lines if shown_threads < len(threads): ContextCommand.context_title( "threads (shown:{:d} / all:{:d})".format(shown_threads, len(threads)), redirect, ) else: ContextCommand.context_title("threads", redirect) # check max threads if nb_lines == 0: return if nb_lines > 0: # bring selected thread to the top selected_thread = gdb.selected_thread() for i, t in enumerate(threads): if t.num == selected_thread.num: threads = [threads[i]] + threads[:i] + threads[i + 1:] break # cut off threads = threads[:nb_lines] # re-sort threads = sorted(threads, key=lambda t: t.num) if not threads: err("No thread selected", redirect) return # get selected frame selected_thread = gdb.selected_thread() try: selected_frame = gdb.selected_frame() except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). selected_frame = None # walk threads lines = [] for thread in threads: # selected, tid tid = str(thread.ptid[1]) or str(thread.ptid[2]) or "???" if thread == selected_thread: line = "[*{:s}] ".format( Color.colorify("Thread Id:{:d}, tid:{:s}".format(thread.num, tid), "bold green"), ) else: line = "[ {:s}] ".format( Color.colorify("Thread Id:{:d}, tid:{:s}".format(thread.num, tid), "bold magenta"), ) # name if thread.name: line += 'Name: "{:s}", '.format(thread.name) # status if thread.is_running(): line += Color.colorify("running", "bold green") elif thread.is_exited(): line += Color.colorify("exited", "bold yellow") elif thread.is_stopped(): line += Color.colorify("stopped", "bold red") # switch test try: thread.switch() except Exception: line += " - Failed to switch to this thread" gef_print(line, redirect=redirect) continue # get pc try: frame = gdb.selected_frame() pc = frame.pc() except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). # if failed, print thread information without frame (but with $pc). pc = get_register("$pc") # make reason sym = Symbol.get_symbol_string(pc, nosymbol_string=" ") line += " at {!s}{:s}".format(ProcessMap.lookup_address(pc), sym) line += ", reason: {:s}".format(Color.colorify(self.reason(), "bold magenta")) lines.append([thread.num, line]) # print for _, line in sorted(lines): gef_print(line, redirect=redirect) # revert selected_thread.switch() if selected_frame is not None: try: selected_frame.select() # A gdb.error will occur if the user patches a range that includes the ret instruction. except gdb.error: pass return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("threads", args.ignore_redirect) try: self.context_threads(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class ContextExtraCommand(GenericCommand): """Context internal command to display extra information or execute command.""" _cmdline_ = "context-extra" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-i", "--ignore-redirect", action="store_true", help="ignore redirect settings.") _syntax_ = parser.format_help() context_messages = [] context_extra_commands = [] def __init__(self): super().__init__() self.add_setting("redirect", "", "The target tty name to redirect `context extra` to") return @staticmethod def push_context_message(level, message): """Push the message to be displayed the next time the context is invoked.""" if level not in ("error", "warn", "ok", "info"): err("Invalid level '{}', discarding message".format(level)) return ContextExtraCommand.context_messages.append((level, message)) return @staticmethod def empty_extra_messages(_event): ContextExtraCommand.context_messages = [] return def context_extra(self, redirect): if not self.context_messages and not self.context_extra_commands: return ContextCommand.context_title("extra", redirect) for level, text in self.context_messages: if level == "error": err(text, redirect=redirect) elif level == "warn": warn(text, redirect=redirect) elif level == "ok": ok(text, redirect=redirect) elif level == "info": info(text, redirect=redirect) for command in self.context_extra_commands: gef_print(titlify(command), redirect) try: ContextCommand.execute_command(command, redirect) except Exception as e: err(str(e), redirect=redirect) return @parse_args def do_invoke(self, args): redirect = ContextCommand.get_redirect("extra", args.ignore_redirect) try: self.context_extra(redirect) except Exception as e: err(str(e), redirect=redirect) return @register_command class MemoryCommand(GenericCommand): """The base command to watch the memory.""" _cmdline_ = "memory" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("watch") subparsers.add_parser("unwatch") subparsers.add_parser("reset") subparsers.add_parser("list") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=True) return @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.usage() return @register_command class MemoryWatchCommand(GenericCommand): """Add address ranges to the memory view.""" _cmdline_ = "memory watch" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="the memory address to register for display in `context memory`.") parser.add_argument("count", metavar="COUNT", nargs="?", type=AddressUtil.parse_address, default=0x10, help="the count of displayed units. (default: %(default)s)") parser.add_argument("unit", nargs="?", default="pointers", choices=["byte", "word", "dword", "qword", "pointers"], help="the size of unit. (default: %(default)s)") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x603000 0x100 byte", "{0:s} $sp", ] _example_ = "\n".join(_example_).format(_cmdline_) mem_watches = {} def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): MemoryWatchCommand.mem_watches[args.address] = (args.count, args.unit) ok("Adding memwatch to {:#x}".format(args.address)) return @register_command class MemoryUnwatchCommand(GenericCommand): """Remove address ranges from the memory view.""" _cmdline_ = "memory unwatch" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="the memory address to deregister for display in `context memory`.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x603000", "{0:s} $sp", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): res = MemoryWatchCommand.mem_watches.pop(args.address, None) if not res: warn("You weren't watching {:#x}".format(args.address)) else: ok("Removed memwatch of {:#x}".format(args.address)) return @register_command class MemoryResetCommand(GenericCommand): """Remove all watchpoints.""" _cmdline_ = "memory reset" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): MemoryWatchCommand.mem_watches.clear() ok("Memory watches cleared") return @register_command class MemoryWatchListCommand(GenericCommand): """List all watchpoints to display in context layout.""" _cmdline_ = "memory list" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): if not MemoryWatchCommand.mem_watches: info("No memory watches") return info("Memory watches:") for address, opt in sorted(MemoryWatchCommand.mem_watches.items()): gef_print("- {:#x} ({}, {})".format(address, opt[0], opt[1])) return @register_command class HexdumpCommand(GenericCommand, BufferingOutput): """Display the hexdump from the memory location specified.""" _cmdline_ = "hexdump" _category_ = "03-b. Memory - View" _repeat_ = True _aliases_ = ["hd"] parser = argparse.ArgumentParser(prog=_cmdline_) modes = ["byte", "word", "dword", "qword", "b", "w", "d", "q"] parser.add_argument("format", choices=modes, nargs="?", default="byte", metavar="{byte,word,dword,qword}", help="dump mode. It also works if you specify the first character. (default: %(default)s)") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to dump.") parser.add_argument("count", metavar="COUNT", nargs="?", type=AddressUtil.parse_address, default=0x100, help="the count of displayed units. (default: %(default)s)") parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("-r", "--reverse", action="store_true", help="display in reverse order line by line.") parser.add_argument("-f", "--full", action="store_true", help="display the same line without omitting.") parser.add_argument("-s", "--symbol", action="store_true", help="display the symbol.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.modes: # already matched return [] if text == "": # no prefix return [s for s in self.modes if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.modes if s and s.startswith(text.strip())] @staticmethod def merge_lines(lines_unmerged, nb_skip_merge=1): lines = [] keep_asterisk = 0 for i, line in enumerate(lines_unmerged): # about first line if i < nb_skip_merge: lines.append(line) continue # don't merge error string etc. if " " not in lines[-1] or " " not in line: lines.append(line) continue # check if mergeable if re.split(" +", lines[-1])[1] == re.split(" +", line)[1]: keep_asterisk += 1 prev_line = line continue # append line if keep_asterisk == 1: lines.append(prev_line) keep_asterisk = 0 elif keep_asterisk > 1: lines.append("*") keep_asterisk = 0 lines.append(line) # final process if keep_asterisk == 1: lines.append(prev_line) elif keep_asterisk > 1: lines.append("*") return lines def read_memory(self, read_from, read_len): if read_len > 0x0100_0000: # Too large return None try: if self.args.phys: mem = read_physmem(read_from, read_len) else: mem = read_memory(read_from, read_len) return mem except (gdb.MemoryError, ValueError, OverflowError): pass # If you get an error, you probably read outside a valid memory page. # Read in page size units. read_end = read_from + read_len read_end &= get_pagesize_mask_high() while read_end - read_from > 0: try: if self.args.phys: mem = read_physmem(read_from, read_end - read_from) else: mem = read_memory(read_from, read_end - read_from) return mem except (gdb.MemoryError, ValueError, OverflowError): pass read_end -= get_pagesize() return None @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys: if not is_qemu_system() and not is_vmware() and not is_kgdb(): err("Unsupported in this gdb mode.") return from_idx = args.count * self.repeat_count to_idx = args.count * (self.repeat_count + 1) if args.reverse: from_idx *= -1 from_idx += args.count to_idx *= -1 to_idx += args.count memalign_size = None if is_x86_16(): memalign_size = 2.5 read_from = AddressUtil.align_address(args.location, memalign_size=memalign_size) + min(from_idx, to_idx) mem = self.read_memory(read_from, args.count) if mem is None: err("Cannot access memory") return unit = {"byte": 1, "word": 2, "dword": 4, "qword": 8, "b": 1, "w": 2, "d": 4, "q": 8}[args.format] lines = hexdump(mem, show_symbol=args.symbol, base=read_from, unit=unit).splitlines() if not args.full: lines = HexdumpCommand.merge_lines(lines) if args.reverse: lines.reverse() self.out = lines self.print_output(check_terminal_size=True) return @register_command class XxdCommand(HexdumpCommand): """Display the hexdump from the memory location specified (shortcut for `hexdump byte`).""" _cmdline_ = "xxd" _category_ = "03-b. Memory - View" _repeat_ = True _aliases_ = [] # re-overwrite parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to dump.") parser.add_argument("count", metavar="COUNT", nargs="?", type=AddressUtil.parse_address, default=0x100, help="the count of displayed units. (default: %(default)s)") parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("-r", "--reverse", action="store_true", help="display in reverse order line by line.") parser.add_argument("-f", "--full", action="store_true", help="display the same line without omitting.") parser.add_argument("-s", "--symbol", action="store_true", help="display the symbol.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running def do_invoke(self, args): flags = [] if args.phys: flags.append("--phys") if args.reverse: flags.append("--reverse") if args.full: flags.append("--full") if args.symbol: flags.append("--symbol") if args.symbol: flags.append("--no-pager") if args.reverse: location = args.location - (args.count * self.repeat_count) else: location = args.location + (args.count * self.repeat_count) flags = " ".join(flags) gdb.execute("hexdump byte {:#x} {:#x} {:s}".format(location, args.count, flags)) return @register_command class HexdumpFlexibleCommand(GenericCommand, BufferingOutput): """Display the hexdump with user-defined format.""" _cmdline_ = "hexdump-flexible" _category_ = "03-b. Memory - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("format", metavar="FORMAT", help="dump format.") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to dump.") parser.add_argument("count", metavar="COUNT", nargs="?", type=AddressUtil.parse_address, default=1, help="the count of displayed units. (default: %(default)s)") parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("-t", "--tag", nargs=2, action="append", metavar=("IDX", "TAG"), help="display with tags.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _example_ = [ '{0:s} "2Q2I2H2B" $rsp 4 # "Show qword*2, dword*2, short*2, byte*2" from $rsp and repeat 4 times', '{0:s} "4Q-2Q" $rsp 4 # "Show qword*4 and skip qword*2" from $rsp and repeat 4 times', ] _example_ = "\n".join(_example_).format(_cmdline_) def extract_each_type(self, fmt): out = [] repeat = 1 for r in re.split(r"(-?\d+|)", fmt): if r == "": continue try: repeat = int(r) continue except ValueError: if 0 < repeat: out.extend([r] * repeat) else: out.extend(["-" + r] * -repeat) repeat = 1 return out def do_dump(self, fmt, size, each_type): base_address_color = Config.get_gef_setting("theme.dereference_base_address") # parse tag max_tag_width = 0 tags_dic = {} if self.args.tag: for idx, tag in self.args.tag: idx = int(idx, 0) tags_dic[idx] = tag max_tag_width = max(max_tag_width, len(tag)) for i in range(self.args.count): # read content address = self.args.location + size * i try: if self.args.phys: data = read_physmem(address, size) else: data = read_memory(address, size) except (gdb.MemoryError, ValueError, OverflowError): self.err_add_out("Failed to read memory") break # unpack values = struct.unpack(fmt.replace("-", ""), data) # make address line if max_tag_width == 0: line = "{:s}|{:+#06x}|{:+04d}: ".format( Color.colorify(AddressUtil.format_address(address), base_address_color), size * i, i, ) else: tag_i = tags_dic.get(i, "") line = "{:s}|{:+#06x}|{:+04d}: {:{:d}s}:".format( Color.colorify(AddressUtil.format_address(address), base_address_color), size * i, i, tag_i, max_tag_width, ) # dump each element for t, v in zip(each_type, values): if t.startswith("-"): continue if t in "BHILQ": line += " {:#0{:d}x}".format(v, 2 + struct.calcsize(t) * 2) elif t in "bhilq": line += " {:+#0{:d}x}".format(v, 2 + struct.calcsize(t) * 2 + 1) elif t in "fd": line += " {:20e}".format(v) else: self.err_add_out("Unsupported format: {:s}".format(t)) return self.out.append(line) return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys: if not is_qemu_system() and not is_vmware() and not is_kgdb(): err("Unsupported in this gdb mode.") return fmt = args.format if not fmt.startswith(("<", ">")): fmt = Endian.endian_str() + fmt try: size = struct.calcsize(fmt.replace("-", "")) except struct.error: err("Format error") return each_type = self.extract_each_type(args.format) self.out = [] self.do_dump(fmt, size, each_type) self.print_output() return @register_command class LoadFileCommand(GenericCommand): """Load the file into memory.""" _cmdline_ = "load-file" _category_ = "03-f. Memory - Dump/Load" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to load.") parser.add_argument("file_path", metavar="FILE_PATH", help="the filepath to load.") parser.add_argument("file_offset", metavar="FILE_OFFSET", nargs="?", type=AddressUtil.parse_address, default=0, help="the offset of the file to load.") parser.add_argument("load_size", metavar="LOAD_SIZE", nargs="?", type=AddressUtil.parse_address, help="the size of the data to load.") _syntax_ = parser.format_help() _note_ = [ "+-memory------+", "| | +-file_start--+", "| | | ^ |", "| | | | |", "| | | v |", "| LOCATION <----------------- FILE_OFFSET |", "| ... | ^ | ... |", "| | | LOAD_SIZE | |", "| ... | v | ... |", "| end <---------------------- end |", "| | | |", "| | | |", "| | +-file_end----+", "| |", "+-------------+", "If there is not enough space, the load will fail halfway.", ] _note_ = "\n".join(_note_) @parse_args @only_if_gdb_running def do_invoke(self, args): if not os.path.exists(args.file_path): err("Could not find {:s}".format(args.file_path)) return if args.load_size is None: data_size = os.path.getsize(args.file_path) if data_size == 0: err("Unsupported zero size mapping") return elif args.load_size < 0: err("Invalid LOAD_SIZE") return else: data_size = args.load_size if args.file_offset < 0: err("Invalid FILE_OFFSET") return # read file and write to memory fd = open(args.file_path, "rb") if args.file_offset > 0: fd.seek(args.file_offset, 0) pos = args.location remain_size = data_size while remain_size > 0: data = fd.read(min(0x1000, remain_size)) if len(data) == 0: break write_memory(pos, data) pos += len(data) remain_size -= len(data) return @register_command class LoadFileMmapCommand(GenericCommand): """Load the file into memory that allocated by `mmap`.""" _cmdline_ = "load-file-mmap" _category_ = "03-f. Memory - Dump/Load" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to load.") parser.add_argument("file_path", metavar="FILE_PATH", help="the filepath to load.") parser.add_argument("file_offset", metavar="FILE_OFFSET", nargs="?", type=AddressUtil.parse_address, default=0, help="the offset of the file to load.") parser.add_argument("load_size", metavar="LOAD_SIZE", nargs="?", type=AddressUtil.parse_address, help="the size of the data to load.") _syntax_ = parser.format_help() _note_ = [ "+-mmap_start--+", "| | +-file_start--+", "| | | ^ |", "| | | | |", "| | | v |", "| LOCATION <----------------- FILE_OFFSET |", "| ... | ^ | ... |", "| | | LOAD_SIZE | |", "| ... | v | ... |", "| end <---------------------- end |", "| | | |", "| | | |", "| | +-file_end----+", "| |", "+-mmap_end----+", ] _note_ = "\n".join(_note_) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @require_arch_set def do_invoke(self, args): if not os.path.exists(args.file_path): err("Could not find {:s}".format(args.file_path)) return if args.load_size is None: data_size = os.path.getsize(args.file_path) if data_size == 0: err("Unsupported zero size mapping") return elif args.load_size < 0: err("Invalid LOAD_SIZE") return else: data_size = args.load_size if args.file_offset < 0: err("Invalid FILE_OFFSET") return # +-mmap_start--+ ^ ^ # | | | | # | | | | page_size # | data_start | ^ | | # | ... | | | | # +-------------+ | data_size | mmap_size v # | ... | | | # | data_end | v | # | | | # | | | # +-mmap_end----+ v mmap_start = args.location & get_pagesize_mask_high() data_start = args.location data_end = data_start + data_size mmap_end = align_to_pagesize(data_end) mmap_size = mmap_end - mmap_start # mmap res = gdb.execute("mmap {:#x} {:#x}".format(mmap_start, mmap_size), to_string=True) if "[!]" in res: err("Failed to mmap") return output_line = res.splitlines()[-1] ret = int(output_line.split()[2], 0) if AddressUtil.is_msb_on(ret): err("Failed to mmap") return # read file and write to memory fd = open(args.file_path, "rb") if args.file_offset > 0: fd.seek(args.file_offset, 0) pos = data_start remain_size = data_size while remain_size > 0: data = fd.read(min(0x1000, remain_size)) if len(data) == 0: break write_memory(pos, data) pos += len(data) remain_size -= len(data) return @register_command class PatchCommand(GenericCommand): """The base command to write specified values to the specified address.""" _cmdline_ = "patch" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("byte") subparsers.add_parser("word") subparsers.add_parser("dword") subparsers.add_parser("qword") subparsers.add_parser("string") subparsers.add_parser("hex") subparsers.add_parser("pattern") subparsers.add_parser("nop") subparsers.add_parser("inf") subparsers.add_parser("trap") subparsers.add_parser("ret") subparsers.add_parser("syscall") subparsers.add_parser("range-replace") subparsers.add_parser("history") subparsers.add_parser("revert") _syntax_ = parser.format_help() patch_history = [] # [ [patch1a, patch1b], [patch2a], ...] def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) self.format = None return class PatchInfo: def __init__(self, addr, data, length=None, phys=None, tag=None): if not isinstance(addr, int): raise ValueError self.addr = addr if not isinstance(data, bytes): raise ValueError self.data = data if length is None: self.length = len(self.data) else: self.length = length self.phys = phys # tag: A key to group multiple patches together if tag is None: self.tag = PatchCommand.PatchInfo.get_unique_tag() else: self.tag = tag return def __repr__(self): return '<{:s}.{:s} object at {:#x}, addr={:#x}, data={}, length={:#x}, phys={}, tag={}>'.format( self.__module__, self.__class__.__name__, id(self), self.addr, self.data, self.length, self.phys, hex(self.tag) if isinstance(self.tag, int) else self.tag, ) @staticmethod def get_unique_tag(): import random tags = PatchCommand.PatchInfo.get_tag_set() while True: v = random.randint(1, 0xffff_ffff) if v not in tags: break return v @staticmethod def get_tag_set(): return {x[0].tag for x in PatchCommand.patch_history} def a(self): a = " ".join(["{:02x}".format(x) for x in self.after_data[:0x10]]) if len(self.after_data) > 0x10: a += " ..." return a def b(self): b = " ".join(["{:02x}".format(x) for x in self.before_data[:0x10]]) if len(self.before_data) > 0x10: b += " ..." return b def patch(self, silent=False): orig_mode = QemuMonitor.get_current_mmu_mode() if orig_mode == "virt" and self.phys: enable_phys() self.before_data = read_memory(self.addr, self.length) write_memory(self.addr, self.data) self.after_data = read_memory(self.addr, self.length) disable_phys() elif orig_mode == "phys" and not self.phys: disable_phys() self.before_data = read_memory(self.addr, self.length) write_memory(self.addr, self.data) self.after_data = read_memory(self.addr, self.length) enable_phys() else: self.before_data = read_memory(self.addr, self.length) write_memory(self.addr, self.data) self.after_data = read_memory(self.addr, self.length) # print if not silent: ok("Patch success: {!s}{:s}: {:s} -> {:s}".format( ProcessMap.lookup_address(self.addr), Symbol.get_symbol_string(self.addr), self.b(), self.a(), )) # history self.insert_history() return def insert_history(self): for i in range(len(PatchCommand.patch_history)): if PatchCommand.patch_history[i][0].tag == self.tag: PatchCommand.patch_history[i].append(self) break else: PatchCommand.patch_history.insert(0, [self]) return def revert(self, silent=False): orig_mode = QemuMonitor.get_current_mmu_mode() if orig_mode == "virt" and self.phys: enable_phys() write_memory(self.addr, self.before_data) disable_phys() elif orig_mode == "phys" and not self.phys: disable_phys() write_memory(self.addr, self.before_data) enable_phys() else: write_memory(self.addr, self.before_data) # print if not silent: ok("Revert success: {!s}{:s}: {:s} -> {:s}".format( ProcessMap.lookup_address(self.addr), Symbol.get_symbol_string(self.addr), self.a(), self.b(), )) # history self.remove_history() return def remove_history(self): for i in range(len(PatchCommand.patch_history)): if PatchCommand.patch_history[i][0].tag == self.tag: PatchCommand.patch_history[i].remove(self) if PatchCommand.patch_history[i] == []: PatchCommand.patch_history.pop(i) break return @staticmethod def revert_to_tag(tag, silent=False): tags = PatchCommand.PatchInfo.get_tag_set() if tag not in tags: err("Not found tag") return None while PatchCommand.patch_history: hist = PatchCommand.patch_history.pop(0) for patch_info in hist: try: patch_info.revert(silent) except Exception as e: err(e) return if tag == hist[0].tag: break return # for qword, dword, word, byte sub-commands @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) def do_invoke(self, args): SUPPORTED_SIZES = { "qword": (8, "Q"), "dword": (4, "L"), "word": (2, "H"), "byte": (1, "B"), } if self.format not in SUPPORTED_SIZES: self.usage() return if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return addr = args.location size, fcode = SUPPORTED_SIZES[self.format] if args.endian_reverse is False: d = "<" if Endian.is_little_endian() else ">" else: d = ">" if Endian.is_little_endian() else "<" tag = PatchCommand.PatchInfo.get_unique_tag() for value in args.values: value = AddressUtil.parse_address(value) & ((1 << size * 8) - 1) vstr = struct.pack(d + fcode, value) try: self.PatchInfo(addr, vstr, size, phys=args.phys, tag=tag).patch() except Exception as e: err(e) return addr += size return @register_command class PatchQwordCommand(PatchCommand): """Write specified QWORD to the specified address.""" _cmdline_ = "patch qword" _category_ = "03-d. Memory - Patch" _aliases_ = ["patch q"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-e", dest="endian_reverse", action="store_true", help="reverse endian.") parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to patch.") parser.add_argument("values", metavar="QWORD", nargs="+", help="the value to patch.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rip 0x4142434445464748 # write `HGFEDCBA` to [rip]", "{0:s} -e $rip 0x4142434445464748 # write `ABCDEFGH` to [rip]", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) self.format = "qword" return @register_command class PatchDwordCommand(PatchCommand): """Write specified DWORD to the specified address.""" _cmdline_ = "patch dword" _category_ = "03-d. Memory - Patch" _aliases_ = ["patch d"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-e", dest="endian_reverse", action="store_true", help="reverse endian.") parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to patch.") parser.add_argument("values", metavar="DWORD", nargs="+", help="the value to patch.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rip 0x41424344 # write `DCBA` to [rip]", "{0:s} -e $rip 0x41424344 # write `ABCD` to [rip]", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) self.format = "dword" return @register_command class PatchWordCommand(PatchCommand): """Write specified WORD to the specified address.""" _cmdline_ = "patch word" _category_ = "03-d. Memory - Patch" _aliases_ = ["patch w"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-e", dest="endian_reverse", action="store_true", help="reverse endian.") parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to patch.") parser.add_argument("values", metavar="WORD", nargs="+", help="the value to patch.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rip 0x4142 # write `BA` to [rip]", "{0:s} -e $rip 0x4142 # write `AB` to [rip]", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) self.format = "word" return @register_command class PatchByteCommand(PatchCommand): """Write specified BYTE to the specified address.""" _cmdline_ = "patch byte" _category_ = "03-d. Memory - Patch" _aliases_ = ["patch b"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-e", dest="endian_reverse", action="store_true", help="reverse endian.") parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to patch.") parser.add_argument("values", metavar="BYTE", nargs="+", help="the value to patch.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rip 0x41 0x41 0x41 0x41 0x41", "{0:s} -e $rip 0x41 0x41 0x41 0x41 0x41 # -e is ignored", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) self.format = "byte" return @register_command class PatchStringCommand(PatchCommand): """Write specified string to the specified memory address.""" _cmdline_ = "patch string" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to patch.") parser.add_argument("vstr", metavar='"double backslash-escaped string"', type=lambda x: codecs.escape_decode(x)[0], help="the string to write to memory.") parser.add_argument("length", metavar="LENGTH", nargs="?", type=AddressUtil.parse_address, help="the number of bytes to patch. (default: %(default)s)") _syntax_ = parser.format_help() _example_ = [ '{0:s} $sp "AAAABBBB"', '{0:s} $sp "\\\\x41\\\\x41\\\\x41\\\\x41\\\\x42\\\\x42\\\\x42\\\\x42"', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) def do_invoke(self, args): if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return if args.length: vstr = args.vstr * (args.length // len(args.vstr) + 1) vstr = vstr[:args.length] else: vstr = args.vstr try: self.PatchInfo(args.location, vstr, phys=args.phys).patch() except Exception as e: err(e) return @register_command class PatchHexCommand(PatchCommand): """Write specified hex string to the specified address.""" _cmdline_ = "patch hex" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to patch.") parser.add_argument("hstr", metavar='"hex-string"', type=lambda x: bytes.fromhex(x), help="the string to write to memory.") parser.add_argument("length", metavar="LENGTH", nargs="?", type=AddressUtil.parse_address, help="the number of bytes to patch. (default: %(default)s)") _syntax_ = parser.format_help() _example_ = [ '{0:s} $sp "4141414142424242"', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) def do_invoke(self, args): if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return if args.length: hstr = args.hstr * (args.length // len(args.hstr) + 1) hstr = hstr[:args.length] else: hstr = args.hstr try: self.PatchInfo(args.location, hstr, phys=args.phys).patch() except Exception as e: err(e) return @register_command class PatchPatternCommand(PatchCommand): """Write a pattern string to the specified memory address.""" _cmdline_ = "patch pattern" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("-c", "--charset", help="the charset of the pattern. (default: abc..z)") parser.add_argument("-d", "--dry-run", action="store_true", help="only generate patterns (do not patch memory).") parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the memory address to patch.") parser.add_argument("length", metavar="LENGTH", type=AddressUtil.parse_address, help="the number of bytes to patch. (default: %(default)s)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $sp 128", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) def do_invoke(self, args): if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return pats = PatternCreateCommand.generate_cyclic_pattern(args.length, args.charset) if args.dry_run: info("Generated pattern: {}".format(pats)) return try: self.PatchInfo(args.location, pats, phys=args.phys).patch() except Exception as e: err(e) return @register_command class PatchNopCommand(PatchCommand): """Patch the instruction(s) at the given address with NOP.""" _cmdline_ = "patch nop" _category_ = "03-d. Memory - Patch" _aliases_ = ["nop"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the memory address to patch. (default: current_arch.pc)") group = parser.add_mutually_exclusive_group() group.add_argument("-b", dest="byte_length", type=AddressUtil.parse_address, help="the patch length in bytes. (default: %(default)s)") group.add_argument("-i", dest="inst_count", type=AddressUtil.parse_address, default=1, help="the number of instructions to patch. (default: %(default)s)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $pc -i 2", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def get_insns_size(self, addr, num_insts): addr_after_n = Disasm.gef_instruction_n(addr, num_insts) return addr_after_n.address - addr def patch_nop(self, addr, num_bytes): if num_bytes == 0: info("Not patching since num_bytes == 0") return if (is_arm32() or is_arm32_cortex_m()) and current_arch.is_thumb() and addr & 1: addr -= 1 nop_op_len = len(current_arch.nop_insn) if nop_op_len > num_bytes: err("Cannot patch instruction at {:#x} (nop_size is {:d}, insn_size is {:d})".format( addr, nop_op_len, num_bytes, )) return count = num_bytes // nop_op_len patch_bytes = nop_op_len * count if patch_bytes != num_bytes: err("Cannot patch instruction at {:#x} (nop instruction does not evenly fit in requested size)".format(addr)) return if Endian.is_big_endian(): insn = current_arch.nop_insn[::-1] else: insn = current_arch.nop_insn self.PatchInfo(addr, insn * count, length=patch_bytes, phys=self.args.phys).patch() return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) @require_arch_set def do_invoke(self, args): if current_arch.nop_insn is None: err("This command is not supported on this architecture") return if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return if args.location is None: location = current_arch.pc else: location = args.location try: if args.byte_length is not None: num_bytes = args.byte_length else: num_bytes = self.get_insns_size(location, args.inst_count) except Exception: err("Failed to get patch bytes") return try: self.patch_nop(location, num_bytes) except Exception as e: err(e) return @register_command class PatchInfloopCommand(PatchCommand): """Patch the instruction(s) at the given address with an infinite loop.""" _cmdline_ = "patch inf" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the memory address to patch. (default: current_arch.pc)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $pc", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def patch_infloop(self, addr): if (is_arm32() or is_arm32_cortex_m()) and current_arch.is_thumb() and addr & 1: addr -= 1 if Endian.is_big_endian(): insn = current_arch.infloop_insn[::-1] if current_arch.has_delay_slot: insn += current_arch.nop_insn[::-1] else: insn = current_arch.infloop_insn if is_arc32() or is_arc64(): if addr % 4 == 2: insn = current_arch.infloop_insn2 else: if current_arch.has_delay_slot: insn += current_arch.nop_insn self.PatchInfo(addr, insn, phys=self.args.phys).patch() return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) @require_arch_set def do_invoke(self, args): if current_arch.infloop_insn is None: err("This command is not supported on this architecture") return if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return if args.location is None: location = current_arch.pc else: location = args.location try: self.patch_infloop(location) except Exception as e: err(e) return @register_command class PatchTrapCommand(PatchCommand): """Patch the instruction(s) at the given address with breakpoint or trap (if available).""" _cmdline_ = "patch trap" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the memory address to patch. (default: current_arch.pc)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $pc", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def patch_trap(self, addr): if (is_arm32() or is_arm32_cortex_m()) and current_arch.is_thumb() and addr & 1: addr -= 1 if Endian.is_big_endian(): insn = current_arch.trap_insn[::-1] if current_arch.has_delay_slot: insn += current_arch.nop_insn[::-1] else: insn = current_arch.trap_insn if current_arch.has_delay_slot: insn += current_arch.nop_insn self.PatchInfo(addr, insn, phys=self.args.phys).patch() return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) @require_arch_set def do_invoke(self, args): if current_arch.trap_insn is None: err("This command is not supported on this architecture") return if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return if args.location is None: location = current_arch.pc else: location = args.location try: self.patch_trap(location) except Exception as e: err(e) return @register_command class PatchRetCommand(PatchCommand): """Patch the instruction(s) at the given address with return.""" _cmdline_ = "patch ret" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the memory address to patch. (default: current_arch.pc)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $pc", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def patch_ret(self, addr): if (is_arm32() or is_arm32_cortex_m()) and current_arch.is_thumb() and addr & 1: addr -= 1 if Endian.is_big_endian(): insn = current_arch.ret_insn[::-1] if current_arch.has_delay_slot: insn += current_arch.nop_insn[::-1] else: insn = current_arch.ret_insn if current_arch.has_delay_slot: insn += current_arch.nop_insn self.PatchInfo(addr, insn, phys=self.args.phys).patch() return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) @require_arch_set def do_invoke(self, args): if current_arch.ret_insn is None: err("This command is not supported on this architecture") return if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return if args.location is None: location = current_arch.pc else: location = args.location try: self.patch_ret(location) except Exception as e: err(e) return @register_command class PatchSyscallCommand(PatchCommand): """Patch the instruction(s) at the given address with syscall instruction.""" _cmdline_ = "patch syscall" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the memory address to patch. (default: current_arch.pc)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $pc", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def patch_syscall(self, addr): if (is_arm32() or is_arm32_cortex_m()) and current_arch.is_thumb() and addr & 1: addr -= 1 if Endian.is_big_endian(): insn = current_arch.syscall_insn[::-1] if current_arch.has_syscall_delay_slot: insn += current_arch.nop_insn[::-1] else: insn = current_arch.syscall_insn if current_arch.has_syscall_delay_slot: insn += current_arch.nop_insn self.PatchInfo(addr, insn, phys=self.args.phys).patch() return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr", "wine")) @require_arch_set def do_invoke(self, args): if current_arch.syscall_insn is None: err("This command is not supported on this architecture") return if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return if args.location is None: location = current_arch.pc else: location = args.location try: self.patch_syscall(location) except Exception as e: err(e) return @register_command class PatchHistoryCommand(PatchCommand, BufferingOutput): """Display the patch history stack.""" _cmdline_ = "patch history" _category_ = "03-d. Memory - Patch" _aliases_ = ["patch list"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) def do_invoke(self, args): self.out = [] if PatchCommand.patch_history: self.out.append(titlify("NEW")) self.out.append("[{:s}] (current state)".format(Color.boldify("0"))) for i, hist in enumerate(PatchCommand.patch_history, start=1): for j, patch_info in enumerate(hist): if not self.args.verbose: if j > 8: self.out.append(" ...") break self.out.append(" {!s}{:s}: {:s} -> {:s}".format( ProcessMap.lookup_address(patch_info.addr), Symbol.get_symbol_string(patch_info.addr), patch_info.b(), patch_info.a(), )) self.out.append("[{:s}]".format(Color.boldify("{:d}".format(i)))) self.out.append(titlify("OLD")) else: self.info_add_out("Patch history stack is empty") self.print_output(check_terminal_size=True) return @register_command class PatchRevertCommand(PatchCommand): """Revert patches recorded in the patch history stack.""" _cmdline_ = "patch revert" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("target_state", metavar="TARGET_STATE", nargs="?", type=int, help="the history state index number to revert.") group.add_argument("--all", action="store_true", help="revert all patches.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0 # do nothing (keep the current state).", "{0:s} 2 # roll back to history state [2].", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) def do_invoke(self, args): if len(PatchCommand.patch_history) == 0: info("Patch history stack is empty") return if args.all: revert_count = len(PatchCommand.patch_history) + 1 else: if not (0 <= args.target_state < len(PatchCommand.patch_history) + 1): err("Invalid target index") gef_print(titlify("Patch history stack")) gdb.execute("patch history") return revert_count = args.target_state while PatchCommand.patch_history and revert_count > 0: hist = PatchCommand.patch_history.pop(0) for patch_info in hist: try: patch_info.revert() except Exception as e: err(e) return revert_count -= 1 return @register_command class PatchRangeReplaceCommand(PatchCommand): """Replace all occurrences of a specific byte sequence in the specified range with another byte sequence.""" _cmdline_ = "patch range-replace" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat LOCATION as a physical address (qemu-system only).") parser.add_argument("range_start", metavar="START_ADDR", type=AddressUtil.parse_address, help="start address to search.") parser.add_argument("range_end", metavar="END_ADDR", type=AddressUtil.parse_address, help="end address to search.") parser.add_argument("hstr_from", metavar="HEX_STR_FROM", type=lambda x: bytes.fromhex(x), help="the hex string to search for (source pattern).") parser.add_argument("hstr_to", metavar="HEX_STR_TO", type=lambda x: bytes.fromhex(x), help="the hex string to replace it with (replacement pattern).") _syntax_ = parser.format_help() _example_ = [ '{0:s} 0x400000 0x401000 "ebfe" "9090"', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def patch_range_replace(self): try: data = read_memory(self.args.range_start, self.args.range_end - self.args.range_start) except gdb.MemoryError: err("Memory read error") return tag = PatchCommand.PatchInfo.get_unique_tag() pos = 0 while True: found_pos = data.find(self.args.hstr_from, pos) if found_pos == -1: break self.PatchInfo(self.args.range_start + found_pos, self.args.hstr_to, tag=tag).patch() pos = found_pos + len(self.args.hstr_from) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) def do_invoke(self, args): if args.phys: if not is_qemu_system(): err("Unsupported in this gdb mode.") return try: self.patch_range_replace() except Exception as e: err(e) return @register_command class DereferenceCommand(GenericCommand): """Dereference recursively from an address and display information.""" _cmdline_ = "dereference" _category_ = "01-a. Debugging Support - Context" _repeat_ = True _aliases_ = ["telescope"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the memory address to dump. (default: current_arch.sp)") parser.add_argument("nb_lines", metavar="NB_LINES", nargs="?", type=AddressUtil.parse_address, help="the count of lines.") parser.add_argument("-a", "--is-addr", action="store_true", help="display only valid addresses.") parser.add_argument("-A", "--is-not-addr", action="store_true", help="display only invalid addresses.") parser.add_argument("-P", "--perm", type=str, help="display only specified permission.") parser.add_argument("-z", "--is-zero", action="store_true", help="display only zero values.") parser.add_argument("-Z", "--is-not-zero", action="store_true", help="display only non-zero values.") parser.add_argument("-m", "--mask-hits", nargs="+", action="append", type=AddressUtil.parse_address, metavar=("MASK", "VALUE"), help="display only mask hits.") parser.add_argument("-M", "--no-mask-hits", nargs="+", action="append", type=AddressUtil.parse_address, metavar=("MASK", "VALUE"), help="display only mask non-hits.") parser.add_argument("-t", "--tag", nargs=2, action="append", metavar=("IDX", "TAG"), help="display with tags.") parser.add_argument("-T", "--tag-offset", type=AddressUtil.parse_address, default=0, help="the slide offset of all tag positions.") parser.add_argument("-r", "--reverse", action="store_true", help="display in reverse order line by line.") parser.add_argument("-f", "--frame-split", action="store_true", help="display with frame split lines (heuristics).") parser.add_argument("-u", "--uniq", action="store_true", help="display with uniq.") parser.add_argument("-i", "--interval", type=AddressUtil.parse_address, default=1, help="the line number of the interval for showing.") parser.add_argument("-d", "--depth", type=AddressUtil.parse_address, default=1, help="depth of recursive. (default: %(default)s)") parser.add_argument("-D", "--depth-nb-lines", type=AddressUtil.parse_address, default=4, help="NB_LINES when recursive. (default: %(default)s)") parser.add_argument("-p", "--phys", action="store_true", help="treat LOCATION as a physical address. (qemu-system only)") parser.add_argument("-l", "--list-head", action="store_true", help="display if LIST_HEAD or not.") parser.add_argument("-s", "--slab-contains", action="store_true", help="display slab_cache name if available.") parser.add_argument("-S", "--slab-contains-unaligned", action="store_true", help="display slab_cache name (allow unaligned) if available.") parser.add_argument("-q", "--quiet", action="store_true", help="do not display other than addresses and values.") parser.add_argument("-Q", "--quiet-offset", action="store_true", help="do not display offset and index values.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # dereference $sp 64", "{0:s} $sp 20 # specify location and number of elements to display", "{0:s} $sp -20 # display memory backwards", "{0:s} --reverse $sp 20 # display reverse order", "{0:s} --depth 2 $sp 20 # display recursively if valid aligned address", "{0:s} --is-addr $sp 20 # display elements which is valid address", "{0:s} --slab-contains $sp 20 # with slab-contains result (available under qemu-system)", "{0:s} --tag 0 next $sp 20 # with tags", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Use blacklist feature if reading the address causes process crash.", 'e.g., `gef config dereference.blacklist "[ [0xffffffffc9000000, 0xffffffffc9001000], ]"', "then `gef save`.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) self.add_setting("max_recursion", 4, "Maximum level of pointer recursion") self.add_setting("blacklist", "[]", 'Dereference black list address ranges (e.g., "[[from1, to1], [from2, to2]]")') self.add_setting("nb_lines", 64, "Number of lines to display") self.add_setting("no_pager", False, "Always enable --no-pager option for this telescope command only") return @staticmethod @Cache.cache_until_next def get_frame_pcs(): frames = [] try: frame = gdb.newest_frame() no_ret_addr = [0, 0xffff_ffff, 0xffff_ffff_ffff_ffff] while frame: pc = frame.pc() if pc in no_ret_addr: break if pc in frames: break frames.append(pc) frame = frame.older() except gdb.error: pass return frames @staticmethod @Cache.cache_this_session def get_target_registers(): regs = [] for reg in current_arch.all_registers: # skip if not ggeneral registers if current_arch.flag_register == reg: continue if current_arch.special_registers and reg in current_arch.special_registers: continue regs.append(reg) return regs @staticmethod @Cache.cache_until_next def get_target_registers_value(): regs = [] for regname in DereferenceCommand.get_target_registers(): regvalue = get_register(regname) if regvalue is None: continue if regvalue == 0: # too noisy, so skip continue regs.append((regname, regvalue)) return regs @staticmethod def pprint_dereferenced(addr, idx, tag=None, phys=False, quiet=False, quiet_offset=False): """Format and display a single dereferenced memory entry, including pointer chains and optional annotations such as retaddr, canary, cookie, or registers. """ base_address_color = Config.get_gef_setting("theme.dereference_base_address") registers_color = Config.get_gef_setting("theme.dereference_register_value") memalign = current_arch.ptrsize offset = idx * memalign # used as first element memalign_size = None if is_x86_16(): memalign_size = 2.5 current_address = AddressUtil.align_address(addr + offset, memalign_size=memalign_size) addrs, error = AddressUtil.recursive_dereference(current_address, phys=phys) if len(addrs) == 1 and not error: # cannot access this area raise # create address link list link = AddressUtil.recursive_dereference_to_string( current_address, skip_idx=1, phys=phys, quiet=quiet, ) # create line of one entry addr_formatted = AddressUtil.format_address(addrs[0], memalign_size=memalign_size) addr_colored = Color.colorify(addr_formatted, base_address_color) if quiet_offset: line = "{:s}: ".format(addr_colored) else: line = "{:s}|{:+#07x}|{:+04d}: ".format(addr_colored, offset, idx) if tag: line += "{:s}: ".format(tag) line += "{:{:d}s}".format(link, memalign * 2 + 2) if len(addrs) == 1: return line if quiet: return line # add extra info (retaddr, canary, cookie, register) extra = [] current_address_value = addrs[1] # retaddr info for i, frame_pc in enumerate(DereferenceCommand.get_frame_pcs()): if not is_valid_addr(frame_pc): continue if current_address_value == frame_pc: extra.append("retaddr[{:d}]".format(i)) break # canary info if not is_qemu_system() and not is_vmware() and not is_kgdb(): res = CanaryCommand.gef_read_canary() if res: canary, location = res if canary != 0: # when Golang binary, canary is 0 if current_address_value == canary: extra.append("canary") # mangle cookie if not is_qemu_system() and not is_vmware() and not is_kgdb(): res = PtrDemangleCommand.get_cookie() if res: cookie = res if cookie != 0: if current_address_value == cookie: extra.append("PTR_MANGLE cookie") # register info if not phys: # for the physical address, 0x0 may be valid, # which tends to clutter the result, so skip if is_valid_addr(current_address_value): for regname, regvalue in DereferenceCommand.get_target_registers_value(): if current_address_value == regvalue: extra.append(regname) # add extra to end of line if extra: extra_str = " <- {:s}".format(", ".join(extra)) line += Color.colorify(extra_str, registers_color) return line def check_list_head(self, start_address, from_idx, to_idx, step): for idx in range(from_idx, to_idx, step): current_address = start_address + idx * current_arch.ptrsize if is_double_link_list(current_address): # next tag = self.tags_dict.get(idx + 0, "") if tag: tag += ", " tag += Color.colorify("list_head.next", "bold magenta") self.tags_dict[idx + 0] = tag self.max_tag_width = max(self.max_tag_width, len(Color.remove_color(tag))) # prev tag = self.tags_dict.get(idx + 1, "") if tag: tag += ", " tag += Color.colorify("list_head.prev", "bold magenta") self.tags_dict[idx + 1] = tag self.max_tag_width = max(self.max_tag_width, len(Color.remove_color(tag))) return def check_slab_contains(self, start_address, from_idx, to_idx, step): for idx in range(from_idx, to_idx, step): current_address = start_address + idx * current_arch.ptrsize if not is_valid_addr(current_address): continue v = read_int_from_memory(current_address) ret = Kernel.get_slab_contains( v, allow_unaligned=self.args.slab_contains_unaligned, keep_color=True, ) if ret: tag = self.tags_dict.get(idx, "") if tag: tag += ", " tag += ret.split()[1] if "unaligned?" in ret: tag += "(unaligned)" self.tags_dict[idx] = tag self.max_tag_width = max(self.max_tag_width, len(Color.remove_color(tag))) return def dereference_line_by_line(self, start_address, from_idx, to_idx, step): if self.args.list_head: self.check_list_head(start_address, from_idx, to_idx, step) if self.args.slab_contains or self.args.slab_contains_unaligned: self.check_slab_contains(start_address, from_idx, to_idx, step) has_tag = bool(self.args.tag) has_tag |= bool(self.args.list_head) has_tag |= bool(self.args.slab_contains) has_tag |= bool(self.args.slab_contains_unaligned) out = [] seen = [] for idx in range(from_idx, to_idx, step): current_address = start_address + idx * current_arch.ptrsize try: # uniq filtering if self.args.uniq: v = self.read_int_from_memory(current_address) if v in seen: if out == [] or out[-1] != "*": out.append("*") continue seen.append(v) # valid address filtering if self.args.is_addr: v = self.read_int_from_memory(current_address) if not is_valid_addr(v): continue # invalid address filtering if self.args.is_not_addr: v = self.read_int_from_memory(current_address) if is_valid_addr(v): continue # zero filtering if self.args.is_zero: v = self.read_int_from_memory(current_address) if v != 0: continue # non-zero filtering if self.args.is_not_zero: v = self.read_int_from_memory(current_address) if v == 0: continue # mask hits filtering if self.args.mask_hits is not None: v = self.read_int_from_memory(current_address) hit = False for m in self.args.mask_hits: masked = v & m[0] if (len(m) == 1 and masked != 0) or (len(m) != 1 and masked in m[1:]): hit = True break if not hit: continue # mask no-hits filtering if self.args.no_mask_hits is not None: v = self.read_int_from_memory(current_address) hit = False for m in self.args.no_mask_hits: masked = v & m[0] if (len(m) == 1 and masked == 0) or (len(m) != 1 and masked not in m[1:]): hit = True break if not hit: continue # permission filtering if self.args.perm is not None: v = self.read_int_from_memory(current_address) try: pm = ProcessMap.lookup_address(v) if not pm.section.permission.match(self.args.perm): continue except Exception: continue # tags if has_tag: tag = self.tags_dict.get(idx, "") padlen = self.max_tag_width - len(Color.remove_color(tag)) tag += " " * padlen else: tag = None # create line line = DereferenceCommand.pprint_dereferenced( start_address, idx, tag=tag, phys=self.args.phys, quiet=self.args.quiet, quiet_offset=self.args.quiet_offset, ) # most left registers info if not self.args.quiet: # register info regs_info = [] for regname, regvalue in DereferenceCommand.get_target_registers_value(): if current_address == regvalue: regs_info.append(regname) regs_info_str = regs_info[0] if regs_info else "" regs_info_ex = "+" if len(regs_info) > 1 else " " line = "{:>{:d}s}{:s} {:s}".format( regs_info_str, current_arch.get_registers_name_max(), regs_info_ex, line, ) # add line out.append(line) # horizontal line if self.args.frame_split: if "<- retaddr[" in line: out.append(titlify("")) except (RuntimeError, gdb.MemoryError): # e.g., nop DWORD PTR [rax+rax*1+0x0] msg = "Cannot access memory at address {:#x}".format(current_address) out.append("{} {}".format(Color.colorify("[!]", "bold red"), msg)) break # multiple level dump if self.args.depth - 1 > 0: v = self.read_int_from_memory(current_address) if v % current_arch.ptrsize == 0 and is_valid_addr(v): args = self.args # backup cmd = "dereference --depth {:d} --no-pager {:#x} {:#x}".format( self.args.depth - 1, v, self.args.depth_nb_lines, ) ret = gdb.execute(cmd, to_string=True) self.args = args # revert for line in ret.splitlines(): out.append(" " + line) if self.args.reverse: out.reverse() return out @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): if args.slab_contains or args.slab_contains_unaligned or args.phys: if not (is_qemu_system() or is_kgdb() or is_vmware()): err("Unsupported gdb mode") return if (args.slab_contains or args.slab_contains_unaligned) and args.phys: err("Unsupported option pairs") return # perm if args.perm: if len(args.perm) != 3: err("Invalid permission length") return if args.perm[0] not in "rR-_?": err("Invalid permission") return if args.perm[1] not in "wW-_?": err("Invalid permission") return if args.perm[2] not in "xX-_?": err("Invalid permission") return # tags self.tags_dict = {} self.max_tag_width = 0 if args.tag: for tag_idx, tag in args.tag: try: tag_idx = int(tag_idx, 0) + args.tag_offset except ValueError: err("Invalid tag idx") return self.tags_dict[tag_idx] = tag self.max_tag_width = max(self.max_tag_width, len(tag)) # read memory function if args.phys: unpack = u32 if is_32bit() else u64 self.read_int_from_memory = lambda x: unpack(read_physmem(x, current_arch.ptrsize)) else: self.read_int_from_memory = read_int_from_memory # start address if args.location is None: start_address = current_arch.sp else: start_address = args.location # line numbers nb_lines = args.nb_lines or Config.get_gef_setting("dereference.nb_lines") from_idx = nb_lines * self.repeat_count to_idx = nb_lines * (self.repeat_count + 1) if from_idx <= to_idx: step = 1 * args.interval else: step = -1 * args.interval if args.depth > 1: err("Unsupported using together -NB_LINES and -d DEPTH") return # doit out = self.dereference_line_by_line(start_address, from_idx, to_idx, step) # Because there is a special configuration, the BufferingOutput class is not inherited no_pager = args.no_pager | Config.get_gef_setting("dereference.no_pager") gef_print("\n".join(out), less=not no_pager) return @register_command class ASLRCommand(GenericCommand): """View / modify the ASLR setting of GDB.""" _cmdline_ = "aslr" _category_ = "02-f. Process Information - Security" parser = argparse.ArgumentParser(prog=_cmdline_) modes = [None, "on", "off"] parser.add_argument("command", nargs="?", default=None, choices=modes, metavar="{on,off}", help="set gdb aslr settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.modes: # already matched return [] if text == "": # no prefix return [s for s in self.modes if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.modes if s and s.startswith(text.strip())] @parse_args @only_if_gdb_target_local @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): if is_attach() or is_remote_debug(): warn("ASLR setting is ignored because it is remote or attached process") if args.command is None: aslr = gdb.parameter("disable-randomization") if aslr: msg = "ASLR is currently " + Color.redify("disabled") else: msg = "ASLR is currently " + Color.greenify("enabled") gef_print(msg) elif args.command == "on": info("Enabling ASLR") gdb.execute("set disable-randomization off") elif args.command == "off": info("Disabling ASLR") gdb.execute("set disable-randomization on") return @register_command class FollowCommand(GenericCommand): """View / modify the follow-fork-mode setting of GDB.""" _cmdline_ = "follow" _category_ = "01-i. Debugging Support - Other" parser = argparse.ArgumentParser(prog=_cmdline_) modes = [None, "child", "parent"] parser.add_argument("command", nargs="?", default=None, choices=modes, metavar="{child,parent}", help="set gdb follow settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.modes: # already matched return [] if text == "": # no prefix return [s for s in self.modes if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.modes if s and s.startswith(text.strip())] @parse_args @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) def do_invoke(self, args): if args.command is None: follow = gdb.parameter("follow-fork-mode") if follow == "child": msg = "follow " + Color.redify("Child") else: msg = "follow " + Color.redify("Parent") gef_print(msg) elif args.command == "child": info("Follow child") gdb.execute("set follow-fork-mode child") elif args.command == "parent": info("Follow parent") gdb.execute("set follow-fork-mode parent") return @register_command class SmartCppFunctionNameCommand(GenericCommand): """Toggle the setting of `context.smart_cpp_function_name`.""" _cmdline_ = "smart-cpp-function-name" _category_ = "01-f. Debugging Support - Context Extension" _aliases_ = ["cpp"] parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): setting = Config.get_gef_setting("context.smart_cpp_function_name") gdb.execute("gef config context.smart_cpp_function_name {!s}".format(not setting), to_string=True) return @register_command class ExtraCommand(GenericCommand): """The base command to add, remove, list or clear user specified command to `context extra`.""" _cmdline_ = "extra" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("add") subparsers.add_parser("remove") subparsers.add_parser("list") subparsers.add_parser("clear") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) super().__init__(prefix=prefix) return @parse_args def do_invoke(self, args): self.usage() return @register_command class ExtraAddCommand(ExtraCommand): """Add user specified command to execute when each step.""" _cmdline_ = "extra add" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("cmd", metavar="CMD", nargs="+", help="the command to execute when each step.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): ContextExtraCommand.context_extra_commands.append(" ".join(args.cmd)) return @register_command class ExtraListCommand(ExtraCommand): """List user specified command to execute when each step.""" _cmdline_ = "extra list" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): if not ContextExtraCommand.context_extra_commands: warn("Nothing to display") return for i, command in enumerate(ContextExtraCommand.context_extra_commands): gef_print("[{:3d}] {:s}".format(i, command)) return @register_command class ExtraRemoveCommand(ExtraCommand): """Remove user specified command to execute when each step.""" _cmdline_ = "extra remove" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("index", metavar="INDEX", type=int, help="the index of command to remove from automatically execution each step.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): if args.index < len(ContextExtraCommand.context_extra_commands): ContextExtraCommand.context_extra_commands.pop(args.index) else: err("Out of index") return @register_command class ExtraClearCommand(ExtraCommand): """Clear all user specified commands to execute when each step.""" _cmdline_ = "extra clear" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): ContextExtraCommand.context_extra_commands = [] return @register_command class CommentCommand(GenericCommand): """The base command to add, remove, list or clear the comment.""" _cmdline_ = "comment" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("add") subparsers.add_parser("remove") subparsers.add_parser("list") subparsers.add_parser("clear") _syntax_ = parser.format_help() _note_ = [ "Comments are temporary only. Note that it will be deleted when GDB exits.", ] _note_= "\n".join(_note_) def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) super().__init__(prefix=prefix) return @parse_args @require_arch_set def do_invoke(self, args): self.usage() return @register_command class CommentAddCommand(CommentCommand): """Add a comment to specific address.""" _cmdline_ = "comment add" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address for comment.") parser.add_argument("comment", metavar="COMMENT", help="the comment to print when hit.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args @require_arch_set def do_invoke(self, args): comms = ContextCodeCommand.context_comments.get(args.location, []) ContextCodeCommand.context_comments[args.location] = comms + [args.comment] return @register_command class CommentLsCommand(CommentCommand): """List the comments.""" _cmdline_ = "comment list" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args @require_arch_set def do_invoke(self, args): if not ContextCodeCommand.context_comments: warn("Nothing to display") return for loc, comms in sorted(ContextCodeCommand.context_comments.items()): for i, comm in enumerate(comms): gef_print("{:#x}: [{:3d}] {:s}".format(loc, i, comm)) return @register_command class CommentRemoveCommand(CommentCommand): """Remove the specified comment.""" _cmdline_ = "comment remove" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address for comment.") parser.add_argument("index", metavar="INDEX", nargs="?", type=int, help="the index of comment to remove. If omitted, all comments for that address will be deleted.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args @require_arch_set def do_invoke(self, args): if args.location not in ContextCodeCommand.context_comments: err("Invalid location") return if args.index is None: del ContextCodeCommand.context_comments[args.location] else: if args.index >= len(ContextCodeCommand.context_comments[args.location]): err("Out of index") return ContextCodeCommand.context_comments[args.location].pop(args.index) if len(ContextCodeCommand.context_comments[args.location]) == 0: del ContextCodeCommand.context_comments[args.location] return @register_command class CommentClearCommand(CommentCommand): """Clear all comments.""" _cmdline_ = "comment clear" _category_ = "01-f. Debugging Support - Context Extension" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args @require_arch_set def do_invoke(self, args): ContextCodeCommand.context_comments = {} return @register_command class VMMapCommand(GenericCommand, BufferingOutput): """Display a comprehensive layout of the virtual memory mapping.""" _cmdline_ = "vmmap" _category_ = "02-c. Process Information - Memory/Section" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--outer", action="store_true", help="display qemu-user's memory map instead of emulated process's memory map.") parser.add_argument("filter", metavar="FILTER", nargs="?", help="filter string.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="do not display register information.") _syntax_ = parser.format_help() _example_ = [ "{0:s} libc # show only lines containing the string `libc`", "{0:s} binary # 'binary' means the area executable itself", "{0:s} 0x555555577ab0 # show only lines included specified address", "{0:s} --outer # show qemu-user memory map; only valid in qemu-user mode", ] _example_ = "\n".join(_example_).format(_cmdline_) def dump_entry(self, entry, print_offset=False): # get color line_color = "" if entry.path.startswith("[stack]"): line_color = Config.get_gef_setting("theme.address_stack") elif entry.path.startswith("[heap]"): line_color = Config.get_gef_setting("theme.address_heap") elif entry.permission.value & Permission.EXECUTE: line_color = Config.get_gef_setting("theme.address_code") elif entry.permission.value & Permission.WRITE: line_color = Config.get_gef_setting("theme.address_writable") elif entry.permission.value & Permission.READ: line_color = Config.get_gef_setting("theme.address_readonly") elif entry.permission.value == Permission.NONE: line_color = Config.get_gef_setting("theme.address_valid_but_none") if entry.permission.value == (Permission.READ | Permission.WRITE | Permission.EXECUTE): line_color += " " + Config.get_gef_setting("theme.address_rwx") # if qemu-xxx(32bit arch) runs on x86-64 machine, memalign_size does not match # AddressUtil.get_memory_alignment() memalign_size = 8 if self.args.outer else None # make line lines = [] lines.append(Color.colorify( AddressUtil.format_address(entry.page_start, memalign_size, long_fmt=True), line_color, )) lines.append(Color.colorify( AddressUtil.format_address(entry.page_end, memalign_size, long_fmt=True), line_color, )) lines.append(Color.colorify( AddressUtil.format_address(entry.size, memalign_size, long_fmt=True), line_color, )) lines.append(Color.colorify( AddressUtil.format_address(entry.offset, memalign_size, long_fmt=True), line_color, )) lines.append(Color.colorify( str(entry.permission), line_color, )) if entry.path: lines.append(Color.colorify(entry.path, line_color)) line = " ".join(lines) # offset info if not self.args.quiet: if print_offset is not False: line += Color.colorify(" {:+#x}".format(print_offset), line_color) # register info if not self.args.quiet: register_hints = [] for regname in current_arch.all_registers: regvalue = get_register(regname) if entry.page_start <= regvalue < entry.page_end: register_hints.append(regname) if register_hints: m = " <- {:s}".format(", ".join(list(register_hints))) registers_color = Config.get_gef_setting("theme.dereference_register_value") line += Color.colorify(m, registers_color) self.out.append(line) return def show_legend(self): legend = "[ Legend: {:s} ]".format( " | ".join([ Color.colorify("Code", Config.get_gef_setting("theme.address_code")), Color.colorify("Heap", Config.get_gef_setting("theme.address_heap")), Color.colorify("Stack", Config.get_gef_setting("theme.address_stack")), Color.colorify("Writable", Config.get_gef_setting("theme.address_writable")), Color.colorify("ReadOnly", Config.get_gef_setting("theme.address_readonly")), Color.colorify("None", Config.get_gef_setting("theme.address_valid_but_none")), Color.colorify("RWX", Config.get_gef_setting("theme.address_rwx")), ]), ) self.out.append(legend) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb",)) def do_invoke(self, args): if is_qemu_system() or is_vmware(): if is_arm32_cortex_m(): info("Redirect to `xfiles` (args are ignored)") gdb.execute("xfiles") return elif is_in_kernel(): info("Redirect to `kvmmap` (args are ignored; use `pagewalk` to show phys addr)") gdb.execute("kvmmap") return else: info("Redirect to `pagewalk` (args are ignored)") gdb.execute("pagewalk --quiet") return if args.outer and not is_qemu_user(): err("Unsupported `--outer` option in this gdb mode") return if is_qemu_user(): # the memory map may be changed, so retry memory exploring in get_process_maps() Cache.reset_gef_caches(all=True) # get maps vmmap = ProcessMap.get_process_maps(args.outer) if not vmmap: for line in gdb.execute("info files", to_string=True).splitlines(): if line.startswith("Symbols from"): break else: err("Missing info about architecture. Please set: `file /path/to/target_binary`") err("No address mapping information found") return # color legend self.out = [] if not Config.get_gef_setting("gef.disable_color"): self.show_legend() # legend fmt = "{:{:d}s} {:{:d}s} {:{:d}s} {:{:d}s} {:4s} {:s}" memalign_size = 8 if args.outer else AddressUtil.get_memory_alignment() width = memalign_size * 2 + 2 legend = ["Start", width, "End", width, "Size", width, "Offset", width, "Perm", "Path"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # for filter filter_addr1 = None filter_addr2 = None if args.filter is not None: try: filter_addr1 = AddressUtil.parse_address(args.filter) except gdb.error: pass try: filter_addr2 = int(args.filter, 0) except ValueError: pass # show each entry for entry in vmmap: # no filter if not args.filter: self.dump_entry(entry) continue # includes address if filter_addr1 is not None: if entry.page_start <= filter_addr1 < entry.page_end: self.dump_entry(entry, print_offset=filter_addr1 - entry.page_start) filter_addr1 = None # It never matches a different region continue # range match if filter_addr2 is not None: if entry.page_start <= filter_addr2 < entry.page_end: self.dump_entry(entry, print_offset=filter_addr2 - entry.page_start) filter_addr2 = None # It never matches a different region continue # `binary` case if args.filter == "binary": if Path.get_filepath(append_proc_root_prefix=False) == entry.path: self.dump_entry(entry) continue # A simple match to the filter string if args.filter in entry.path: self.dump_entry(entry) continue # warning message if is_qemu_user() and not args.outer: if ProcessMap.__gef_use_info_proc_mappings__ is False: self.info_add_out("Some areas may be undetectable due to heuristic search (auxv, registers, stack)") self.info_add_out("Permissions use ELF header or default rw-; dynamic changes undetectable") # print self.print_output(check_terminal_size=True) return @register_command class XFilesCommand(GenericCommand, BufferingOutput): """Display all libraries (and sections) loaded by binary.""" _cmdline_ = "xfiles" _category_ = "02-c. Process Information - Memory/Section" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("filter", metavar="FILTER", nargs="*", help="regex filter string.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} libc", "{0:s} got plt", "{0:s} IO_vtables", ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running def do_invoke(self, args): self.out = [] fmt = "{:{:d}s} {:{:d}s} {:<21s} {:s}" width = AddressUtil.get_format_address_width() legend = ["Start", width, "End", width, "Name", "File"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for xfile in ProcessMap.get_info_files(): lines = [] lines.append(str(ProcessMap.lookup_address(xfile.zone_start))) lines.append(str(ProcessMap.lookup_address(xfile.zone_end))) lines.append("{:<21s}".format(xfile.name)) lines.append(xfile.filename) line = " ".join(lines) if not args.filter: self.out.append(line) continue for filt in args.filter: if re.search(filt, line): self.out.append(line) break self.print_output(check_terminal_size=True) return @register_command class XInfoCommand(GenericCommand): """Retrieve and display runtime information for the location(s) given as parameter.""" _cmdline_ = "xinfo" _category_ = "02-c. Process Information - Memory/Section" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="*", type=AddressUtil.parse_address, help="the memory address to show the information. (default: current_arch.pc)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $pc", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def xinfo(self, address): addr = ProcessMap.lookup_address(address) if not addr.valid: warn("Cannot reach {:#x} in memory space".format(address)) return gdb.execute("vmmap {:#x}".format(address)) if addr.section: page_start = ProcessMap.lookup_address(addr.section.page_start) gef_print("Offset (from mapped): {!s} + {:#x}".format( page_start, addr.value - addr.section.page_start, )) if addr.section.path and addr.section.path.startswith("/"): base_start = ProcessMap.lookup_address(ProcessMap.get_section_base_address(addr.section.path)) gef_print("Offset (from base): {!s} + {:#x}".format( base_start, addr.value - base_start.section.page_start, )) if addr.info: zone_start = ProcessMap.lookup_address(addr.info.zone_start) gef_print("Offset (from segment): {!s} ({:s}) + {:#x}".format( zone_start, addr.info.name, addr.value - addr.info.zone_start, )) sym = Symbol.get_symbol_string(address) if sym: msg = "Symbol: {:s}".format(sym.strip()) gef_print(msg) if addr.section and addr.section.inode: gef_print("Inode: {:d}".format(addr.section.inode)) return def xinfo_kernel_pagewalk(self, address): ret = gdb.execute("pagewalk --vrange {:#x} --no-pager --quiet".format(address), to_string=True) ret = [x for x in ret.splitlines() if not Color.remove_color(x).startswith(("---", "[+]"))] if not ret: err("Not found") return gef_print("\n".join(ret)) if ret[-1].startswith("0x"): virt, phys, *_ = ret[-1].split() vstart = int(virt.split("-")[0], 16) pstart = int(phys.split("-")[0], 16) offset = address - vstart gef_print("Offset (from virt mapped): {:#x} + {:#x}".format(vstart, offset)) gef_print("Offset (from phys mapped): {:#x} + {:#x}".format(pstart, offset)) return def xinfo_kernel_kvmmap(self, address): ret = gdb.execute("kvmmap {:#x} --no-pager --quiet".format(address), to_string=True) gef_print(ret.rstrip()) return def xinfo_kernel_slab(self, address): ret = gdb.execute("slab-contains {:#x}".format(address), to_string=True) gef_print(ret.rstrip()) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb",)) @require_arch_set def do_invoke(self, args): if args.location == []: locations = [current_arch.pc] else: locations = args.location # kernel xinfo if is_qemu_system() or is_vmware(): for location in locations: gef_print(titlify("xinfo (from pagewalk): {:#x}".format(location))) self.xinfo_kernel_pagewalk(location) gef_print(titlify("xinfo (from kvmmap): {:#x}".format(location))) self.xinfo_kernel_kvmmap(location) gef_print(titlify("xinfo (from slab-contains): {:#x}".format(location))) self.xinfo_kernel_slab(location) return # userland xinfo for location in locations: try: gef_print(titlify("xinfo: {:#x}".format(location))) self.xinfo(location) except gdb.error as gdb_error: err(str(gdb_error)) return @register_command class XorMemoryCommand(GenericCommand): """The base command to xor a block of memory.""" _cmdline_ = "xor-memory" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("display") subparsers.add_parser("patch") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=True) return @parse_args @only_if_gdb_running def do_invoke(self, args): self.usage() return @register_command class XorMemoryDisplayCommand(GenericCommand, BufferingOutput): """Display a block of memory by xor-ing each byte with specified key.""" _cmdline_ = "xor-memory display" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address of data to xor.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size of data to xor.") parser.add_argument("key", metavar="KEY", type=lambda x: bytes.fromhex(x), help="the data to xor as key.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $sp 16 41414141", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running def do_invoke(self, args): self.out = [] start_addr = args.location end_addr = args.location + args.size try: block = read_memory(start_addr, args.size) except gdb.MemoryError: err("Failed to read memory") return self.info_add_out("Displaying XOR-ing {:#x}-{:#x} with {:s}".format(start_addr, end_addr, repr(args.key))) self.out.append(titlify("Original block")) self.out.append(hexdump(block, base=start_addr)) self.out.append(titlify("XOR-ed block")) xored_block = xor(block, args.key) self.out.append(hexdump(xored_block, base=start_addr)) self.print_output(check_terminal_size=True) return @register_command class XorMemoryPatchCommand(GenericCommand): """Patch a block of memory by xor-ing each byte with specified key.""" _cmdline_ = "xor-memory patch" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the address of data to xor.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size of data to xor.") parser.add_argument("key", metavar="KEY", type=lambda x: bytes.fromhex(x), help="the data to xor as key.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $sp 16 41414141", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running def do_invoke(self, args): start_addr = args.location end_addr = args.location + args.size try: block = read_memory(start_addr, args.size) except gdb.MemoryError: err("Failed to read memory") return info("Patching XOR-ing {:#x}-{:#x} with '{:s}'".format(start_addr, end_addr, repr(args.key))) xored_block = xor(block, args.key) gdb.execute("patch hex {:#x} {:s}".format(start_addr, xored_block.hex())) return @register_command class PatternCommand(GenericCommand): """The base command to create or search for a De Bruijn cyclic pattern (used pwntools).""" _cmdline_ = "pattern" _category_ = "07-c. Misc - Generation" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("create") subparsers.add_parser("search") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): super().__init__(prefix=True) self.add_setting("length", 1024, "Initial length of a cyclic buffer to generate") return @parse_args def do_invoke(self, args): self.usage() return @register_command class PatternCreateCommand(GenericCommand): """Generate a de Bruijn cyclic pattern.""" _cmdline_ = "pattern create" _category_ = "07-c. Misc - Generation" _aliases_ = ["pattc"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-c", "--charset", help="the charset of pattern. (default: abc..z)") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, nargs="?", help="the size of pattern. (default: 1024)") _syntax_ = parser.format_help() @staticmethod def de_bruijn(alphabet, n): """De Bruijn sequence for alphabet and subsequences of length n (for compat. w/ pwnlib).""" k = len(alphabet) a = [0] * k * n def db(t, p): if t > n: if n % p == 0: for j in range(1, p + 1): yield alphabet[a[j]] else: a[t] = a[t - p] for c in db(t + 1, p): yield c for j in range(a[t - p] + 1, k): a[t] = j for c in db(t + 1, t): yield c return db(1, 1) @staticmethod def generate_cyclic_pattern(length, charset=None): """Create a `length` byte bytearray of a de Bruijn cyclic pattern.""" if charset is None: charset = bytearray(b"abcdefghijklmnopqrstuvwxyz") elif isinstance(charset, str): charset = String.str2bytes(charset) cycle = AddressUtil.get_memory_alignment() return bytes(itertools.islice(PatternCreateCommand.de_bruijn(charset, cycle), length)) @parse_args def do_invoke(self, args): if args.size is None: size = Config.get_gef_setting("pattern.length") else: size = args.size info("Generating a pattern of {:d} bytes".format(size)) pattern_str = PatternCreateCommand.generate_cyclic_pattern(size, args.charset) gef_print(pattern_str) conv_var = GefUtil.gef_convenience(String.bytes2str(pattern_str)) ok("Saved as '{:s}'".format(conv_var)) return @register_command class PatternSearchCommand(GenericCommand): """Search for the cyclic de Bruijn pattern generated by the `pattern create`.""" _cmdline_ = "pattern search" _category_ = "07-c. Misc - Generation" _aliases_ = ["patto"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-c", "--charset", help="the charset of pattern. (default: abc..z)") parser.add_argument("pattern", metavar="PATTERN", help="the pattern to offset search.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, nargs="?", help="the size of pattern. (default: 0x10000)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $pc", "{0:s} 0x61616164", "{0:s} aaab", ] _example_ = "\n".join(_example_).format(_cmdline_) def search(self, tag, cyclic_pattern, pattern): gef_print(titlify(tag)) def search_pattern(pattern): info("Searching for {}".format(pattern)) found = 0 off = 0 while found < 10: off = cyclic_pattern.find(pattern, off) if off == -1: break ok("Found at offset {:d} ({:#x})".format(off, off)) found += 1 off += 1 if found == 0: err("Not found") if found == 10: ok("...") return # little endian search_pattern(pattern) # big endian inv_pattern = pattern[::-1] if pattern != inv_pattern: search_pattern(inv_pattern) return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.size is None: size = Config.get_gef_setting("pattern.length") * 64 else: size = args.size cyclic_pattern = PatternCreateCommand.generate_cyclic_pattern(size, args.charset) pack = p32 if is_32bit() else p64 # 1. check if it's a symbol (like "$sp") try: address = AddressUtil.parse_address(args.pattern) value = read_int_from_memory(address) self.search("As symbol (with dereference)", cyclic_pattern, pack(value)) except gdb.error: pass # 2. check if it's a not symbol, but value (like "0x1337") try: value = AddressUtil.parse_address(args.pattern) self.search("As value (without dereference)", cyclic_pattern, pack(value)) except gdb.error: pass # 3. plain text pattern = String.str2bytes(args.pattern) if set(pattern) - set(cyclic_pattern) == set(): self.search("As string", cyclic_pattern, pattern) return @register_command class SigreturnCommand(GenericCommand): """Display stack values for sigreturn syscall.""" _cmdline_ = "sigreturn" _category_ = "03-b. Memory - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address interpreted as the beginning of a sigframe. (default: current_arch.sp)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("wine",)) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): if args.location is None: base = current_arch.sp else: base = args.location if is_x86_64(): sigreturn_defines = [ "rt_sigframe.pretcode", "rt_sigframe.uc.uc_flags", "rt_sigframe.uc.uc_link", "rt_sigframe.uc.uc_stack.ss_sp", "rt_sigframe.uc.uc_stack.ss_flags|ss_size", "rt_sigframe.uc.uc_mcontext.r8", "rt_sigframe.uc.uc_mcontext.r9", "rt_sigframe.uc.uc_mcontext.r10", "rt_sigframe.uc.uc_mcontext.r11", "rt_sigframe.uc.uc_mcontext.r12", "rt_sigframe.uc.uc_mcontext.r13", "rt_sigframe.uc.uc_mcontext.r14", "rt_sigframe.uc.uc_mcontext.r15", "rt_sigframe.uc.uc_mcontext.rdi", "rt_sigframe.uc.uc_mcontext.rsi", "rt_sigframe.uc.uc_mcontext.rbp", "rt_sigframe.uc.uc_mcontext.rbx", "rt_sigframe.uc.uc_mcontext.rdx", "rt_sigframe.uc.uc_mcontext.rax", "rt_sigframe.uc.uc_mcontext.rcx", "rt_sigframe.uc.uc_mcontext.rsp", "rt_sigframe.uc.uc_mcontext.rip", "rt_sigframe.uc.uc_mcontext.rflags", "rt_sigframe.uc.uc_mcontext.cs|gs|fs|__pad0", "rt_sigframe.uc.uc_mcontext.err", "rt_sigframe.uc.uc_mcontext.trapno", "rt_sigframe.uc.uc_mcontext.oldmask", "rt_sigframe.uc.uc_mcontext.cr2", "rt_sigframe.uc.uc_mcontext.fpstate", "rt_sigframe.uc.uc_mcontext.reserved[8]", "rt_sigframe.uc.uc_sigmask", "rt_sigframe.info", ] elif is_x86_32(): sigreturn_defines = [ "sigframe.sc.gs", "sigframe.sc.fs", "sigframe.sc.es", "sigframe.sc.ds", "sigframe.sc.edi", "sigframe.sc.esi", "sigframe.sc.ebp", "sigframe.sc.esp", "sigframe.sc.ebx", "sigframe.sc.edx", "sigframe.sc.ecx", "sigframe.sc.eax", "sigframe.sc.trapno", "sigframe.sc.err", "sigframe.sc.eip", "sigframe.sc.cs", "sigframe.sc.eflags", "sigframe.sc.esp_at_signal", "sigframe.sc.ss", "sigframe.sc.fpstate", "sigframe.sc.oldmask", "sigframe.sc.cr2", ] elif is_arm32(): sigreturn_defines = [ "sigframe.uc.uc_flags", "sigframe.uc.uc_link", "sigframe.uc.uc_stack.ss_sp", "sigframe.uc.uc_stack.ss_flags", "sigframe.uc.uc_stack.ss_size", "sigframe.uc.uc_mcontext.trapno", "sigframe.uc.uc_mcontext.error_code", "sigframe.uc.uc_mcontext.oldmask", "sigframe.uc.uc_mcontext.arm_r0", "sigframe.uc.uc_mcontext.arm_r1", "sigframe.uc.uc_mcontext.arm_r2", "sigframe.uc.uc_mcontext.arm_r3", "sigframe.uc.uc_mcontext.arm_r4", "sigframe.uc.uc_mcontext.arm_r5", "sigframe.uc.uc_mcontext.arm_r6", "sigframe.uc.uc_mcontext.arm_r7", "sigframe.uc.uc_mcontext.arm_r8", "sigframe.uc.uc_mcontext.arm_r9", "sigframe.uc.uc_mcontext.arm_r10", "sigframe.uc.uc_mcontext.arm_fp", "sigframe.uc.uc_mcontext.arm_ip", "sigframe.uc.uc_mcontext.arm_sp", "sigframe.uc.uc_mcontext.arm_lr", "sigframe.uc.uc_mcontext.arm_pc", "sigframe.uc.uc_mcontext.arm_cpsr", "sigframe.uc.uc_mcontext.fault_address", "sigframe.uc.uc_sigmask", ] elif is_arm64(): sigreturn_defines = [ "rt_sigframe.info+0x00", "rt_sigframe.info+0x08", "rt_sigframe.info+0x10", "rt_sigframe.info+0x18", "rt_sigframe.info+0x20", "rt_sigframe.info+0x28", "rt_sigframe.info+0x30", "rt_sigframe.info+0x38", "rt_sigframe.info+0x40", "rt_sigframe.info+0x48", "rt_sigframe.info+0x50", "rt_sigframe.info+0x58", "rt_sigframe.info+0x60", "rt_sigframe.info+0x68", "rt_sigframe.info+0x70", "rt_sigframe.info+0x78", "rt_sigframe.uc.uc_flags", "rt_sigframe.uc.uc_link", "rt_sigframe.uc.uc_stack.ss_sp", "rt_sigframe.uc.uc_stack.ss_flags", "rt_sigframe.uc.uc_stack.ss_size", "rt_sigframe.uc.uc_stack.__unused", "rt_sigframe.uc.uc_sigmask", "rt_sigframe.uc.__unused[120]+0x00", "rt_sigframe.uc.__unused[120]+0x08", "rt_sigframe.uc.__unused[120]+0x10", "rt_sigframe.uc.__unused[120]+0x18", "rt_sigframe.uc.__unused[120]+0x20", "rt_sigframe.uc.__unused[120]+0x28", "rt_sigframe.uc.__unused[120]+0x30", "rt_sigframe.uc.__unused[120]+0x38", "rt_sigframe.uc.__unused[120]+0x40", "rt_sigframe.uc.__unused[120]+0x48", "rt_sigframe.uc.__unused[120]+0x50", "rt_sigframe.uc.__unused[120]+0x58", "rt_sigframe.uc.__unused[120]+0x60", "rt_sigframe.uc.__unused[120]+0x68", "rt_sigframe.uc.__unused[120]+0x70", "rt_sigframe.uc.uc_mcontext.fault_address", "rt_sigframe.uc.uc_mcontext.regs[31].x0", "rt_sigframe.uc.uc_mcontext.regs[31].x1", "rt_sigframe.uc.uc_mcontext.regs[31].x2", "rt_sigframe.uc.uc_mcontext.regs[31].x3", "rt_sigframe.uc.uc_mcontext.regs[31].x4", "rt_sigframe.uc.uc_mcontext.regs[31].x5", "rt_sigframe.uc.uc_mcontext.regs[31].x6", "rt_sigframe.uc.uc_mcontext.regs[31].x7", "rt_sigframe.uc.uc_mcontext.regs[31].x8", "rt_sigframe.uc.uc_mcontext.regs[31].x9", "rt_sigframe.uc.uc_mcontext.regs[31].x10", "rt_sigframe.uc.uc_mcontext.regs[31].x11", "rt_sigframe.uc.uc_mcontext.regs[31].x12", "rt_sigframe.uc.uc_mcontext.regs[31].x13", "rt_sigframe.uc.uc_mcontext.regs[31].x14", "rt_sigframe.uc.uc_mcontext.regs[31].x15", "rt_sigframe.uc.uc_mcontext.regs[31].x16", "rt_sigframe.uc.uc_mcontext.regs[31].x17", "rt_sigframe.uc.uc_mcontext.regs[31].x18", "rt_sigframe.uc.uc_mcontext.regs[31].x19", "rt_sigframe.uc.uc_mcontext.regs[31].x20", "rt_sigframe.uc.uc_mcontext.regs[31].x21", "rt_sigframe.uc.uc_mcontext.regs[31].x22", "rt_sigframe.uc.uc_mcontext.regs[31].x23", "rt_sigframe.uc.uc_mcontext.regs[31].x24", "rt_sigframe.uc.uc_mcontext.regs[31].x25", "rt_sigframe.uc.uc_mcontext.regs[31].x26", "rt_sigframe.uc.uc_mcontext.regs[31].x27", "rt_sigframe.uc.uc_mcontext.regs[31].x28", "rt_sigframe.uc.uc_mcontext.regs[31].x29", "rt_sigframe.uc.uc_mcontext.regs[31].x30", "rt_sigframe.uc.uc_mcontext.sp", "rt_sigframe.uc.uc_mcontext.pc", "rt_sigframe.uc.uc_mcontext.pstate", ] max_name_width = max(len(x) for x in sigreturn_defines) out = [] for i, tag in enumerate(sigreturn_defines): line = DereferenceCommand.pprint_dereferenced(base, i, tag.ljust(max_name_width)) out.append(line) gef_print("\n".join(out), less=not args.no_pager) return @register_command class SropHintCommand(GenericCommand): """Hint for sigreturn oriented programming.""" _cmdline_ = "srop-hint" _category_ = "07-d. Misc - Show Example" parser = argparse.ArgumentParser(prog=_cmdline_) architectures = [None, "x86", "x64", "arm", "aarch64"] parser.add_argument("arch", nargs="?", default=None, choices=architectures, metavar="{x86,x64,arm,aarch64}", help="the target architecture.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.architectures: # already matched return [] if text == "": # no prefix return [s for s in self.architectures if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.architectures if s and s.startswith(text.strip())] @parse_args @exclude_specific_gdb_mode(mode=("wine",)) def do_invoke(self, args): if args.arch is None: if is_x86_64(): mode = "x64" elif is_x86_32(): mode = "x86" elif is_arm32(): mode = "arm" elif is_arm64(): mode = "aarch64" else: mode = "x64" # default else: mode = args.arch s = "" if mode == "x64": s += 'exp = struct.pack("= (2, 36): DT_NUM = 38 else: DT_NUM = 35 # Note that the number of elements in an array varies depending on the architecture. DT_THISPROCNUM, ARCH_SPECIFIC_DT_TABLE = DynamicCommand.get_ARCH_SPECIFIC_DT_TABLE() # These values do not change between versions or architectures. DT_VERSIONTAGNUM = 16 DT_EXTRANUM = 3 DT_VALNUM = 12 DT_ADDRNUM = 11 DT_TABLE = DynamicCommand.get_DT_TABLE() # include/link.h l_info_length = DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM # include/link.h members += ["l_real", "l_ns", "l_libname"] for i in range(l_info_length): mb = "l_info[{:d}]".format(i) if i < DT_NUM: tag = i mb += "(={:s})".format(DT_TABLE.get(tag, "???")) elif i < DT_NUM + DT_THISPROCNUM: tag = 0x7000_0000 + (i - DT_NUM) mb += "(={:s})".format(DT_TABLE.get(tag, "???")) elif i < DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM: tag = 0x6fff_ffff - (i - (DT_NUM + DT_THISPROCNUM)) mb += "(={:s})".format(DT_TABLE.get(tag, "???")) elif i < DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM: tag = 0x7fff_ffff - (i - (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM)) mb += "(={:s})".format(DT_TABLE.get(tag, "???")) elif i < DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM: tag = 0x6fff_fdff - (i - (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM)) mb += "(={:s})".format(DT_TABLE.get(tag, "???")) elif i < DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM: tag = 0x6fff_feff - (i - (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM)) mb += "(={:s})".format(DT_TABLE.get(tag, "???")) members.append(mb) members += ["l_phdr", "l_entry", "l_ldnum || l_phnum"] tag_maxlen = max(len(x) for x in members) + 1 current = link_map.value while True: l_name = ProcessMap.lookup_address(read_int_from_memory(current + current_arch.ptrsize * 1)) name = read_cstring_from_memory(l_name.value) l_next = ProcessMap.lookup_address(read_int_from_memory(current + current_arch.ptrsize * 3)) if not name: if Path.get_filepath(): name = "(binary itself: {:s})".format(Path.get_filepath()) else: name = "(binary itself)" self.out.append(titlify(name)) for i, tag in enumerate(members): line = DereferenceCommand.pprint_dereferenced(current, i, tag.ljust(tag_maxlen)) self.out.append(line) if l_next.value == 0: break current = l_next.value return @staticmethod def get_link_map(filename_or_addr=None, silent=False): if not filename_or_addr: # fast path try: link_map = AddressUtil.parse_address("(void*) _rtld_global") link_map = ProcessMap.lookup_address(link_map) return link_map except gdb.error: pass # slow path dynamic = DynamicCommand.get_dynamic(filename_or_addr, silent) DT_TABLE = DynamicCommand.get_DT_TABLE() if dynamic is None: return None current = dynamic.value while True: tag = read_int_from_memory(current) current += current_arch.ptrsize val = ProcessMap.lookup_address(read_int_from_memory(current)) current += current_arch.ptrsize if tag not in DT_TABLE: if not silent: info("Could not find link_map") return None if DT_TABLE[tag] == "DT_DEBUG": dt_debug = val val_addr = ProcessMap.lookup_address(current - current_arch.ptrsize) val_addr_offset = val_addr.value - dynamic.value if not silent: info("_DYNAMIC+{:#x}(=DT_DEBUG): {!s} -> {!s}".format( val_addr_offset, val_addr, dt_debug, )) link_map_ptr = ProcessMap.lookup_address(dt_debug.value + current_arch.ptrsize) if not is_valid_addr(link_map_ptr.value): return None link_map = ProcessMap.lookup_address(read_int_from_memory(link_map_ptr.value)) if not silent: info("DT_DEBUG+{:#x}: {!s} -> {!s}".format( current_arch.ptrsize, link_map_ptr, link_map, )) break return link_map @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): Cache.reset_gef_caches(all=True) self.verbose = False if args.verbose: res = gdb.execute("libc", to_string=True) if "GNU C Library" in res: self.verbose = True if args.link_map_address: link_map = ProcessMap.lookup_address(args.link_map_address) else: try: link_map = self.get_link_map(args.elf_address) except gdb.error: err("Failed to get link_map") return self.out = [] try: self.dump_link_map(link_map) except Exception: err("Failed to parse link_map") return self.print_output(check_terminal_size=True) return @register_command class DynamicCommand(GenericCommand, BufferingOutput): """Display current status of the _DYNAMIC area.""" _cmdline_ = "dynamic" _category_ = "02-e. Process Information - Complex Structure Information" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group() group.add_argument("-f", dest="filename", help="the filename to parse.") group.add_argument("-e", dest="elf_address", type=AddressUtil.parse_address, help="the ELF address to parse.") group.add_argument("-d", dest="dynamic_address", type=AddressUtil.parse_address, help="the dynamic address to parse.") parser.add_argument("--size", dest="dynamic_size", type=AddressUtil.parse_address, help="use specified size of dynamic region.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # dump itself", "{0:s} -f /usr/lib/x86_64-linux-gnu/libc.so.6 # dump specified binary", "{0:s} -e 0x555555554000 # dump specified address as ELF", "{0:s} -d 0x555555575a98 # dump specified address as dynamic", "{0:s} -d 0x555555575a98 --size 0x1c0 # dump specified address with specified size", ] _example_ = "\n".join(_example_).format(_cmdline_) DT_TABLE = { 0: "DT_NULL", 1: "DT_NEEDED", 2: "DT_PLTRELSZ", 3: "DT_PLTGOT", 4: "DT_HASH", 5: "DT_STRTAB", 6: "DT_SYMTAB", 7: "DT_RELA", 8: "DT_RELASZ", 9: "DT_RELAENT", 10: "DT_STRSZ", 11: "DT_SYMENT", 12: "DT_INIT", 13: "DT_FINI", 14: "DT_SONAME", 15: "DT_RPATH", 16: "DT_SYMBOLIC", 17: "DT_REL", 18: "DT_RELSZ", 19: "DT_RELENT", 20: "DT_PLTREL", 21: "DT_DEBUG", 22: "DT_TEXTREL", 23: "DT_JMPREL", 24: "DT_BIND_NOW", 25: "DT_INIT_ARRAY", 26: "DT_FINI_ARRAY", 27: "DT_INIT_ARRAYSZ", 28: "DT_FINI_ARRAYSZ", 29: "DT_RUNPATH", 30: "DT_FLAGS", #32: "DT_ENCODING", # unspecified 32: "DT_PREINIT_ARRAY", 33: "DT_PREINIT_ARRAYSZ", 34: "DT_SYMTAB_SHNDX", 35: "DT_RELRSZ", 36: "DT_RELR", 37: "DT_RELRENT", #0x6000000d: "DT_LOOS", # unspecified 0x6000000e: "DT_SUNW_RTLDINF", 0x6000000f: "DT_ANDROID_REL", 0x60000010: "DT_ANDROID_RELSZ", 0x60000011: "DT_ANDROID_RELA", 0x60000012: "DT_ANDROID_RELASZ", 0x6fffe000: "DT_ANDROID_RELR", 0x6fffe001: "DT_ANDROID_RELRSZ", 0x6fffe003: "DT_ANDROID_RELRENT", 0x6fffe005: "DT_ANDROID_RELRCOUNT", #0x6ffff000: "DT_HIOS", # unspecified #0x6ffffd00: "DT_VALRNGLO", # unspecified 0x6ffffdf5: "DT_GNU_PRELINKED", 0x6ffffdf6: "DT_GNU_CONFLICTSZ", 0x6ffffdf7: "DT_GNU_LIBLISTSZ", 0x6ffffdf8: "DT_CHECKSUM", 0x6ffffdf9: "DT_PLTPADSZ", 0x6ffffdfa: "DT_MOVEENT", 0x6ffffdfb: "DT_MOVESZ", 0x6ffffdfc: "DT_FEATURE_1", 0x6ffffdfd: "DT_POSFLAG_1", 0x6ffffdfe: "DT_SYMINSZ", 0x6ffffdff: "DT_SYMINENT", #0x6ffffdff: "DT_VALRNGHI", # unspecified #0x6ffffe00: "DT_ADDRRNGLO", # unspecified 0x6ffffef5: "DT_GNU_HASH", 0x6ffffef6: "DT_TLSDESC_PLT", 0x6ffffef7: "DT_TLSDESC_GOT", 0x6ffffef8: "DT_GNU_CONFLICT", 0x6ffffef9: "DT_GNU_LIBLIST", 0x6ffffefa: "DT_CONFIG", 0x6ffffefb: "DT_DEPAUDIT", 0x6ffffefc: "DT_AUDIT", 0x6ffffefd: "DT_PLTPAD", 0x6ffffefe: "DT_MOVETAB", 0x6ffffeff: "DT_SYMINFO", #0x6ffffeff: "DT_ADDRRNGHI", # unspecified 0x6ffffff0: "DT_VERSYM", 0x6ffffff9: "DT_RELACOUNT", 0x6ffffffa: "DT_RELCOUNT", 0x6ffffffb: "DT_FLAGS_1", 0x6ffffffc: "DT_VERDEF", 0x6ffffffd: "DT_VERDEFNUM", 0x6ffffffe: "DT_VERNEED", 0x6fffffff: "DT_VERNEEDNUM", #0x70000000: "DT_LOPROC", # unspecified 0x7ffffffd: "DT_AUXILIARY", 0x7ffffffe: "DT_USED", 0x7fffffff: "DT_FILTER", #0x7fffffff: "DT_HIPROC", # unspecified } ARCH_SPECIFIC_DT_TABLE = { "arm64": { 0x70000001: "DT_AARCH64_BTI_PLT", 0x70000003: "DT_AARCH64_PAC_PLT", 0x70000005: "DT_AARCH64_VARIANT_PCS", }, "alpha": { 0x70000000: "DT_ALPHA_PLTRO", }, "mips": { 0x70000001: "DT_MIPS_RLD_VERSION", 0x70000002: "DT_MIPS_TIME_STAMP", 0x70000003: "DT_MIPS_ICHECKSUM", 0x70000004: "DT_MIPS_IVERSION", 0x70000005: "DT_MIPS_FLAGS", 0x70000006: "DT_MIPS_BASE_ADDRESS", 0x70000007: "DT_MIPS_MSYM", 0x70000008: "DT_MIPS_CONFLICT", 0x70000009: "DT_MIPS_LIBLIST", 0x7000000a: "DT_MIPS_LOCAL_GOTNO", 0x7000000b: "DT_MIPS_CONFLICTNO", 0x70000010: "DT_MIPS_LIBLISTNO", 0x70000011: "DT_MIPS_SYMTABNO", 0x70000012: "DT_MIPS_UNREFEXTNO", 0x70000013: "DT_MIPS_GOTSYM", 0x70000014: "DT_MIPS_HIPAGENO", 0x70000016: "DT_MIPS_RLD_MAP", 0x70000017: "DT_MIPS_DELTA_CLASS", 0x70000018: "DT_MIPS_DELTA_CLASS_NO", 0x70000019: "DT_MIPS_DELTA_INSTANCE", 0x7000001a: "DT_MIPS_DELTA_INSTANCE_NO", 0x7000001b: "DT_MIPS_DELTA_RELOC", 0x7000001c: "DT_MIPS_DELTA_RELOC_NO", 0x7000001d: "DT_MIPS_DELTA_SYM", 0x7000001e: "DT_MIPS_DELTA_SYM_NO", 0x70000020: "DT_MIPS_DELTA_CLASSSYM", 0x70000021: "DT_MIPS_DELTA_CLASSSYM_NO", 0x70000022: "DT_MIPS_CXX_FLAGS", 0x70000023: "DT_MIPS_PIXIE_INIT", 0x70000024: "DT_MIPS_SYMBOL_LIB", 0x70000025: "DT_MIPS_LOCALPAGE_GOTIDX", 0x70000026: "DT_MIPS_LOCAL_GOTIDX", 0x70000027: "DT_MIPS_HIDDEN_GOTIDX", 0x70000028: "DT_MIPS_PROTECTED_GOTIDX", 0x70000029: "DT_MIPS_OPTIONS", 0x7000002a: "DT_MIPS_INTERFACE", 0x7000002b: "DT_MIPS_DYNSTR_ALIGN", 0x7000002c: "DT_MIPS_INTERFACE_SIZE", 0x7000002d: "DT_MIPS_RLD_TEXT_RESOLVE_ADDR", 0x7000002e: "DT_MIPS_PERF_SUFFIX", 0x7000002f: "DT_MIPS_COMPACT_SIZE", 0x70000030: "DT_MIPS_GP_VALUE", 0x70000031: "DT_MIPS_AUX_DYNAMIC", 0x70000032: "DT_MIPS_PLTGOT", 0x70000034: "DT_MIPS_RWPLT", 0x70000035: "DT_MIPS_RLD_MAP_REL", 0x70000036: "DT_MIPS_XHASH", }, "nios2": { 0x70000002: "DT_NIOS2_GP", }, "ppc32": { 0x70000000: "DT_PPC_GOT", 0x70000001: "DT_PPC_OPT", }, "ppc64": { 0x70000000: "DT_PPC64_GLINK", 0x70000001: "DT_PPC64_OPD", 0x70000002: "DT_PPC64_OPDSZ", 0x70000003: "DT_PPC64_OPT", }, "riscv": { 0x70000001: "DT_RISCV_VARIANT_CC", }, "sparc": { 0x70000001: "DT_SPARC_REGISTER", }, "x86-64": { 0x70000000: "DT_X86_64_PLT", 0x70000001: "DT_X86_64_PLTSZ", 0x70000003: "DT_X86_64_PLTENT", }, "xtensa": { 0x70000000: "DT_XTENSA_GOT_LOC_OFF", 0x70000001: "DT_XTENSA_GOT_LOC_SZ", }, } @staticmethod def get_ARCH_SPECIFIC_DT_TABLE(): # default value DT_THISPROCNUM = 0 ARCH_SPECIFIC_DT_TABLE = {} # Considering glibc 2.20 and later if is_arm64(): if get_libc_version() >= (2, 33): DT_THISPROCNUM = 6 ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["arm64"] elif is_alpha(): DT_THISPROCNUM = 1 ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["alpha"] elif is_mips32() or is_mips64() or is_mipsn32(): if get_libc_version() >= (2, 31): DT_THISPROCNUM = 0x37 elif get_libc_version() >= (2, 22): DT_THISPROCNUM = 0x36 else: DT_THISPROCNUM = 0x35 ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["mips"] # 2.20~ elif is_nios2(): # DT_THISPROCNUM is not changed ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["nios2"] elif is_ppc32(): if get_libc_version() >= (2, 22): DT_THISPROCNUM = 2 else: DT_THISPROCNUM = 1 ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["ppc32"] elif is_ppc64(): DT_THISPROCNUM = 4 ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["ppc64"] elif is_riscv32() or is_riscv64(): if get_libc_version() >= (2, 36): # DT_THISPROCNUM is not changed ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["riscv"] elif is_sparc32() or is_sparc32plus() or is_sparc64(): DT_THISPROCNUM = 2 ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["sparc"] elif is_x86_64(): if get_libc_version() >= (2, 39): DT_THISPROCNUM = 4 ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["x86-64"] elif is_xtensa(): # DT_THISPROCNUM is not changed ARCH_SPECIFIC_DT_TABLE = DynamicCommand.ARCH_SPECIFIC_DT_TABLE["xtensa"] return DT_THISPROCNUM, ARCH_SPECIFIC_DT_TABLE @staticmethod @Cache.cache_this_session def get_DT_TABLE(): _, ARCH_SPECIFIC_DT_TABLE = DynamicCommand.get_ARCH_SPECIFIC_DT_TABLE() return DynamicCommand.DT_TABLE | ARCH_SPECIFIC_DT_TABLE def __init__(self): super().__init__(complete=gdb.COMPLETE_FILENAME) return def dump_dynamic(self, dynamic, remain_size): base_address_color = Config.get_gef_setting("theme.dereference_base_address") if dynamic is None: info("Could not find _DYNAMIC") return width = AddressUtil.get_format_address_width() fmt = "{:{:d}s} {:{:d}s} {:{:d}s} {:s}" legend = ["Address", width, "Tag", width, "Value", width, "TagName"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) DT_TABLE = DynamicCommand.get_DT_TABLE() current = dynamic.value while True: addr = current tag = read_int_from_memory(current) current += current_arch.ptrsize val = read_int_from_memory(current) current += current_arch.ptrsize if remain_size is None: if tag not in DT_TABLE: break else: remain_size -= current_arch.ptrsize * 2 val = ProcessMap.lookup_address(val) tag_description = DT_TABLE.get(tag, "Unknown") colored_addr = Color.colorify("{:#0{:d}x}".format(addr, width), base_address_color) self.out.append("{:s}: {:#0{:d}x} {!s} | {:s}".format( colored_addr, tag, width, val, tag_description, )) if remain_size is not None and remain_size <= 0: break return @staticmethod def get_dynamic(filename_or_addr=None, silent=False): if not filename_or_addr: # fast path try: dynamic = AddressUtil.parse_address("(void*) &_DYNAMIC") dynamic = ProcessMap.lookup_address(dynamic) return dynamic except gdb.error: pass # prepare slow path filename_or_addr = Path.get_filepath() if filename_or_addr is None: if not silent: err("Failed to get filename") return None # slow path if isinstance(filename_or_addr, str): # use as filename if not silent: info("filename: {:s}".format(filename_or_addr)) if not os.path.exists(filename_or_addr): if not silent: err("Could not find {:s}".format(filename_or_addr)) return None elf = Elf.get_elf(filename_or_addr) if elf is None or not elf.is_valid(): if not silent: err("Invalid ELF") return None if not elf.has_dynamic(): if not silent: info("The binary has no _DYNAMIC") return None if ProcessMap.get_section_base_address(filename_or_addr) is None: if not silent: err("{:s} is not loaded".format(filename_or_addr)) return None else: # use as address if not silent: info("address: {:#x}".format(filename_or_addr)) try: elf = Elf.get_elf(filename_or_addr) except gdb.MemoryError: if not silent: err("Memory read error") return None if elf is None or not elf.is_valid(): if not silent: err("Invalid ELF") return None phdr = elf.get_phdr(Elf.Phdr.PT_DYNAMIC) if phdr is None: return None if isinstance(filename_or_addr, str): if elf.is_pie(): load_base = ProcessMap.get_section_base_address(filename_or_addr) dynamic = phdr.p_vaddr + load_base else: dynamic = phdr.p_vaddr else: if phdr.p_vaddr < filename_or_addr: dynamic = phdr.p_vaddr + filename_or_addr else: dynamic = phdr.p_vaddr dynamic = ProcessMap.lookup_address(dynamic) if not silent: info("_DNYAMIC: {!s} [{!s}]".format(dynamic, dynamic.section.permission)) return dynamic @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): if args.dynamic_address: dynamic = ProcessMap.lookup_address(args.dynamic_address) else: try: dynamic = self.get_dynamic(args.elf_address or args.filename) except gdb.error: err("Failed to get _DYNAMIC") return self.out = [] try: self.dump_dynamic(dynamic, args.dynamic_size) except Exception: err("Failed to parse _DYNAMIC") return self.print_output(check_terminal_size=True) return @register_command class DestructorDumpCommand(GenericCommand): """Display registered destructor functions.""" _cmdline_ = "dtor-dump" _category_ = "02-e. Process Information - Complex Structure Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--remote", action="store_true", help="parse remote binary if download feature is available.") parser.add_argument("-f", "--file", help="the file path to parse.") parser.add_argument("--tdl", type=AddressUtil.parse_address, help="specify the offset of `tls_dtor_list` from TLS base.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} --tdl 0x50 # specify offset of tls_dtor_list", "{0:s} --tdl 0xffffffffffffffa8 # specify negative offset", ] _example_ = "\n".join(_example_).format(_cmdline_) def C(self, addr): base_address_color = Config.get_gef_setting("theme.dereference_base_address") a = Color.colorify("{:#0{:d}x}".format( addr, AddressUtil.get_format_address_width(), ), base_address_color) try: b = "[{!s}]".format(ProcessMap.lookup_address(addr).section.permission) return a + b except Exception: return a + "[???]" """ glibc 2.37~ x86_32: dynamic symboled: linkmap-relative x86_32: dynamic stripped: linkmap-relative x86_32: static symboled: msymbols x86_32: static stripped: heuristic x86_64: dynamic symboled: linkmap-relative x86_64: dynamic stripped: linkmap-relative x86_64: static symboled: msymbols x86_64: static stripped: heuristic arm32: dynamic symboled: linkmap-relative arm32: dynamic stripped: linkmap-relative arm32: static symboled: msymbols arm32: static stripped: heuristic arm64: dynamic symboled: linkmap-relative arm64: dynamic stripped: linkmap-relative arm64: static symboled: msymbols arm64: static stripped: heuristic sparc64: dynamic symboled: linkmap-relative sparc64: dynamic stripped: linkmap-relative sparc64: static symboled: msymbols sparc64: static stripped: heuristic s390x: dynamic symboled: linkmap-relative s390x: dynamic stripped: linkmap-relative s390x: static symboled: msymbols s390x: static stripped: heuristic loongarch64: dynamic symboled: linkmap-relative loongarch64: dynamic stripped: linkmap-relative loongarch64: static symboled: msymbols loongarch64: static stripped: heuristic sh4: dynamic symboled: heuristic (link_map is not in TLS) sh4: dynamic stripped: heuristic (link_map is not in TLS) sh4: static symboled: msymbols sh4: static stripped: heuristic ppc32: dynamic symboled: heuristic (link_map is not in TLS) ppc32: dynamic stripped: heuristic (link_map is not in TLS) ppc32: static symboled: heuristic ppc32: static stripped: heuristic ppc64: dynamic symboled: heuristic (link_map is not in TLS) ppc64: dynamic stripped: heuristic (link_map is not in TLS) ppc64: static symboled: msymbols ppc64: static stripped: heuristic nios2: dynamic symboled: heuristic (link_map is not in TLS) nios2: dynamic stripped: heuristic (link_map is not in TLS) nios2: static symboled: msymbols nios2: static stripped: heuristic alpha: dynamic symboled: heuristic (link_map is not in TLS) alpha: dynamic stripped: heuristic (link_map is not in TLS) alpha: static symboled: msymbols alpha: static stripped: heuristic riscv64: dynamic symboled: linkmap-relative riscv64: dynamic stripped: linkmap-relative riscv64: static symboled: msymbols riscv64: static stripped: NG (PTR_MANGLE is no-XOR) riscv32: dynamic symboled: linkmap-relative riscv32: dynamic stripped: linkmap-relative riscv32: static symboled: msymbols riscv32: static stripped: NG (PTR_MANGLE is no-XOR) or1k: dynamic symboled: linkmap-relative or1k: dynamic stripped: linkmap-relative or1k: static symboled: msymbols or1k: static stripped: NG (PTR_MANGLE is no-XOR) arc32: dynamic symboled: linkmap-relative arc32: dynamic stripped: linkmap-relative arc32: static symboled: msymbols arc32: static stripped: NG (PTR_MANGLE is no-XOR) m68k: dynamic symboled: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) m68k: dynamic stripped: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) m68k: static symboled: msymbols m68k: static stripped: NG (PTR_MANGLE is no-XOR) mips32: dynamic symboled: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) mips32: dynamic stripped: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) mips32: static symboled: msymbols mips32: static stripped: NG (PTR_MANGLE is no-XOR) mips64: dynamic symboled: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) mips64: dynamic stripped: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) mips64: static symboled: msymbols mips64: static stripped: NG (PTR_MANGLE is no-XOR) hppa32: dynamic symboled: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) hppa32: dynamic stripped: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) hppa32: static symboled: msymbols hppa32: static stripped: NG (PTR_MANGLE is no-XOR) microblaze: dynamic symboled: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) microblaze: dynamic stripped: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) microblaze: static symboled: msymbols microblaze: static stripped: NG (PTR_MANGLE is no-XOR) arc64: dynamic symboled: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) arc64: dynamic stripped: NG (link_map is not in TLS and PTR_MANGLE is no-XOR) arc64: static symboled: msymbols arc64: static stripped: NG (PTR_MANGLE is no-XOR) csky: dynamic symboled: ??? csky: dynamic stripped: ??? csky: static symboled: msymbols csky: static stripped: heuristic """ def get_dtor_list_from_msymbols(self): # Statically linked binaries cannot resolve the address of thread-local storage variables. # Therefore, identify it from the output of the msymbols command and the offset of thread_arena. # thread_arena address of main thread main_thread_main_arena = GlibcHeap.search_for_main_arena_from_tls() if main_thread_main_arena is None: return None # thread_arena offset from .tbss ret = gdb.execute("maintenance print msymbols", to_string=True) m = re.search(r"(0x\S+) thread_arena section .tbss", ret) if not m: return None thread_arena_offset = int(m.group(1), 16) # tls_dtor_list offset from .tbss m = re.search(r"(0x\S+) tls_dtor_list section .tbss", ret) if not m: return None tls_dtor_list_offset = int(m.group(1), 16) # tls_dtor_list offset from TLS main_thread_tbss_base = main_thread_main_arena - thread_arena_offset main_thread_tls_dtor_list = main_thread_tbss_base + tls_dtor_list_offset # TLS address of main thread orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() threads = gdb.selected_inferior().threads() main_thread = [th for th in threads if th.num == 1][0] main_thread.switch() # switch temporarily main_tls = current_arch.get_tls() orig_thread.switch() # revert thread orig_frame.select() # tls_dotr_list of current thread tls_dtor_list = main_thread_tls_dtor_list - main_tls + current_arch.get_tls() return tls_dtor_list def get_dtor_list_from_linkmap_relative(self): if self.codebase is None: return None direction = TlsCommand.get_direction() """ --- TLS-0x80 --- 0x7ffff7fa06c0|+0x0000|+000: 0x0000000000000000 0x7ffff7fa06c8|+0x0008|+001: 0x00007ffff7d9b4c0 <_nl_C_LC_CTYPE_tolower+0x200> -> 0x0000000100000000 0x7ffff7fa06d0|+0x0010|+002: 0x00007ffff7d9bac0 <_nl_C_LC_CTYPE_toupper+0x200> -> 0x0000000100000000 0x7ffff7fa06d8|+0x0018|+003: 0x00007ffff7d9c3c0 <_nl_C_LC_CTYPE_class+0x100> -> 0x0002000200020002 0x7ffff7fa06e0|+0x0020|+004: 0x00007ffff7ffe2c0 -> 0x0000555555554000 -> 0x00010102464c457f <- link_map 0x7ffff7fa06e8|+0x0028|+005: 0x00005555555592a0 -> 0x3c56fdd6341540d8 <- tls_dtor_list 0x7ffff7fa06f0|+0x0030|+006: 0x0000000000000000 0x7ffff7fa06f8|+0x0038|+007: 0x0000555555559010 -> 0x0000000000000000 0x7ffff7fa0700|+0x0040|+008: 0x0000000000000000 0x7ffff7fa0708|+0x0048|+009: 0x00007ffff7df6c80 -> 0x0000000000000000 0x7ffff7fa0710|+0x0050|+010: 0x0000000000000000 0x7ffff7fa0718|+0x0058|+011: 0x0000000000000000 0x7ffff7fa0720|+0x0060|+012: 0x0000000000000000 0x7ffff7fa0728|+0x0068|+013: 0x0000000000000000 0x7ffff7fa0730|+0x0070|+014: 0x0000000000000000 0x7ffff7fa0738|+0x0078|+015: 0x0000000000000000 --- TLS --- ... """ tls = current_arch.get_tls() for i in range(1, 16): addr = tls + (current_arch.ptrsize * i) * direction if not is_valid_addr(addr): break candidate_link_map = read_int_from_memory(addr) if not is_valid_addr(candidate_link_map): continue candidate_codebase = read_int_from_memory(candidate_link_map) if candidate_codebase == self.codebase: # found if is_s390x(): tls_dtor_list = tls + (current_arch.ptrsize * (i + 1)) * direction else: tls_dtor_list = tls + (current_arch.ptrsize * (i - 1)) * direction if is_valid_addr(tls_dtor_list): # maybe valid return tls_dtor_list else: # invalid return None return None def get_dtor_list_from_heuristic(self): direction = TlsCommand.get_direction() """ --- TLS-0x80 --- 0x0000004af340|+0x0000|+000: 0x0000000000000000 0x0000004af348|+0x0008|+001: 0x0000000000000000 0x0000004af350|+0x0010|+002: 0x00000000004a83e0 -> 0x00000000004a4ae0 -> 0x000000000047e150 -> ... 0x0000004af358|+0x0018|+003: 0x00000000004a83e8 -> 0x00000000004a5020 -> 0x000000000047e150 -> ... 0x0000004af360|+0x0020|+004: 0x00000000004a83e0 -> 0x00000000004a4ae0 -> 0x000000000047e150 -> ... 0x0000004af368|+0x0028|+005: 0x0000000000000000 <- $r13 0x0000004af370|+0x0030|+006: 0x0000000000000000 0x0000004af378|+0x0038|+007: 0x0000000000000000 0x0000004af380|+0x0040|+008: 0x00000000004b0210 -> 0xac500ef775892689 <- tls_dtor_list 0x0000004af388|+0x0048|+009: 0x00000000004afd50 -> 0x0000000000000000 0x0000004af390|+0x0050|+010: 0x0000000000000000 0x0000004af398|+0x0058|+011: 0x00000000004a71e0 -> 0x0000000000000000 0x0000004af3a0|+0x0060|+012: 0x0000000000484e60 -> 0x0000000100000000 0x0000004af3a8|+0x0068|+013: 0x0000000000485460 -> 0x0000000100000000 0x0000004af3b0|+0x0070|+014: 0x0000000000485d60 -> 0x0002000200020002 0x0000004af3b8|+0x0078|+015: 0x0000000000000000 --- TLS --- ... """ tls = current_arch.get_tls() for i in range(1, 16): addr = tls + (current_arch.ptrsize * i) * direction if not is_valid_addr(addr): break x = read_int_from_memory(addr) if not is_valid_addr(x): continue y = read_int_from_memory(x) if is_valid_addr(y): continue yb = bytearray(read_memory(x, 8)) if yb.count(b"\x00") <= 2: # statistically ok return addr return None def dump_tls_dtors(self, offset_tls_dtor_list): info("Probably only exists in glibc") if not self.tls: err("Could not find the TLS") return tls_dtor_list = None # user specified if offset_tls_dtor_list: tls_dtor_list = AddressUtil.align_address(current_arch.get_tls() + offset_tls_dtor_list) # method 1 (directly) if tls_dtor_list is None: try: tls_dtor_list = AddressUtil.parse_address("&tls_dtor_list") if not is_valid_addr(tls_dtor_list): tls_dtor_list = None except gdb.error: pass # method 2 (from msymbols) if tls_dtor_list is None: if self.elf.is_static(): tls_dtor_list = self.get_dtor_list_from_msymbols() # method 3 (from link-map) if tls_dtor_list is None: if not self.elf.is_static(): tls_dtor_list = self.get_dtor_list_from_linkmap_relative() # method 4 (from tls with likely pattern) if tls_dtor_list is None: if current_arch.encode_cookie(0x1, 0xdeadbeef) != 0x1: # use cookie xor tls_dtor_list = self.get_dtor_list_from_heuristic() if tls_dtor_list is None: err("Could not find tls_dtor_list") return # parse tls_dtor_list and print head_p = tls_dtor_list head = ProcessMap.lookup_address(read_int_from_memory(head_p)) current = head.value if head.section is None: gef_print("{:s}: {:s}: {!s}".format("tls_dtor_list", self.C(head_p), head)) else: gef_print("{:s}: {:s}: {!s}[{!s}]".format("tls_dtor_list", self.C(head_p), head, head.section.permission)) ptrsize = current_arch.ptrsize def read_fns(addr): func = ProcessMap.lookup_address(read_int_from_memory(current)) obj = ProcessMap.lookup_address(read_int_from_memory(current + ptrsize * 1)) link_map = ProcessMap.lookup_address(read_int_from_memory(current + ptrsize * 2)) next = ProcessMap.lookup_address(read_int_from_memory(current + ptrsize * 3)) return func, obj, link_map, next while current: try: func, obj, link_map, next = read_fns(current) except gdb.MemoryError: err("Memory read error at {:#x}".format(current)) break decoded_fn = current_arch.decode_cookie(func.value, self.cookie) decoded_fn = ProcessMap.lookup_address(decoded_fn) sym = Symbol.get_symbol_string(decoded_fn.value) if is_valid_addr(decoded_fn.value): valid_msg = Color.colorify("valid", "bold green") else: valid_msg = Color.colorify("invalid", "bold red") gef_print(" -> func: {:s}: {!s} (={!s}{:s}) [{:s}]".format( self.C(current), func, decoded_fn, sym, valid_msg, )) gef_print(" obj: {:s}: {!s}".format( self.C(current + ptrsize * 1), obj, )) gef_print(" link_map: {:s}: {!s}".format( self.C(current + ptrsize * 2), link_map, )) gef_print(" next: {:s}: {!s}".format( self.C(current + ptrsize * 3), next, )) current = next.value return def dump_exit_funcs(self, name): try: head_p = AddressUtil.parse_address("&" + name) except gdb.error: err("Could not find symbol ({:s})".format(name)) return head = ProcessMap.lookup_address(read_int_from_memory(head_p)) current = head.value if head.section is None: gef_print("{:s}: {:s}: {!s}".format(name, self.C(head_p), head)) else: gef_print("{:s}: {:s}: {!s}[{!s}]".format(name, self.C(head_p), head, head.section.permission)) if current == 0: return ptrsize = current_arch.ptrsize try: next = ProcessMap.lookup_address(read_int_from_memory(current)) idx = ProcessMap.lookup_address(read_int_from_memory(current + ptrsize)) except gdb.MemoryError: err("Memory read error at {:#x}".format(current)) return current += ptrsize * 2 gef_print(" -> next: {:s}: {!s}".format(self.C(head.value + ptrsize * 0), next)) gef_print(" idx: {:s}: {!s}".format(self.C(head.value + ptrsize * 1), idx)) def read_fns(addr): flavor = ProcessMap.lookup_address(read_int_from_memory(addr)) fn = ProcessMap.lookup_address(read_int_from_memory(addr + ptrsize * 1)) arg = ProcessMap.lookup_address(read_int_from_memory(addr + ptrsize * 2)) dso_handle = ProcessMap.lookup_address(read_int_from_memory(addr + ptrsize * 3)) return flavor, fn, arg, dso_handle fns_size = ptrsize * 4 # flavor, fn, arg, dso_handle for i in range(idx.value, -1, -1): addr = AddressUtil.align_address(current + fns_size * i) try: flavor, fn, arg, dso_handle = read_fns(addr) except gdb.MemoryError: err("Memory read error at {:#x}".format(addr)) break if fn.value == 0: continue decoded_fn = current_arch.decode_cookie(fn.value, self.cookie) decoded_fn = ProcessMap.lookup_address(decoded_fn) sym = Symbol.get_symbol_string(decoded_fn.value) if is_valid_addr(decoded_fn.value): valid_msg = Color.colorify("valid", "bold green") else: valid_msg = Color.colorify("invalid", "bold red") fns = " fns[{:#x}]: {:s}:".format(i, self.C(addr)) width = len(fns) - 9 gef_print("{} flavor: {!s}".format(fns, flavor)) gef_print("{} func: {!s} (={!s}{:s}) [{:s}]".format(" " * width, fn, decoded_fn, sym, valid_msg)) gef_print("{} arg: {!s}".format(" " * width, arg)) gef_print("{} dso_handle: {!s}".format(" " * width, dso_handle)) return def yield_link_map(self, codebase): link_map = LinkMapCommand.get_link_map(codebase, silent=True) if link_map is None: return current = link_map.value while current: dic = {} dic["load_address"] = read_int_from_memory(current) name_ptr = read_int_from_memory(current + current_arch.ptrsize * 1) dic["name"] = dic["name_org"] = read_cstring_from_memory(name_ptr) if dic["name_org"] == "": dic["name"] = "{:s}".format(self.local_filepath) dic["dynamic"] = read_int_from_memory(current + current_arch.ptrsize * 2) dic["next"] = read_int_from_memory(current + current_arch.ptrsize * 3) LinkMap = collections.namedtuple("LinkMap", dic.keys()) link_map = LinkMap(*dic.values()) yield link_map current = dic["next"] return def dump_fini(self): if not self.codebase: return None DT_TABLE = DynamicCommand.get_DT_TABLE() if self.elf.has_dynamic(): # Parse all loaded libraries. for link_map in self.yield_link_map(self.codebase): # get dynamic dynamic = DynamicCommand.get_dynamic(link_map.load_address or link_map.name, silent=True) if dynamic is None: continue # search for .fini fini = None current = dynamic.value while True: tag = read_int_from_memory(current) if tag == 13: # DT_FINI fini = read_int_from_memory(current + current_arch.ptrsize) if fini < link_map.load_address: fini += link_map.load_address break if tag not in DT_TABLE: break current += current_arch.ptrsize * 2 if fini is None: continue # print .fini gef_print(link_map.name) fini = ProcessMap.lookup_address(fini) sym = Symbol.get_symbol_string(fini.value) gef_print(" -> {!s}{:s}".format(fini, sym)) else: # Static binary has no _DYNAMIC, but we can resolve the target address # from section name due to local file path. shdr = self.elf.get_shdr(".fini") if shdr is None: err("Could not find .fini section") return fini = shdr.sh_addr if fini < self.codebase: fini += self.codebase gef_print(self.local_filepath) fini = ProcessMap.lookup_address(fini) sym = Symbol.get_symbol_string(fini.value) gef_print(" -> {!s}{:s}".format(fini, sym)) return def dump_fini_array(self): if not self.codebase: return None DT_TABLE = DynamicCommand.get_DT_TABLE() if self.elf.has_dynamic(): # Parse all loaded libraries. for link_map in self.yield_link_map(self.codebase): # get dynamic dynamic = DynamicCommand.get_dynamic(link_map.load_address or link_map.name, silent=True) if dynamic is None: continue # search for .fini_array, fini_array_sz fini_array = None fini_array_sz = None current = dynamic.value while True: tag = read_int_from_memory(current) if tag == 26: # DT_FINI_ARRAY fini_array = read_int_from_memory(current + current_arch.ptrsize) if fini_array < link_map.load_address: fini_array += link_map.load_address if tag == 28: # DT_FINI_ARRAY_SZ fini_array_sz = read_int_from_memory(current + current_arch.ptrsize) if fini_array is not None and fini_array_sz is not None: break if tag not in DT_TABLE: break current += current_arch.ptrsize * 2 if fini_array is None or fini_array_sz is None: continue # parse .fini_array entries = [] for i in range(fini_array_sz // current_arch.ptrsize): addr = fini_array + current_arch.ptrsize * i func = read_int_from_memory(addr) if not is_valid_addr(func): continue entries.append([addr, func]) if not entries: continue # print .fini_array gef_print(link_map.name) for addr, func in entries: func = ProcessMap.lookup_address(func) sym = Symbol.get_symbol_string(func.value) gef_print(" -> {:s}: {!s}{:s}".format(self.C(addr), func, sym)) else: # Static binary has no _DYNAMIC, but we can resolve the target address # from section name due to local file path. shdr = self.elf.get_shdr(".fini_array") if shdr is None: err("Could not find .fini_array section") return entries = [] vend = AddressUtil.get_vmem_end() - 1 for i in range(shdr.sh_size // current_arch.ptrsize): addr = shdr.sh_addr + current_arch.ptrsize * i func = read_int_from_memory(addr) if func in [0, vend]: continue entries.append([addr, func]) if not entries: err("Could not find valid entry") return gef_print(self.local_filepath) for addr, func in entries: func = ProcessMap.lookup_address(func) sym = Symbol.get_symbol_string(func.value) gef_print(" -> {:s}: {!s}{:s}".format(self.C(addr), func, sym)) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @exclude_specific_arch(arch=("SPARC32", "XTENSA", "CRIS")) @require_arch_set def do_invoke(self, args): Cache.reset_gef_caches(all=True) # init local_filepath = None remote_filepath = None tmp_filepath = None if args.remote: if not is_remote_debug(): err("-r option is allowed only remote debug") return if args.file: remote_filepath = args.file # if specified, assume it is remote elif gdb.current_progspace().filename: f = gdb.current_progspace().filename if f.startswith("target:"): # gdbserver f = f[7:] remote_filepath = f elif Pid.get_pid(remote=True): remote_filepath = "/proc/{:d}/exe".format(Pid.get_pid(remote=True)) else: err("File name could not be determined") return data = Path.read_remote_file(remote_filepath, as_byte=True) # qemu-user is failed here, it is ok if not data: err("Failed to read remote filepath") return tmp_fd, tmp_filepath = GefUtil.mkstemp(prefix="dtor-dump", suffix=".elf") os.fdopen(tmp_fd, "wb").write(data) local_filepath = tmp_filepath del data elif args.file: local_filepath = args.file elif args.file is None: local_filepath = Path.get_filepath() if local_filepath is None: err("File name could not be determined") return # filepath and elf self.local_filepath = local_filepath self.elf = Elf.get_elf(local_filepath) if self.elf is None or not self.elf.is_valid(): err("Invalid ELF") return # codebase if remote_filepath: self.codebase = ProcessMap.get_section_base_address(remote_filepath) elif local_filepath: self.codebase = ProcessMap.get_section_base_address(local_filepath) if self.codebase is None: self.codebase = ProcessMap.get_section_base_address(Path.get_filepath(append_proc_root_prefix=False)) if self.codebase is None: self.codebase = ProcessMap.get_section_base_address(Path.get_filepath_from_info_proc()) if self.codebase is None: warn("Could not find codebase") # tls self.tls = current_arch.get_tls() if self.tls is None or not is_valid_addr(self.tls): warn("Could not find tls") # cookie self.cookie = PtrDemangleCommand.get_cookie() if self.cookie is None: warn("Could not find cookie") # dump gef_print(titlify("tls_dtor_list: registered by __cxa_thread_atexit_impl()")) self.dump_tls_dtors(args.tdl) gef_print(titlify("__exit_funcs: registered by atexit(), on_exit()")) self.dump_exit_funcs("__exit_funcs") gef_print(titlify("__quick_exit_funcs: registered by at_quick_exit()")) self.dump_exit_funcs("__quick_exit_funcs") gef_print(titlify(".fini_array section")) self.dump_fini_array() gef_print(titlify(".fini section")) self.dump_fini() # cleanup if tmp_filepath and os.path.exists(tmp_filepath): os.unlink(tmp_filepath) return @register_command class FpChainCommand(GenericCommand): """Dump chains from __IO_list_all.""" _cmdline_ = "fpchain" _category_ = "02-e. Process Information - Complex Structure Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", nargs="?", type=AddressUtil.parse_address, help="the _IO_list_all address to parse.") _syntax_ = parser.format_help() def get_io_list_all(self): try: return AddressUtil.parse_address("(void*) &_IO_list_all") except gdb.error: pass return None @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): if args.address is None: io_list_all = self.get_io_list_all() if not io_list_all: err("Could not find _IO_list_all") return else: io_list_all = args.address gef_print("[0] {!s}{:s}".format( ProcessMap.lookup_address(io_list_all), Symbol.get_symbol_string(io_list_all), )) if is_64bit(): offset_of_chain = 0x68 else: offset_of_chain = 0x34 current = io_list_all i = 1 while is_valid_addr(current): if i == 1: current = read_int_from_memory(current) else: current = read_int_from_memory(current + offset_of_chain) gef_print("[{:d}] -> {!s}{:s}".format( i, ProcessMap.lookup_address(current), Symbol.get_symbol_string(current), )) i += 1 return @register_command class StandardIoCommand(GenericCommand, BufferingOutput): """Dump members of stdin/stdout/stderr.""" _cmdline_ = "stdio-dump" _category_ = "02-e. Process Information - Complex Structure Information" _aliases_ = ["fp"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", nargs="*", type=AddressUtil.parse_address, help="the ELF address to parse (default: stdin, stdout, stderr).") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def get_offset(self, member_name, member_defines): sizes, names = zip(*member_defines) member_idx = names.index(member_name) member_offset = sum(sizes[:member_idx]) return member_offset def get_size(self, member_name, member_defines): sizes, names = zip(*member_defines) member_idx = names.index(member_name) member_size = sizes[member_idx] return member_size def process_member(self, member_name, member_defines, struct_array): # member name if not member_name: return 0 elif member_name == "__addr__": member_offset = 0 member_size = current_arch.ptrsize msg = "{:>5s} | {:16s}: ".format("off", "member") else: member_offset = self.get_offset(member_name, member_defines) member_size = self.get_size(member_name, member_defines) msg = "{:+#05x} | {:16s}: ".format(member_offset, member_name) adjust = 0 val_width = [10, 18][is_64bit()] sym_width = [25, 29][is_arm32()] # member of each struct for st in struct_array: member_addr = st + member_offset if member_name == "__addr__": address_obj = ProcessMap.lookup_address(st) sym = Symbol.get_symbol_string(st) msg += "{:s}{:{:d}s} ".format(address_obj.long_fmt(), sym, sym_width) elif not is_valid_addr(member_addr): msg += "{:{:d}s}{:{:d}s} ".format("", val_width, "", sym_width) elif member_size == current_arch.ptrsize: val = read_int_from_memory(member_addr) address_obj = ProcessMap.lookup_address(val) sym = Symbol.get_symbol_string(val) msg += "{:s}{:{:d}s} ".format(address_obj.long_fmt(), sym, sym_width) elif member_size == 8: val = read_int64_from_memory(member_addr) # special case if is_32bit() and member_name == "_offset": if Endian.is_big_endian(): val_ = byteswap(val, 8) else: val_ = val if val_ == 0xffff_ffff_0000_0000: adjust = 4 member_offset += adjust msg = "{:+#05x} | {:16s}: ".format(member_offset, member_name) member_addr += adjust val = read_int64_from_memory(member_addr) val_s = "{:#018x}".format(val) msg += "{:s}{:{:d}s} ".format(val_s, "", sym_width - [8, 0][is_64bit()]) elif member_size == 4: val = read_int32_from_memory(member_addr) val_s = "{:#010x}".format(val) msg += "{:{:d}s}{:{:d}s} ".format(val_s, val_width, "", sym_width) elif member_size == 2: val = read_int16_from_memory(member_addr) val_s = "{:#06x}".format(val) msg += "{:{:d}s}{:{:d}s} ".format(val_s, val_width, "", sym_width) elif member_size == 1: val = read_int8_from_memory(member_addr) val_s = "{:#04x}".format(val) msg += "{:{:d}s}{:{:d}s} ".format(val_s, val_width, "", sym_width) else: msg += "{:{:d}s}{:{:d}s} ".format("...", val_width, "", sym_width) self.out.append(msg.rstrip()) return adjust def stdio_dump(self, struct_io_file_array): self.process_member("__addr__", None, struct_io_file_array) # _IO_FILE self.out.append(titlify("FILE")) struct_io_file_member = [ [4, "_flags"], [[0, 4][is_64bit()], ""], [current_arch.ptrsize, "_IO_read_ptr"], [current_arch.ptrsize, "_IO_read_end"], [current_arch.ptrsize, "_IO_read_base"], [current_arch.ptrsize, "_IO_write_base"], [current_arch.ptrsize, "_IO_write_ptr"], [current_arch.ptrsize, "_IO_write_end"], [current_arch.ptrsize, "_IO_buf_base"], [current_arch.ptrsize, "_IO_buf_end"], [current_arch.ptrsize, "_IO_save_base"], [current_arch.ptrsize, "_IO_backup_base"], [current_arch.ptrsize, "_IO_save_end"], [current_arch.ptrsize, "_markers"], [current_arch.ptrsize, "_chain"], [4, "_fileno"], [4, "_flags2"], [current_arch.ptrsize, "_old_offset"], [2, "_cur_column"], [1, "_vtable_offset"], [1, "_shortbuf"], [[0, 4][is_64bit()], ""], [current_arch.ptrsize, "_lock"], [0, ""], # varies depending on environment [8, "_offset"], [current_arch.ptrsize, "_codecvt"], [current_arch.ptrsize, "_wide_data"], [current_arch.ptrsize, "_freeres_list"], [current_arch.ptrsize, "_freeres_buf"], [current_arch.ptrsize, "__pad5"], [4, "_mode"], [[40, 20][is_64bit()], "_unused2"], [current_arch.ptrsize, "vtable"], ] for _, m in struct_io_file_member: adjust = self.process_member(m, struct_io_file_member, struct_io_file_array) if adjust: if is_32bit() and m == "_offset": struct_io_file_member[23][0] = adjust # vtable self.out.append(titlify("FILE->vtable")) vtable_offset = self.get_offset("vtable", struct_io_file_member) struct_io_jump_t_array = [] for x in struct_io_file_array: vtable_addr = x + vtable_offset if not is_valid_addr(vtable_addr): struct_io_jump_t_array.append(0) else: vtable = read_int_from_memory(vtable_addr) if not is_valid_addr(vtable): struct_io_jump_t_array.append(0) else: struct_io_jump_t_array.append(vtable) struct_io_jump_t_member = [ [current_arch.ptrsize, "__dummy"], [current_arch.ptrsize, "__dummy2"], [current_arch.ptrsize, "__finish"], [current_arch.ptrsize, "__overflow"], [current_arch.ptrsize, "__underflow"], [current_arch.ptrsize, "__uflow"], [current_arch.ptrsize, "__pbackfail"], [current_arch.ptrsize, "__xsputn"], [current_arch.ptrsize, "__xsgetn"], [current_arch.ptrsize, "__seekoff"], [current_arch.ptrsize, "__seekpos"], [current_arch.ptrsize, "__setbuf"], [current_arch.ptrsize, "__sync"], [current_arch.ptrsize, "__doallocate"], [current_arch.ptrsize, "__read"], [current_arch.ptrsize, "__write"], [current_arch.ptrsize, "__seek"], [current_arch.ptrsize, "__close"], [current_arch.ptrsize, "__stat"], [current_arch.ptrsize, "__showmanyc"], [current_arch.ptrsize, "__imbue"], ] for _, m in struct_io_jump_t_member: self.process_member(m, struct_io_jump_t_member, struct_io_jump_t_array) # wide_data self.out.append(titlify("FILE->_wide_data")) wide_data_offset = self.get_offset("_wide_data", struct_io_file_member) struct_io_wide_data_array = [] for x in struct_io_file_array: wide_data_addr = x + wide_data_offset if not is_valid_addr(wide_data_addr): struct_io_wide_data_array.append(0) else: wide_data = read_int_from_memory(wide_data_addr) if not is_valid_addr(wide_data): struct_io_wide_data_array.append(0) else: struct_io_wide_data_array.append(wide_data) struct_io_wide_data_member = [ [current_arch.ptrsize, "_IO_read_ptr"], [current_arch.ptrsize, "_IO_read_end"], [current_arch.ptrsize, "_IO_read_base"], [current_arch.ptrsize, "_IO_write_base"], [current_arch.ptrsize, "_IO_write_ptr"], [current_arch.ptrsize, "_IO_write_end"], [current_arch.ptrsize, "_IO_buf_base"], [current_arch.ptrsize, "_IO_buf_end"], [current_arch.ptrsize, "_IO_save_base"], [current_arch.ptrsize, "_IO_backup_base"], [current_arch.ptrsize, "_IO_save_end"], [8, "_IO_state"], [8, "_IO_last_state"], [[0x48, 0x70][is_64bit()], "_codecvt"], [4, "_shortbuf"], [[0, 4][is_64bit()], ""], [current_arch.ptrsize, "_wide_vtable"], ] for _, m in struct_io_wide_data_member: self.process_member(m, struct_io_wide_data_member, struct_io_wide_data_array) # wide_data vtable self.out.append(titlify("FILE->_wide_data->_wide_vtable")) wide_data_vtable_offset = self.get_offset("_wide_vtable", struct_io_wide_data_member) struct_io_wide_data_jump_t_array = [] for x in struct_io_wide_data_array: wide_data_vtable_addr = x + wide_data_vtable_offset if not is_valid_addr(wide_data_vtable_addr): struct_io_wide_data_jump_t_array.append(0) else: wide_data_vtable = read_int_from_memory(wide_data_vtable_addr) if not is_valid_addr(wide_data_vtable): struct_io_wide_data_jump_t_array.append(0) else: struct_io_wide_data_jump_t_array.append(wide_data_vtable) for _, m in struct_io_jump_t_member: self.process_member(m, struct_io_jump_t_member, struct_io_wide_data_jump_t_array) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): if args.address: struct_io_file_array = [] for x in args.address: if not is_valid_addr(x): err("Memory read error") return struct_io_file_array.append(x) else: try: stdin = AddressUtil.parse_address("(void*) stdin") stdout = AddressUtil.parse_address("(void*) stdout") stderr = AddressUtil.parse_address("(void*) stderr") except gdb.error: err("Could not find stdin, stdout, and stderr") return if not is_valid_addr(stdin): err("stdin: memory read error") return if not is_valid_addr(stdout): err("stdout: memory read error") return if not is_valid_addr(stderr): err("stderr: memory read error") return struct_io_file_array = [stdin, stdout, stderr] self.out = [] self.stdio_dump(struct_io_file_array) self.print_output(check_terminal_size=True) return @register_command class GotCommand(GenericCommand, BufferingOutput): """Display current status of the got/plt inside the process.""" _cmdline_ = "got" _category_ = "02-e. Process Information - Complex Structure Information" _aliases_ = ["plt"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--file", help="the filename to parse.") parser.add_argument("-e", "--elf-address", type=AddressUtil.parse_address, help="the ELF address to parse.") parser.add_argument("-r", "--remote", action="store_true", help="parse remote binary if download feature is available.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") parser.add_argument("filter", metavar="FILTER", nargs="*", default=[], help="filter string.") parser.add_argument("--exact", action="store_true", help="use exact match for function name.") parser.add_argument("--cppfilt", action="store_true", help="use c++filt to demangle.") _syntax_ = parser.format_help() _example_ = [ "{0:s} read print # filter specified keyword", "{0:s} -f /usr/lib/x86_64-linux-gnu/libc.so.6 # specified target binary", "{0:s} -f /bin/ls -e 0x4000000000 # use specified address, it is useful under qemu", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self, *args, **kwargs): super().__init__(complete=gdb.COMPLETE_FILENAME) self.add_setting("function_resolved", "green", "Line color of the got command output if the function has been resolved") self.add_setting("function_not_resolved", "yellow", "Line color of the got command output if the function has not been resolved") return def get_jmp_slots(self): try: readelf = GefUtil.which(Config.get_gef_setting("gef.readelf_command")) cmd = [readelf, "--relocs", "--wide", self.filename] lines = GefUtil.gef_execute_external(cmd, as_list=True) except (FileNotFoundError, subprocess.CalledProcessError): return [] elf = Elf.get_elf(self.filename) output = {} section_name = None reloc_count = 0 for line in lines: # get section r = re.findall("'(.+?)' at offset", line) if r: section_name = r[0] continue # GOT entry pattern 1 if "JUMP_SLOT" in line or "JMP_SLOT" in line: type = "JUMP_SLOT" address, _, _, _, name = line.split()[:5] address = int(address, 16) name = name.split("@")[0] # GOT entry pattern 2 (?) elif "GLOB_DAT" in line: type = "GLOB_DAT" address, _, _, _, name = line.split()[:5] address = int(address, 16) name = name.split("@")[0] # GOT entry pattern 3 (?) elif "IRELATIVE" in line: type = "IRELATIVE" if is_32bit(): address = line.split()[0] address = int(address, 16) name = "*ABS*" else: address, _, _, addend = line.split()[:4] address = int(address, 16) name = "*ABS*+{:#x}".format(int(addend, 16)) # Not GOT entry else: continue # count up reloc_arg if elf.is_static(): reloc_arg = None elif section_name not in [".rel.plt", ".rela.plt"]: reloc_arg = None else: reloc_arg = reloc_count * [1, 8][is_32bit()] reloc_count += 1 # fix address if elf.is_pie(): address += self.base_address # save array = output.get(type, []) output[type] = array + [[address, name, section_name, type, reloc_arg]] # flatten a = output.get("JUMP_SLOT", []) b = output.get("IRELATIVE", []) c = output.get("GLOB_DAT", []) return a + b + c def get_jmp_slots_arch_specific(self): try: readelf = GefUtil.which(Config.get_gef_setting("gef.readelf_command")) cmd = [readelf, "--arch-specific", "--wide", self.filename] lines = GefUtil.gef_execute_external(cmd, as_list=True) except (FileNotFoundError, subprocess.CalledProcessError): return [] elf = Elf.get_elf(self.filename) output = [] ncol = -1 for line in lines: r = re.search(r"^\s+Address\s+.*\s+Initial\s+", line) if r: ncol = len(line.split()) continue r = re.search(r"^\s+[0-9a-f]+", line) if not r: continue ls = line.split() address = int(ls[0], 16) # fix address if elf.is_pie(): address += self.base_address if ncol == 3: name = "" elif ncol == 4: name = " ".join(ls[3:]) else: name = ls[-1] output.append([address, name, ".got", "???", None]) return output def get_plt_addresses(self): try: objdump = GefUtil.which(Config.get_gef_setting("gef.objdump_command")) cmd = [objdump, "-j", ".plt", "-j", ".plt.sec", "-j", ".plt.got", "-d", self.filename] lines = GefUtil.gef_execute_external(cmd, as_list=True) except (FileNotFoundError, subprocess.CalledProcessError): return {} elf = Elf.get_elf(self.filename) output = {} for line in lines: # get function name r = re.findall(r"^([0-9a-f]+) <(.+)@plt>:", line) if not r: continue address, func_name = int(r[0][0], 16), r[0][1] # fix address if elf.is_pie(): address += self.base_address # save # Since DT_REL (used at i386) has no r_addend, the information of identification does not exist. # So there are multiple "*ABS*" entries, keep them in a list. array = output.get(func_name, []) output[func_name] = array + [address] return output def get_plt_addresses_arch_specific(self): try: readelf = GefUtil.which(Config.get_gef_setting("gef.readelf_command")) cmd = [readelf, "--arch-specific", "--wide", self.filename] lines = GefUtil.gef_execute_external(cmd, as_list=True) except (FileNotFoundError, subprocess.CalledProcessError): return [] elf = Elf.get_elf(self.filename) output = {} ncol = -1 for line in lines: r = re.search(r"^\s+Address\s+.*\s+Initial\s+", line) if r: initial_idx = line.split().index("Initial") ncol = len(line.split()) continue r = re.search(r"^\s+[0-9a-f]+", line) if not r: continue ls = line.split() plt_address = int(ls[initial_idx], 16) # fix address if elf.is_pie(): plt_address += self.base_address if not is_valid_addr(plt_address): plt_address = 0 if ncol == 3: name = "" elif ncol == 4: name = " ".join(ls[3:]) else: name = ls[-1] output[name] = [plt_address] return output def get_plt_range(self): # The PLT range is required to determine whether the information in the GOT is resolved or not. elf = Elf.get_elf(self.filename) sections = [x for x in elf.shdrs if x.sh_name in [".plt", ".plt.got", ".plt.sec", ".MIPS.stubs"]] if len(sections) == 0: return 0, 0 plt_begin = min([x.sh_addr for x in sections]) plt_end = max([x.sh_addr + x.sh_size for x in sections]) # fix address if elf.is_pie(): plt_begin += self.base_address plt_end += self.base_address return plt_begin, plt_end def perm(self, addr): sec = ProcessMap.lookup_address(addr).section if sec is None: return "[???]" return "[{!s}]".format(sec.permission) def get_shdr_range(self): # Required to identify the section name. elf = Elf.get_elf(self.filename) ranges = [] for shdr in elf.shdrs: sh_start = shdr.sh_addr sh_end = shdr.sh_addr + shdr.sh_size if elf.is_pie(): sh_start += self.base_address sh_end += self.base_address ranges.append([shdr.sh_name, sh_start, sh_end]) return ranges def get_section_name(self, addr): ranges = self.get_shdr_range() for name, start, end in ranges: if start <= addr < end: return name return "???" def get_section_sym(self, addr): ranges = self.get_shdr_range() for name, start, end in ranges: if start <= addr < end: return " <{:s}+{:#x}>".format(name, addr - start) return "" def parse_plt_got(self): # retrieve jump slots using readelf jmpslots = self.get_jmp_slots() if jmpslots == []: # On some architectures, such as mips, the GOT detection fails. # Some information will be lost, but detection will still be performed in such cases. jmpslots = self.get_jmp_slots_arch_specific() # retrieve plt address using objdump plts = self.get_plt_addresses() if plts == {}: # On some architectures, such as mips, the PLT detection fails. plts = self.get_plt_addresses_arch_specific() # retrieve the end of plt from elf parsing plt_begin, plt_end = self.get_plt_range() # link each PLT entries and each GOT entries resolved_info = [] for got_address, name, section_name, type, reloc_arg in jmpslots: # resolve PLT from GOT name if section_name != ".rel.plt" and name == "*ABS*": # 32-bit arch special case. plt_address = None else: # in many other case. # This includes the common *ABS* duplication pattern on 32-bit arch. plt_address = plts.get(name, None) if plt_address: # It is actually popped from plts[name]. plt_address is reassigned by int value. plt_address = plt_address.pop(0) # resolve plt section if plt_address: plt_section = self.get_section_name(plt_address) + self.perm(plt_address) else: plt_section = "" # resolve got section got_section = self.get_section_name(got_address) + self.perm(got_address) # resolve offset from absolute address got_offset = got_address - self.base_address if plt_address: plt_offset = plt_address - self.base_address else: plt_offset = 0 # read the address of the function try: got_value = read_int_from_memory(got_address) except gdb.error: self.quiet_err("Memory read error") return # resolve got value's symbol if got_value == 0: got_value_sym = "" elif plt_begin <= got_value < plt_end: # Non-PIE got_value_sym = self.get_section_sym(got_value) elif plt_begin - self.base_address <= got_value < plt_end - self.base_address: # PIE got_value_sym = self.get_section_sym(got_value) else: got_value_sym = Symbol.get_symbol_string(got_value) # different colors if the function has been resolved or not if got_value == 0: got_value_color = Config.get_gef_setting("got.function_resolved") # .rela.dyn && uninitialized, etc. elif plt_begin <= got_value < plt_end: # Non-PIE got_value_color = Config.get_gef_setting("got.function_not_resolved") elif plt_begin - self.base_address <= got_value < plt_end - self.base_address: # PIE got_value_color = Config.get_gef_setting("got.function_not_resolved") else: got_value_color = Config.get_gef_setting("got.function_resolved") # c++filt if self.args.cppfilt: if name.startswith("_Z"): cppfilt_command = GefUtil.which(Config.get_gef_setting("gef.cppfilt_command")) res = GefUtil.gef_execute_external([cppfilt_command, name], as_list=True) if len(res) == 1: name = res[0] # aggregate dic = { "name": name, "type": type, "section_name": section_name, "plt_address": plt_address, "plt_section": plt_section, "plt_offset": plt_offset, "reloc_arg": reloc_arg, "got_address": got_address, "got_section": got_section, "got_offset": got_offset, "got_value": got_value, "got_value_sym": got_value_sym, "got_value_color": got_value_color, } PltGotInfo = collections.namedtuple("PltGotInfo", dic.keys()) plt_got_info = PltGotInfo(*dic.values()) resolved_info.append(plt_got_info) return resolved_info def make_output(self, resolved_info): # calc each width width = AddressUtil.get_format_address_width() name_width = min(max([len(info.name) for info in resolved_info] + [len("Name")]), 50) if self.args.verbose: got_section_width = max([len(info.got_section) for info in resolved_info] + [len("Section")]) plt_section_width = max([len(info.plt_section) for info in resolved_info] + [len("Section")]) got_offset_width = max([len(hex(info.got_offset)) for info in resolved_info] + [len("Offset")]) plt_offset_width = max([len(hex(info.plt_offset)) for info in resolved_info] + [len("Offset")]) # print legend if not self.args.quiet: if self.args.verbose: name_s = "{:<{:d}}".format("Name", name_width) type_s = "{:9s}".format("Type") plt_s = "{:{:d}s} @{:{:d}s} {:>{:d}s} {:>9s}".format( "PLT", width, "Section", plt_section_width, "Offset", plt_offset_width, "reloc_arg", ) got_s = "{:{:d}s} @{:{:d}s} {:>{:d}s}".format( "GOT", width, "Section", got_section_width, "Offset", got_offset_width, ) gotv_s = "{:{:d}}".format("GOT value", width) legend = " | ".join([name_s, type_s, plt_s, got_s, gotv_s]) else: name_s = "{:<{:d}}".format("Name", name_width) plt_s = "{:{:d}s}".format("PLT", width) got_s = "{:{:d}s}".format("GOT", width) gotv_s = "{:{:d}}".format("GOT value", width) legend = " | ".join([name_s, plt_s, got_s, gotv_s]) self.out.append(GefUtil.make_legend(legend)) entries = [] for info in resolved_info: # make reloc_arg format if info.reloc_arg is None: reloc_arg_info = "{:>9s}".format("-") else: reloc_arg_info = "{:#9x}".format(info.reloc_arg) # make name format if len(info.name) <= name_width: name_info = "{:{:d}s}".format(info.name, name_width) else: name_info = "{:{:d}s}".format(info.name[:name_width - 3] + "...", name_width) # make plt format if self.args.verbose: if info.plt_address: plt_info = "{!s} @{:{:d}s} {:#{:d}x} {:9s}".format( ProcessMap.lookup_address(info.plt_address), info.plt_section, plt_section_width, info.plt_offset, plt_offset_width, reloc_arg_info, ) else: plt_info = "{:{:d}s} {:{:d}s} {:>{:d}s} {:9s}".format( "Not found", width, "", plt_section_width, "", plt_offset_width, reloc_arg_info, ) else: if info.plt_address: plt_info = "{!s}".format(ProcessMap.lookup_address(info.plt_address)) else: plt_info = "{:{:d}s}".format("Not found", width) # make got format if self.args.verbose: got_info = "{!s} @{:{:d}s} {:#{:d}x}".format( ProcessMap.lookup_address(info.got_address), info.got_section, got_section_width, info.got_offset, got_offset_width, ) else: got_info = "{!s}".format(ProcessMap.lookup_address(info.got_address)) # make got value format got_value_info = Color.colorify( "{:#0{:d}x}{:s}".format(info.got_value, width, info.got_value_sym), info.got_value_color, ) # make line if self.args.verbose: type_info = "{:9s}".format(info.type) line_element = [name_info, type_info, plt_info, got_info, got_value_info] else: line_element = [name_info, plt_info, got_info, got_value_info] line = " | ".join(line_element) # save temporarily entries.append([info.got_address, info, line]) # sort by GOT address entries = sorted(entries) # print prev_section = None for _, info, line in sorted(entries): # print section name if prev_section != info.section_name: self.quiet_add_out(titlify(info.section_name)) prev_section = info.section_name # if we have a filter let's skip the entries that are not requested if self.args.filter: if self.args.exact: if not any(pattern == info.name for pattern in self.args.filter): continue else: if not any(pattern in line for pattern in self.args.filter): continue self.out.append(line) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): try: GefUtil.which(Config.get_gef_setting("gef.objdump_command")) GefUtil.which(Config.get_gef_setting("gef.readelf_command")) if args.cppfilt: GefUtil.which(Config.get_gef_setting("gef.cppfilt_command")) except FileNotFoundError as e: self.quiet_err("{}".format(e)) return # A valid path even if the mount namespace is different. local_filepath = None # A path in /proc/PID/maps. Ignore namespace differences. # Used to find the base address. vmmap_filepath = None # A path in remote environment. remote_filepath = None # A path downloaded file from remote environment. # It should be removed later. tmp_filepath = None # get local_filepath if args.remote: if not is_remote_debug(): self.quiet_err("-r option is allowed only remote debug") return if args.file: remote_filepath = args.file # if specified, assume it is remote vmmap_filepath = args.file elif gdb.current_progspace().filename: f = gdb.current_progspace().filename if f.startswith("target:"): # gdbserver f = f[7:] remote_filepath = f vmmap_filepath = f elif Pid.get_pid(remote=True): remote_filepath = "/proc/{:d}/exe".format(Pid.get_pid(remote=True)) else: self.quiet_err("File name could not be determined") return data = Path.read_remote_file(remote_filepath, as_byte=True) # qemu-user is failed here, it is ok if not data: self.quiet_err("Failed to read remote filepath") return tmp_fd, tmp_filepath = GefUtil.mkstemp(prefix="got", suffix=".elf") os.fdopen(tmp_fd, "wb").write(data) local_filepath = tmp_filepath del data elif args.file: local_filepath = args.file elif args.file is None: local_filepath = Path.get_filepath() # /proc//root/path/to/binary if another mnt namespace vmmap_filepath = Path.get_filepath(append_proc_root_prefix=False) # check local filepath if local_filepath is None: self.quiet_err("File name could not be determined") return if not os.path.exists(local_filepath): self.quiet_err("{:s} does not exist".format(local_filepath)) return elf = Elf.get_elf(local_filepath) if elf is None or not elf.is_valid(): self.quiet_err("Invalid ELF") return # title self.out = [] if not args.quiet: if remote_filepath: print_filename = "{:s} (remote: {:s})".format(local_filepath, remote_filepath) else: print_filename = local_filepath if elf.is_relro(): if elf.is_full_relro(): relro_status = "Full RELRO" else: relro_status = "Partial RELRO" else: relro_status = "No RELRO" self.out.append(titlify("PLT / GOT - {:s} - {:s}".format(print_filename, relro_status))) # get base address if args.elf_address: if not args.file: self.quiet_err("-e option needs -f option: in-memory ELF lacks Shdr, preventing full information resolution") return base_address = args.elf_address else: vmmap = ProcessMap.get_process_maps() target_filepath = vmmap_filepath or local_filepath # get the address matching the specified path path_match = [x.page_start for x in vmmap if x.path == target_filepath] if path_match: base_address = min(path_match) else: # When using the -L option with qemu-user, # the file path on the disk and the file path on vmmap are different. # # e.g., qemu-arm -g 1234 -L /usr/arm-linux-gnueabihf ./a.out # gef> vmm # [ Legend: Code | Heap | Stack | Writable | ReadOnly | None | RWX ] # Start End Size Offset Perm Path # 0x3f694000 0x3f79f000 0x0010b000 0x00000000 r-x /lib/libc.so.6 # 0x3f79f000 0x3f7b9000 0x0001a000 0x0010a000 r-- /lib/libc.so.6 # 0x3f7b9000 0x3f7c4000 0x0000b000 0x00124000 rw- /lib/libc.so.6 # ... path_match_end = [x.page_start for x in vmmap if x.path and target_filepath.endswith(x.path)] if path_match_end: base_address = min(path_match_end) else: self.quiet_err("Could not find {:s} in memory (Use -e option)".format(target_filepath)) return # get the filtering parameter self.filename = local_filepath self.base_address = base_address # doit resolved_info = self.parse_plt_got() self.make_output(resolved_info) self.print_output(check_terminal_size=True) # clean up if tmp_filepath and os.path.exists(tmp_filepath): os.unlink(tmp_filepath) return @register_command class GotAllCommand(GenericCommand, BufferingOutput): """Show got entries for all libraries.""" _cmdline_ = "got-all" _category_ = "02-e. Process Information - Complex Structure Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--remote", action="store_true", help="parse remote binary if download feature is available.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") parser.add_argument("filter", metavar="FILTER", nargs="*", help="filter string.") parser.add_argument("--exact", action="store_true", help="use exact match for function name.") parser.add_argument("--cppfilt", action="store_true", help="use c++filt to demangle.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): verbose = ["", "-v"][args.verbose] remote = ["", "-r"][args.remote] exact = ["", "--exact"][args.exact] cppfilt = ["", "--cppfilt"][args.cppfilt] extra_args = "{:s} {:s} {:s} {:s} {:s}".format(verbose, remote, cppfilt, exact, " ".join(args.filter)) self.out = [] processed = [] for m in ProcessMap.get_process_maps(): if not m.path: continue if m.path.startswith(("[", "<")) or m.path.endswith(("]", ">")): continue if m.path in processed: continue if not is_valid_addr(m.page_start): continue x = read_memory(m.page_start, 4) if x != b"\x7fELF": continue ret = gdb.execute("got -f {!r} -n {:s}".format(m.path, extra_args), to_string=True) self.out.extend(ret.splitlines()) self.out.append("") processed.append(m.path) self.print_output(check_terminal_size=True) return class FormatStringBreakpoint(gdb.Breakpoint): """Inspect stack for format string.""" def __init__(self, func_address, func_name, num_args, verbose=False): super().__init__("*{:#x}".format(func_address), type=gdb.BP_BREAKPOINT, internal=not verbose) self.func_name = func_name self.num_args = num_args self.enabled = True return def stop(self): Cache.reset_gef_caches() msg = [] ptr, addr = current_arch.get_ith_parameter(self.num_args) addr = ProcessMap.lookup_address(addr) if not addr.valid: return False if addr.section.permission.value & Permission.WRITE: msg.append(Color.colorify("Format string helper", "bold yellow")) content = read_cstring_from_memory(addr.value) or "" msg.append("Possible insecure format string: {:s}('{:s}' -> {:#x}: '{:s}')".format( self.func_name, ptr, addr.value, content, )) name = addr.info.name if addr.info else addr.section.path msg.append("Reason: '{:s}()' with format-string arg #{:d} is in writable page {:s} ({:s})".format( self.func_name, self.num_args, str(addr), name, )) ContextExtraCommand.push_context_message("warn", "\n".join(msg)) return True return False @register_command class FormatStringSearchCommand(GenericCommand): """The helper to search for exploitable format strings.""" _cmdline_ = "format-string-helper" _category_ = "01-i. Debugging Support - Other" _aliases_ = ["fmtstr-helper"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--remove-breakpoint", action="store_true", help="remove the format-string-helper related breakpoints.") parser.add_argument("-v", "--verbose", action="store_true", help="display target functions of breakpoint.") _syntax_ = parser.format_help() dangerous_functions = { "printf": 0, # int printf(const char *fmt, ...); "fprintf": 1, # int fprintf(FILE *stream, const char *fmt, ...); "dprintf": 1, # int dprintf(int fd, const char *fmt, ...); "sprintf": 1, # int sprintf(char *str, const char *fmt, ...); "asprintf": 1, # int asprintf(char **strp, const char *fmt, ...); "snprintf": 2, # int snprintf(char *str, size_t size, const char *fmt, ...); "wprintf": 0, # int wprintf(const wchar_t *fmt, ...); "fwprintf": 1, # int fwprintf(FILE *stream, const wchar_t *fmt, ...); "swprintf": 2, # int swprintf(wchar_t *str, size_t n, const wchar_t *fmt, ...); "obstack_printf": 1, # int obstack_printf(struct obstack *obstack, const char *fmt, ...); "__printf_chk": 1, # int __printf_chk(int flag, const char *fmt); "__fprintf_chk": 2, # int __fprintf_chk(FILE *stream, int flag, const char *fmt, ...); "__dprintf_chk": 2, # int __dprintf_chk(int d, int flags, const char *fmt, ...) "__sprintf_chk": 3, # int __sprintf_chk(char *str, int flag, size_t strlen, const char *fmt, ...); "__asprintf_chk": 2, # int __asprintf_chk(char **strp, int flag, const char *fmt, ...) "__snprintf_chk": 4, # int __snprintf_chk(char *str, size_t maxlen, int flag, size_t strlen, const char *fmt, ...); "__wprintf_chk": 1, # int __wprintf_chk(int flag, const wchar_t *format, ...); "__fwprintf_chk": 2, # int __fwprintf_chk(FILE *stream, int flag, const wchar_t *format, ...); "__swprintf_chk": 4, # int __swprintf_chk(wchar_t *str, size_t maxlen, int flag, size_t slen, const wchar_t *fmt, ...); "__obstack_printf_chk": 2, # int __obstack_printf_chk(struct obstack *obstack, int flag, const char *fmt, ...); "vprintf": 0, # int vprintf(const char *fmt, va_list ap); "vfprintf": 1, # int vfprintf(FILE *stream, const char *fmt, va_list ap); "vdprintf": 1, # int vdprintf(int fd, const char *fmt, va_list ap); "vsprintf": 1, # int vsprintf(char *str, const char *fmt, va_list ap); "vasprintf": 1, # int vasprintf(char **strp, const char *fmt, va_list ap); "vsnprintf": 2, # int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); "vwprintf": 0, # int vwprintf(const wchar_t *fmt, va_list ap); "vfwprintf": 1, # int vfwprintf(FILE *stream, const wchar_t *fmt, va_list ap); "vswprintf": 2, # int vswprintf(wchar_t *str, size_t maxlen, const wchar_t *fmt, va_list ap); "obstack_vprintf": 1, # int obstack_vprintf(struct obstack *obstack, const char *fmt, va_list ap); "__vprintf_chk": 1, # int __vprintf_chk(int flag, const char *fmt, va_list ap); "__vfprintf_chk": 2, # int __vfprintf_chk(FILE *stream, int flag, const char *fmt, va_list ap); "__vdprintf_chk": 2, # int __vdprintf_chk(int d, int flag, const char *fmt, va_list ap); "__vsprintf_chk": 3, # int __vsprintf_chk(char *str, int flag, size_t slen, const char *fmt, va_list ap); "__vasprintf_chk": 2, # int __vasprintf_chk(char **strp, int flag, const char *fmt, va_list ap); "__vsnprintf_chk": 4, # int __vsnprintf_chk(char *str, size_t maxlen, int flag, size_t slen, const char *fmt, va_list ap); "__vwprintf_chk": 1, # int __vwprintf_chk(int flag, const wchar_t *fmt, va_list ap); "__vfwprintf_chk": 2, # int __vfwprintf_chk(FILE *stream, int flag, const wchar_t *fmt, va_list ap); "__vswprintf_chk": 4, # int __vswprintf_chk(wchar_t *str, size_t maxlen, int flag, size_t slen, const wchar_t *fmt, va_list ap); "__obstack_vprintf_chk": 2, # int __obstack_vprintf_chk(struct obstack *obstack, int flag, const char *fmt, va_list ap); "syslog": 1, # void syslog(int priority, const char *fmt, ...); "vsyslog": 1, # void vsyslog(int priority, const char *fmt, va_list ap); "__syslog_chk": 2, # void __syslog_chk(int priority, int flag, const char *fmt, ...); "__vsyslog_chk": 2, # void __vsyslog_chk(int priority, int flag, const char *fmt, va_list ap); "scanf": 0, # int scanf(const char *fmt, ...); "fscanf": 1, # int fscanf(FILE *stream, const char *fmt, ...); "sscanf": 1, # int sscanf(const char *str, const char *fmt, ...); "wscanf": 0, # int wscanf(const wchar_t *fmt, ...); "fwscanf": 1, # int fwscanf(FILE *stream, const wchar_t *fmt, ...); "swscanf": 1, # int swscanf(const wchar_t *ws, const wchar_t *fmt, ...); "vscanf": 0, # int vscanf(const char *fmt, va_list ap); "vfscanf": 1, # int vfscanf(FILE *stream, const char *fmt, va_list ap); "vsscanf": 1, # int vsscanf(const char *str, const char *fmt, va_list ap); "vwscanf": 0, # int vwscanf(const wchar_t *fmt, va_list ap); "vfwscanf": 1, # int vfwscanf(FILE *stream, const wchar_t *fmt, va_list ap); "vswscanf": 1, # int vswscanf(const wchar_t *s, const wchar_t *fmt, va_list ap); "warn": 0, # void warn(const char *fmt, ...); "warnx": 0, # void warnx(const char *fmt, ...); "err": 1, # void err(int status, const char *fmt, ...); "errx": 1, # void errx(int status, const char *fmt, ...); "vwarn": 0, # void vwarn(const char *fmt, va_list ap); "vwarnx": 0, # void vwarnx(const char *fmt, va_list ap); "verr": 1, # void verr(int status, const char *fmt, va_list ap); "verrx": 1, # void verrx(int status, const char *fmt, va_list ap); "error": 2, # void error(int status, int errnum, const char *fmt, ...); "error_at_line": 4, # void error_at_line(int status, int errnum, const char *filename, uint linenum, const char *fmt, ...); "argp_error": 1, # void argp_error(const struct argp_state *state, const char *fmt, ...); "argp_failure": 3, # void argp_failure(const struct argp_state *state, int status, int errnum, const char *fmt, ...); "xasprintf": 0, # char* xasprintf(const char *fmt, ...); "xvasprintf": 0, # char* xvasprintf(const char *fmt, va_list ap); } breakpoints = [] def remove_breakpoints(self): bp_count = 0 while FormatStringSearchCommand.breakpoints: bp = FormatStringSearchCommand.breakpoints.pop() bp.delete() bp_count += 1 ok("Removed {:d} FormatStringBreakpoint".format(bp_count)) return @parse_args @exclude_specific_gdb_mode(mode=("wine",)) @require_arch_set def do_invoke(self, args): if args.remove_breakpoint: self.remove_breakpoints() return if FormatStringSearchCommand.breakpoints: err("Breakpoints have been set already") return bp_count = 0 for func_name, num_arg in self.dangerous_functions.items(): try: func_address = AddressUtil.parse_address(func_name) except gdb.error: continue if args.verbose: # The reason for the `end=""` is that when you set a breakpoint, # gdb automatically outputs the following message: # printf: Breakpoint 1 at 0x7ffff7c63f90: file ./stdio-common/printf.c, line 28. gef_print(func_name + ": ", end="") bp = FormatStringBreakpoint(func_address, func_name, num_arg, verbose=args.verbose) FormatStringSearchCommand.breakpoints.append(bp) bp_count += 1 ok("Enabled {:d}/{:d} FormatStringBreakpoint".format(bp_count, len(self.dangerous_functions))) return class TraceMallocBreakpoint(gdb.Breakpoint): """Track allocations for malloc() etc.""" def __init__(self, name, loc): super().__init__("*{:#x}".format(loc.value), gdb.BP_BREAKPOINT, internal=True) self.silent = True self.name = name self.loc = loc return def check_nested(self): tid = Pid.get_tid() for bp in gdb.breakpoints(): try: if bp.__class__.__name__ in ["TraceMallocRetBreakpoint", "TraceReallocRetBreakpoint"]: if tid == bp.tid: if bp.enabled: return True except Exception: pass return False def stop(self): Cache.reset_gef_caches() # fast return if nested break if self.check_nested(): return False # set bp to return address if self.name in ["malloc", "valloc"]: _, size = current_arch.get_ith_parameter(0) nmemb = 1 memptr = None alignment = None elif self.name == "calloc": _, nmemb = current_arch.get_ith_parameter(0) _, size = current_arch.get_ith_parameter(1) memptr = None alignment = None elif self.name in ["aligned_alloc", "memalign"]: _, alignment = current_arch.get_ith_parameter(0) _, size = current_arch.get_ith_parameter(1) nmemb = 1 memptr = None elif self.name == "posix_memalign": _, memptr = current_arch.get_ith_parameter(0) _, alignment = current_arch.get_ith_parameter(1) _, size = current_arch.get_ith_parameter(2) nmemb = 1 TraceMallocRetBreakpoint(self.name, nmemb, size, memptr, alignment) return False class TraceMallocRetBreakpoint(gdb.Breakpoint): """Internal breakpoint to retrieve the return value of malloc() etc.""" def __init__(self, name, nmemb, size, memptr, alignment): ret_addr = gdb.newest_frame().older().pc() super().__init__("*{:#x}".format(ret_addr), gdb.BP_BREAKPOINT, internal=True) self.silent = True self.name = name self.nmemb = nmemb self.size = size self.memptr = memptr self.alignment = alignment self.tid = Pid.get_tid() GlibcHeapTracerCommand.clear_disabled_breakpoints() return def search_allocated_index(self, addr): for idx, (_action_index, allocated, _size) in enumerate(GlibcHeapTracerCommand.heap_allocated_list): if allocated.value == addr.value: return idx return None def search_freed_index(self, addr): for idx, (_action_index, freed, _size) in enumerate(GlibcHeapTracerCommand.heap_freed_list): if freed.value == addr.value: return idx return None def show_information(self, allocated): def get_offset_str(v): if v == 0: return "" arenas = GlibcHeap.get_all_arenas() for arena in arenas: if arena.heap_base is None: return "" heap_base = arena.heap_base size = to_unsigned_long(arena.system_mem) if heap_base <= v < heap_base + size: return Color.colorify("${:+#x}".format(v - heap_base), "lilac") return "" # show information text1 = "{:s} - {!s}{:s}{:s}".format( Color.colorify("Heap-Analysis", "bold yellow"), allocated, Color.colorify("#{:d}".format(GlibcHeapTracerCommand.heap_action_index), "bold cyan"), get_offset_str(allocated.value), ) if self.name in ["malloc", "valloc"]: text2 = "{:s}({:#x})".format( self.name, self.size, ) elif self.name == "calloc": text2 = "{:s}({:#x}, {:#x})".format( self.name, self.nmemb, self.size, ) elif self.name in ["aligned_alloc", "memalign"]: text2 = "{:s}({:#x}, {:#x})".format( self.name, self.alignment, self.size, ) elif self.name == "posix_memalign": text2 = "{:s}({:#x}, {:#x}, {:#x})".format( self.name, self.memptr, self.alignment, self.size, ) padlen = 44 - len(Color.remove_color(text1)) gef_print("{:s}{:s} = {:s}".format(text1, " " * padlen, text2)) return def check_inconsistency(self, allocated): idx = self.search_allocated_index(allocated) if idx is None: return False msg = [] msg.append(Color.colorify("Heap-Analysis", "bold yellow")) msg.append("Heap inconsistency detected:") msg.append("Attempting to allocate used address: {!s}".format(allocated)) ContextExtraCommand.push_context_message("warn", "\n".join(msg)) return True def update_list(self, allocated): # pop from freed list if it was in it idx = self.search_freed_index(allocated) if idx is not None: GlibcHeapTracerCommand.heap_freed_list.pop(idx) # add it to alloc-ed list item = (GlibcHeapTracerCommand.heap_action_index, allocated, self.nmemb * self.size) GlibcHeapTracerCommand.heap_allocated_list.append(item) return def stop(self): # check if expected thread if Pid.get_tid() != self.tid: return False # invalidate self.enabled = False Cache.reset_gef_caches() # count up action index GlibcHeapTracerCommand.heap_action_index += 1 # get returned address if self.name == "posix_memalign": allocated = read_int_from_memory(self.memptr) else: allocated = AddressUtil.parse_address(current_arch.return_register) allocated = ProcessMap.lookup_address(allocated) # show information self.show_information(allocated) # fast return if NULL if allocated.value == 0: return False # check inconsistency ret = self.check_inconsistency(allocated) if ret: return True # break # update list self.update_list(allocated) return False class TraceReallocBreakpoint(gdb.Breakpoint): """Track re-allocations for realloc() etc.""" def __init__(self, name, loc): super().__init__("*{:#x}".format(loc.value), gdb.BP_BREAKPOINT, internal=True) self.silent = True self.name = name self.loc = loc return def check_nested(self): tid = Pid.get_tid() for bp in gdb.breakpoints(): try: if bp.__class__.__name__ in ["TraceMallocRetBreakpoint", "TraceReallocRetBreakpoint"]: if tid == bp.tid: if bp.enabled: return True except Exception: pass return False def stop(self): Cache.reset_gef_caches() # fast return if nested break if self.check_nested(): return False # set bp to return address _, old_loc = current_arch.get_ith_parameter(0) old_loc = ProcessMap.lookup_address(old_loc) if self.name == "realloc": nmemb = 1 _, size = current_arch.get_ith_parameter(1) elif self.name == "reallocarray": _, nmemb = current_arch.get_ith_parameter(1) _, size = current_arch.get_ith_parameter(2) TraceReallocRetBreakpoint(self.name, old_loc, nmemb, size) return False class TraceReallocRetBreakpoint(gdb.Breakpoint): """Internal breakpoint to retrieve the return value of realloc() etc.""" def __init__(self, name, old_loc, nmemb, size): ret_addr = gdb.newest_frame().older().pc() super().__init__("*{:#x}".format(ret_addr), gdb.BP_BREAKPOINT, internal=True) self.silent = True self.name = name self.old_loc = old_loc self.nmemb = nmemb self.size = size self.tid = Pid.get_tid() GlibcHeapTracerCommand.clear_disabled_breakpoints() return def search_allocated_index(self, addr): for idx, (_action_index, allocated, _size) in enumerate(GlibcHeapTracerCommand.heap_allocated_list): if allocated.value == addr.value: return idx return None def search_freed_index(self, addr): for idx, (_action_index, freed, _size) in enumerate(GlibcHeapTracerCommand.heap_freed_list): if freed.value == addr.value: return idx return None def show_information(self, new_loc): def get_offset_str(v): if v == 0: return "" arenas = GlibcHeap.get_all_arenas() for arena in arenas: if arena.heap_base is None: return "" heap_base = arena.heap_base size = to_unsigned_long(arena.system_mem) if heap_base <= v < heap_base + size: return Color.colorify("${:+#x}".format(v - heap_base), "lilac") return "" # get action index idx = self.search_allocated_index(self.old_loc) if idx is None: action_index_s = "" else: action_index = GlibcHeapTracerCommand.heap_allocated_list[idx][0] action_index_s = "{:s}".format(Color.colorify("#{:d}".format(action_index), "bold cyan")) # check realloc result type if self.old_loc.value == 0: extra = Color.colorify("return new chunk", "bold yellow") elif self.old_loc.value != new_loc.value: extra = Color.colorify("return another chunk", "bold red") else: extra = Color.colorify("return same chunk", "bold green") # show information text1 = "{:s} - {!s}{:s}{:s}".format( Color.colorify("Heap-Analysis", "bold yellow"), new_loc, Color.colorify("#{:d}".format(GlibcHeapTracerCommand.heap_action_index), "bold cyan"), get_offset_str(new_loc.value), ) if self.name == "realloc": text2 = "{:s}({!s}{:s}{:s}, {:#x}) // {:s}".format( self.name, self.old_loc if self.old_loc.value != 0 else Color.boldify("NULL"), action_index_s, get_offset_str(self.old_loc.value), self.size, extra, ) elif self.name == "reallocarray": text2 = "{:s}({!s}{:s}{:s}, {:#x}, {:#x}) // {:s}".format( self.name, self.old_loc if self.old_loc.value != 0 else Color.boldify("NULL"), action_index_s, get_offset_str(self.old_loc.value), self.nmemb, self.size, extra, ) padlen = 44 - len(Color.remove_color(text1)) gef_print("{:s}{:s} = {:s}".format(text1, " " * padlen, text2)) return def check_double_free(self, to_free): if to_free.value == 0: return False idx = self.search_freed_index(to_free) if idx is None: return False msg = [] msg.append(Color.colorify("Heap-Analysis", "bold yellow")) msg.append("Double-free detected:") msg.append("{!s} is freed but it is already in the freed list".format( to_free, )) ContextExtraCommand.push_context_message("warn", "\n".join(msg)) return True def check_inconsistency(self, new_loc): if self.old_loc.value == new_loc.value: return False idx = self.search_allocated_index(new_loc) if idx is None: return False msg = [] msg.append(Color.colorify("Heap-Analysis", "bold yellow")) msg.append("Heap inconsistency detected:") msg.append("Attempting to allocate used address: {!s}".format(new_loc)) ContextExtraCommand.push_context_message("warn", "\n".join(msg)) return True def update_list(self, new_loc): if self.old_loc.value == 0: # pop from freed list if it was in it idx = self.search_freed_index(new_loc) if idx is not None: GlibcHeapTracerCommand.heap_freed_list.pop(idx) elif self.old_loc.value != new_loc.value: # pop from allocated list if it was in it idx = self.search_allocated_index(self.old_loc) if idx is not None: GlibcHeapTracerCommand.heap_allocated_list.pop(idx) # pop from freed list if it was in it idx = self.search_freed_index(new_loc) if idx is not None: GlibcHeapTracerCommand.heap_freed_list.pop(idx) else: # pop from allocated list if it was in it idx = self.search_allocated_index(self.old_loc) if idx is not None: GlibcHeapTracerCommand.heap_allocated_list.pop(idx) # add new item to alloc-ed list item = (GlibcHeapTracerCommand.heap_action_index, new_loc, self.nmemb * self.size) GlibcHeapTracerCommand.heap_allocated_list.append(item) return def stop(self): # check if expected thread if Pid.get_tid() != self.tid: return False # invalidate self.enabled = False Cache.reset_gef_caches() # count up action index GlibcHeapTracerCommand.heap_action_index += 1 # get returned address new_loc = AddressUtil.parse_address(current_arch.return_register) new_loc = ProcessMap.lookup_address(new_loc) # show information self.show_information(new_loc) # fast return if NULL if new_loc.value == 0: return False # check double free ret = self.check_double_free(self.old_loc) if ret: return True # break # check inconsistency ret = self.check_inconsistency(new_loc) if ret: return True # break # update list self.update_list(new_loc) return False class TraceFreeBreakpoint(gdb.Breakpoint): """Track calls to free() and attempts to detect inconsistencies.""" def __init__(self, name, loc): super().__init__("*{:#x}".format(loc.value), gdb.BP_BREAKPOINT, internal=True) self.silent = True self.name = name self.loc = loc return def search_allocated_index(self, addr): if addr.value == 0: return None for idx, (_action_index, allocated, _size) in enumerate(GlibcHeapTracerCommand.heap_allocated_list): if allocated.value == addr.value: return idx return None def search_freed_index(self, addr): for idx, (_action_index, freed, _size) in enumerate(GlibcHeapTracerCommand.heap_freed_list): if freed.value == addr.value: return idx return None def show_information(self, to_free): def get_offset_str(v): if v == 0: return "" arenas = GlibcHeap.get_all_arenas() for arena in arenas: if arena.heap_base is None: return "" heap_base = arena.heap_base size = to_unsigned_long(arena.system_mem) if heap_base <= v < heap_base + size: return Color.colorify("${:+#x}".format(v - heap_base), "lilac") return "" # get action index idx = self.search_allocated_index(to_free) if idx is None: action_index_s = "" else: action_index = GlibcHeapTracerCommand.heap_allocated_list[idx][0] action_index_s = "{:s}".format(Color.colorify("#{:d}".format(action_index), "bold cyan")) # show information text1 = "{:s} -".format( Color.colorify("Heap-Analysis", "bold yellow"), ) text2 = "free({!s}{:s}{:s})".format( to_free if to_free.value != 0 else Color.boldify("NULL"), action_index_s, get_offset_str(to_free.value), ) padlen = 44 - len(Color.remove_color(text1)) gef_print("{:s}{:s} = {:s}".format(text1, " " * padlen, text2)) return def check_double_free(self, to_free): if to_free.value == 0: return False idx = self.search_freed_index(to_free) if idx is None: return False msg = [] msg.append(Color.colorify("Heap-Analysis", "bold yellow")) msg.append("Double-free detected:") msg.append("{!s} is freed but it is already in the freed list".format( to_free, )) ContextExtraCommand.push_context_message("warn", "\n".join(msg)) return True def check_inconsistency(self, to_free): idx = self.search_allocated_index(to_free) if idx is not None: return False msg = [] msg.append(Color.colorify("Heap-Analysis", "bold yellow")) msg.append("Heap inconsistency detected:") msg.append("Attempting to free an unknown value: {!s}".format(to_free)) ContextExtraCommand.push_context_message("warn", "\n".join(msg)) return True def update_list(self, to_free): # move from allocated list to freed list idx = self.search_allocated_index(to_free) item = GlibcHeapTracerCommand.heap_allocated_list.pop(idx) item = (GlibcHeapTracerCommand.heap_action_index, item[1], item[2]) GlibcHeapTracerCommand.heap_freed_list.append(item) return def stop(self): Cache.reset_gef_caches() # count up action index GlibcHeapTracerCommand.heap_action_index += 1 # get the address to free _, to_free = current_arch.get_ith_parameter(0) to_free = ProcessMap.lookup_address(to_free) # show information self.show_information(to_free) # fast return if free(NULL) if to_free.value == 0: return False # check double free ret = self.check_double_free(to_free) if ret: return True # break # check free(unknown address) ret = self.check_inconsistency(to_free) if ret: return True # break # update list self.update_list(to_free) return False @register_command class GlibcHeapTracerCommand(GenericCommand): """Trace malloc/free to check heap integrity for UAF / Double-Free.""" _cmdline_ = "heap tracer" _category_ = "05-a. Heap - Glibc" _aliases_ = ["heap-analysis-helper"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-d", "--dump-current-list", action="store_true", help="show the tracked allocations.") parser.add_argument("-r", "--reset", action="store_true", help="remove breakpoints etc.") _syntax_ = parser.format_help() _note_ = [ "Note that splits and consolidates (which are performed inside `malloc` and `free`) are not tracked.", "So this is not a strict trace.", ] _note_ = "\n".join(_note_) heap_allocated_list = [] heap_freed_list = [] heap_breakpoints = [] heap_action_index = 0 @staticmethod def clear_disabled_breakpoints(force=False): names = [ "TraceMallocRetBreakpoint", "TraceReallocRetBreakpoint", ] for bp in gdb.breakpoints(): try: if bp.__class__.__name__ not in names: continue if force is False and bp.enabled: continue bp.delete() except Exception: pass return def dump_tracked_allocations(self): if GlibcHeapTracerCommand.heap_allocated_list: ok("Tracked as in-use chunks:") for action_idx, addr, sz in GlibcHeapTracerCommand.heap_allocated_list: gef_print("{:d}: {!s} = allocate({:#x})".format(action_idx, addr, sz)) else: ok("No allocated chunk tracked") if GlibcHeapTracerCommand.heap_freed_list: ok("Tracked as freed chunks:") for action_idx, addr, _sz in GlibcHeapTracerCommand.heap_freed_list: gef_print("{:#d}: free({!s})".format(action_idx, addr)) else: ok("No freed chunk tracked") return def setup(self): def setup_breakpoints(bp_class, name): try: address = AddressUtil.parse_address(name) address = ProcessMap.lookup_address(address) except gdb.error: warn("breakpoint setup failed: {:#x}".format(name)) return bp = bp_class(name, address) GlibcHeapTracerCommand.heap_breakpoints.append(bp) return self.clean(None) ok("Tracking malloc()") setup_breakpoints(TraceMallocBreakpoint, "malloc") ok("Tracking free()") setup_breakpoints(TraceFreeBreakpoint, "free") ok("Tracking realloc()") setup_breakpoints(TraceReallocBreakpoint, "realloc") ok("Tracking reallocarray()") setup_breakpoints(TraceReallocBreakpoint, "reallocarray") ok("Tracking calloc()") setup_breakpoints(TraceMallocBreakpoint, "calloc") ok("Tracking aligned_alloc()") setup_breakpoints(TraceMallocBreakpoint, "aligned_alloc") ok("Tracking memalign()") setup_breakpoints(TraceMallocBreakpoint, "memalign") ok("Tracking posix_memalign()") setup_breakpoints(TraceMallocBreakpoint, "posix_memalign") ok("Tracking valloc()") setup_breakpoints(TraceMallocBreakpoint, "valloc") EventHooking.gef_on_exit_hook(self.clean) return def clean(self, event): ok("{:s} - Cleaning up".format(Color.colorify("Heap-Analysis", "bold yellow"))) for bp in GlibcHeapTracerCommand.heap_breakpoints: try: bp.delete() except Exception: pass GlibcHeapTracerCommand.clear_disabled_breakpoints(force=True) GlibcHeapTracerCommand.heap_breakpoints = [] GlibcHeapTracerCommand.heap_allocated_list = [] GlibcHeapTracerCommand.heap_freed_list = [] GlibcHeapTracerCommand.heap_action_index = 0 try: EventHooking.gef_on_exit_unhook(self.clean) except Exception: pass return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): if args.dump_current_list: self.dump_tracked_allocations() return if args.reset: self.clean(None) return self.setup() return @register_command class SyscallSearchCommand(GenericCommand, BufferingOutput): """Search for the syscall number for a specified architecture.""" _cmdline_ = "syscall-search" _category_ = "01-g. Debugging Support - Syscall" _aliases_ = ["ss"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", dest="arch", help="specify the architecture. (default: current_arch.arch)") parser.add_argument("-m", dest="mode", help="specify the mode. (default: current_arch.mode)") parser.add_argument("search_pattern", metavar="SYSCALL_NAME|SYSCALL_NUM", nargs="?", default=".", help="syscall name or number to search. Regex is available.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="display prototype of syscall.") _syntax_ = parser.format_help() _example_ = [ '{0:s} -a X86 -m 64 "^writev?" # amd64', '{0:s} -a X86 -m 32 "^writev?" # i386 on amd64', '{0:s} -a X86 -m N32 "^writev?" # i386 native', '{0:s} -a ARM64 -m ARM "^writev?" # arm64', '{0:s} -a ARM -m 32 "^writev?" # arm32 on arm64', '{0:s} -a ARM -m N32 "^writev?" # arm32 native', '{0:s} -a MIPS -m 32 "^writev?" # mips32', '{0:s} -a MIPS -m n32 "^writev?" # mipsn32', '{0:s} -a MIPS -m 64 "^writev?" # mips64', '{0:s} -a PPC -m 32 "^writev?" # ppc32', '{0:s} -a PPC -m 64 "^writev?" # ppc64', '{0:s} -a SPARC -m 32 "^writev?" # sparc32', '{0:s} -a SPARC -m 32PLUS "^writev?" # sparc32plus', '{0:s} -a SPARC -m 64 "^writev?" # sparc64', '{0:s} -a RISCV -m 32 "^writev?" # riscv32', '{0:s} -a RISCV -m 64 "^writev?" # riscv64', '{0:s} -a S390X "^writev?" # s390x', '{0:s} -a SH4 "^writev?" # sh4', '{0:s} -a M68K -m 32 "^writev?" # m68k', '{0:s} -a ALPHA "^writev?" # alpha', '{0:s} -a HPPA -m 32 "^writev?" # hppa32', '{0:s} -a HPPA -m 64 "^writev?" # hppa64', '{0:s} -a OR1K "^writev?" # or1k', '{0:s} -a NIOS2 "^writev?" # nios2', '{0:s} -a MICROBLAZE "^writev?" # microblaze', '{0:s} -a XTENSA "^writev?" # xtensa', '{0:s} -a CRIS "^writev?" # cris', '{0:s} -a LOONGARCH -m 64 "^writev?" # loongarch64', '{0:s} -a ARC -m 32 "^writev?" # arc32', '{0:s} -a ARC -m 64 "^writev?" # arc64', '{0:s} -a CSKY "^writev?" # csky', ] _example_ = "\n".join(_example_).format(_cmdline_) def make_output(self, syscall_table, syscall_num, syscall_name_pattern): self.out.append(titlify("arch={:s}, mode={:s}".format(syscall_table.arch, syscall_table.mode))) fmt = "{:<17}{:s}" legend = ["Syscall Num", "Syscall Name"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for entry in syscall_table.nr_table.values(): if not re.search(syscall_name_pattern, entry.name): continue if syscall_num is not None and entry.nr != syscall_num: continue params = "" if self.args.verbose: params = "(" + ", ".join(entry.args_full) + ");" self.out.append("NR={:<#14x}{:s}{:s}".format(entry.nr, Color.boldify(entry.name), params)) return @parse_args def do_invoke(self, args): syscall_num = None syscall_name_pattern = ".*" target_arch = args.arch target_mode = args.mode # force fixing if target_arch and target_mode is None: arch_need_not_mode = ["SH4", "ALPHA", "OR1K", "NIOS2", "MICROBLAZE", "XTENSA", "CRIS", "CSKY"] if target_arch.upper() in arch_need_not_mode: target_arch = target_arch.upper() target_mode = target_arch.upper() if target_arch.upper() in ["S390X"]: target_arch = "S390X" target_mode = "64" if target_arch.upper() in ["ARM64", "AARCH64"]: target_arch = "ARM64" target_mode = "ARM" if target_arch.upper() in ["ARM", "ARM32"]: target_arch = "ARM" target_mode = "32" if target_arch.upper() in ["X86_64", "X86-64", "X64"]: target_arch = "X86" target_mode = "64" if target_arch.upper() in ["X86_32", "X86-32", "X86"]: target_arch = "X86" target_mode = "32" if target_arch.upper() in ["MIPS32"]: target_arch = "MIPS" target_mode = "32" if target_arch.upper() in ["MIPS64"]: target_arch = "MIPS" target_mode = "64" if target_arch.upper() in ["PPC32"]: target_arch = "PPC" target_mode = "32" if target_arch.upper() in ["PPC64"]: target_arch = "PPC" target_mode = "64" if target_arch.upper() in ["SPARC32"]: target_arch = "SPARC" target_mode = "32" if target_arch.upper() in ["SPARC64"]: target_arch = "SPARC" target_mode = "64" if target_arch.upper() in ["RISCV32"]: target_arch = "RISCV" target_mode = "32" if target_arch.upper() in ["RISCV64"]: target_arch = "RISCV" target_mode = "64" if target_arch.upper() in ["ARC32"]: target_arch = "ARC" target_mode = "32" if target_arch.upper() in ["ARC64"]: target_arch = "ARC" target_mode = "64" if target_arch.upper() in ["LOONGARCH"]: target_arch = "LOONGARCH" target_mode = "64" if target_arch.upper() in ["M68K"]: target_arch = "M68K" target_mode = "32" try: syscall_num = int(args.search_pattern, 0) except ValueError: syscall_name_pattern = args.search_pattern syscall_table = get_syscall_table(target_arch, target_mode) if syscall_table is None: err("Please specify the valid architecture.") self.usage() return self.out = [] self.make_output(syscall_table, syscall_num, syscall_name_pattern) self.print_output(check_terminal_size=True) return # System call table (linux-6.10) # [How to make] # clang-format --style='{BasedOnStyle: Google, ColumnLimit: 1000}' FILENAME | grep ^asmlinkage # `!` at the beginning of the line: manually fixed the argument information # `#` at the beginning of the line: excluded for reasons such as duplication # include/linux/syscalls.h syscall_defs = """ asmlinkage long sys_io_setup(unsigned nr_reqs, aio_context_t __user *ctx); asmlinkage long sys_io_destroy(aio_context_t ctx); !asmlinkage long sys_io_submit(aio_context_t ctx_id, long nr, struct iocb __user * __user *iocbpp); asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb, struct io_event __user *result); asmlinkage long sys_io_getevents(aio_context_t ctx_id, long min_nr, long nr, struct io_event __user *events, struct __kernel_timespec __user *timeout); asmlinkage long sys_io_getevents_time32(__u32 ctx_id, __s32 min_nr, __s32 nr, struct io_event __user *events, struct old_timespec32 __user *timeout); asmlinkage long sys_io_pgetevents(aio_context_t ctx_id, long min_nr, long nr, struct io_event __user *events, struct __kernel_timespec __user *timeout, const struct __aio_sigset __user *sig); asmlinkage long sys_io_pgetevents_time32(aio_context_t ctx_id, long min_nr, long nr, struct io_event __user *events, struct old_timespec32 __user *timeout, const struct __aio_sigset __user *sig); asmlinkage long sys_io_uring_setup(u32 entries, struct io_uring_params __user *p); asmlinkage long sys_io_uring_enter(unsigned int fd, u32 to_submit, u32 min_complete, u32 flags, const void __user *argp, size_t argsz); asmlinkage long sys_io_uring_register(unsigned int fd, unsigned int op, void __user *arg, unsigned int nr_args); asmlinkage long sys_setxattr(const char __user *path, const char __user *name, const void __user *value, size_t size, int flags); asmlinkage long sys_setxattrat(int dfd, const char __user *path, unsigned int at_flags, const char __user *name, const struct xattr_args __user *args, size_t size); asmlinkage long sys_lsetxattr(const char __user *path, const char __user *name, const void __user *value, size_t size, int flags); asmlinkage long sys_fsetxattr(int fd, const char __user *name, const void __user *value, size_t size, int flags); asmlinkage long sys_getxattr(const char __user *path, const char __user *name, void __user *value, size_t size); asmlinkage long sys_getxattrat(int dfd, const char __user *path, unsigned int at_flags, const char __user *name, struct xattr_args __user *args, size_t size); asmlinkage long sys_lgetxattr(const char __user *path, const char __user *name, void __user *value, size_t size); asmlinkage long sys_fgetxattr(int fd, const char __user *name, void __user *value, size_t size); asmlinkage long sys_listxattr(const char __user *path, char __user *list, size_t size); asmlinkage long sys_listxattrat(int dfd, const char __user *path, unsigned int at_flags, char __user *list, size_t size); asmlinkage long sys_llistxattr(const char __user *path, char __user *list, size_t size); asmlinkage long sys_flistxattr(int fd, char __user *list, size_t size); asmlinkage long sys_removexattr(const char __user *path, const char __user *name); asmlinkage long sys_removexattrat(int dfd, const char __user *path, unsigned int at_flags, const char __user *name); asmlinkage long sys_lremovexattr(const char __user *path, const char __user *name); asmlinkage long sys_fremovexattr(int fd, const char __user *name); asmlinkage long sys_file_getattr(int dfd, const char __user *filename, struct file_attr __user *attr, size_t usize, unsigned int at_flags); asmlinkage long sys_file_setattr(int dfd, const char __user *filename, struct file_attr __user *attr, size_t usize, unsigned int at_flags); asmlinkage long sys_getcwd(char __user *buf, unsigned long size); asmlinkage long sys_eventfd2(unsigned int count, int flags); asmlinkage long sys_epoll_create1(int flags); asmlinkage long sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event); asmlinkage long sys_epoll_pwait(int epfd, struct epoll_event __user *events, int maxevents, int timeout, const sigset_t __user *sigmask, size_t sigsetsize); asmlinkage long sys_epoll_pwait2(int epfd, struct epoll_event __user *events, int maxevents, const struct __kernel_timespec __user *timeout, const sigset_t __user *sigmask, size_t sigsetsize); asmlinkage long sys_dup(unsigned int fildes); asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags); asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg); asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg); asmlinkage long sys_inotify_init1(int flags); asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask); asmlinkage long sys_inotify_rm_watch(int fd, __s32 wd); asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); asmlinkage long sys_ioprio_set(int which, int who, int ioprio); asmlinkage long sys_ioprio_get(int which, int who); asmlinkage long sys_flock(unsigned int fd, unsigned int cmd); asmlinkage long sys_mknodat(int dfd, const char __user *filename, umode_t mode, unsigned dev); asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, umode_t mode); asmlinkage long sys_unlinkat(int dfd, const char __user *pathname, int flag); asmlinkage long sys_symlinkat(const char __user *oldname, int newdfd, const char __user *newname); asmlinkage long sys_linkat(int olddfd, const char __user *oldname, int newdfd, const char __user *newname, int flags); asmlinkage long sys_renameat(int olddfd, const char __user *oldname, int newdfd, const char __user *newname); asmlinkage long sys_umount(char __user *name, int flags); asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name, char __user *type, unsigned long flags, void __user *data); asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *put_old); asmlinkage long sys_statfs(const char __user *path, struct statfs __user *buf); asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf); asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user *buf); asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user *buf); asmlinkage long sys_statmount(const struct mnt_id_req __user *req, struct statmount __user *buf, size_t bufsize, unsigned int flags); asmlinkage long sys_listmount(const struct mnt_id_req __user *req, u64 __user *mnt_ids, size_t nr_mnt_ids, unsigned int flags); asmlinkage long sys_listns(const struct ns_id_req __user *req, u64 __user *ns_ids, size_t nr_ns_ids, unsigned int flags); asmlinkage long sys_truncate(const char __user *path, long length); asmlinkage long sys_ftruncate(unsigned int fd, off_t length); asmlinkage long sys_truncate64(const char __user *path, loff_t length); asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length); asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len); asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode); asmlinkage long sys_faccessat2(int dfd, const char __user *filename, int mode, int flags); asmlinkage long sys_chdir(const char __user *filename); asmlinkage long sys_fchdir(unsigned int fd); asmlinkage long sys_chroot(const char __user *filename); asmlinkage long sys_fchmod(unsigned int fd, umode_t mode); asmlinkage long sys_fchmodat(int dfd, const char __user *filename, umode_t mode); asmlinkage long sys_fchmodat2(int dfd, const char __user *filename, umode_t mode, unsigned int flags); asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group); asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, umode_t mode); asmlinkage long sys_openat2(int dfd, const char __user *filename, struct open_how __user *how, size_t size); asmlinkage long sys_close(unsigned int fd); asmlinkage long sys_close_range(unsigned int fd, unsigned int max_fd, unsigned int flags); asmlinkage long sys_vhangup(void); asmlinkage long sys_pipe2(int __user *fildes, int flags); asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t id, void __user *addr); asmlinkage long sys_quotactl_fd(unsigned int fd, unsigned int cmd, qid_t id, void __user *addr); asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, unsigned int count); asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, loff_t __user *result, unsigned int whence); asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int whence); asmlinkage long sys_read(unsigned int fd, char __user *buf, size_t count); asmlinkage long sys_write(unsigned int fd, const char __user *buf, size_t count); asmlinkage long sys_readv(unsigned long fd, const struct iovec __user *vec, unsigned long vlen); asmlinkage long sys_writev(unsigned long fd, const struct iovec __user *vec, unsigned long vlen); asmlinkage long sys_pread64(unsigned int fd, char __user *buf, size_t count, loff_t pos); asmlinkage long sys_pwrite64(unsigned int fd, const char __user *buf, size_t count, loff_t pos); asmlinkage long sys_preadv(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h); asmlinkage long sys_pwritev(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h); asmlinkage long sys_sendfile64(int out_fd, int in_fd, loff_t __user *offset, size_t count); !asmlinkage long sys_pselect6(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct __kernel_timespec __user *tsp, void __user *sig); !asmlinkage long sys_pselect6_time32(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct old_timespec32 __user *tsp, void __user *sig); !asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds, struct __kernel_timespec __user *tsp, const sigset_t __user *sigmask, size_t sigsetsize); !asmlinkage long sys_ppoll_time32(struct pollfd __user *ufds, unsigned int nfds, struct old_timespec32 __user *tsp, const sigset_t __user *sigmask, size_t sigsetsize); asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, size_t sizemask, int flags); asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov, unsigned long nr_segs, unsigned int flags); asmlinkage long sys_splice(int fd_in, loff_t __user *off_in, int fd_out, loff_t __user *off_out, size_t len, unsigned int flags); asmlinkage long sys_tee(int fdin, int fdout, size_t len, unsigned int flags); asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf, int bufsiz); asmlinkage long sys_newfstatat(int dfd, const char __user *filename, struct stat __user *statbuf, int flag); asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf); asmlinkage long sys_fstat64(unsigned long fd, struct stat64 __user *statbuf); asmlinkage long sys_fstatat64(int dfd, const char __user *filename, struct stat64 __user *statbuf, int flag); asmlinkage long sys_sync(void); asmlinkage long sys_fsync(unsigned int fd); asmlinkage long sys_fdatasync(unsigned int fd); asmlinkage long sys_sync_file_range2(int fd, unsigned int flags, loff_t offset, loff_t nbytes); asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes, unsigned int flags); asmlinkage long sys_timerfd_create(int clockid, int flags); asmlinkage long sys_timerfd_settime(int ufd, int flags, const struct __kernel_itimerspec __user *utmr, struct __kernel_itimerspec __user *otmr); asmlinkage long sys_timerfd_gettime(int ufd, struct __kernel_itimerspec __user *otmr); asmlinkage long sys_timerfd_gettime32(int ufd, struct old_itimerspec32 __user *otmr); asmlinkage long sys_timerfd_settime32(int ufd, int flags, const struct old_itimerspec32 __user *utmr, struct old_itimerspec32 __user *otmr); asmlinkage long sys_utimensat(int dfd, const char __user *filename, struct __kernel_timespec __user *utimes, int flags); asmlinkage long sys_utimensat_time32(unsigned int dfd, const char __user *filename, struct old_timespec32 __user *t, int flags); asmlinkage long sys_acct(const char __user *name); asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr); asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data); asmlinkage long sys_personality(unsigned int personality); asmlinkage long sys_exit(int error_code); asmlinkage long sys_exit_group(int error_code); asmlinkage long sys_waitid(int which, pid_t pid, struct siginfo __user *infop, int options, struct rusage __user *ru); asmlinkage long sys_set_tid_address(int __user *tidptr); asmlinkage long sys_unshare(unsigned long unshare_flags); asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, const struct __kernel_timespec __user *utime, u32 __user *uaddr2, u32 val3); asmlinkage long sys_futex_time32(u32 __user *uaddr, int op, u32 val, const struct old_timespec32 __user *utime, u32 __user *uaddr2, u32 val3); asmlinkage long sys_get_robust_list(int pid, struct robust_list_head __user *__user *head_ptr, size_t __user *len_ptr); asmlinkage long sys_set_robust_list(struct robust_list_head __user *head, size_t len); asmlinkage long sys_futex_waitv(struct futex_waitv __user *waiters, unsigned int nr_futexes, unsigned int flags, struct __kernel_timespec __user *timeout, clockid_t clockid); asmlinkage long sys_futex_wake(void __user *uaddr, unsigned long mask, int nr, unsigned int flags); asmlinkage long sys_futex_wait(void __user *uaddr, unsigned long val, unsigned long mask, unsigned int flags, struct __kernel_timespec __user *timespec, clockid_t clockid); asmlinkage long sys_futex_requeue(struct futex_waitv __user *waiters, unsigned int flags, int nr_wake, int nr_requeue); asmlinkage long sys_nanosleep(struct __kernel_timespec __user *rqtp, struct __kernel_timespec __user *rmtp); asmlinkage long sys_nanosleep_time32(struct old_timespec32 __user *rqtp, struct old_timespec32 __user *rmtp); asmlinkage long sys_getitimer(int which, struct __kernel_old_itimerval __user *value); asmlinkage long sys_setitimer(int which, struct __kernel_old_itimerval __user *value, struct __kernel_old_itimerval __user *ovalue); asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment __user *segments, unsigned long flags); asmlinkage long sys_init_module(void __user *umod, unsigned long len, const char __user *uargs); asmlinkage long sys_delete_module(const char __user *name_user, unsigned int flags); asmlinkage long sys_timer_create(clockid_t which_clock, struct sigevent __user *timer_event_spec, timer_t __user *created_timer_id); asmlinkage long sys_timer_gettime(timer_t timer_id, struct __kernel_itimerspec __user *setting); asmlinkage long sys_timer_getoverrun(timer_t timer_id); asmlinkage long sys_timer_settime(timer_t timer_id, int flags, const struct __kernel_itimerspec __user *new_setting, struct __kernel_itimerspec __user *old_setting); asmlinkage long sys_timer_delete(timer_t timer_id); asmlinkage long sys_clock_settime(clockid_t which_clock, const struct __kernel_timespec __user *tp); asmlinkage long sys_clock_gettime(clockid_t which_clock, struct __kernel_timespec __user *tp); asmlinkage long sys_clock_getres(clockid_t which_clock, struct __kernel_timespec __user *tp); asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags, const struct __kernel_timespec __user *rqtp, struct __kernel_timespec __user *rmtp); asmlinkage long sys_timer_gettime32(timer_t timer_id, struct old_itimerspec32 __user *setting); asmlinkage long sys_timer_settime32(timer_t timer_id, int flags, struct old_itimerspec32 __user *new, struct old_itimerspec32 __user *old); asmlinkage long sys_clock_settime32(clockid_t which_clock, struct old_timespec32 __user *tp); asmlinkage long sys_clock_gettime32(clockid_t which_clock, struct old_timespec32 __user *tp); asmlinkage long sys_clock_getres_time32(clockid_t which_clock, struct old_timespec32 __user *tp); asmlinkage long sys_clock_nanosleep_time32(clockid_t which_clock, int flags, struct old_timespec32 __user *rqtp, struct old_timespec32 __user *rmtp); asmlinkage long sys_syslog(int type, char __user *buf, int len); asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, unsigned long data); asmlinkage long sys_sched_setparam(pid_t pid, struct sched_param __user *param); asmlinkage long sys_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param); asmlinkage long sys_sched_getscheduler(pid_t pid); asmlinkage long sys_sched_getparam(pid_t pid, struct sched_param __user *param); asmlinkage long sys_sched_setaffinity(pid_t pid, unsigned int len, unsigned long __user *user_mask_ptr); asmlinkage long sys_sched_getaffinity(pid_t pid, unsigned int len, unsigned long __user *user_mask_ptr); asmlinkage long sys_sched_yield(void); asmlinkage long sys_sched_get_priority_max(int policy); asmlinkage long sys_sched_get_priority_min(int policy); asmlinkage long sys_sched_rr_get_interval(pid_t pid, struct __kernel_timespec __user *interval); asmlinkage long sys_sched_rr_get_interval_time32(pid_t pid, struct old_timespec32 __user *interval); asmlinkage long sys_restart_syscall(void); asmlinkage long sys_kill(pid_t pid, int sig); asmlinkage long sys_tkill(pid_t pid, int sig); asmlinkage long sys_tgkill(pid_t tgid, pid_t pid, int sig); asmlinkage long sys_sigaltstack(const struct sigaltstack __user *uss, struct sigaltstack __user *uoss); asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize); !asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act, struct sigaction __user *oact, size_t sigsetsize); asmlinkage long sys_rt_sigprocmask(int how, sigset_t __user *set, sigset_t __user *oset, size_t sigsetsize); asmlinkage long sys_rt_sigpending(sigset_t __user *set, size_t sigsetsize); asmlinkage long sys_rt_sigtimedwait(const sigset_t __user *uthese, siginfo_t __user *uinfo, const struct __kernel_timespec __user *uts, size_t sigsetsize); asmlinkage long sys_rt_sigtimedwait_time32(const sigset_t __user *uthese, siginfo_t __user *uinfo, const struct old_timespec32 __user *uts, size_t sigsetsize); asmlinkage long sys_rt_sigqueueinfo(pid_t pid, int sig, siginfo_t __user *uinfo); asmlinkage long sys_setpriority(int which, int who, int niceval); asmlinkage long sys_getpriority(int which, int who); asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user *arg); asmlinkage long sys_setregid(gid_t rgid, gid_t egid); asmlinkage long sys_setgid(gid_t gid); asmlinkage long sys_setreuid(uid_t ruid, uid_t euid); asmlinkage long sys_setuid(uid_t uid); asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid); asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid); asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid); asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid); asmlinkage long sys_setfsuid(uid_t uid); asmlinkage long sys_setfsgid(gid_t gid); asmlinkage long sys_times(struct tms __user *tbuf); asmlinkage long sys_setpgid(pid_t pid, pid_t pgid); asmlinkage long sys_getpgid(pid_t pid); asmlinkage long sys_getsid(pid_t pid); asmlinkage long sys_setsid(void); asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist); asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist); asmlinkage long sys_newuname(struct new_utsname __user *name); asmlinkage long sys_sethostname(char __user *name, int len); asmlinkage long sys_setdomainname(char __user *name, int len); asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit __user *rlim); asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim); asmlinkage long sys_getrusage(int who, struct rusage __user *ru); asmlinkage long sys_umask(int mask); asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); asmlinkage long sys_getcpu(unsigned __user *cpu, unsigned __user *node, struct getcpu_cache __user *cache); asmlinkage long sys_gettimeofday(struct __kernel_old_timeval __user *tv, struct timezone __user *tz); asmlinkage long sys_settimeofday(struct __kernel_old_timeval __user *tv, struct timezone __user *tz); asmlinkage long sys_adjtimex(struct __kernel_timex __user *txc_p); asmlinkage long sys_adjtimex_time32(struct old_timex32 __user *txc_p); asmlinkage long sys_getpid(void); asmlinkage long sys_getppid(void); asmlinkage long sys_getuid(void); asmlinkage long sys_geteuid(void); asmlinkage long sys_getgid(void); asmlinkage long sys_getegid(void); asmlinkage long sys_gettid(void); asmlinkage long sys_sysinfo(struct sysinfo __user *info); asmlinkage long sys_mq_open(const char __user *name, int oflag, umode_t mode, struct mq_attr __user *attr); asmlinkage long sys_mq_unlink(const char __user *name); asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct __kernel_timespec __user *abs_timeout); asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespec __user *abs_timeout); asmlinkage long sys_mq_notify(mqd_t mqdes, const struct sigevent __user *notification); asmlinkage long sys_mq_getsetattr(mqd_t mqdes, const struct mq_attr __user *mqstat, struct mq_attr __user *omqstat); asmlinkage long sys_mq_timedreceive_time32(mqd_t mqdes, char __user *u_msg_ptr, unsigned int msg_len, unsigned int __user *u_msg_prio, const struct old_timespec32 __user *u_abs_timeout); asmlinkage long sys_mq_timedsend_time32(mqd_t mqdes, const char __user *u_msg_ptr, unsigned int msg_len, unsigned int msg_prio, const struct old_timespec32 __user *u_abs_timeout); asmlinkage long sys_msgget(key_t key, int msgflg); asmlinkage long sys_old_msgctl(int msqid, int cmd, struct msqid_ds __user *buf); asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf); asmlinkage long sys_msgrcv(int msqid, struct msgbuf __user *msgp, size_t msgsz, long msgtyp, int msgflg); asmlinkage long sys_msgsnd(int msqid, struct msgbuf __user *msgp, size_t msgsz, int msgflg); asmlinkage long sys_semget(key_t key, int nsems, int semflg); asmlinkage long sys_semctl(int semid, int semnum, int cmd, unsigned long arg); asmlinkage long sys_old_semctl(int semid, int semnum, int cmd, unsigned long arg); asmlinkage long sys_semtimedop(int semid, struct sembuf __user *sops, unsigned nsops, const struct __kernel_timespec __user *timeout); asmlinkage long sys_semtimedop_time32(int semid, struct sembuf __user *sops, unsigned nsops, const struct old_timespec32 __user *timeout); asmlinkage long sys_semop(int semid, struct sembuf __user *sops, unsigned nsops); asmlinkage long sys_shmget(key_t key, size_t size, int flag); asmlinkage long sys_old_shmctl(int shmid, int cmd, struct shmid_ds __user *buf); asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf); asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg); asmlinkage long sys_shmdt(char __user *shmaddr); !asmlinkage long sys_socket(int family, int type, int protocol); !asmlinkage long sys_socketpair(int family, int type, int protocol, int __user *usockvec); !asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen); !asmlinkage long sys_listen(int fd, int backlog); !asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen); !asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen); !asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len); !asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len); !asmlinkage long sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, struct sockaddr __user *addr, int addr_len); !asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags, struct sockaddr __user *addr, int __user *addr_len); asmlinkage long sys_setsockopt(int fd, int level, int optname, char __user *optval, int optlen); asmlinkage long sys_getsockopt(int fd, int level, int optname, char __user *optval, int __user *optlen); !asmlinkage long sys_shutdown(int fd, int how); asmlinkage long sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned flags); asmlinkage long sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned flags); asmlinkage long sys_readahead(int fd, loff_t offset, size_t count); asmlinkage long sys_brk(unsigned long brk); asmlinkage long sys_munmap(unsigned long addr, size_t len); asmlinkage long sys_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr); asmlinkage long sys_add_key(const char __user *_type, const char __user *_description, const void __user *_payload, size_t plen, key_serial_t destringid); asmlinkage long sys_request_key(const char __user *_type, const char __user *_description, const char __user *_callout_info, key_serial_t destringid); asmlinkage long sys_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); #asmlinkage long sys_clone(unsigned long, unsigned long, int __user *, unsigned long, int __user *); #asmlinkage long sys_clone(unsigned long, unsigned long, int, int __user *, int __user *, unsigned long); #asmlinkage long sys_clone(unsigned long, unsigned long, int __user *, int __user *, unsigned long); asmlinkage long sys_clone3(struct clone_args __user *uargs, size_t size); asmlinkage long sys_execve(const char __user *filename, const char __user *const __user *argv, const char __user *const __user *envp); asmlinkage long sys_fadvise64_64(int fd, loff_t offset, loff_t len, int advice); asmlinkage long sys_swapon(const char __user *specialfile, int swap_flags); asmlinkage long sys_swapoff(const char __user *specialfile); asmlinkage long sys_mprotect(unsigned long start, size_t len, unsigned long prot); asmlinkage long sys_msync(unsigned long start, size_t len, int flags); asmlinkage long sys_mlock(unsigned long start, size_t len); asmlinkage long sys_munlock(unsigned long start, size_t len); asmlinkage long sys_mlockall(int flags); asmlinkage long sys_munlockall(void); asmlinkage long sys_mincore(unsigned long start, size_t len, unsigned char __user *vec); asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior); asmlinkage long sys_process_madvise(int pidfd, const struct iovec __user *vec, size_t vlen, int behavior, unsigned int flags); asmlinkage long sys_process_mrelease(int pidfd, unsigned int flags); asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size, unsigned long prot, unsigned long pgoff, unsigned long flags); asmlinkage long sys_mseal(unsigned long start, size_t len, unsigned long flags); asmlinkage long sys_mbind(unsigned long start, unsigned long len, unsigned long mode, const unsigned long __user *nmask, unsigned long maxnode, unsigned flags); asmlinkage long sys_get_mempolicy(int __user *policy, unsigned long __user *nmask, unsigned long maxnode, unsigned long addr, unsigned long flags); asmlinkage long sys_set_mempolicy(int mode, const unsigned long __user *nmask, unsigned long maxnode); asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode, const unsigned long __user *from, const unsigned long __user *to); asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages, const void __user *__user *pages, const int __user *nodes, int __user *status, int flags); asmlinkage long sys_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t __user *uinfo); asmlinkage long sys_perf_event_open(struct perf_event_attr __user *attr_uptr, pid_t pid, int cpu, int group_fd, unsigned long flags); !asmlinkage long sys_accept4(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen, int flags); asmlinkage long sys_recvmmsg(int fd, struct mmsghdr __user *msg, unsigned int vlen, unsigned flags, struct __kernel_timespec __user *timeout); asmlinkage long sys_recvmmsg_time32(int fd, struct mmsghdr __user *msg, unsigned int vlen, unsigned flags, struct old_timespec32 __user *timeout); asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, int options, struct rusage __user *ru); asmlinkage long sys_prlimit64(pid_t pid, unsigned int resource, const struct rlimit64 __user *new_rlim, struct rlimit64 __user *old_rlim); asmlinkage long sys_fanotify_init(unsigned int flags, unsigned int event_f_flags); #asmlinkage long sys_fanotify_mark(int fanotify_fd, unsigned int flags, unsigned int mask_1, unsigned int mask_2, int dfd, const char __user *pathname); #asmlinkage long sys_fanotify_mark(int fanotify_fd, unsigned int flags, u64 mask, int fd, const char __user *pathname); asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name, struct file_handle __user *handle, void __user *mnt_id, int flag); asmlinkage long sys_open_by_handle_at(int mountdirfd, struct file_handle __user *handle, int flags); asmlinkage long sys_clock_adjtime(clockid_t which_clock, struct __kernel_timex __user *tx); asmlinkage long sys_clock_adjtime32(clockid_t which_clock, struct old_timex32 __user *tx); asmlinkage long sys_syncfs(int fd); asmlinkage long sys_setns(int fd, int nstype); asmlinkage long sys_pidfd_open(pid_t pid, unsigned int flags); asmlinkage long sys_sendmmsg(int fd, struct mmsghdr __user *msg, unsigned int vlen, unsigned flags); asmlinkage long sys_process_vm_readv(pid_t pid, const struct iovec __user *lvec, unsigned long liovcnt, const struct iovec __user *rvec, unsigned long riovcnt, unsigned long flags); asmlinkage long sys_process_vm_writev(pid_t pid, const struct iovec __user *lvec, unsigned long liovcnt, const struct iovec __user *rvec, unsigned long riovcnt, unsigned long flags); asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2); asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags); asmlinkage long sys_sched_setattr(pid_t pid, struct sched_attr __user *attr, unsigned int flags); asmlinkage long sys_sched_getattr(pid_t pid, struct sched_attr __user *attr, unsigned int size, unsigned int flags); asmlinkage long sys_renameat2(int olddfd, const char __user *oldname, int newdfd, const char __user *newname, unsigned int flags); asmlinkage long sys_seccomp(unsigned int op, unsigned int flags, void __user *uargs); asmlinkage long sys_getrandom(char __user *buf, size_t count, unsigned int flags); asmlinkage long sys_memfd_create(const char __user *uname_ptr, unsigned int flags); asmlinkage long sys_bpf(int cmd, union bpf_attr __user *attr, unsigned int size); asmlinkage long sys_execveat(int dfd, const char __user *filename, const char __user *const __user *argv, const char __user *const __user *envp, int flags); asmlinkage long sys_userfaultfd(int flags); asmlinkage long sys_membarrier(int cmd, unsigned int flags, int cpu_id); asmlinkage long sys_mlock2(unsigned long start, size_t len, int flags); asmlinkage long sys_copy_file_range(int fd_in, loff_t __user *off_in, int fd_out, loff_t __user *off_out, size_t len, unsigned int flags); asmlinkage long sys_preadv2(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h, rwf_t flags); asmlinkage long sys_pwritev2(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h, rwf_t flags); asmlinkage long sys_pkey_mprotect(unsigned long start, size_t len, unsigned long prot, int pkey); asmlinkage long sys_pkey_alloc(unsigned long flags, unsigned long init_val); asmlinkage long sys_pkey_free(int pkey); asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags, unsigned mask, struct statx __user *buffer); asmlinkage long sys_rseq(struct rseq __user *rseq, uint32_t rseq_len, int flags, uint32_t sig); asmlinkage long sys_open_tree(int dfd, const char __user *path, unsigned flags); asmlinkage long sys_open_tree_attr(int dfd, const char __user *path, unsigned flags, struct mount_attr __user *uattr, size_t usize); asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path, int to_dfd, const char __user *to_path, unsigned int ms_flags); asmlinkage long sys_mount_setattr(int dfd, const char __user *path, unsigned int flags, struct mount_attr __user *uattr, size_t usize); asmlinkage long sys_fsopen(const char __user *fs_name, unsigned int flags); asmlinkage long sys_fsconfig(int fs_fd, unsigned int cmd, const char __user *key, const void __user *value, int aux); asmlinkage long sys_fsmount(int fs_fd, unsigned int flags, unsigned int ms_flags); asmlinkage long sys_fspick(int dfd, const char __user *path, unsigned int flags); asmlinkage long sys_pidfd_send_signal(int pidfd, int sig, siginfo_t __user *info, unsigned int flags); asmlinkage long sys_pidfd_getfd(int pidfd, int fd, unsigned int flags); asmlinkage long sys_landlock_create_ruleset(const struct landlock_ruleset_attr __user *attr, size_t size, __u32 flags); asmlinkage long sys_landlock_add_rule(int ruleset_fd, enum landlock_rule_type rule_type, const void __user *rule_attr, __u32 flags); asmlinkage long sys_landlock_restrict_self(int ruleset_fd, __u32 flags); asmlinkage long sys_memfd_secret(unsigned int flags); asmlinkage long sys_set_mempolicy_home_node(unsigned long start, unsigned long len, unsigned long home_node, unsigned long flags); asmlinkage long sys_cachestat(unsigned int fd, struct cachestat_range __user *cstat_range, struct cachestat __user *cstat, unsigned int flags); asmlinkage long sys_map_shadow_stack(unsigned long addr, unsigned long size, unsigned int flags); asmlinkage long sys_lsm_get_self_attr(unsigned int attr, struct lsm_ctx __user *ctx, u32 __user *size, u32 flags); asmlinkage long sys_lsm_set_self_attr(unsigned int attr, struct lsm_ctx __user *ctx, u32 size, u32 flags); asmlinkage long sys_lsm_list_modules(u64 __user *ids, u32 __user *size, u32 flags); asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); asmlinkage long sys_uretprobe(void); asmlinkage long sys_uprobe(void); asmlinkage long sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, void __user *buf); asmlinkage long sys_pciconfig_write(unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, void __user *buf); asmlinkage long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn); asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus); asmlinkage long sys_spu_create(const char __user *name, unsigned int flags, umode_t mode, int fd); asmlinkage long sys_open(const char __user *filename, int flags, umode_t mode); asmlinkage long sys_link(const char __user *oldname, const char __user *newname); asmlinkage long sys_unlink(const char __user *pathname); asmlinkage long sys_mknod(const char __user *filename, umode_t mode, unsigned dev); asmlinkage long sys_chmod(const char __user *filename, umode_t mode); asmlinkage long sys_chown(const char __user *filename, uid_t user, gid_t group); asmlinkage long sys_mkdir(const char __user *pathname, umode_t mode); asmlinkage long sys_rmdir(const char __user *pathname); asmlinkage long sys_lchown(const char __user *filename, uid_t user, gid_t group); asmlinkage long sys_access(const char __user *filename, int mode); asmlinkage long sys_rename(const char __user *oldname, const char __user *newname); asmlinkage long sys_symlink(const char __user *old, const char __user *new); asmlinkage long sys_stat64(const char __user *filename, struct stat64 __user *statbuf); asmlinkage long sys_lstat64(const char __user *filename, struct stat64 __user *statbuf); asmlinkage long sys_pipe(int __user *fildes); asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd); asmlinkage long sys_epoll_create(int size); asmlinkage long sys_inotify_init(void); asmlinkage long sys_eventfd(unsigned int count); asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemask); asmlinkage long sys_sendfile(int out_fd, int in_fd, off_t __user *offset, size_t count); asmlinkage long sys_newstat(const char __user *filename, struct stat __user *statbuf); asmlinkage long sys_newlstat(const char __user *filename, struct stat __user *statbuf); asmlinkage long sys_fadvise64(int fd, loff_t offset, size_t len, int advice); asmlinkage long sys_alarm(unsigned int seconds); asmlinkage long sys_getpgrp(void); asmlinkage long sys_pause(void); asmlinkage long sys_time(__kernel_old_time_t __user *tloc); asmlinkage long sys_time32(old_time32_t __user *tloc); asmlinkage long sys_utime(char __user *filename, struct utimbuf __user *times); asmlinkage long sys_utimes(char __user *filename, struct __kernel_old_timeval __user *utimes); asmlinkage long sys_futimesat(int dfd, const char __user *filename, struct __kernel_old_timeval __user *utimes); asmlinkage long sys_futimesat_time32(unsigned int dfd, const char __user *filename, struct old_timeval32 __user *t); asmlinkage long sys_utime32(const char __user *filename, struct old_utimbuf32 __user *t); asmlinkage long sys_utimes_time32(const char __user *filename, struct old_timeval32 __user *t); asmlinkage long sys_creat(const char __user *pathname, umode_t mode); asmlinkage long sys_getdents(unsigned int fd, struct linux_dirent __user *dirent, unsigned int count); asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct __kernel_old_timeval __user *tvp); asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, int timeout); asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, int maxevents, int timeout); asmlinkage long sys_ustat(unsigned dev, struct ustat __user *ubuf); asmlinkage long sys_vfork(void); asmlinkage long sys_recv(int, void __user *, size_t, unsigned); asmlinkage long sys_send(int, void __user *, size_t, unsigned); asmlinkage long sys_oldumount(char __user *name); asmlinkage long sys_uselib(const char __user *library); asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2); asmlinkage long sys_fork(void); asmlinkage long sys_stime(__kernel_old_time_t __user *tptr); asmlinkage long sys_stime32(old_time32_t __user *tptr); asmlinkage long sys_sigpending(old_sigset_t __user *uset); asmlinkage long sys_sigprocmask(int how, old_sigset_t __user *set, old_sigset_t __user *oset); asmlinkage long sys_sigsuspend(old_sigset_t mask); #asmlinkage long sys_sigsuspend(int unused1, int unused2, old_sigset_t mask); asmlinkage long sys_sigaction(int, const struct old_sigaction __user *, struct old_sigaction __user *); asmlinkage long sys_sgetmask(void); asmlinkage long sys_ssetmask(int newmask); asmlinkage long sys_signal(int sig, __sighandler_t handler); asmlinkage long sys_nice(int increment); asmlinkage long sys_kexec_file_load(int kernel_fd, int initrd_fd, unsigned long cmdline_len, const char __user *cmdline_ptr, unsigned long flags); asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options); asmlinkage long sys_chown16(const char __user *filename, old_uid_t user, old_gid_t group); asmlinkage long sys_lchown16(const char __user *filename, old_uid_t user, old_gid_t group); asmlinkage long sys_fchown16(unsigned int fd, old_uid_t user, old_gid_t group); asmlinkage long sys_setregid16(old_gid_t rgid, old_gid_t egid); asmlinkage long sys_setgid16(old_gid_t gid); asmlinkage long sys_setreuid16(old_uid_t ruid, old_uid_t euid); asmlinkage long sys_setuid16(old_uid_t uid); asmlinkage long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid); asmlinkage long sys_getresuid16(old_uid_t __user *ruid, old_uid_t __user *euid, old_uid_t __user *suid); asmlinkage long sys_setresgid16(old_gid_t rgid, old_gid_t egid, old_gid_t sgid); asmlinkage long sys_getresgid16(old_gid_t __user *rgid, old_gid_t __user *egid, old_gid_t __user *sgid); asmlinkage long sys_setfsuid16(old_uid_t uid); asmlinkage long sys_setfsgid16(old_gid_t gid); asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t __user *grouplist); asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t __user *grouplist); asmlinkage long sys_getuid16(void); asmlinkage long sys_geteuid16(void); asmlinkage long sys_getgid16(void); asmlinkage long sys_getegid16(void); asmlinkage long sys_socketcall(int call, unsigned long __user *args); asmlinkage long sys_stat(const char __user *filename, struct __old_kernel_stat __user *statbuf); asmlinkage long sys_lstat(const char __user *filename, struct __old_kernel_stat __user *statbuf); asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat __user *statbuf); asmlinkage long sys_readlink(const char __user *path, char __user *buf, int bufsiz); asmlinkage long sys_old_select(struct sel_arg_struct __user *arg); asmlinkage long sys_old_readdir(unsigned int, struct old_linux_dirent __user *, unsigned int); asmlinkage long sys_gethostname(char __user *name, int len); asmlinkage long sys_uname(struct old_utsname __user *); asmlinkage long sys_olduname(struct oldold_utsname __user *); asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *rlim); asmlinkage long sys_ipc(unsigned int call, int first, unsigned long second, unsigned long third, void __user *ptr, long fifth); asmlinkage long sys_mmap_pgoff(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff); asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg); #asmlinkage long sys_ni_syscall(void); asmlinkage long sys_ni_posix_timers(void); """ # include/linux/compat.h syscall_defs_compat = """ asmlinkage long compat_sys_io_setup(unsigned nr_reqs, u32 __user *ctx32p); asmlinkage long compat_sys_io_submit(compat_aio_context_t ctx_id, int nr, u32 __user *iocb); !asmlinkage long compat_sys_io_pgetevents(compat_aio_context_t ctx_id, compat_long_t min_nr, compat_long_t nr, struct io_event __user *events, struct old_timespec32 __user *timeout, const struct __compat_aio_sigset __user *usig); # codespell:ignore !asmlinkage long compat_sys_io_pgetevents_time64(compat_aio_context_t ctx_id, compat_long_t min_nr, compat_long_t nr, struct io_event __user *events, struct __kernel_timespec __user *timeout, const struct __compat_aio_sigset __user *usig); # codespell:ignore asmlinkage long compat_sys_epoll_pwait(int epfd, struct epoll_event __user *events, int maxevents, int timeout, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); asmlinkage long compat_sys_epoll_pwait2(int epfd, struct epoll_event __user *events, int maxevents, const struct __kernel_timespec __user *timeout, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); asmlinkage long compat_sys_fcntl(unsigned int fd, unsigned int cmd, compat_ulong_t arg); asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, compat_ulong_t arg); asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, compat_ulong_t arg); asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf); asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf); asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf); asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf); asmlinkage long compat_sys_truncate(const char __user *, compat_off_t); asmlinkage long compat_sys_ftruncate(unsigned int, compat_off_t); asmlinkage long compat_sys_openat(int dfd, const char __user *filename, int flags, umode_t mode); asmlinkage long compat_sys_getdents(unsigned int fd, struct compat_linux_dirent __user *dirent, unsigned int count); asmlinkage long compat_sys_lseek(unsigned int, compat_off_t, unsigned int); asmlinkage ssize_t compat_sys_preadv(compat_ulong_t fd, const struct iovec __user *vec, compat_ulong_t vlen, u32 pos_low, u32 pos_high); asmlinkage ssize_t compat_sys_pwritev(compat_ulong_t fd, const struct iovec __user *vec, compat_ulong_t vlen, u32 pos_low, u32 pos_high); asmlinkage long compat_sys_preadv64(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, loff_t pos); asmlinkage long compat_sys_pwritev64(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, loff_t pos); asmlinkage long compat_sys_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, compat_size_t count); asmlinkage long compat_sys_sendfile64(int out_fd, int in_fd, compat_loff_t __user *offset, compat_size_t count); asmlinkage long compat_sys_pselect6_time32(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, compat_ulong_t __user *exp, struct old_timespec32 __user *tsp, void __user *sig); asmlinkage long compat_sys_pselect6_time64(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, compat_ulong_t __user *exp, struct __kernel_timespec __user *tsp, void __user *sig); asmlinkage long compat_sys_ppoll_time32(struct pollfd __user *ufds, unsigned int nfds, struct old_timespec32 __user *tsp, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); asmlinkage long compat_sys_ppoll_time64(struct pollfd __user *ufds, unsigned int nfds, struct __kernel_timespec __user *tsp, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); asmlinkage long compat_sys_signalfd4(int ufd, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize, int flags); asmlinkage long compat_sys_newfstatat(unsigned int dfd, const char __user *filename, struct compat_stat __user *statbuf, int flag); asmlinkage long compat_sys_newfstat(unsigned int fd, struct compat_stat __user *statbuf); !asmlinkage long compat_sys_waitid(int which, compat_pid_t pid, struct compat_siginfo __user *waitid, int options, struct compat_rusage __user *uru); asmlinkage long compat_sys_set_robust_list(struct compat_robust_list_head __user *head, compat_size_t len); asmlinkage long compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, compat_size_t __user *len_ptr); asmlinkage long compat_sys_getitimer(int which, struct old_itimerval32 __user *it); asmlinkage long compat_sys_setitimer(int which, struct old_itimerval32 __user *in, struct old_itimerval32 __user *out); !asmlinkage long compat_sys_kexec_load(compat_ulong_t entry, compat_ulong_t nr_segments, struct compat_kexec_segment __user *segments, compat_ulong_t flags); asmlinkage long compat_sys_timer_create(clockid_t which_clock, struct compat_sigevent __user *timer_event_spec, timer_t __user *created_timer_id); asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, compat_long_t addr, compat_long_t data); asmlinkage long compat_sys_sched_setaffinity(compat_pid_t pid, unsigned int len, compat_ulong_t __user *user_mask_ptr); asmlinkage long compat_sys_sched_getaffinity(compat_pid_t pid, unsigned int len, compat_ulong_t __user *user_mask_ptr); asmlinkage long compat_sys_sigaltstack(const compat_stack_t __user *uss_ptr, compat_stack_t __user *uoss_ptr); asmlinkage long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat_size_t sigsetsize); !asmlinkage long compat_sys_rt_sigaction(int sig, const struct compat_sigaction __user *act, struct compat_sigaction __user *oact, compat_size_t sigsetsize); asmlinkage long compat_sys_rt_sigprocmask(int how, compat_sigset_t __user *set, compat_sigset_t __user *oset, compat_size_t sigsetsize); asmlinkage long compat_sys_rt_sigpending(compat_sigset_t __user *uset, compat_size_t sigsetsize); asmlinkage long compat_sys_rt_sigtimedwait_time32(compat_sigset_t __user *uthese, struct compat_siginfo __user *uinfo, struct old_timespec32 __user *uts, compat_size_t sigsetsize); asmlinkage long compat_sys_rt_sigtimedwait_time64(compat_sigset_t __user *uthese, struct compat_siginfo __user *uinfo, struct __kernel_timespec __user *uts, compat_size_t sigsetsize); asmlinkage long compat_sys_rt_sigqueueinfo(compat_pid_t pid, int sig, struct compat_siginfo __user *uinfo); asmlinkage long compat_sys_times(struct compat_tms __user *tbuf); asmlinkage long compat_sys_getrlimit(unsigned int resource, struct compat_rlimit __user *rlim); asmlinkage long compat_sys_setrlimit(unsigned int resource, struct compat_rlimit __user *rlim); asmlinkage long compat_sys_getrusage(int who, struct compat_rusage __user *ru); asmlinkage long compat_sys_gettimeofday(struct old_timeval32 __user *tv, struct timezone __user *tz); asmlinkage long compat_sys_settimeofday(struct old_timeval32 __user *tv, struct timezone __user *tz); asmlinkage long compat_sys_sysinfo(struct compat_sysinfo __user *info); asmlinkage long compat_sys_mq_open(const char __user *u_name, int oflag, compat_mode_t mode, struct compat_mq_attr __user *u_attr); asmlinkage long compat_sys_mq_notify(mqd_t mqdes, const struct compat_sigevent __user *u_notification); asmlinkage long compat_sys_mq_getsetattr(mqd_t mqdes, const struct compat_mq_attr __user *u_mqstat, struct compat_mq_attr __user *u_omqstat); asmlinkage long compat_sys_msgctl(int first, int second, void __user *uptr); asmlinkage long compat_sys_msgrcv(int msqid, compat_uptr_t msgp, compat_ssize_t msgsz, compat_long_t msgtyp, int msgflg); asmlinkage long compat_sys_msgsnd(int msqid, compat_uptr_t msgp, compat_ssize_t msgsz, int msgflg); asmlinkage long compat_sys_semctl(int semid, int semnum, int cmd, int arg); asmlinkage long compat_sys_shmctl(int first, int second, void __user *uptr); asmlinkage long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg); asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, compat_size_t len, unsigned flags, struct sockaddr __user *addr, int __user *addrlen); asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags); asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, unsigned int flags); asmlinkage long compat_sys_keyctl(u32 option, u32 arg2, u32 arg3, u32 arg4, u32 arg5); asmlinkage long compat_sys_execve(const char __user *filename, const compat_uptr_t __user *argv, const compat_uptr_t __user *envp); asmlinkage long compat_sys_rt_tgsigqueueinfo(compat_pid_t tgid, compat_pid_t pid, int sig, struct compat_siginfo __user *uinfo); asmlinkage long compat_sys_recvmmsg_time64(int fd, struct compat_mmsghdr __user *mmsg, unsigned vlen, unsigned int flags, struct __kernel_timespec __user *timeout); asmlinkage long compat_sys_recvmmsg_time32(int fd, struct compat_mmsghdr __user *mmsg, unsigned vlen, unsigned int flags, struct old_timespec32 __user *timeout); asmlinkage long compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options, struct compat_rusage __user *ru); !asmlinkage long compat_sys_fanotify_mark(int fanotify_fd, unsigned int flags, __u32 mask_1, __u32 mask_2, int dfd, const char __user *pathname); asmlinkage long compat_sys_open_by_handle_at(int mountdirfd, struct file_handle __user *handle, int flags); asmlinkage long compat_sys_sendmmsg(int fd, struct compat_mmsghdr __user *mmsg, unsigned vlen, unsigned int flags); asmlinkage long compat_sys_execveat(int dfd, const char __user *filename, const compat_uptr_t __user *argv, const compat_uptr_t __user *envp, int flags); asmlinkage ssize_t compat_sys_preadv2(compat_ulong_t fd, const struct iovec __user *vec, compat_ulong_t vlen, u32 pos_low, u32 pos_high, rwf_t flags); asmlinkage ssize_t compat_sys_pwritev2(compat_ulong_t fd, const struct iovec __user *vec, compat_ulong_t vlen, u32 pos_low, u32 pos_high, rwf_t flags); asmlinkage long compat_sys_preadv64v2(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, loff_t pos, rwf_t flags); asmlinkage long compat_sys_pwritev64v2(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, loff_t pos, rwf_t flags); asmlinkage long compat_sys_open(const char __user *filename, int flags, umode_t mode); asmlinkage long compat_sys_signalfd(int ufd, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); asmlinkage long compat_sys_newstat(const char __user *filename, struct compat_stat __user *statbuf); asmlinkage long compat_sys_newlstat(const char __user *filename, struct compat_stat __user *statbuf); asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, compat_ulong_t __user *exp, struct old_timeval32 __user *tvp); asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u32); asmlinkage long compat_sys_recv(int fd, void __user *buf, compat_size_t len, unsigned flags); asmlinkage long compat_sys_old_readdir(unsigned int fd, struct compat_old_linux_dirent __user *, unsigned int count); asmlinkage long compat_sys_old_select(struct compat_sel_arg_struct __user *arg); asmlinkage long compat_sys_ipc(u32, int, int, u32, compat_uptr_t, u32); asmlinkage long compat_sys_sigpending(compat_old_sigset_t __user *set); asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t __user *nset, compat_old_sigset_t __user *oset); asmlinkage long compat_sys_sigaction(int sig, const struct compat_old_sigaction __user *act, struct compat_old_sigaction __user *oact); asmlinkage long compat_sys_socketcall(int call, u32 __user *args); asmlinkage long compat_sys_truncate64(const char __user *pathname, compat_arg_u64(len)); asmlinkage long compat_sys_ftruncate64(unsigned int fd, compat_arg_u64(len)); asmlinkage long compat_sys_fallocate(int fd, int mode, compat_arg_u64(offset), compat_arg_u64(len)); asmlinkage long compat_sys_pread64(unsigned int fd, char __user *buf, size_t count, compat_arg_u64(pos)); asmlinkage long compat_sys_pwrite64(unsigned int fd, const char __user *buf, size_t count, compat_arg_u64(pos)); asmlinkage long compat_sys_sync_file_range(int fd, compat_arg_u64(pos), compat_arg_u64(nbytes), unsigned int flags); asmlinkage long compat_sys_fadvise64_64(int fd, compat_arg_u64(pos), compat_arg_u64(len), int advice); asmlinkage long compat_sys_readahead(int fd, compat_arg_u64(offset), size_t count); """ # x86_64 # - arch/x86/entry/syscalls/syscall_64.tbl x64_syscall_tbl = """ 0 common read sys_read 1 common write sys_write 2 common open sys_open 3 common close sys_close 4 common stat sys_newstat 5 common fstat sys_newfstat 6 common lstat sys_newlstat 7 common poll sys_poll 8 common lseek sys_lseek 9 common mmap sys_mmap 10 common mprotect sys_mprotect 11 common munmap sys_munmap 12 common brk sys_brk 13 64 rt_sigaction sys_rt_sigaction 14 common rt_sigprocmask sys_rt_sigprocmask 15 64 rt_sigreturn sys_rt_sigreturn 16 64 ioctl sys_ioctl 17 common pread64 sys_pread64 18 common pwrite64 sys_pwrite64 19 64 readv sys_readv 20 64 writev sys_writev 21 common access sys_access 22 common pipe sys_pipe 23 common select sys_select 24 common sched_yield sys_sched_yield 25 common mremap sys_mremap 26 common msync sys_msync 27 common mincore sys_mincore 28 common madvise sys_madvise 29 common shmget sys_shmget 30 common shmat sys_shmat 31 common shmctl sys_shmctl 32 common dup sys_dup 33 common dup2 sys_dup2 34 common pause sys_pause 35 common nanosleep sys_nanosleep 36 common getitimer sys_getitimer 37 common alarm sys_alarm 38 common setitimer sys_setitimer 39 common getpid sys_getpid 40 common sendfile sys_sendfile64 41 common socket sys_socket 42 common connect sys_connect 43 common accept sys_accept 44 common sendto sys_sendto 45 64 recvfrom sys_recvfrom 46 64 sendmsg sys_sendmsg 47 64 recvmsg sys_recvmsg 48 common shutdown sys_shutdown 49 common bind sys_bind 50 common listen sys_listen 51 common getsockname sys_getsockname 52 common getpeername sys_getpeername 53 common socketpair sys_socketpair 54 64 setsockopt sys_setsockopt 55 64 getsockopt sys_getsockopt 56 common clone sys_clone 57 common fork sys_fork 58 common vfork sys_vfork 59 64 execve sys_execve 60 common exit sys_exit - noreturn 61 common wait4 sys_wait4 62 common kill sys_kill 63 common uname sys_newuname 64 common semget sys_semget 65 common semop sys_semop 66 common semctl sys_semctl 67 common shmdt sys_shmdt 68 common msgget sys_msgget 69 common msgsnd sys_msgsnd 70 common msgrcv sys_msgrcv 71 common msgctl sys_msgctl 72 common fcntl sys_fcntl 73 common flock sys_flock 74 common fsync sys_fsync 75 common fdatasync sys_fdatasync 76 common truncate sys_truncate 77 common ftruncate sys_ftruncate 78 common getdents sys_getdents 79 common getcwd sys_getcwd 80 common chdir sys_chdir 81 common fchdir sys_fchdir 82 common rename sys_rename 83 common mkdir sys_mkdir 84 common rmdir sys_rmdir 85 common creat sys_creat 86 common link sys_link 87 common unlink sys_unlink 88 common symlink sys_symlink 89 common readlink sys_readlink 90 common chmod sys_chmod 91 common fchmod sys_fchmod 92 common chown sys_chown 93 common fchown sys_fchown 94 common lchown sys_lchown 95 common umask sys_umask 96 common gettimeofday sys_gettimeofday 97 common getrlimit sys_getrlimit 98 common getrusage sys_getrusage 99 common sysinfo sys_sysinfo 100 common times sys_times 101 64 ptrace sys_ptrace 102 common getuid sys_getuid 103 common syslog sys_syslog 104 common getgid sys_getgid 105 common setuid sys_setuid 106 common setgid sys_setgid 107 common geteuid sys_geteuid 108 common getegid sys_getegid 109 common setpgid sys_setpgid 110 common getppid sys_getppid 111 common getpgrp sys_getpgrp 112 common setsid sys_setsid 113 common setreuid sys_setreuid 114 common setregid sys_setregid 115 common getgroups sys_getgroups 116 common setgroups sys_setgroups 117 common setresuid sys_setresuid 118 common getresuid sys_getresuid 119 common setresgid sys_setresgid 120 common getresgid sys_getresgid 121 common getpgid sys_getpgid 122 common setfsuid sys_setfsuid 123 common setfsgid sys_setfsgid 124 common getsid sys_getsid 125 common capget sys_capget 126 common capset sys_capset 127 64 rt_sigpending sys_rt_sigpending 128 64 rt_sigtimedwait sys_rt_sigtimedwait 129 64 rt_sigqueueinfo sys_rt_sigqueueinfo 130 common rt_sigsuspend sys_rt_sigsuspend 131 64 sigaltstack sys_sigaltstack 132 common utime sys_utime 133 common mknod sys_mknod 134 64 uselib 135 common personality sys_personality 136 common ustat sys_ustat 137 common statfs sys_statfs 138 common fstatfs sys_fstatfs 139 common sysfs sys_sysfs 140 common getpriority sys_getpriority 141 common setpriority sys_setpriority 142 common sched_setparam sys_sched_setparam 143 common sched_getparam sys_sched_getparam 144 common sched_setscheduler sys_sched_setscheduler 145 common sched_getscheduler sys_sched_getscheduler 146 common sched_get_priority_max sys_sched_get_priority_max 147 common sched_get_priority_min sys_sched_get_priority_min 148 common sched_rr_get_interval sys_sched_rr_get_interval 149 common mlock sys_mlock 150 common munlock sys_munlock 151 common mlockall sys_mlockall 152 common munlockall sys_munlockall 153 common vhangup sys_vhangup 154 common modify_ldt sys_modify_ldt 155 common pivot_root sys_pivot_root 156 64 _sysctl sys_ni_syscall 157 common prctl sys_prctl 158 common arch_prctl sys_arch_prctl 159 common adjtimex sys_adjtimex 160 common setrlimit sys_setrlimit 161 common chroot sys_chroot 162 common sync sys_sync 163 common acct sys_acct 164 common settimeofday sys_settimeofday 165 common mount sys_mount 166 common umount2 sys_umount 167 common swapon sys_swapon 168 common swapoff sys_swapoff 169 common reboot sys_reboot 170 common sethostname sys_sethostname 171 common setdomainname sys_setdomainname 172 common iopl sys_iopl 173 common ioperm sys_ioperm 174 64 create_module 175 common init_module sys_init_module 176 common delete_module sys_delete_module 177 64 get_kernel_syms 178 64 query_module 179 common quotactl sys_quotactl 180 64 nfsservctl 181 common getpmsg 182 common putpmsg 183 common afs_syscall 184 common tuxcall 185 common security 186 common gettid sys_gettid 187 common readahead sys_readahead 188 common setxattr sys_setxattr 189 common lsetxattr sys_lsetxattr 190 common fsetxattr sys_fsetxattr 191 common getxattr sys_getxattr 192 common lgetxattr sys_lgetxattr 193 common fgetxattr sys_fgetxattr 194 common listxattr sys_listxattr 195 common llistxattr sys_llistxattr 196 common flistxattr sys_flistxattr 197 common removexattr sys_removexattr 198 common lremovexattr sys_lremovexattr 199 common fremovexattr sys_fremovexattr 200 common tkill sys_tkill 201 common time sys_time 202 common futex sys_futex 203 common sched_setaffinity sys_sched_setaffinity 204 common sched_getaffinity sys_sched_getaffinity 205 64 set_thread_area 206 64 io_setup sys_io_setup 207 common io_destroy sys_io_destroy 208 common io_getevents sys_io_getevents 209 64 io_submit sys_io_submit 210 common io_cancel sys_io_cancel 211 64 get_thread_area 212 common lookup_dcookie 213 common epoll_create sys_epoll_create 214 64 epoll_ctl_old 215 64 epoll_wait_old 216 common remap_file_pages sys_remap_file_pages 217 common getdents64 sys_getdents64 218 common set_tid_address sys_set_tid_address 219 common restart_syscall sys_restart_syscall 220 common semtimedop sys_semtimedop 221 common fadvise64 sys_fadvise64 222 64 timer_create sys_timer_create 223 common timer_settime sys_timer_settime 224 common timer_gettime sys_timer_gettime 225 common timer_getoverrun sys_timer_getoverrun 226 common timer_delete sys_timer_delete 227 common clock_settime sys_clock_settime 228 common clock_gettime sys_clock_gettime 229 common clock_getres sys_clock_getres 230 common clock_nanosleep sys_clock_nanosleep 231 common exit_group sys_exit_group - noreturn 232 common epoll_wait sys_epoll_wait 233 common epoll_ctl sys_epoll_ctl 234 common tgkill sys_tgkill 235 common utimes sys_utimes 236 64 vserver 237 common mbind sys_mbind 238 common set_mempolicy sys_set_mempolicy 239 common get_mempolicy sys_get_mempolicy 240 common mq_open sys_mq_open 241 common mq_unlink sys_mq_unlink 242 common mq_timedsend sys_mq_timedsend 243 common mq_timedreceive sys_mq_timedreceive 244 64 mq_notify sys_mq_notify 245 common mq_getsetattr sys_mq_getsetattr 246 64 kexec_load sys_kexec_load 247 64 waitid sys_waitid 248 common add_key sys_add_key 249 common request_key sys_request_key 250 common keyctl sys_keyctl 251 common ioprio_set sys_ioprio_set 252 common ioprio_get sys_ioprio_get 253 common inotify_init sys_inotify_init 254 common inotify_add_watch sys_inotify_add_watch 255 common inotify_rm_watch sys_inotify_rm_watch 256 common migrate_pages sys_migrate_pages 257 common openat sys_openat 258 common mkdirat sys_mkdirat 259 common mknodat sys_mknodat 260 common fchownat sys_fchownat 261 common futimesat sys_futimesat 262 common newfstatat sys_newfstatat 263 common unlinkat sys_unlinkat 264 common renameat sys_renameat 265 common linkat sys_linkat 266 common symlinkat sys_symlinkat 267 common readlinkat sys_readlinkat 268 common fchmodat sys_fchmodat 269 common faccessat sys_faccessat 270 common pselect6 sys_pselect6 271 common ppoll sys_ppoll 272 common unshare sys_unshare 273 64 set_robust_list sys_set_robust_list 274 64 get_robust_list sys_get_robust_list 275 common splice sys_splice 276 common tee sys_tee 277 common sync_file_range sys_sync_file_range 278 64 vmsplice sys_vmsplice 279 64 move_pages sys_move_pages 280 common utimensat sys_utimensat 281 common epoll_pwait sys_epoll_pwait 282 common signalfd sys_signalfd 283 common timerfd_create sys_timerfd_create 284 common eventfd sys_eventfd 285 common fallocate sys_fallocate 286 common timerfd_settime sys_timerfd_settime 287 common timerfd_gettime sys_timerfd_gettime 288 common accept4 sys_accept4 289 common signalfd4 sys_signalfd4 290 common eventfd2 sys_eventfd2 291 common epoll_create1 sys_epoll_create1 292 common dup3 sys_dup3 293 common pipe2 sys_pipe2 294 common inotify_init1 sys_inotify_init1 295 64 preadv sys_preadv 296 64 pwritev sys_pwritev 297 64 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 298 common perf_event_open sys_perf_event_open 299 64 recvmmsg sys_recvmmsg 300 common fanotify_init sys_fanotify_init 301 common fanotify_mark sys_fanotify_mark 302 common prlimit64 sys_prlimit64 303 common name_to_handle_at sys_name_to_handle_at 304 common open_by_handle_at sys_open_by_handle_at 305 common clock_adjtime sys_clock_adjtime 306 common syncfs sys_syncfs 307 64 sendmmsg sys_sendmmsg 308 common setns sys_setns 309 common getcpu sys_getcpu 310 64 process_vm_readv sys_process_vm_readv 311 64 process_vm_writev sys_process_vm_writev 312 common kcmp sys_kcmp 313 common finit_module sys_finit_module 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2 317 common seccomp sys_seccomp 318 common getrandom sys_getrandom 319 common memfd_create sys_memfd_create 320 common kexec_file_load sys_kexec_file_load 321 common bpf sys_bpf 322 64 execveat sys_execveat 323 common userfaultfd sys_userfaultfd 324 common membarrier sys_membarrier 325 common mlock2 sys_mlock2 326 common copy_file_range sys_copy_file_range 327 64 preadv2 sys_preadv2 328 64 pwritev2 sys_pwritev2 329 common pkey_mprotect sys_pkey_mprotect 330 common pkey_alloc sys_pkey_alloc 331 common pkey_free sys_pkey_free 332 common statx sys_statx 333 common io_pgetevents sys_io_pgetevents 334 common rseq sys_rseq 335 common uretprobe sys_uretprobe 336 common uprobe sys_uprobe 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 447 common memfd_secret sys_memfd_secret 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns 512 x32 rt_sigaction compat_sys_rt_sigaction 513 x32 rt_sigreturn compat_sys_x32_rt_sigreturn 514 x32 ioctl compat_sys_ioctl 515 x32 readv sys_readv 516 x32 writev sys_writev 517 x32 recvfrom compat_sys_recvfrom 518 x32 sendmsg compat_sys_sendmsg 519 x32 recvmsg compat_sys_recvmsg 520 x32 execve compat_sys_execve 521 x32 ptrace compat_sys_ptrace 522 x32 rt_sigpending compat_sys_rt_sigpending 523 x32 rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 524 x32 rt_sigqueueinfo compat_sys_rt_sigqueueinfo 525 x32 sigaltstack compat_sys_sigaltstack 526 x32 timer_create compat_sys_timer_create 527 x32 mq_notify compat_sys_mq_notify 528 x32 kexec_load compat_sys_kexec_load 529 x32 waitid compat_sys_waitid 530 x32 set_robust_list compat_sys_set_robust_list 531 x32 get_robust_list compat_sys_get_robust_list 532 x32 vmsplice sys_vmsplice 533 x32 move_pages sys_move_pages 534 x32 preadv compat_sys_preadv64 535 x32 pwritev compat_sys_pwritev64 536 x32 rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 537 x32 recvmmsg compat_sys_recvmmsg_time64 538 x32 sendmmsg compat_sys_sendmmsg 539 x32 process_vm_readv sys_process_vm_readv 540 x32 process_vm_writev sys_process_vm_writev 541 x32 setsockopt sys_setsockopt 542 x32 getsockopt sys_getsockopt 543 x32 io_setup compat_sys_io_setup 544 x32 io_submit compat_sys_io_submit 545 x32 execveat compat_sys_execveat 546 x32 preadv2 compat_sys_preadv64v2 547 x32 pwritev2 compat_sys_pwritev64v2 """ # i386 (native / compat(emulated)) # - arch/x86/entry/syscalls/syscall_32.tbl x86_syscall_tbl = """ 0 i386 restart_syscall sys_restart_syscall 1 i386 exit sys_exit - noreturn 2 i386 fork sys_fork 3 i386 read sys_read 4 i386 write sys_write 5 i386 open sys_open compat_sys_open 6 i386 close sys_close 7 i386 waitpid sys_waitpid 8 i386 creat sys_creat 9 i386 link sys_link 10 i386 unlink sys_unlink 11 i386 execve sys_execve compat_sys_execve 12 i386 chdir sys_chdir 13 i386 time sys_time32 14 i386 mknod sys_mknod 15 i386 chmod sys_chmod 16 i386 lchown sys_lchown16 17 i386 break 18 i386 oldstat sys_stat 19 i386 lseek sys_lseek compat_sys_lseek 20 i386 getpid sys_getpid 21 i386 mount sys_mount 22 i386 umount sys_oldumount 23 i386 setuid sys_setuid16 24 i386 getuid sys_getuid16 25 i386 stime sys_stime32 26 i386 ptrace sys_ptrace compat_sys_ptrace 27 i386 alarm sys_alarm 28 i386 oldfstat sys_fstat 29 i386 pause sys_pause 30 i386 utime sys_utime32 31 i386 stty 32 i386 gtty 33 i386 access sys_access 34 i386 nice sys_nice 35 i386 ftime 36 i386 sync sys_sync 37 i386 kill sys_kill 38 i386 rename sys_rename 39 i386 mkdir sys_mkdir 40 i386 rmdir sys_rmdir 41 i386 dup sys_dup 42 i386 pipe sys_pipe 43 i386 times sys_times compat_sys_times 44 i386 prof 45 i386 brk sys_brk 46 i386 setgid sys_setgid16 47 i386 getgid sys_getgid16 48 i386 signal sys_signal 49 i386 geteuid sys_geteuid16 50 i386 getegid sys_getegid16 51 i386 acct sys_acct 52 i386 umount2 sys_umount 53 i386 lock 54 i386 ioctl sys_ioctl compat_sys_ioctl 55 i386 fcntl sys_fcntl compat_sys_fcntl64 56 i386 mpx 57 i386 setpgid sys_setpgid 58 i386 ulimit 59 i386 oldolduname sys_olduname 60 i386 umask sys_umask 61 i386 chroot sys_chroot 62 i386 ustat sys_ustat compat_sys_ustat 63 i386 dup2 sys_dup2 64 i386 getppid sys_getppid 65 i386 getpgrp sys_getpgrp 66 i386 setsid sys_setsid 67 i386 sigaction sys_sigaction compat_sys_sigaction 68 i386 sgetmask sys_sgetmask 69 i386 ssetmask sys_ssetmask 70 i386 setreuid sys_setreuid16 71 i386 setregid sys_setregid16 72 i386 sigsuspend sys_sigsuspend 73 i386 sigpending sys_sigpending compat_sys_sigpending 74 i386 sethostname sys_sethostname 75 i386 setrlimit sys_setrlimit compat_sys_setrlimit 76 i386 getrlimit sys_old_getrlimit compat_sys_old_getrlimit 77 i386 getrusage sys_getrusage compat_sys_getrusage 78 i386 gettimeofday sys_gettimeofday compat_sys_gettimeofday 79 i386 settimeofday sys_settimeofday compat_sys_settimeofday 80 i386 getgroups sys_getgroups16 81 i386 setgroups sys_setgroups16 82 i386 select sys_old_select compat_sys_old_select 83 i386 symlink sys_symlink 84 i386 oldlstat sys_lstat 85 i386 readlink sys_readlink 86 i386 uselib sys_uselib 87 i386 swapon sys_swapon 88 i386 reboot sys_reboot 89 i386 readdir sys_old_readdir compat_sys_old_readdir 90 i386 mmap sys_old_mmap compat_sys_ia32_mmap 91 i386 munmap sys_munmap 92 i386 truncate sys_truncate compat_sys_truncate 93 i386 ftruncate sys_ftruncate compat_sys_ftruncate 94 i386 fchmod sys_fchmod 95 i386 fchown sys_fchown16 96 i386 getpriority sys_getpriority 97 i386 setpriority sys_setpriority 98 i386 profil 99 i386 statfs sys_statfs compat_sys_statfs 100 i386 fstatfs sys_fstatfs compat_sys_fstatfs 101 i386 ioperm sys_ioperm 102 i386 socketcall sys_socketcall compat_sys_socketcall 103 i386 syslog sys_syslog 104 i386 setitimer sys_setitimer compat_sys_setitimer 105 i386 getitimer sys_getitimer compat_sys_getitimer 106 i386 stat sys_newstat compat_sys_newstat 107 i386 lstat sys_newlstat compat_sys_newlstat 108 i386 fstat sys_newfstat compat_sys_newfstat 109 i386 olduname sys_uname 110 i386 iopl sys_iopl 111 i386 vhangup sys_vhangup 112 i386 idle 113 i386 vm86old sys_vm86old sys_ni_syscall 114 i386 wait4 sys_wait4 compat_sys_wait4 115 i386 swapoff sys_swapoff 116 i386 sysinfo sys_sysinfo compat_sys_sysinfo 117 i386 ipc sys_ipc compat_sys_ipc 118 i386 fsync sys_fsync 119 i386 sigreturn sys_sigreturn compat_sys_sigreturn 120 i386 clone sys_clone compat_sys_ia32_clone 121 i386 setdomainname sys_setdomainname 122 i386 uname sys_newuname 123 i386 modify_ldt sys_modify_ldt 124 i386 adjtimex sys_adjtimex_time32 125 i386 mprotect sys_mprotect 126 i386 sigprocmask sys_sigprocmask compat_sys_sigprocmask 127 i386 create_module 128 i386 init_module sys_init_module 129 i386 delete_module sys_delete_module 130 i386 get_kernel_syms 131 i386 quotactl sys_quotactl 132 i386 getpgid sys_getpgid 133 i386 fchdir sys_fchdir 134 i386 bdflush sys_ni_syscall 135 i386 sysfs sys_sysfs 136 i386 personality sys_personality 137 i386 afs_syscall 138 i386 setfsuid sys_setfsuid16 139 i386 setfsgid sys_setfsgid16 140 i386 _llseek sys_llseek 141 i386 getdents sys_getdents compat_sys_getdents 142 i386 _newselect sys_select compat_sys_select 143 i386 flock sys_flock 144 i386 msync sys_msync 145 i386 readv sys_readv 146 i386 writev sys_writev 147 i386 getsid sys_getsid 148 i386 fdatasync sys_fdatasync 149 i386 _sysctl sys_ni_syscall 150 i386 mlock sys_mlock 151 i386 munlock sys_munlock 152 i386 mlockall sys_mlockall 153 i386 munlockall sys_munlockall 154 i386 sched_setparam sys_sched_setparam 155 i386 sched_getparam sys_sched_getparam 156 i386 sched_setscheduler sys_sched_setscheduler 157 i386 sched_getscheduler sys_sched_getscheduler 158 i386 sched_yield sys_sched_yield 159 i386 sched_get_priority_max sys_sched_get_priority_max 160 i386 sched_get_priority_min sys_sched_get_priority_min 161 i386 sched_rr_get_interval sys_sched_rr_get_interval_time32 162 i386 nanosleep sys_nanosleep_time32 163 i386 mremap sys_mremap 164 i386 setresuid sys_setresuid16 165 i386 getresuid sys_getresuid16 166 i386 vm86 sys_vm86 sys_ni_syscall 167 i386 query_module 168 i386 poll sys_poll 169 i386 nfsservctl 170 i386 setresgid sys_setresgid16 171 i386 getresgid sys_getresgid16 172 i386 prctl sys_prctl 173 i386 rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn 174 i386 rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction 175 i386 rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask 176 i386 rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending 177 i386 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 178 i386 rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 179 i386 rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 180 i386 pread64 sys_ia32_pread64 181 i386 pwrite64 sys_ia32_pwrite64 182 i386 chown sys_chown16 183 i386 getcwd sys_getcwd 184 i386 capget sys_capget 185 i386 capset sys_capset 186 i386 sigaltstack sys_sigaltstack compat_sys_sigaltstack 187 i386 sendfile sys_sendfile compat_sys_sendfile 188 i386 getpmsg 189 i386 putpmsg 190 i386 vfork sys_vfork 191 i386 ugetrlimit sys_getrlimit compat_sys_getrlimit 192 i386 mmap2 sys_mmap_pgoff 193 i386 truncate64 sys_ia32_truncate64 194 i386 ftruncate64 sys_ia32_ftruncate64 195 i386 stat64 sys_stat64 compat_sys_ia32_stat64 196 i386 lstat64 sys_lstat64 compat_sys_ia32_lstat64 197 i386 fstat64 sys_fstat64 compat_sys_ia32_fstat64 198 i386 lchown32 sys_lchown 199 i386 getuid32 sys_getuid 200 i386 getgid32 sys_getgid 201 i386 geteuid32 sys_geteuid 202 i386 getegid32 sys_getegid 203 i386 setreuid32 sys_setreuid 204 i386 setregid32 sys_setregid 205 i386 getgroups32 sys_getgroups 206 i386 setgroups32 sys_setgroups 207 i386 fchown32 sys_fchown 208 i386 setresuid32 sys_setresuid 209 i386 getresuid32 sys_getresuid 210 i386 setresgid32 sys_setresgid 211 i386 getresgid32 sys_getresgid 212 i386 chown32 sys_chown 213 i386 setuid32 sys_setuid 214 i386 setgid32 sys_setgid 215 i386 setfsuid32 sys_setfsuid 216 i386 setfsgid32 sys_setfsgid 217 i386 pivot_root sys_pivot_root 218 i386 mincore sys_mincore 219 i386 madvise sys_madvise 220 i386 getdents64 sys_getdents64 221 i386 fcntl64 sys_fcntl64 compat_sys_fcntl64 224 i386 gettid sys_gettid 225 i386 readahead sys_ia32_readahead 226 i386 setxattr sys_setxattr 227 i386 lsetxattr sys_lsetxattr 228 i386 fsetxattr sys_fsetxattr 229 i386 getxattr sys_getxattr 230 i386 lgetxattr sys_lgetxattr 231 i386 fgetxattr sys_fgetxattr 232 i386 listxattr sys_listxattr 233 i386 llistxattr sys_llistxattr 234 i386 flistxattr sys_flistxattr 235 i386 removexattr sys_removexattr 236 i386 lremovexattr sys_lremovexattr 237 i386 fremovexattr sys_fremovexattr 238 i386 tkill sys_tkill 239 i386 sendfile64 sys_sendfile64 240 i386 futex sys_futex_time32 241 i386 sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity 242 i386 sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity 243 i386 set_thread_area sys_set_thread_area 244 i386 get_thread_area sys_get_thread_area 245 i386 io_setup sys_io_setup compat_sys_io_setup 246 i386 io_destroy sys_io_destroy 247 i386 io_getevents sys_io_getevents_time32 248 i386 io_submit sys_io_submit compat_sys_io_submit 249 i386 io_cancel sys_io_cancel 250 i386 fadvise64 sys_ia32_fadvise64 252 i386 exit_group sys_exit_group - noreturn 253 i386 lookup_dcookie 254 i386 epoll_create sys_epoll_create 255 i386 epoll_ctl sys_epoll_ctl 256 i386 epoll_wait sys_epoll_wait 257 i386 remap_file_pages sys_remap_file_pages 258 i386 set_tid_address sys_set_tid_address 259 i386 timer_create sys_timer_create compat_sys_timer_create 260 i386 timer_settime sys_timer_settime32 261 i386 timer_gettime sys_timer_gettime32 262 i386 timer_getoverrun sys_timer_getoverrun 263 i386 timer_delete sys_timer_delete 264 i386 clock_settime sys_clock_settime32 265 i386 clock_gettime sys_clock_gettime32 266 i386 clock_getres sys_clock_getres_time32 267 i386 clock_nanosleep sys_clock_nanosleep_time32 268 i386 statfs64 sys_statfs64 compat_sys_statfs64 269 i386 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 270 i386 tgkill sys_tgkill 271 i386 utimes sys_utimes_time32 272 i386 fadvise64_64 sys_ia32_fadvise64_64 273 i386 vserver 274 i386 mbind sys_mbind 275 i386 get_mempolicy sys_get_mempolicy 276 i386 set_mempolicy sys_set_mempolicy 277 i386 mq_open sys_mq_open compat_sys_mq_open 278 i386 mq_unlink sys_mq_unlink 279 i386 mq_timedsend sys_mq_timedsend_time32 280 i386 mq_timedreceive sys_mq_timedreceive_time32 281 i386 mq_notify sys_mq_notify compat_sys_mq_notify 282 i386 mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr 283 i386 kexec_load sys_kexec_load compat_sys_kexec_load 284 i386 waitid sys_waitid compat_sys_waitid 286 i386 add_key sys_add_key 287 i386 request_key sys_request_key 288 i386 keyctl sys_keyctl compat_sys_keyctl 289 i386 ioprio_set sys_ioprio_set 290 i386 ioprio_get sys_ioprio_get 291 i386 inotify_init sys_inotify_init 292 i386 inotify_add_watch sys_inotify_add_watch 293 i386 inotify_rm_watch sys_inotify_rm_watch 294 i386 migrate_pages sys_migrate_pages 295 i386 openat sys_openat compat_sys_openat 296 i386 mkdirat sys_mkdirat 297 i386 mknodat sys_mknodat 298 i386 fchownat sys_fchownat 299 i386 futimesat sys_futimesat_time32 300 i386 fstatat64 sys_fstatat64 compat_sys_ia32_fstatat64 301 i386 unlinkat sys_unlinkat 302 i386 renameat sys_renameat 303 i386 linkat sys_linkat 304 i386 symlinkat sys_symlinkat 305 i386 readlinkat sys_readlinkat 306 i386 fchmodat sys_fchmodat 307 i386 faccessat sys_faccessat 308 i386 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 309 i386 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 310 i386 unshare sys_unshare 311 i386 set_robust_list sys_set_robust_list compat_sys_set_robust_list 312 i386 get_robust_list sys_get_robust_list compat_sys_get_robust_list 313 i386 splice sys_splice 314 i386 sync_file_range sys_ia32_sync_file_range 315 i386 tee sys_tee 316 i386 vmsplice sys_vmsplice 317 i386 move_pages sys_move_pages 318 i386 getcpu sys_getcpu 319 i386 epoll_pwait sys_epoll_pwait 320 i386 utimensat sys_utimensat_time32 321 i386 signalfd sys_signalfd compat_sys_signalfd 322 i386 timerfd_create sys_timerfd_create 323 i386 eventfd sys_eventfd 324 i386 fallocate sys_ia32_fallocate 325 i386 timerfd_settime sys_timerfd_settime32 326 i386 timerfd_gettime sys_timerfd_gettime32 327 i386 signalfd4 sys_signalfd4 compat_sys_signalfd4 328 i386 eventfd2 sys_eventfd2 329 i386 epoll_create1 sys_epoll_create1 330 i386 dup3 sys_dup3 331 i386 pipe2 sys_pipe2 332 i386 inotify_init1 sys_inotify_init1 333 i386 preadv sys_preadv compat_sys_preadv 334 i386 pwritev sys_pwritev compat_sys_pwritev 335 i386 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 336 i386 perf_event_open sys_perf_event_open 337 i386 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 338 i386 fanotify_init sys_fanotify_init 339 i386 fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark 340 i386 prlimit64 sys_prlimit64 341 i386 name_to_handle_at sys_name_to_handle_at 342 i386 open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at 343 i386 clock_adjtime sys_clock_adjtime32 344 i386 syncfs sys_syncfs 345 i386 sendmmsg sys_sendmmsg compat_sys_sendmmsg 346 i386 setns sys_setns 347 i386 process_vm_readv sys_process_vm_readv 348 i386 process_vm_writev sys_process_vm_writev 349 i386 kcmp sys_kcmp 350 i386 finit_module sys_finit_module 351 i386 sched_setattr sys_sched_setattr 352 i386 sched_getattr sys_sched_getattr 353 i386 renameat2 sys_renameat2 354 i386 seccomp sys_seccomp 355 i386 getrandom sys_getrandom 356 i386 memfd_create sys_memfd_create 357 i386 bpf sys_bpf 358 i386 execveat sys_execveat compat_sys_execveat 359 i386 socket sys_socket 360 i386 socketpair sys_socketpair 361 i386 bind sys_bind 362 i386 connect sys_connect 363 i386 listen sys_listen 364 i386 accept4 sys_accept4 365 i386 getsockopt sys_getsockopt sys_getsockopt 366 i386 setsockopt sys_setsockopt sys_setsockopt 367 i386 getsockname sys_getsockname 368 i386 getpeername sys_getpeername 369 i386 sendto sys_sendto 370 i386 sendmsg sys_sendmsg compat_sys_sendmsg 371 i386 recvfrom sys_recvfrom compat_sys_recvfrom 372 i386 recvmsg sys_recvmsg compat_sys_recvmsg 373 i386 shutdown sys_shutdown 374 i386 userfaultfd sys_userfaultfd 375 i386 membarrier sys_membarrier 376 i386 mlock2 sys_mlock2 377 i386 copy_file_range sys_copy_file_range 378 i386 preadv2 sys_preadv2 compat_sys_preadv2 379 i386 pwritev2 sys_pwritev2 compat_sys_pwritev2 380 i386 pkey_mprotect sys_pkey_mprotect 381 i386 pkey_alloc sys_pkey_alloc 382 i386 pkey_free sys_pkey_free 383 i386 statx sys_statx 384 i386 arch_prctl sys_arch_prctl 385 i386 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents 386 i386 rseq sys_rseq 393 i386 semget sys_semget 394 i386 semctl sys_semctl compat_sys_semctl 395 i386 shmget sys_shmget 396 i386 shmctl sys_shmctl compat_sys_shmctl 397 i386 shmat sys_shmat compat_sys_shmat 398 i386 shmdt sys_shmdt 399 i386 msgget sys_msgget 400 i386 msgsnd sys_msgsnd compat_sys_msgsnd 401 i386 msgrcv sys_msgrcv compat_sys_msgrcv 402 i386 msgctl sys_msgctl compat_sys_msgctl 403 i386 clock_gettime64 sys_clock_gettime 404 i386 clock_settime64 sys_clock_settime 405 i386 clock_adjtime64 sys_clock_adjtime 406 i386 clock_getres_time64 sys_clock_getres 407 i386 clock_nanosleep_time64 sys_clock_nanosleep 408 i386 timer_gettime64 sys_timer_gettime 409 i386 timer_settime64 sys_timer_settime 410 i386 timerfd_gettime64 sys_timerfd_gettime 411 i386 timerfd_settime64 sys_timerfd_settime 412 i386 utimensat_time64 sys_utimensat 413 i386 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 i386 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 416 i386 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 i386 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 i386 mq_timedsend_time64 sys_mq_timedsend 419 i386 mq_timedreceive_time64 sys_mq_timedreceive 420 i386 semtimedop_time64 sys_semtimedop 421 i386 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 422 i386 futex_time64 sys_futex 423 i386 sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 i386 pidfd_send_signal sys_pidfd_send_signal 425 i386 io_uring_setup sys_io_uring_setup 426 i386 io_uring_enter sys_io_uring_enter 427 i386 io_uring_register sys_io_uring_register 428 i386 open_tree sys_open_tree 429 i386 move_mount sys_move_mount 430 i386 fsopen sys_fsopen 431 i386 fsconfig sys_fsconfig 432 i386 fsmount sys_fsmount 433 i386 fspick sys_fspick 434 i386 pidfd_open sys_pidfd_open 435 i386 clone3 sys_clone3 436 i386 close_range sys_close_range 437 i386 openat2 sys_openat2 438 i386 pidfd_getfd sys_pidfd_getfd 439 i386 faccessat2 sys_faccessat2 440 i386 process_madvise sys_process_madvise 441 i386 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 i386 mount_setattr sys_mount_setattr 443 i386 quotactl_fd sys_quotactl_fd 444 i386 landlock_create_ruleset sys_landlock_create_ruleset 445 i386 landlock_add_rule sys_landlock_add_rule 446 i386 landlock_restrict_self sys_landlock_restrict_self 447 i386 memfd_secret sys_memfd_secret 448 i386 process_mrelease sys_process_mrelease 449 i386 futex_waitv sys_futex_waitv 450 i386 set_mempolicy_home_node sys_set_mempolicy_home_node 451 i386 cachestat sys_cachestat 452 i386 fchmodat2 sys_fchmodat2 453 i386 map_shadow_stack sys_map_shadow_stack 454 i386 futex_wake sys_futex_wake 455 i386 futex_wait sys_futex_wait 456 i386 futex_requeue sys_futex_requeue 457 i386 statmount sys_statmount 458 i386 listmount sys_listmount 459 i386 lsm_get_self_attr sys_lsm_get_self_attr 460 i386 lsm_set_self_attr sys_lsm_set_self_attr 461 i386 lsm_list_modules sys_lsm_list_modules 462 i386 mseal sys_mseal 463 i386 setxattrat sys_setxattrat 464 i386 getxattrat sys_getxattrat 465 i386 listxattrat sys_listxattrat 466 i386 removexattrat sys_removexattrat 467 i386 open_tree_attr sys_open_tree_attr 468 i386 file_getattr sys_file_getattr 469 i386 file_setattr sys_file_setattr 470 i386 listns sys_listns """ # ARM64 # - arch/arm64/tools/syscall_64_tbl -> scripts/syscall.tbl arm64_syscall_tbl = """ 0 common io_setup sys_io_setup compat_sys_io_setup 1 common io_destroy sys_io_destroy 2 common io_submit sys_io_submit compat_sys_io_submit 3 common io_cancel sys_io_cancel 4 time32 io_getevents sys_io_getevents_time32 4 64 io_getevents sys_io_getevents 5 common setxattr sys_setxattr 6 common lsetxattr sys_lsetxattr 7 common fsetxattr sys_fsetxattr 8 common getxattr sys_getxattr 9 common lgetxattr sys_lgetxattr 10 common fgetxattr sys_fgetxattr 11 common listxattr sys_listxattr 12 common llistxattr sys_llistxattr 13 common flistxattr sys_flistxattr 14 common removexattr sys_removexattr 15 common lremovexattr sys_lremovexattr 16 common fremovexattr sys_fremovexattr 17 common getcwd sys_getcwd 18 common lookup_dcookie sys_ni_syscall 19 common eventfd2 sys_eventfd2 20 common epoll_create1 sys_epoll_create1 21 common epoll_ctl sys_epoll_ctl 22 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 23 common dup sys_dup 24 common dup3 sys_dup3 25 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 25 64 fcntl sys_fcntl 26 common inotify_init1 sys_inotify_init1 27 common inotify_add_watch sys_inotify_add_watch 28 common inotify_rm_watch sys_inotify_rm_watch 29 common ioctl sys_ioctl compat_sys_ioctl 30 common ioprio_set sys_ioprio_set 31 common ioprio_get sys_ioprio_get 32 common flock sys_flock 33 common mknodat sys_mknodat 34 common mkdirat sys_mkdirat 35 common unlinkat sys_unlinkat 36 common symlinkat sys_symlinkat 37 common linkat sys_linkat 38 renameat renameat sys_renameat 39 common umount2 sys_umount 40 common mount sys_mount 41 common pivot_root sys_pivot_root 42 common nfsservctl sys_ni_syscall 43 32 statfs64 sys_statfs64 compat_sys_statfs64 43 64 statfs sys_statfs 44 32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 44 64 fstatfs sys_fstatfs 45 32 truncate64 sys_truncate64 compat_sys_truncate64 45 64 truncate sys_truncate 46 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 46 64 ftruncate sys_ftruncate 47 common fallocate sys_fallocate compat_sys_fallocate 48 common faccessat sys_faccessat 49 common chdir sys_chdir 50 common fchdir sys_fchdir 51 common chroot sys_chroot 52 common fchmod sys_fchmod 53 common fchmodat sys_fchmodat 54 common fchownat sys_fchownat 55 common fchown sys_fchown 56 common openat sys_openat 57 common close sys_close 58 common vhangup sys_vhangup 59 common pipe2 sys_pipe2 60 common quotactl sys_quotactl 61 common getdents64 sys_getdents64 62 32 llseek sys_llseek 62 64 lseek sys_lseek 63 common read sys_read 64 common write sys_write 65 common readv sys_readv sys_readv 66 common writev sys_writev sys_writev 67 common pread64 sys_pread64 compat_sys_pread64 68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 69 common preadv sys_preadv compat_sys_preadv 70 common pwritev sys_pwritev compat_sys_pwritev 71 32 sendfile64 sys_sendfile64 71 64 sendfile sys_sendfile64 72 time32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 72 64 pselect6 sys_pselect6 73 time32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 73 64 ppoll sys_ppoll 74 common signalfd4 sys_signalfd4 compat_sys_signalfd4 75 common vmsplice sys_vmsplice 76 common splice sys_splice 77 common tee sys_tee 78 common readlinkat sys_readlinkat 79 stat64 fstatat64 sys_fstatat64 79 64 newfstatat sys_newfstatat 80 stat64 fstat64 sys_fstat64 80 64 fstat sys_newfstat 81 common sync sys_sync 82 common fsync sys_fsync 83 common fdatasync sys_fdatasync 84 common sync_file_range sys_sync_file_range compat_sys_sync_file_range 85 common timerfd_create sys_timerfd_create 86 time32 timerfd_settime sys_timerfd_settime32 86 64 timerfd_settime sys_timerfd_settime 87 time32 timerfd_gettime sys_timerfd_gettime32 87 64 timerfd_gettime sys_timerfd_gettime 88 time32 utimensat sys_utimensat_time32 88 64 utimensat sys_utimensat 89 common acct sys_acct 90 common capget sys_capget 91 common capset sys_capset 92 common personality sys_personality 93 common exit sys_exit 94 common exit_group sys_exit_group 95 common waitid sys_waitid compat_sys_waitid 96 common set_tid_address sys_set_tid_address 97 common unshare sys_unshare 98 time32 futex sys_futex_time32 98 64 futex sys_futex 99 common set_robust_list sys_set_robust_list compat_sys_set_robust_list 100 common get_robust_list sys_get_robust_list compat_sys_get_robust_list 101 time32 nanosleep sys_nanosleep_time32 101 64 nanosleep sys_nanosleep 102 common getitimer sys_getitimer compat_sys_getitimer 103 common setitimer sys_setitimer compat_sys_setitimer 104 common kexec_load sys_kexec_load compat_sys_kexec_load 105 common init_module sys_init_module 106 common delete_module sys_delete_module 107 common timer_create sys_timer_create compat_sys_timer_create 108 time32 timer_gettime sys_timer_gettime32 108 64 timer_gettime sys_timer_gettime 109 common timer_getoverrun sys_timer_getoverrun 110 time32 timer_settime sys_timer_settime32 110 64 timer_settime sys_timer_settime 111 common timer_delete sys_timer_delete 112 time32 clock_settime sys_clock_settime32 112 64 clock_settime sys_clock_settime 113 time32 clock_gettime sys_clock_gettime32 113 64 clock_gettime sys_clock_gettime 114 time32 clock_getres sys_clock_getres_time32 114 64 clock_getres sys_clock_getres 115 time32 clock_nanosleep sys_clock_nanosleep_time32 115 64 clock_nanosleep sys_clock_nanosleep 116 common syslog sys_syslog 117 common ptrace sys_ptrace compat_sys_ptrace 118 common sched_setparam sys_sched_setparam 119 common sched_setscheduler sys_sched_setscheduler 120 common sched_getscheduler sys_sched_getscheduler 121 common sched_getparam sys_sched_getparam 122 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity 123 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity 124 common sched_yield sys_sched_yield 125 common sched_get_priority_max sys_sched_get_priority_max 126 common sched_get_priority_min sys_sched_get_priority_min 127 time32 sched_rr_get_interval sys_sched_rr_get_interval_time32 127 64 sched_rr_get_interval sys_sched_rr_get_interval 128 common restart_syscall sys_restart_syscall 129 common kill sys_kill 130 common tkill sys_tkill 131 common tgkill sys_tgkill 132 common sigaltstack sys_sigaltstack compat_sys_sigaltstack 133 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 134 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction 135 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask 136 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending 137 time32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 137 64 rt_sigtimedwait sys_rt_sigtimedwait 138 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 139 common rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn 140 common setpriority sys_setpriority 141 common getpriority sys_getpriority 142 common reboot sys_reboot 143 common setregid sys_setregid 144 common setgid sys_setgid 145 common setreuid sys_setreuid 146 common setuid sys_setuid 147 common setresuid sys_setresuid 148 common getresuid sys_getresuid 149 common setresgid sys_setresgid 150 common getresgid sys_getresgid 151 common setfsuid sys_setfsuid 152 common setfsgid sys_setfsgid 153 common times sys_times compat_sys_times 154 common setpgid sys_setpgid 155 common getpgid sys_getpgid 156 common getsid sys_getsid 157 common setsid sys_setsid 158 common getgroups sys_getgroups 159 common setgroups sys_setgroups 160 common uname sys_newuname 161 common sethostname sys_sethostname 162 common setdomainname sys_setdomainname 163 rlimit getrlimit sys_getrlimit compat_sys_getrlimit 164 rlimit setrlimit sys_setrlimit compat_sys_setrlimit 165 common getrusage sys_getrusage compat_sys_getrusage 166 common umask sys_umask 167 common prctl sys_prctl 168 common getcpu sys_getcpu 169 time32 gettimeofday sys_gettimeofday compat_sys_gettimeofday 169 64 gettimeofday sys_gettimeofday 170 time32 settimeofday sys_settimeofday compat_sys_settimeofday 170 64 settimeofday sys_settimeofday 171 time32 adjtimex sys_adjtimex_time32 171 64 adjtimex sys_adjtimex 172 common getpid sys_getpid 173 common getppid sys_getppid 174 common getuid sys_getuid 175 common geteuid sys_geteuid 176 common getgid sys_getgid 177 common getegid sys_getegid 178 common gettid sys_gettid 179 common sysinfo sys_sysinfo compat_sys_sysinfo 180 common mq_open sys_mq_open compat_sys_mq_open 181 common mq_unlink sys_mq_unlink 182 time32 mq_timedsend sys_mq_timedsend_time32 182 64 mq_timedsend sys_mq_timedsend 183 time32 mq_timedreceive sys_mq_timedreceive_time32 183 64 mq_timedreceive sys_mq_timedreceive 184 common mq_notify sys_mq_notify compat_sys_mq_notify 185 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr 186 common msgget sys_msgget 187 common msgctl sys_msgctl compat_sys_msgctl 188 common msgrcv sys_msgrcv compat_sys_msgrcv 189 common msgsnd sys_msgsnd compat_sys_msgsnd 190 common semget sys_semget 191 common semctl sys_semctl compat_sys_semctl 192 time32 semtimedop sys_semtimedop_time32 192 64 semtimedop sys_semtimedop 193 common semop sys_semop 194 common shmget sys_shmget 195 common shmctl sys_shmctl compat_sys_shmctl 196 common shmat sys_shmat compat_sys_shmat 197 common shmdt sys_shmdt 198 common socket sys_socket 199 common socketpair sys_socketpair 200 common bind sys_bind 201 common listen sys_listen 202 common accept sys_accept 203 common connect sys_connect 204 common getsockname sys_getsockname 205 common getpeername sys_getpeername 206 common sendto sys_sendto 207 common recvfrom sys_recvfrom compat_sys_recvfrom 208 common setsockopt sys_setsockopt sys_setsockopt 209 common getsockopt sys_getsockopt sys_getsockopt 210 common shutdown sys_shutdown 211 common sendmsg sys_sendmsg compat_sys_sendmsg 212 common recvmsg sys_recvmsg compat_sys_recvmsg 213 common readahead sys_readahead compat_sys_readahead 214 common brk sys_brk 215 common munmap sys_munmap 216 common mremap sys_mremap 217 common add_key sys_add_key 218 common request_key sys_request_key 219 common keyctl sys_keyctl compat_sys_keyctl 220 common clone sys_clone 221 common execve sys_execve compat_sys_execve 222 32 mmap2 sys_mmap2 222 64 mmap sys_mmap 223 32 fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 223 64 fadvise64 sys_fadvise64_64 224 common swapon sys_swapon 225 common swapoff sys_swapoff 226 common mprotect sys_mprotect 227 common msync sys_msync 228 common mlock sys_mlock 229 common munlock sys_munlock 230 common mlockall sys_mlockall 231 common munlockall sys_munlockall 232 common mincore sys_mincore 233 common madvise sys_madvise 234 common remap_file_pages sys_remap_file_pages 235 common mbind sys_mbind 236 common get_mempolicy sys_get_mempolicy 237 common set_mempolicy sys_set_mempolicy 238 common migrate_pages sys_migrate_pages 239 common move_pages sys_move_pages 240 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 241 common perf_event_open sys_perf_event_open 242 common accept4 sys_accept4 243 time32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 243 64 recvmmsg sys_recvmmsg 244 arc cacheflush sys_cacheflush 245 arc arc_settls sys_arc_settls 246 arc arc_gettls sys_arc_gettls 247 arc sysfs sys_sysfs 248 arc arc_usr_cmpxchg sys_arc_usr_cmpxchg 244 csky set_thread_area sys_set_thread_area 245 csky cacheflush sys_cacheflush 244 nios2 cacheflush sys_cacheflush 244 or1k or1k_atomic sys_or1k_atomic 258 riscv riscv_hwprobe sys_riscv_hwprobe 259 riscv riscv_flush_icache sys_riscv_flush_icache 260 time32 wait4 sys_wait4 compat_sys_wait4 260 64 wait4 sys_wait4 261 common prlimit64 sys_prlimit64 262 common fanotify_init sys_fanotify_init 263 common fanotify_mark sys_fanotify_mark 264 common name_to_handle_at sys_name_to_handle_at 265 common open_by_handle_at sys_open_by_handle_at 266 time32 clock_adjtime sys_clock_adjtime32 266 64 clock_adjtime sys_clock_adjtime 267 common syncfs sys_syncfs 268 common setns sys_setns 269 common sendmmsg sys_sendmmsg compat_sys_sendmmsg 270 common process_vm_readv sys_process_vm_readv 271 common process_vm_writev sys_process_vm_writev 272 common kcmp sys_kcmp 273 common finit_module sys_finit_module 274 common sched_setattr sys_sched_setattr 275 common sched_getattr sys_sched_getattr 276 common renameat2 sys_renameat2 277 common seccomp sys_seccomp 278 common getrandom sys_getrandom 279 common memfd_create sys_memfd_create 280 common bpf sys_bpf 281 common execveat sys_execveat compat_sys_execveat 282 common userfaultfd sys_userfaultfd 283 common membarrier sys_membarrier 284 common mlock2 sys_mlock2 285 common copy_file_range sys_copy_file_range 286 common preadv2 sys_preadv2 compat_sys_preadv2 287 common pwritev2 sys_pwritev2 compat_sys_pwritev2 288 common pkey_mprotect sys_pkey_mprotect 289 common pkey_alloc sys_pkey_alloc 290 common pkey_free sys_pkey_free 291 common statx sys_statx 292 time32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents 292 64 io_pgetevents sys_io_pgetevents 293 common rseq sys_rseq 294 common kexec_file_load sys_kexec_file_load 403 32 clock_gettime64 sys_clock_gettime 404 32 clock_settime64 sys_clock_settime 405 32 clock_adjtime64 sys_clock_adjtime 406 32 clock_getres_time64 sys_clock_getres 407 32 clock_nanosleep_time64 sys_clock_nanosleep 408 32 timer_gettime64 sys_timer_gettime 409 32 timer_settime64 sys_timer_settime 410 32 timerfd_gettime64 sys_timerfd_gettime 411 32 timerfd_settime64 sys_timerfd_settime 412 32 utimensat_time64 sys_utimensat 413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 32 mq_timedsend_time64 sys_mq_timedsend 419 32 mq_timedreceive_time64 sys_mq_timedreceive 420 32 semtimedop_time64 sys_semtimedop 421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 422 32 futex_time64 sys_futex 423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 447 memfd_secret memfd_secret sys_memfd_secret 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # ARM (compat(emulated)) # - arch/arm64/tools/syscall_32.tbl arm_compat_syscall_tbl = """ 0 common restart_syscall sys_restart_syscall 1 common exit sys_exit 2 common fork sys_fork 3 common read sys_read 4 common write sys_write 5 common open sys_open compat_sys_open 6 common close sys_close 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 common execve sys_execve compat_sys_execve 12 common chdir sys_chdir 14 common mknod sys_mknod 15 common chmod sys_chmod 16 common lchown sys_lchown16 19 common lseek sys_lseek compat_sys_lseek 20 common getpid sys_getpid 21 common mount sys_mount 23 common setuid sys_setuid16 24 common getuid sys_getuid16 26 common ptrace sys_ptrace compat_sys_ptrace 29 common pause sys_pause 33 common access sys_access 34 common nice sys_nice 36 common sync sys_sync 37 common kill sys_kill 38 common rename sys_rename 39 common mkdir sys_mkdir 40 common rmdir sys_rmdir 41 common dup sys_dup 42 common pipe sys_pipe 43 common times sys_times compat_sys_times 45 common brk sys_brk 46 common setgid sys_setgid16 47 common getgid sys_getgid16 49 common geteuid sys_geteuid16 50 common getegid sys_getegid16 51 common acct sys_acct 52 common umount2 sys_umount 54 common ioctl sys_ioctl compat_sys_ioctl 55 common fcntl sys_fcntl compat_sys_fcntl 57 common setpgid sys_setpgid 60 common umask sys_umask 61 common chroot sys_chroot 62 common ustat sys_ustat compat_sys_ustat 63 common dup2 sys_dup2 64 common getppid sys_getppid 65 common getpgrp sys_getpgrp 66 common setsid sys_setsid 67 common sigaction sys_sigaction compat_sys_sigaction 70 common setreuid sys_setreuid16 71 common setregid sys_setregid16 72 common sigsuspend sys_sigsuspend 73 common sigpending sys_sigpending compat_sys_sigpending 74 common sethostname sys_sethostname 75 common setrlimit sys_setrlimit compat_sys_setrlimit 77 common getrusage sys_getrusage compat_sys_getrusage 78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday 79 common settimeofday sys_settimeofday compat_sys_settimeofday 80 common getgroups sys_getgroups16 81 common setgroups sys_setgroups16 83 common symlink sys_symlink 85 common readlink sys_readlink 86 common uselib sys_uselib 87 common swapon sys_swapon 88 common reboot sys_reboot 91 common munmap sys_munmap 92 common truncate sys_truncate compat_sys_truncate 93 common ftruncate sys_ftruncate compat_sys_ftruncate 94 common fchmod sys_fchmod 95 common fchown sys_fchown16 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority 99 common statfs sys_statfs compat_sys_statfs 100 common fstatfs sys_fstatfs compat_sys_fstatfs 103 common syslog sys_syslog 104 common setitimer sys_setitimer compat_sys_setitimer 105 common getitimer sys_getitimer compat_sys_getitimer 106 common stat sys_newstat compat_sys_newstat 107 common lstat sys_newlstat compat_sys_newlstat 108 common fstat sys_newfstat compat_sys_newfstat 111 common vhangup sys_vhangup 114 common wait4 sys_wait4 compat_sys_wait4 115 common swapoff sys_swapoff 116 common sysinfo sys_sysinfo compat_sys_sysinfo 118 common fsync sys_fsync 119 common sigreturn sys_sigreturn_wrapper compat_sys_sigreturn 120 common clone sys_clone 121 common setdomainname sys_setdomainname 122 common uname sys_newuname 124 common adjtimex sys_adjtimex_time32 125 common mprotect sys_mprotect 126 common sigprocmask sys_sigprocmask compat_sys_sigprocmask 128 common init_module sys_init_module 129 common delete_module sys_delete_module 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir 134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality 138 common setfsuid sys_setfsuid16 139 common setfsgid sys_setfsgid16 140 common _llseek sys_llseek 141 common getdents sys_getdents compat_sys_getdents 142 common _newselect sys_select compat_sys_select 143 common flock sys_flock 144 common msync sys_msync 145 common readv sys_readv 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync 149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall 153 common munlockall sys_munlockall 154 common sched_setparam sys_sched_setparam 155 common sched_getparam sys_sched_getparam 156 common sched_setscheduler sys_sched_setscheduler 157 common sched_getscheduler sys_sched_getscheduler 158 common sched_yield sys_sched_yield 159 common sched_get_priority_max sys_sched_get_priority_max 160 common sched_get_priority_min sys_sched_get_priority_min 161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 162 common nanosleep sys_nanosleep_time32 163 common mremap sys_mremap 164 common setresuid sys_setresuid16 165 common getresuid sys_getresuid16 168 common poll sys_poll 169 common nfsservctl sys_ni_syscall 170 common setresgid sys_setresgid16 171 common getresgid sys_getresgid16 172 common prctl sys_prctl 173 common rt_sigreturn sys_rt_sigreturn_wrapper compat_sys_rt_sigreturn 174 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction 175 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask 176 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending 177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 178 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 179 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 180 common pread64 sys_pread64 compat_sys_aarch32_pread64 181 common pwrite64 sys_pwrite64 compat_sys_aarch32_pwrite64 182 common chown sys_chown16 183 common getcwd sys_getcwd 184 common capget sys_capget 185 common capset sys_capset 186 common sigaltstack sys_sigaltstack compat_sys_sigaltstack 187 common sendfile sys_sendfile compat_sys_sendfile 190 common vfork sys_vfork 191 common ugetrlimit sys_getrlimit compat_sys_getrlimit 192 common mmap2 sys_mmap2 compat_sys_aarch32_mmap2 193 common truncate64 sys_truncate64 compat_sys_aarch32_truncate64 194 common ftruncate64 sys_ftruncate64 compat_sys_aarch32_ftruncate64 195 common stat64 sys_stat64 196 common lstat64 sys_lstat64 197 common fstat64 sys_fstat64 198 common lchown32 sys_lchown 199 common getuid32 sys_getuid 200 common getgid32 sys_getgid 201 common geteuid32 sys_geteuid 202 common getegid32 sys_getegid 203 common setreuid32 sys_setreuid 204 common setregid32 sys_setregid 205 common getgroups32 sys_getgroups 206 common setgroups32 sys_setgroups 207 common fchown32 sys_fchown 208 common setresuid32 sys_setresuid 209 common getresuid32 sys_getresuid 210 common setresgid32 sys_setresgid 211 common getresgid32 sys_getresgid 212 common chown32 sys_chown 213 common setuid32 sys_setuid 214 common setgid32 sys_setgid 215 common setfsuid32 sys_setfsuid 216 common setfsgid32 sys_setfsgid 217 common getdents64 sys_getdents64 218 common pivot_root sys_pivot_root 219 common mincore sys_mincore 220 common madvise sys_madvise 221 common fcntl64 sys_fcntl64 compat_sys_fcntl64 224 common gettid sys_gettid 225 common readahead sys_readahead compat_sys_aarch32_readahead 226 common setxattr sys_setxattr 227 common lsetxattr sys_lsetxattr 228 common fsetxattr sys_fsetxattr 229 common getxattr sys_getxattr 230 common lgetxattr sys_lgetxattr 231 common fgetxattr sys_fgetxattr 232 common listxattr sys_listxattr 233 common llistxattr sys_llistxattr 234 common flistxattr sys_flistxattr 235 common removexattr sys_removexattr 236 common lremovexattr sys_lremovexattr 237 common fremovexattr sys_fremovexattr 238 common tkill sys_tkill 239 common sendfile64 sys_sendfile64 240 common futex sys_futex_time32 241 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity 242 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity 243 common io_setup sys_io_setup compat_sys_io_setup 244 common io_destroy sys_io_destroy 245 common io_getevents sys_io_getevents_time32 246 common io_submit sys_io_submit compat_sys_io_submit 247 common io_cancel sys_io_cancel 248 common exit_group sys_exit_group 249 common lookup_dcookie sys_ni_syscall 250 common epoll_create sys_epoll_create 251 common epoll_ctl sys_epoll_ctl 252 common epoll_wait sys_epoll_wait 253 common remap_file_pages sys_remap_file_pages 256 common set_tid_address sys_set_tid_address 257 common timer_create sys_timer_create compat_sys_timer_create 258 common timer_settime sys_timer_settime32 259 common timer_gettime sys_timer_gettime32 260 common timer_getoverrun sys_timer_getoverrun 261 common timer_delete sys_timer_delete 262 common clock_settime sys_clock_settime32 263 common clock_gettime sys_clock_gettime32 264 common clock_getres sys_clock_getres_time32 265 common clock_nanosleep sys_clock_nanosleep_time32 266 common statfs64 sys_statfs64_wrapper compat_sys_aarch32_statfs64 267 common fstatfs64 sys_fstatfs64_wrapper compat_sys_aarch32_fstatfs64 268 common tgkill sys_tgkill 269 common utimes sys_utimes_time32 270 common arm_fadvise64_64 sys_arm_fadvise64_64 compat_sys_aarch32_fadvise64_64 271 common pciconfig_iobase sys_pciconfig_iobase 272 common pciconfig_read sys_pciconfig_read 273 common pciconfig_write sys_pciconfig_write 274 common mq_open sys_mq_open compat_sys_mq_open 275 common mq_unlink sys_mq_unlink 276 common mq_timedsend sys_mq_timedsend_time32 277 common mq_timedreceive sys_mq_timedreceive_time32 278 common mq_notify sys_mq_notify compat_sys_mq_notify 279 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr 280 common waitid sys_waitid compat_sys_waitid 281 common socket sys_socket 282 common bind sys_bind 283 common connect sys_connect 284 common listen sys_listen 285 common accept sys_accept 286 common getsockname sys_getsockname 287 common getpeername sys_getpeername 288 common socketpair sys_socketpair 289 common send sys_send 290 common sendto sys_sendto 291 common recv sys_recv compat_sys_recv 292 common recvfrom sys_recvfrom compat_sys_recvfrom 293 common shutdown sys_shutdown 294 common setsockopt sys_setsockopt 295 common getsockopt sys_getsockopt 296 common sendmsg sys_sendmsg compat_sys_sendmsg 297 common recvmsg sys_recvmsg compat_sys_recvmsg 298 common semop sys_semop 299 common semget sys_semget 300 common semctl sys_old_semctl compat_sys_old_semctl 301 common msgsnd sys_msgsnd compat_sys_msgsnd 302 common msgrcv sys_msgrcv compat_sys_msgrcv 303 common msgget sys_msgget 304 common msgctl sys_old_msgctl compat_sys_old_msgctl 305 common shmat sys_shmat compat_sys_shmat 306 common shmdt sys_shmdt 307 common shmget sys_shmget 308 common shmctl sys_old_shmctl compat_sys_old_shmctl 309 common add_key sys_add_key 310 common request_key sys_request_key 311 common keyctl sys_keyctl compat_sys_keyctl 312 common semtimedop sys_semtimedop_time32 313 common vserver sys_ni_syscall 314 common ioprio_set sys_ioprio_set 315 common ioprio_get sys_ioprio_get 316 common inotify_init sys_inotify_init 317 common inotify_add_watch sys_inotify_add_watch 318 common inotify_rm_watch sys_inotify_rm_watch 319 common mbind sys_mbind 320 common get_mempolicy sys_get_mempolicy 321 common set_mempolicy sys_set_mempolicy 322 common openat sys_openat compat_sys_openat 323 common mkdirat sys_mkdirat 324 common mknodat sys_mknodat 325 common fchownat sys_fchownat 326 common futimesat sys_futimesat_time32 327 common fstatat64 sys_fstatat64 328 common unlinkat sys_unlinkat 329 common renameat sys_renameat 330 common linkat sys_linkat 331 common symlinkat sys_symlinkat 332 common readlinkat sys_readlinkat 333 common fchmodat sys_fchmodat 334 common faccessat sys_faccessat 335 common pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 336 common ppoll sys_ppoll_time32 compat_sys_ppoll_time32 337 common unshare sys_unshare 338 common set_robust_list sys_set_robust_list compat_sys_set_robust_list 339 common get_robust_list sys_get_robust_list compat_sys_get_robust_list 340 common splice sys_splice 341 common arm_sync_file_range sys_sync_file_range2 compat_sys_aarch32_sync_file_range2 342 common tee sys_tee 343 common vmsplice sys_vmsplice 344 common move_pages sys_move_pages 345 common getcpu sys_getcpu 346 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 347 common kexec_load sys_kexec_load compat_sys_kexec_load 348 common utimensat sys_utimensat_time32 349 common signalfd sys_signalfd compat_sys_signalfd 350 common timerfd_create sys_timerfd_create 351 common eventfd sys_eventfd 352 common fallocate sys_fallocate compat_sys_aarch32_fallocate 353 common timerfd_settime sys_timerfd_settime32 354 common timerfd_gettime sys_timerfd_gettime32 355 common signalfd4 sys_signalfd4 compat_sys_signalfd4 356 common eventfd2 sys_eventfd2 357 common epoll_create1 sys_epoll_create1 358 common dup3 sys_dup3 359 common pipe2 sys_pipe2 360 common inotify_init1 sys_inotify_init1 361 common preadv sys_preadv compat_sys_preadv 362 common pwritev sys_pwritev compat_sys_pwritev 363 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 364 common perf_event_open sys_perf_event_open 365 common recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 366 common accept4 sys_accept4 367 common fanotify_init sys_fanotify_init 368 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark 369 common prlimit64 sys_prlimit64 370 common name_to_handle_at sys_name_to_handle_at 371 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at 372 common clock_adjtime sys_clock_adjtime32 373 common syncfs sys_syncfs 374 common sendmmsg sys_sendmmsg compat_sys_sendmmsg 375 common setns sys_setns 376 common process_vm_readv sys_process_vm_readv 377 common process_vm_writev sys_process_vm_writev 378 common kcmp sys_kcmp 379 common finit_module sys_finit_module 380 common sched_setattr sys_sched_setattr 381 common sched_getattr sys_sched_getattr 382 common renameat2 sys_renameat2 383 common seccomp sys_seccomp 384 common getrandom sys_getrandom 385 common memfd_create sys_memfd_create 386 common bpf sys_bpf 387 common execveat sys_execveat compat_sys_execveat 388 common userfaultfd sys_userfaultfd 389 common membarrier sys_membarrier 390 common mlock2 sys_mlock2 391 common copy_file_range sys_copy_file_range 392 common preadv2 sys_preadv2 compat_sys_preadv2 393 common pwritev2 sys_pwritev2 compat_sys_pwritev2 394 common pkey_mprotect sys_pkey_mprotect 395 common pkey_alloc sys_pkey_alloc 396 common pkey_free sys_pkey_free 397 common statx sys_statx 398 common rseq sys_rseq 399 common io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents 400 common migrate_pages sys_migrate_pages 401 common kexec_file_load sys_kexec_file_load 403 common clock_gettime64 sys_clock_gettime 404 common clock_settime64 sys_clock_settime 405 common clock_adjtime64 sys_clock_adjtime 406 common clock_getres_time64 sys_clock_getres 407 common clock_nanosleep_time64 sys_clock_nanosleep 408 common timer_gettime64 sys_timer_gettime 409 common timer_settime64 sys_timer_settime 410 common timerfd_gettime64 sys_timerfd_gettime 411 common timerfd_settime64 sys_timerfd_settime 412 common utimensat_time64 sys_utimensat 413 common pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 common ppoll_time64 sys_ppoll compat_sys_ppoll_time64 416 common io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 common recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 common mq_timedsend_time64 sys_mq_timedsend 419 common mq_timedreceive_time64 sys_mq_timedreceive 420 common semtimedop_time64 sys_semtimedop 421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 422 common futex_time64 sys_futex 423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # ARM (native) # - arch/arm/tools/syscall.tbl arm_native_syscall_tbl = """ 0 common restart_syscall sys_restart_syscall 1 common exit sys_exit 2 common fork sys_fork 3 common read sys_read 4 common write sys_write 5 common open sys_open 6 common close sys_close 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 common execve sys_execve 12 common chdir sys_chdir 13 oabi time sys_time32 14 common mknod sys_mknod 15 common chmod sys_chmod 16 common lchown sys_lchown16 19 common lseek sys_lseek 20 common getpid sys_getpid 21 common mount sys_mount 22 oabi umount sys_oldumount 23 common setuid sys_setuid16 24 common getuid sys_getuid16 25 oabi stime sys_stime32 26 common ptrace sys_ptrace 27 oabi alarm sys_alarm 29 common pause sys_pause 30 oabi utime sys_utime32 33 common access sys_access 34 common nice sys_nice 36 common sync sys_sync 37 common kill sys_kill 38 common rename sys_rename 39 common mkdir sys_mkdir 40 common rmdir sys_rmdir 41 common dup sys_dup 42 common pipe sys_pipe 43 common times sys_times 45 common brk sys_brk 46 common setgid sys_setgid16 47 common getgid sys_getgid16 49 common geteuid sys_geteuid16 50 common getegid sys_getegid16 51 common acct sys_acct 52 common umount2 sys_umount 54 common ioctl sys_ioctl 55 common fcntl sys_fcntl 57 common setpgid sys_setpgid 60 common umask sys_umask 61 common chroot sys_chroot 62 common ustat sys_ustat 63 common dup2 sys_dup2 64 common getppid sys_getppid 65 common getpgrp sys_getpgrp 66 common setsid sys_setsid 67 common sigaction sys_sigaction 70 common setreuid sys_setreuid16 71 common setregid sys_setregid16 72 common sigsuspend sys_sigsuspend 73 common sigpending sys_sigpending 74 common sethostname sys_sethostname 75 common setrlimit sys_setrlimit 76 oabi getrlimit sys_old_getrlimit 77 common getrusage sys_getrusage 78 common gettimeofday sys_gettimeofday 79 common settimeofday sys_settimeofday 80 common getgroups sys_getgroups16 81 common setgroups sys_setgroups16 82 oabi select sys_old_select 83 common symlink sys_symlink 85 common readlink sys_readlink 86 common uselib sys_uselib 87 common swapon sys_swapon 88 common reboot sys_reboot 89 oabi readdir sys_old_readdir 90 oabi mmap sys_old_mmap 91 common munmap sys_munmap 92 common truncate sys_truncate 93 common ftruncate sys_ftruncate 94 common fchmod sys_fchmod 95 common fchown sys_fchown16 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority 99 common statfs sys_statfs 100 common fstatfs sys_fstatfs 102 oabi socketcall sys_socketcall sys_oabi_socketcall 103 common syslog sys_syslog 104 common setitimer sys_setitimer 105 common getitimer sys_getitimer 106 common stat sys_newstat 107 common lstat sys_newlstat 108 common fstat sys_newfstat 111 common vhangup sys_vhangup 113 oabi syscall sys_syscall 114 common wait4 sys_wait4 115 common swapoff sys_swapoff 116 common sysinfo sys_sysinfo 117 oabi ipc sys_ipc sys_oabi_ipc 118 common fsync sys_fsync 119 common sigreturn sys_sigreturn_wrapper 120 common clone sys_clone 121 common setdomainname sys_setdomainname 122 common uname sys_newuname 124 common adjtimex sys_adjtimex_time32 125 common mprotect sys_mprotect 126 common sigprocmask sys_sigprocmask 128 common init_module sys_init_module 129 common delete_module sys_delete_module 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir 134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality 138 common setfsuid sys_setfsuid16 139 common setfsgid sys_setfsgid16 140 common _llseek sys_llseek 141 common getdents sys_getdents 142 common _newselect sys_select 143 common flock sys_flock 144 common msync sys_msync 145 common readv sys_readv 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync 149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall 153 common munlockall sys_munlockall 154 common sched_setparam sys_sched_setparam 155 common sched_getparam sys_sched_getparam 156 common sched_setscheduler sys_sched_setscheduler 157 common sched_getscheduler sys_sched_getscheduler 158 common sched_yield sys_sched_yield 159 common sched_get_priority_max sys_sched_get_priority_max 160 common sched_get_priority_min sys_sched_get_priority_min 161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 162 common nanosleep sys_nanosleep_time32 163 common mremap sys_mremap 164 common setresuid sys_setresuid16 165 common getresuid sys_getresuid16 168 common poll sys_poll 169 common nfsservctl 170 common setresgid sys_setresgid16 171 common getresgid sys_getresgid16 172 common prctl sys_prctl 173 common rt_sigreturn sys_rt_sigreturn_wrapper 174 common rt_sigaction sys_rt_sigaction 175 common rt_sigprocmask sys_rt_sigprocmask 176 common rt_sigpending sys_rt_sigpending 177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 178 common rt_sigqueueinfo sys_rt_sigqueueinfo 179 common rt_sigsuspend sys_rt_sigsuspend 180 common pread64 sys_pread64 sys_oabi_pread64 181 common pwrite64 sys_pwrite64 sys_oabi_pwrite64 182 common chown sys_chown16 183 common getcwd sys_getcwd 184 common capget sys_capget 185 common capset sys_capset 186 common sigaltstack sys_sigaltstack 187 common sendfile sys_sendfile 190 common vfork sys_vfork 191 common ugetrlimit sys_getrlimit 192 common mmap2 sys_mmap2 193 common truncate64 sys_truncate64 sys_oabi_truncate64 194 common ftruncate64 sys_ftruncate64 sys_oabi_ftruncate64 195 common stat64 sys_stat64 sys_oabi_stat64 196 common lstat64 sys_lstat64 sys_oabi_lstat64 197 common fstat64 sys_fstat64 sys_oabi_fstat64 198 common lchown32 sys_lchown 199 common getuid32 sys_getuid 200 common getgid32 sys_getgid 201 common geteuid32 sys_geteuid 202 common getegid32 sys_getegid 203 common setreuid32 sys_setreuid 204 common setregid32 sys_setregid 205 common getgroups32 sys_getgroups 206 common setgroups32 sys_setgroups 207 common fchown32 sys_fchown 208 common setresuid32 sys_setresuid 209 common getresuid32 sys_getresuid 210 common setresgid32 sys_setresgid 211 common getresgid32 sys_getresgid 212 common chown32 sys_chown 213 common setuid32 sys_setuid 214 common setgid32 sys_setgid 215 common setfsuid32 sys_setfsuid 216 common setfsgid32 sys_setfsgid 217 common getdents64 sys_getdents64 218 common pivot_root sys_pivot_root 219 common mincore sys_mincore 220 common madvise sys_madvise 221 common fcntl64 sys_fcntl64 sys_oabi_fcntl64 224 common gettid sys_gettid 225 common readahead sys_readahead sys_oabi_readahead 226 common setxattr sys_setxattr 227 common lsetxattr sys_lsetxattr 228 common fsetxattr sys_fsetxattr 229 common getxattr sys_getxattr 230 common lgetxattr sys_lgetxattr 231 common fgetxattr sys_fgetxattr 232 common listxattr sys_listxattr 233 common llistxattr sys_llistxattr 234 common flistxattr sys_flistxattr 235 common removexattr sys_removexattr 236 common lremovexattr sys_lremovexattr 237 common fremovexattr sys_fremovexattr 238 common tkill sys_tkill 239 common sendfile64 sys_sendfile64 240 common futex sys_futex_time32 241 common sched_setaffinity sys_sched_setaffinity 242 common sched_getaffinity sys_sched_getaffinity 243 common io_setup sys_io_setup 244 common io_destroy sys_io_destroy 245 common io_getevents sys_io_getevents_time32 246 common io_submit sys_io_submit 247 common io_cancel sys_io_cancel 248 common exit_group sys_exit_group 249 common lookup_dcookie sys_ni_syscall 250 common epoll_create sys_epoll_create 251 common epoll_ctl sys_epoll_ctl sys_oabi_epoll_ctl 252 common epoll_wait sys_epoll_wait 253 common remap_file_pages sys_remap_file_pages 256 common set_tid_address sys_set_tid_address 257 common timer_create sys_timer_create 258 common timer_settime sys_timer_settime32 259 common timer_gettime sys_timer_gettime32 260 common timer_getoverrun sys_timer_getoverrun 261 common timer_delete sys_timer_delete 262 common clock_settime sys_clock_settime32 263 common clock_gettime sys_clock_gettime32 264 common clock_getres sys_clock_getres_time32 265 common clock_nanosleep sys_clock_nanosleep_time32 266 common statfs64 sys_statfs64_wrapper 267 common fstatfs64 sys_fstatfs64_wrapper 268 common tgkill sys_tgkill 269 common utimes sys_utimes_time32 270 common arm_fadvise64_64 sys_arm_fadvise64_64 271 common pciconfig_iobase sys_pciconfig_iobase 272 common pciconfig_read sys_pciconfig_read 273 common pciconfig_write sys_pciconfig_write 274 common mq_open sys_mq_open 275 common mq_unlink sys_mq_unlink 276 common mq_timedsend sys_mq_timedsend_time32 277 common mq_timedreceive sys_mq_timedreceive_time32 278 common mq_notify sys_mq_notify 279 common mq_getsetattr sys_mq_getsetattr 280 common waitid sys_waitid 281 common socket sys_socket 282 common bind sys_bind sys_oabi_bind 283 common connect sys_connect sys_oabi_connect 284 common listen sys_listen 285 common accept sys_accept 286 common getsockname sys_getsockname 287 common getpeername sys_getpeername 288 common socketpair sys_socketpair 289 common send sys_send 290 common sendto sys_sendto sys_oabi_sendto 291 common recv sys_recv 292 common recvfrom sys_recvfrom 293 common shutdown sys_shutdown 294 common setsockopt sys_setsockopt 295 common getsockopt sys_getsockopt 296 common sendmsg sys_sendmsg sys_oabi_sendmsg 297 common recvmsg sys_recvmsg 298 common semop sys_semop sys_oabi_semop 299 common semget sys_semget 300 common semctl sys_old_semctl 301 common msgsnd sys_msgsnd 302 common msgrcv sys_msgrcv 303 common msgget sys_msgget 304 common msgctl sys_old_msgctl 305 common shmat sys_shmat 306 common shmdt sys_shmdt 307 common shmget sys_shmget 308 common shmctl sys_old_shmctl 309 common add_key sys_add_key 310 common request_key sys_request_key 311 common keyctl sys_keyctl 312 common semtimedop sys_semtimedop_time32 sys_oabi_semtimedop 313 common vserver 314 common ioprio_set sys_ioprio_set 315 common ioprio_get sys_ioprio_get 316 common inotify_init sys_inotify_init 317 common inotify_add_watch sys_inotify_add_watch 318 common inotify_rm_watch sys_inotify_rm_watch 319 common mbind sys_mbind 320 common get_mempolicy sys_get_mempolicy 321 common set_mempolicy sys_set_mempolicy 322 common openat sys_openat 323 common mkdirat sys_mkdirat 324 common mknodat sys_mknodat 325 common fchownat sys_fchownat 326 common futimesat sys_futimesat_time32 327 common fstatat64 sys_fstatat64 sys_oabi_fstatat64 328 common unlinkat sys_unlinkat 329 common renameat sys_renameat 330 common linkat sys_linkat 331 common symlinkat sys_symlinkat 332 common readlinkat sys_readlinkat 333 common fchmodat sys_fchmodat 334 common faccessat sys_faccessat 335 common pselect6 sys_pselect6_time32 336 common ppoll sys_ppoll_time32 337 common unshare sys_unshare 338 common set_robust_list sys_set_robust_list 339 common get_robust_list sys_get_robust_list 340 common splice sys_splice 341 common arm_sync_file_range sys_sync_file_range2 342 common tee sys_tee 343 common vmsplice sys_vmsplice 344 common move_pages sys_move_pages 345 common getcpu sys_getcpu 346 common epoll_pwait sys_epoll_pwait 347 common kexec_load sys_kexec_load 348 common utimensat sys_utimensat_time32 349 common signalfd sys_signalfd 350 common timerfd_create sys_timerfd_create 351 common eventfd sys_eventfd 352 common fallocate sys_fallocate 353 common timerfd_settime sys_timerfd_settime32 354 common timerfd_gettime sys_timerfd_gettime32 355 common signalfd4 sys_signalfd4 356 common eventfd2 sys_eventfd2 357 common epoll_create1 sys_epoll_create1 358 common dup3 sys_dup3 359 common pipe2 sys_pipe2 360 common inotify_init1 sys_inotify_init1 361 common preadv sys_preadv 362 common pwritev sys_pwritev 363 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 364 common perf_event_open sys_perf_event_open 365 common recvmmsg sys_recvmmsg_time32 366 common accept4 sys_accept4 367 common fanotify_init sys_fanotify_init 368 common fanotify_mark sys_fanotify_mark 369 common prlimit64 sys_prlimit64 370 common name_to_handle_at sys_name_to_handle_at 371 common open_by_handle_at sys_open_by_handle_at 372 common clock_adjtime sys_clock_adjtime32 373 common syncfs sys_syncfs 374 common sendmmsg sys_sendmmsg 375 common setns sys_setns 376 common process_vm_readv sys_process_vm_readv 377 common process_vm_writev sys_process_vm_writev 378 common kcmp sys_kcmp 379 common finit_module sys_finit_module 380 common sched_setattr sys_sched_setattr 381 common sched_getattr sys_sched_getattr 382 common renameat2 sys_renameat2 383 common seccomp sys_seccomp 384 common getrandom sys_getrandom 385 common memfd_create sys_memfd_create 386 common bpf sys_bpf 387 common execveat sys_execveat 388 common userfaultfd sys_userfaultfd 389 common membarrier sys_membarrier 390 common mlock2 sys_mlock2 391 common copy_file_range sys_copy_file_range 392 common preadv2 sys_preadv2 393 common pwritev2 sys_pwritev2 394 common pkey_mprotect sys_pkey_mprotect 395 common pkey_alloc sys_pkey_alloc 396 common pkey_free sys_pkey_free 397 common statx sys_statx 398 common rseq sys_rseq 399 common io_pgetevents sys_io_pgetevents_time32 400 common migrate_pages sys_migrate_pages 401 common kexec_file_load sys_kexec_file_load 403 common clock_gettime64 sys_clock_gettime 404 common clock_settime64 sys_clock_settime 405 common clock_adjtime64 sys_clock_adjtime 406 common clock_getres_time64 sys_clock_getres 407 common clock_nanosleep_time64 sys_clock_nanosleep 408 common timer_gettime64 sys_timer_gettime 409 common timer_settime64 sys_timer_settime 410 common timerfd_gettime64 sys_timerfd_gettime 411 common timerfd_settime64 sys_timerfd_settime 412 common utimensat_time64 sys_utimensat 413 common pselect6_time64 sys_pselect6 414 common ppoll_time64 sys_ppoll 416 common io_pgetevents_time64 sys_io_pgetevents 417 common recvmmsg_time64 sys_recvmmsg 418 common mq_timedsend_time64 sys_mq_timedsend 419 common mq_timedreceive_time64 sys_mq_timedreceive 420 common semtimedop_time64 sys_semtimedop 421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait 422 common futex_time64 sys_futex 423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # MIPS o32 (backward compatible ABI, present in /lib) # - arch/mips/kernel/syscalls/syscall_o32.tbl mips_o32_syscall_tbl = """ 0 o32 syscall sys_syscall sys32_syscall 1 o32 exit sys_exit 2 o32 fork __sys_fork 3 o32 read sys_read 4 o32 write sys_write 5 o32 open sys_open compat_sys_open 6 o32 close sys_close 7 o32 waitpid sys_waitpid 8 o32 creat sys_creat 9 o32 link sys_link 10 o32 unlink sys_unlink 11 o32 execve sys_execve compat_sys_execve 12 o32 chdir sys_chdir 13 o32 time sys_time32 14 o32 mknod sys_mknod 15 o32 chmod sys_chmod 16 o32 lchown sys_lchown 17 o32 break sys_ni_syscall 18 o32 unused18 sys_ni_syscall 19 o32 lseek sys_lseek compat_sys_lseek 20 o32 getpid sys_getpid 21 o32 mount sys_mount 22 o32 umount sys_oldumount 23 o32 setuid sys_setuid 24 o32 getuid sys_getuid 25 o32 stime sys_stime32 26 o32 ptrace sys_ptrace compat_sys_ptrace 27 o32 alarm sys_alarm 28 o32 unused28 sys_ni_syscall 29 o32 pause sys_pause 30 o32 utime sys_utime32 31 o32 stty sys_ni_syscall 32 o32 gtty sys_ni_syscall 33 o32 access sys_access 34 o32 nice sys_nice 35 o32 ftime sys_ni_syscall 36 o32 sync sys_sync 37 o32 kill sys_kill 38 o32 rename sys_rename 39 o32 mkdir sys_mkdir 40 o32 rmdir sys_rmdir 41 o32 dup sys_dup 42 o32 pipe sysm_pipe 43 o32 times sys_times compat_sys_times 44 o32 prof sys_ni_syscall 45 o32 brk sys_brk 46 o32 setgid sys_setgid 47 o32 getgid sys_getgid 48 o32 signal sys_ni_syscall 49 o32 geteuid sys_geteuid 50 o32 getegid sys_getegid 51 o32 acct sys_acct 52 o32 umount2 sys_umount 53 o32 lock sys_ni_syscall 54 o32 ioctl sys_ioctl compat_sys_ioctl 55 o32 fcntl sys_fcntl compat_sys_fcntl 56 o32 mpx sys_ni_syscall 57 o32 setpgid sys_setpgid 58 o32 ulimit sys_ni_syscall 59 o32 unused59 sys_olduname 60 o32 umask sys_umask 61 o32 chroot sys_chroot 62 o32 ustat sys_ustat compat_sys_ustat 63 o32 dup2 sys_dup2 64 o32 getppid sys_getppid 65 o32 getpgrp sys_getpgrp 66 o32 setsid sys_setsid 67 o32 sigaction sys_sigaction sys_32_sigaction 68 o32 sgetmask sys_sgetmask 69 o32 ssetmask sys_ssetmask 70 o32 setreuid sys_setreuid 71 o32 setregid sys_setregid 72 o32 sigsuspend sys_sigsuspend sys32_sigsuspend 73 o32 sigpending sys_sigpending compat_sys_sigpending 74 o32 sethostname sys_sethostname 75 o32 setrlimit sys_setrlimit compat_sys_setrlimit 76 o32 getrlimit sys_getrlimit compat_sys_getrlimit 77 o32 getrusage sys_getrusage compat_sys_getrusage 78 o32 gettimeofday sys_gettimeofday compat_sys_gettimeofday 79 o32 settimeofday sys_settimeofday compat_sys_settimeofday 80 o32 getgroups sys_getgroups 81 o32 setgroups sys_setgroups 82 o32 reserved82 sys_ni_syscall 83 o32 symlink sys_symlink 84 o32 unused84 sys_ni_syscall 85 o32 readlink sys_readlink 86 o32 uselib sys_uselib 87 o32 swapon sys_swapon 88 o32 reboot sys_reboot 89 o32 readdir sys_old_readdir compat_sys_old_readdir 90 o32 mmap sys_mips_mmap 91 o32 munmap sys_munmap 92 o32 truncate sys_truncate compat_sys_truncate 93 o32 ftruncate sys_ftruncate compat_sys_ftruncate 94 o32 fchmod sys_fchmod 95 o32 fchown sys_fchown 96 o32 getpriority sys_getpriority 97 o32 setpriority sys_setpriority 98 o32 profil sys_ni_syscall 99 o32 statfs sys_statfs compat_sys_statfs 100 o32 fstatfs sys_fstatfs compat_sys_fstatfs 101 o32 ioperm sys_ni_syscall 102 o32 socketcall sys_socketcall compat_sys_socketcall 103 o32 syslog sys_syslog 104 o32 setitimer sys_setitimer compat_sys_setitimer 105 o32 getitimer sys_getitimer compat_sys_getitimer 106 o32 stat sys_newstat compat_sys_newstat 107 o32 lstat sys_newlstat compat_sys_newlstat 108 o32 fstat sys_newfstat compat_sys_newfstat 109 o32 unused109 sys_uname 110 o32 iopl sys_ni_syscall 111 o32 vhangup sys_vhangup 112 o32 idle sys_ni_syscall 113 o32 vm86 sys_ni_syscall 114 o32 wait4 sys_wait4 compat_sys_wait4 115 o32 swapoff sys_swapoff 116 o32 sysinfo sys_sysinfo compat_sys_sysinfo 117 o32 ipc sys_ipc compat_sys_ipc 118 o32 fsync sys_fsync 119 o32 sigreturn sys_sigreturn sys32_sigreturn 120 o32 clone __sys_clone 121 o32 setdomainname sys_setdomainname 122 o32 uname sys_newuname 123 o32 modify_ldt sys_ni_syscall 124 o32 adjtimex sys_adjtimex_time32 125 o32 mprotect sys_mprotect 126 o32 sigprocmask sys_sigprocmask compat_sys_sigprocmask 127 o32 create_module sys_ni_syscall 128 o32 init_module sys_init_module 129 o32 delete_module sys_delete_module 130 o32 get_kernel_syms sys_ni_syscall 131 o32 quotactl sys_quotactl 132 o32 getpgid sys_getpgid 133 o32 fchdir sys_fchdir 134 o32 bdflush sys_ni_syscall 135 o32 sysfs sys_sysfs 136 o32 personality sys_personality sys_32_personality 137 o32 afs_syscall sys_ni_syscall 138 o32 setfsuid sys_setfsuid 139 o32 setfsgid sys_setfsgid 140 o32 _llseek sys_llseek sys_32_llseek 141 o32 getdents sys_getdents compat_sys_getdents 142 o32 _newselect sys_select compat_sys_select 143 o32 flock sys_flock 144 o32 msync sys_msync 145 o32 readv sys_readv 146 o32 writev sys_writev 147 o32 cacheflush sys_cacheflush 148 o32 cachectl sys_cachectl 149 o32 sysmips __sys_sysmips 150 o32 unused150 sys_ni_syscall 151 o32 getsid sys_getsid 152 o32 fdatasync sys_fdatasync 153 o32 _sysctl sys_ni_syscall 154 o32 mlock sys_mlock 155 o32 munlock sys_munlock 156 o32 mlockall sys_mlockall 157 o32 munlockall sys_munlockall 158 o32 sched_setparam sys_sched_setparam 159 o32 sched_getparam sys_sched_getparam 160 o32 sched_setscheduler sys_sched_setscheduler 161 o32 sched_getscheduler sys_sched_getscheduler 162 o32 sched_yield sys_sched_yield 163 o32 sched_get_priority_max sys_sched_get_priority_max 164 o32 sched_get_priority_min sys_sched_get_priority_min 165 o32 sched_rr_get_interval sys_sched_rr_get_interval_time32 166 o32 nanosleep sys_nanosleep_time32 167 o32 mremap sys_mremap 168 o32 accept sys_accept 169 o32 bind sys_bind 170 o32 connect sys_connect 171 o32 getpeername sys_getpeername 172 o32 getsockname sys_getsockname 173 o32 getsockopt sys_getsockopt sys_getsockopt 174 o32 listen sys_listen 175 o32 recv sys_recv compat_sys_recv 176 o32 recvfrom sys_recvfrom compat_sys_recvfrom 177 o32 recvmsg sys_recvmsg compat_sys_recvmsg 178 o32 send sys_send 179 o32 sendmsg sys_sendmsg compat_sys_sendmsg 180 o32 sendto sys_sendto 181 o32 setsockopt sys_setsockopt sys_setsockopt 182 o32 shutdown sys_shutdown 183 o32 socket sys_socket 184 o32 socketpair sys_socketpair 185 o32 setresuid sys_setresuid 186 o32 getresuid sys_getresuid 187 o32 query_module sys_ni_syscall 188 o32 poll sys_poll 189 o32 nfsservctl sys_ni_syscall 190 o32 setresgid sys_setresgid 191 o32 getresgid sys_getresgid 192 o32 prctl sys_prctl 193 o32 rt_sigreturn sys_rt_sigreturn sys32_rt_sigreturn 194 o32 rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction 195 o32 rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask 196 o32 rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending 197 o32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 198 o32 rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 199 o32 rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 200 o32 pread64 sys_pread64 sys_32_pread 201 o32 pwrite64 sys_pwrite64 sys_32_pwrite 202 o32 chown sys_chown 203 o32 getcwd sys_getcwd 204 o32 capget sys_capget 205 o32 capset sys_capset 206 o32 sigaltstack sys_sigaltstack compat_sys_sigaltstack 207 o32 sendfile sys_sendfile compat_sys_sendfile 208 o32 getpmsg sys_ni_syscall 209 o32 putpmsg sys_ni_syscall 210 o32 mmap2 sys_mips_mmap2 211 o32 truncate64 sys_truncate64 sys_32_truncate64 212 o32 ftruncate64 sys_ftruncate64 sys_32_ftruncate64 213 o32 stat64 sys_stat64 sys_newstat 214 o32 lstat64 sys_lstat64 sys_newlstat 215 o32 fstat64 sys_fstat64 sys_newfstat 216 o32 pivot_root sys_pivot_root 217 o32 mincore sys_mincore 218 o32 madvise sys_madvise 219 o32 getdents64 sys_getdents64 220 o32 fcntl64 sys_fcntl64 compat_sys_fcntl64 221 o32 reserved221 sys_ni_syscall 222 o32 gettid sys_gettid 223 o32 readahead sys_readahead sys32_readahead 224 o32 setxattr sys_setxattr 225 o32 lsetxattr sys_lsetxattr 226 o32 fsetxattr sys_fsetxattr 227 o32 getxattr sys_getxattr 228 o32 lgetxattr sys_lgetxattr 229 o32 fgetxattr sys_fgetxattr 230 o32 listxattr sys_listxattr 231 o32 llistxattr sys_llistxattr 232 o32 flistxattr sys_flistxattr 233 o32 removexattr sys_removexattr 234 o32 lremovexattr sys_lremovexattr 235 o32 fremovexattr sys_fremovexattr 236 o32 tkill sys_tkill 237 o32 sendfile64 sys_sendfile64 238 o32 futex sys_futex_time32 239 o32 sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity 240 o32 sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity 241 o32 io_setup sys_io_setup compat_sys_io_setup 242 o32 io_destroy sys_io_destroy 243 o32 io_getevents sys_io_getevents_time32 244 o32 io_submit sys_io_submit compat_sys_io_submit 245 o32 io_cancel sys_io_cancel 246 o32 exit_group sys_exit_group 247 o32 lookup_dcookie sys_ni_syscall 248 o32 epoll_create sys_epoll_create 249 o32 epoll_ctl sys_epoll_ctl 250 o32 epoll_wait sys_epoll_wait 251 o32 remap_file_pages sys_remap_file_pages 252 o32 set_tid_address sys_set_tid_address 253 o32 restart_syscall sys_restart_syscall 254 o32 fadvise64 sys_fadvise64_64 sys32_fadvise64_64 255 o32 statfs64 sys_statfs64 compat_sys_statfs64 256 o32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 257 o32 timer_create sys_timer_create compat_sys_timer_create 258 o32 timer_settime sys_timer_settime32 259 o32 timer_gettime sys_timer_gettime32 260 o32 timer_getoverrun sys_timer_getoverrun 261 o32 timer_delete sys_timer_delete 262 o32 clock_settime sys_clock_settime32 263 o32 clock_gettime sys_clock_gettime32 264 o32 clock_getres sys_clock_getres_time32 265 o32 clock_nanosleep sys_clock_nanosleep_time32 266 o32 tgkill sys_tgkill 267 o32 utimes sys_utimes_time32 268 o32 mbind sys_mbind 269 o32 get_mempolicy sys_get_mempolicy 270 o32 set_mempolicy sys_set_mempolicy 271 o32 mq_open sys_mq_open compat_sys_mq_open 272 o32 mq_unlink sys_mq_unlink 273 o32 mq_timedsend sys_mq_timedsend_time32 274 o32 mq_timedreceive sys_mq_timedreceive_time32 275 o32 mq_notify sys_mq_notify compat_sys_mq_notify 276 o32 mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr 277 o32 vserver sys_ni_syscall 278 o32 waitid sys_waitid compat_sys_waitid 280 o32 add_key sys_add_key 281 o32 request_key sys_request_key 282 o32 keyctl sys_keyctl compat_sys_keyctl 283 o32 set_thread_area sys_set_thread_area 284 o32 inotify_init sys_inotify_init 285 o32 inotify_add_watch sys_inotify_add_watch 286 o32 inotify_rm_watch sys_inotify_rm_watch 287 o32 migrate_pages sys_migrate_pages 288 o32 openat sys_openat compat_sys_openat 289 o32 mkdirat sys_mkdirat 290 o32 mknodat sys_mknodat 291 o32 fchownat sys_fchownat 292 o32 futimesat sys_futimesat_time32 293 o32 fstatat64 sys_fstatat64 sys_newfstatat 294 o32 unlinkat sys_unlinkat 295 o32 renameat sys_renameat 296 o32 linkat sys_linkat 297 o32 symlinkat sys_symlinkat 298 o32 readlinkat sys_readlinkat 299 o32 fchmodat sys_fchmodat 300 o32 faccessat sys_faccessat 301 o32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 302 o32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 303 o32 unshare sys_unshare 304 o32 splice sys_splice 305 o32 sync_file_range sys_sync_file_range sys32_sync_file_range 306 o32 tee sys_tee 307 o32 vmsplice sys_vmsplice 308 o32 move_pages sys_move_pages 309 o32 set_robust_list sys_set_robust_list compat_sys_set_robust_list 310 o32 get_robust_list sys_get_robust_list compat_sys_get_robust_list 311 o32 kexec_load sys_kexec_load compat_sys_kexec_load 312 o32 getcpu sys_getcpu 313 o32 epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 314 o32 ioprio_set sys_ioprio_set 315 o32 ioprio_get sys_ioprio_get 316 o32 utimensat sys_utimensat_time32 317 o32 signalfd sys_signalfd compat_sys_signalfd 318 o32 timerfd sys_ni_syscall 319 o32 eventfd sys_eventfd 320 o32 fallocate sys_fallocate sys32_fallocate 321 o32 timerfd_create sys_timerfd_create 322 o32 timerfd_gettime sys_timerfd_gettime32 323 o32 timerfd_settime sys_timerfd_settime32 324 o32 signalfd4 sys_signalfd4 compat_sys_signalfd4 325 o32 eventfd2 sys_eventfd2 326 o32 epoll_create1 sys_epoll_create1 327 o32 dup3 sys_dup3 328 o32 pipe2 sys_pipe2 329 o32 inotify_init1 sys_inotify_init1 330 o32 preadv sys_preadv compat_sys_preadv 331 o32 pwritev sys_pwritev compat_sys_pwritev 332 o32 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 333 o32 perf_event_open sys_perf_event_open 334 o32 accept4 sys_accept4 335 o32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 336 o32 fanotify_init sys_fanotify_init 337 o32 fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark 338 o32 prlimit64 sys_prlimit64 339 o32 name_to_handle_at sys_name_to_handle_at 340 o32 open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at 341 o32 clock_adjtime sys_clock_adjtime32 342 o32 syncfs sys_syncfs 343 o32 sendmmsg sys_sendmmsg compat_sys_sendmmsg 344 o32 setns sys_setns 345 o32 process_vm_readv sys_process_vm_readv 346 o32 process_vm_writev sys_process_vm_writev 347 o32 kcmp sys_kcmp 348 o32 finit_module sys_finit_module 349 o32 sched_setattr sys_sched_setattr 350 o32 sched_getattr sys_sched_getattr 351 o32 renameat2 sys_renameat2 352 o32 seccomp sys_seccomp 353 o32 getrandom sys_getrandom 354 o32 memfd_create sys_memfd_create 355 o32 bpf sys_bpf 356 o32 execveat sys_execveat compat_sys_execveat 357 o32 userfaultfd sys_userfaultfd 358 o32 membarrier sys_membarrier 359 o32 mlock2 sys_mlock2 360 o32 copy_file_range sys_copy_file_range 361 o32 preadv2 sys_preadv2 compat_sys_preadv2 362 o32 pwritev2 sys_pwritev2 compat_sys_pwritev2 363 o32 pkey_mprotect sys_pkey_mprotect 364 o32 pkey_alloc sys_pkey_alloc 365 o32 pkey_free sys_pkey_free 366 o32 statx sys_statx 367 o32 rseq sys_rseq 368 o32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents 393 o32 semget sys_semget 394 o32 semctl sys_semctl compat_sys_semctl 395 o32 shmget sys_shmget 396 o32 shmctl sys_shmctl compat_sys_shmctl 397 o32 shmat sys_shmat compat_sys_shmat 398 o32 shmdt sys_shmdt 399 o32 msgget sys_msgget 400 o32 msgsnd sys_msgsnd compat_sys_msgsnd 401 o32 msgrcv sys_msgrcv compat_sys_msgrcv 402 o32 msgctl sys_msgctl compat_sys_msgctl 403 o32 clock_gettime64 sys_clock_gettime sys_clock_gettime 404 o32 clock_settime64 sys_clock_settime sys_clock_settime 405 o32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime 406 o32 clock_getres_time64 sys_clock_getres sys_clock_getres 407 o32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep 408 o32 timer_gettime64 sys_timer_gettime sys_timer_gettime 409 o32 timer_settime64 sys_timer_settime sys_timer_settime 410 o32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime 411 o32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime 412 o32 utimensat_time64 sys_utimensat sys_utimensat 413 o32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 o32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 416 o32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 o32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 o32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend 419 o32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive 420 o32 semtimedop_time64 sys_semtimedop sys_semtimedop 421 o32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 422 o32 futex_time64 sys_futex sys_futex 423 o32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval 424 o32 pidfd_send_signal sys_pidfd_send_signal 425 o32 io_uring_setup sys_io_uring_setup 426 o32 io_uring_enter sys_io_uring_enter 427 o32 io_uring_register sys_io_uring_register 428 o32 open_tree sys_open_tree 429 o32 move_mount sys_move_mount 430 o32 fsopen sys_fsopen 431 o32 fsconfig sys_fsconfig 432 o32 fsmount sys_fsmount 433 o32 fspick sys_fspick 434 o32 pidfd_open sys_pidfd_open 435 o32 clone3 __sys_clone3 436 o32 close_range sys_close_range 437 o32 openat2 sys_openat2 438 o32 pidfd_getfd sys_pidfd_getfd 439 o32 faccessat2 sys_faccessat2 440 o32 process_madvise sys_process_madvise 441 o32 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 o32 mount_setattr sys_mount_setattr 443 o32 quotactl_fd sys_quotactl_fd 444 o32 landlock_create_ruleset sys_landlock_create_ruleset 445 o32 landlock_add_rule sys_landlock_add_rule 446 o32 landlock_restrict_self sys_landlock_restrict_self 448 o32 process_mrelease sys_process_mrelease 449 o32 futex_waitv sys_futex_waitv 450 o32 set_mempolicy_home_node sys_set_mempolicy_home_node 451 o32 cachestat sys_cachestat 452 o32 fchmodat2 sys_fchmodat2 453 o32 map_shadow_stack sys_map_shadow_stack 454 o32 futex_wake sys_futex_wake 455 o32 futex_wait sys_futex_wait 456 o32 futex_requeue sys_futex_requeue 457 o32 statmount sys_statmount 458 o32 listmount sys_listmount 459 o32 lsm_get_self_attr sys_lsm_get_self_attr 460 o32 lsm_set_self_attr sys_lsm_set_self_attr 461 o32 lsm_list_modules sys_lsm_list_modules 462 o32 mseal sys_mseal 463 o32 setxattrat sys_setxattrat 464 o32 getxattrat sys_getxattrat 465 o32 listxattrat sys_listxattrat 466 o32 removexattrat sys_removexattrat 467 o32 open_tree_attr sys_open_tree_attr 468 o32 file_getattr sys_file_getattr 469 o32 file_setattr sys_file_setattr 470 o32 listns sys_listns """ # MIPS n32 (default ABI, present in /lib32) # - arch/mips/kernel/syscalls/syscall_n32.tbl mips_n32_syscall_tbl = """ 0 n32 read sys_read 1 n32 write sys_write 2 n32 open sys_open 3 n32 close sys_close 4 n32 stat sys_newstat 5 n32 fstat sys_newfstat 6 n32 lstat sys_newlstat 7 n32 poll sys_poll 8 n32 lseek sys_lseek 9 n32 mmap sys_mips_mmap 10 n32 mprotect sys_mprotect 11 n32 munmap sys_munmap 12 n32 brk sys_brk 13 n32 rt_sigaction compat_sys_rt_sigaction 14 n32 rt_sigprocmask compat_sys_rt_sigprocmask 15 n32 ioctl compat_sys_ioctl 16 n32 pread64 sys_pread64 17 n32 pwrite64 sys_pwrite64 18 n32 readv sys_readv 19 n32 writev sys_writev 20 n32 access sys_access 21 n32 pipe sysm_pipe 22 n32 _newselect compat_sys_select 23 n32 sched_yield sys_sched_yield 24 n32 mremap sys_mremap 25 n32 msync sys_msync 26 n32 mincore sys_mincore 27 n32 madvise sys_madvise 28 n32 shmget sys_shmget 29 n32 shmat sys_shmat 30 n32 shmctl compat_sys_old_shmctl 31 n32 dup sys_dup 32 n32 dup2 sys_dup2 33 n32 pause sys_pause 34 n32 nanosleep sys_nanosleep_time32 35 n32 getitimer compat_sys_getitimer 36 n32 setitimer compat_sys_setitimer 37 n32 alarm sys_alarm 38 n32 getpid sys_getpid 39 n32 sendfile compat_sys_sendfile 40 n32 socket sys_socket 41 n32 connect sys_connect 42 n32 accept sys_accept 43 n32 sendto sys_sendto 44 n32 recvfrom compat_sys_recvfrom 45 n32 sendmsg compat_sys_sendmsg 46 n32 recvmsg compat_sys_recvmsg 47 n32 shutdown sys_shutdown 48 n32 bind sys_bind 49 n32 listen sys_listen 50 n32 getsockname sys_getsockname 51 n32 getpeername sys_getpeername 52 n32 socketpair sys_socketpair 53 n32 setsockopt sys_setsockopt 54 n32 getsockopt sys_getsockopt 55 n32 clone __sys_clone 56 n32 fork __sys_fork 57 n32 execve compat_sys_execve 58 n32 exit sys_exit 59 n32 wait4 compat_sys_wait4 60 n32 kill sys_kill 61 n32 uname sys_newuname 62 n32 semget sys_semget 63 n32 semop sys_semop 64 n32 semctl compat_sys_old_semctl 65 n32 shmdt sys_shmdt 66 n32 msgget sys_msgget 67 n32 msgsnd compat_sys_msgsnd 68 n32 msgrcv compat_sys_msgrcv 69 n32 msgctl compat_sys_old_msgctl 70 n32 fcntl compat_sys_fcntl 71 n32 flock sys_flock 72 n32 fsync sys_fsync 73 n32 fdatasync sys_fdatasync 74 n32 truncate sys_truncate 75 n32 ftruncate sys_ftruncate 76 n32 getdents compat_sys_getdents 77 n32 getcwd sys_getcwd 78 n32 chdir sys_chdir 79 n32 fchdir sys_fchdir 80 n32 rename sys_rename 81 n32 mkdir sys_mkdir 82 n32 rmdir sys_rmdir 83 n32 creat sys_creat 84 n32 link sys_link 85 n32 unlink sys_unlink 86 n32 symlink sys_symlink 87 n32 readlink sys_readlink 88 n32 chmod sys_chmod 89 n32 fchmod sys_fchmod 90 n32 chown sys_chown 91 n32 fchown sys_fchown 92 n32 lchown sys_lchown 93 n32 umask sys_umask 94 n32 gettimeofday compat_sys_gettimeofday 95 n32 getrlimit compat_sys_getrlimit 96 n32 getrusage compat_sys_getrusage 97 n32 sysinfo compat_sys_sysinfo 98 n32 times compat_sys_times 99 n32 ptrace compat_sys_ptrace 100 n32 getuid sys_getuid 101 n32 syslog sys_syslog 102 n32 getgid sys_getgid 103 n32 setuid sys_setuid 104 n32 setgid sys_setgid 105 n32 geteuid sys_geteuid 106 n32 getegid sys_getegid 107 n32 setpgid sys_setpgid 108 n32 getppid sys_getppid 109 n32 getpgrp sys_getpgrp 110 n32 setsid sys_setsid 111 n32 setreuid sys_setreuid 112 n32 setregid sys_setregid 113 n32 getgroups sys_getgroups 114 n32 setgroups sys_setgroups 115 n32 setresuid sys_setresuid 116 n32 getresuid sys_getresuid 117 n32 setresgid sys_setresgid 118 n32 getresgid sys_getresgid 119 n32 getpgid sys_getpgid 120 n32 setfsuid sys_setfsuid 121 n32 setfsgid sys_setfsgid 122 n32 getsid sys_getsid 123 n32 capget sys_capget 124 n32 capset sys_capset 125 n32 rt_sigpending compat_sys_rt_sigpending 126 n32 rt_sigtimedwait compat_sys_rt_sigtimedwait_time32 127 n32 rt_sigqueueinfo compat_sys_rt_sigqueueinfo 128 n32 rt_sigsuspend compat_sys_rt_sigsuspend 129 n32 sigaltstack compat_sys_sigaltstack 130 n32 utime sys_utime32 131 n32 mknod sys_mknod 132 n32 personality sys_32_personality 133 n32 ustat compat_sys_ustat 134 n32 statfs compat_sys_statfs 135 n32 fstatfs compat_sys_fstatfs 136 n32 sysfs sys_sysfs 137 n32 getpriority sys_getpriority 138 n32 setpriority sys_setpriority 139 n32 sched_setparam sys_sched_setparam 140 n32 sched_getparam sys_sched_getparam 141 n32 sched_setscheduler sys_sched_setscheduler 142 n32 sched_getscheduler sys_sched_getscheduler 143 n32 sched_get_priority_max sys_sched_get_priority_max 144 n32 sched_get_priority_min sys_sched_get_priority_min 145 n32 sched_rr_get_interval sys_sched_rr_get_interval_time32 146 n32 mlock sys_mlock 147 n32 munlock sys_munlock 148 n32 mlockall sys_mlockall 149 n32 munlockall sys_munlockall 150 n32 vhangup sys_vhangup 151 n32 pivot_root sys_pivot_root 152 n32 _sysctl sys_ni_syscall 153 n32 prctl sys_prctl 154 n32 adjtimex sys_adjtimex_time32 155 n32 setrlimit compat_sys_setrlimit 156 n32 chroot sys_chroot 157 n32 sync sys_sync 158 n32 acct sys_acct 159 n32 settimeofday compat_sys_settimeofday 160 n32 mount sys_mount 161 n32 umount2 sys_umount 162 n32 swapon sys_swapon 163 n32 swapoff sys_swapoff 164 n32 reboot sys_reboot 165 n32 sethostname sys_sethostname 166 n32 setdomainname sys_setdomainname 167 n32 create_module sys_ni_syscall 168 n32 init_module sys_init_module 169 n32 delete_module sys_delete_module 170 n32 get_kernel_syms sys_ni_syscall 171 n32 query_module sys_ni_syscall 172 n32 quotactl sys_quotactl 173 n32 nfsservctl sys_ni_syscall 174 n32 getpmsg sys_ni_syscall 175 n32 putpmsg sys_ni_syscall 176 n32 afs_syscall sys_ni_syscall 177 n32 reserved177 sys_ni_syscall 178 n32 gettid sys_gettid 179 n32 readahead sys_readahead 180 n32 setxattr sys_setxattr 181 n32 lsetxattr sys_lsetxattr 182 n32 fsetxattr sys_fsetxattr 183 n32 getxattr sys_getxattr 184 n32 lgetxattr sys_lgetxattr 185 n32 fgetxattr sys_fgetxattr 186 n32 listxattr sys_listxattr 187 n32 llistxattr sys_llistxattr 188 n32 flistxattr sys_flistxattr 189 n32 removexattr sys_removexattr 190 n32 lremovexattr sys_lremovexattr 191 n32 fremovexattr sys_fremovexattr 192 n32 tkill sys_tkill 193 n32 reserved193 sys_ni_syscall 194 n32 futex sys_futex_time32 195 n32 sched_setaffinity compat_sys_sched_setaffinity 196 n32 sched_getaffinity compat_sys_sched_getaffinity 197 n32 cacheflush sys_cacheflush 198 n32 cachectl sys_cachectl 199 n32 sysmips __sys_sysmips 200 n32 io_setup compat_sys_io_setup 201 n32 io_destroy sys_io_destroy 202 n32 io_getevents sys_io_getevents_time32 203 n32 io_submit compat_sys_io_submit 204 n32 io_cancel sys_io_cancel 205 n32 exit_group sys_exit_group 206 n32 lookup_dcookie sys_ni_syscall 207 n32 epoll_create sys_epoll_create 208 n32 epoll_ctl sys_epoll_ctl 209 n32 epoll_wait sys_epoll_wait 210 n32 remap_file_pages sys_remap_file_pages 211 n32 rt_sigreturn sysn32_rt_sigreturn 212 n32 fcntl64 compat_sys_fcntl64 213 n32 set_tid_address sys_set_tid_address 214 n32 restart_syscall sys_restart_syscall 215 n32 semtimedop sys_semtimedop_time32 216 n32 fadvise64 sys_fadvise64_64 217 n32 statfs64 compat_sys_statfs64 218 n32 fstatfs64 compat_sys_fstatfs64 219 n32 sendfile64 sys_sendfile64 220 n32 timer_create compat_sys_timer_create 221 n32 timer_settime sys_timer_settime32 222 n32 timer_gettime sys_timer_gettime32 223 n32 timer_getoverrun sys_timer_getoverrun 224 n32 timer_delete sys_timer_delete 225 n32 clock_settime sys_clock_settime32 226 n32 clock_gettime sys_clock_gettime32 227 n32 clock_getres sys_clock_getres_time32 228 n32 clock_nanosleep sys_clock_nanosleep_time32 229 n32 tgkill sys_tgkill 230 n32 utimes sys_utimes_time32 231 n32 mbind sys_mbind 232 n32 get_mempolicy sys_get_mempolicy 233 n32 set_mempolicy sys_set_mempolicy 234 n32 mq_open compat_sys_mq_open 235 n32 mq_unlink sys_mq_unlink 236 n32 mq_timedsend sys_mq_timedsend_time32 237 n32 mq_timedreceive sys_mq_timedreceive_time32 238 n32 mq_notify compat_sys_mq_notify 239 n32 mq_getsetattr compat_sys_mq_getsetattr 240 n32 vserver sys_ni_syscall 241 n32 waitid compat_sys_waitid 243 n32 add_key sys_add_key 244 n32 request_key sys_request_key 245 n32 keyctl compat_sys_keyctl 246 n32 set_thread_area sys_set_thread_area 247 n32 inotify_init sys_inotify_init 248 n32 inotify_add_watch sys_inotify_add_watch 249 n32 inotify_rm_watch sys_inotify_rm_watch 250 n32 migrate_pages sys_migrate_pages 251 n32 openat sys_openat 252 n32 mkdirat sys_mkdirat 253 n32 mknodat sys_mknodat 254 n32 fchownat sys_fchownat 255 n32 futimesat sys_futimesat_time32 256 n32 newfstatat sys_newfstatat 257 n32 unlinkat sys_unlinkat 258 n32 renameat sys_renameat 259 n32 linkat sys_linkat 260 n32 symlinkat sys_symlinkat 261 n32 readlinkat sys_readlinkat 262 n32 fchmodat sys_fchmodat 263 n32 faccessat sys_faccessat 264 n32 pselect6 compat_sys_pselect6_time32 265 n32 ppoll compat_sys_ppoll_time32 266 n32 unshare sys_unshare 267 n32 splice sys_splice 268 n32 sync_file_range sys_sync_file_range 269 n32 tee sys_tee 270 n32 vmsplice sys_vmsplice 271 n32 move_pages sys_move_pages 272 n32 set_robust_list compat_sys_set_robust_list 273 n32 get_robust_list compat_sys_get_robust_list 274 n32 kexec_load compat_sys_kexec_load 275 n32 getcpu sys_getcpu 276 n32 epoll_pwait compat_sys_epoll_pwait 277 n32 ioprio_set sys_ioprio_set 278 n32 ioprio_get sys_ioprio_get 279 n32 utimensat sys_utimensat_time32 280 n32 signalfd compat_sys_signalfd 281 n32 timerfd sys_ni_syscall 282 n32 eventfd sys_eventfd 283 n32 fallocate sys_fallocate 284 n32 timerfd_create sys_timerfd_create 285 n32 timerfd_gettime sys_timerfd_gettime32 286 n32 timerfd_settime sys_timerfd_settime32 287 n32 signalfd4 compat_sys_signalfd4 288 n32 eventfd2 sys_eventfd2 289 n32 epoll_create1 sys_epoll_create1 290 n32 dup3 sys_dup3 291 n32 pipe2 sys_pipe2 292 n32 inotify_init1 sys_inotify_init1 293 n32 preadv compat_sys_preadv 294 n32 pwritev compat_sys_pwritev 295 n32 rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 296 n32 perf_event_open sys_perf_event_open 297 n32 accept4 sys_accept4 298 n32 recvmmsg compat_sys_recvmmsg_time32 299 n32 getdents64 sys_getdents64 300 n32 fanotify_init sys_fanotify_init 301 n32 fanotify_mark sys_fanotify_mark 302 n32 prlimit64 sys_prlimit64 303 n32 name_to_handle_at sys_name_to_handle_at 304 n32 open_by_handle_at sys_open_by_handle_at 305 n32 clock_adjtime sys_clock_adjtime32 306 n32 syncfs sys_syncfs 307 n32 sendmmsg compat_sys_sendmmsg 308 n32 setns sys_setns 309 n32 process_vm_readv sys_process_vm_readv 310 n32 process_vm_writev sys_process_vm_writev 311 n32 kcmp sys_kcmp 312 n32 finit_module sys_finit_module 313 n32 sched_setattr sys_sched_setattr 314 n32 sched_getattr sys_sched_getattr 315 n32 renameat2 sys_renameat2 316 n32 seccomp sys_seccomp 317 n32 getrandom sys_getrandom 318 n32 memfd_create sys_memfd_create 319 n32 bpf sys_bpf 320 n32 execveat compat_sys_execveat 321 n32 userfaultfd sys_userfaultfd 322 n32 membarrier sys_membarrier 323 n32 mlock2 sys_mlock2 324 n32 copy_file_range sys_copy_file_range 325 n32 preadv2 compat_sys_preadv2 326 n32 pwritev2 compat_sys_pwritev2 327 n32 pkey_mprotect sys_pkey_mprotect 328 n32 pkey_alloc sys_pkey_alloc 329 n32 pkey_free sys_pkey_free 330 n32 statx sys_statx 331 n32 rseq sys_rseq 332 n32 io_pgetevents compat_sys_io_pgetevents 403 n32 clock_gettime64 sys_clock_gettime 404 n32 clock_settime64 sys_clock_settime 405 n32 clock_adjtime64 sys_clock_adjtime 406 n32 clock_getres_time64 sys_clock_getres 407 n32 clock_nanosleep_time64 sys_clock_nanosleep 408 n32 timer_gettime64 sys_timer_gettime 409 n32 timer_settime64 sys_timer_settime 410 n32 timerfd_gettime64 sys_timerfd_gettime 411 n32 timerfd_settime64 sys_timerfd_settime 412 n32 utimensat_time64 sys_utimensat 413 n32 pselect6_time64 compat_sys_pselect6_time64 414 n32 ppoll_time64 compat_sys_ppoll_time64 416 n32 io_pgetevents_time64 compat_sys_io_pgetevents_time64 417 n32 recvmmsg_time64 compat_sys_recvmmsg_time64 418 n32 mq_timedsend_time64 sys_mq_timedsend 419 n32 mq_timedreceive_time64 sys_mq_timedreceive 420 n32 semtimedop_time64 sys_semtimedop 421 n32 rt_sigtimedwait_time64 compat_sys_rt_sigtimedwait_time64 422 n32 futex_time64 sys_futex 423 n32 sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 n32 pidfd_send_signal sys_pidfd_send_signal 425 n32 io_uring_setup sys_io_uring_setup 426 n32 io_uring_enter sys_io_uring_enter 427 n32 io_uring_register sys_io_uring_register 428 n32 open_tree sys_open_tree 429 n32 move_mount sys_move_mount 430 n32 fsopen sys_fsopen 431 n32 fsconfig sys_fsconfig 432 n32 fsmount sys_fsmount 433 n32 fspick sys_fspick 434 n32 pidfd_open sys_pidfd_open 435 n32 clone3 __sys_clone3 436 n32 close_range sys_close_range 437 n32 openat2 sys_openat2 438 n32 pidfd_getfd sys_pidfd_getfd 439 n32 faccessat2 sys_faccessat2 440 n32 process_madvise sys_process_madvise 441 n32 epoll_pwait2 compat_sys_epoll_pwait2 442 n32 mount_setattr sys_mount_setattr 443 n32 quotactl_fd sys_quotactl_fd 444 n32 landlock_create_ruleset sys_landlock_create_ruleset 445 n32 landlock_add_rule sys_landlock_add_rule 446 n32 landlock_restrict_self sys_landlock_restrict_self 448 n32 process_mrelease sys_process_mrelease 449 n32 futex_waitv sys_futex_waitv 450 n32 set_mempolicy_home_node sys_set_mempolicy_home_node 451 n32 cachestat sys_cachestat 452 n32 fchmodat2 sys_fchmodat2 453 n32 map_shadow_stack sys_map_shadow_stack 454 n32 futex_wake sys_futex_wake 455 n32 futex_wait sys_futex_wait 456 n32 futex_requeue sys_futex_requeue 457 n32 statmount sys_statmount 458 n32 listmount sys_listmount 459 n32 lsm_get_self_attr sys_lsm_get_self_attr 460 n32 lsm_set_self_attr sys_lsm_set_self_attr 461 n32 lsm_list_modules sys_lsm_list_modules 462 n32 mseal sys_mseal 463 n32 setxattrat sys_setxattrat 464 n32 getxattrat sys_getxattrat 465 n32 listxattrat sys_listxattrat 466 n32 removexattrat sys_removexattrat 467 n32 open_tree_attr sys_open_tree_attr 468 n32 file_getattr sys_file_getattr 469 n32 file_setattr sys_file_setattr 470 n32 listns sys_listns """ # MIPS n64 (for 64-bit ABI, present in /lib64) # - arch/mips/kernel/syscalls/syscall_n64.tbl mips_n64_syscall_tbl = """ 0 n64 read sys_read 1 n64 write sys_write 2 n64 open sys_open 3 n64 close sys_close 4 n64 stat sys_newstat 5 n64 fstat sys_newfstat 6 n64 lstat sys_newlstat 7 n64 poll sys_poll 8 n64 lseek sys_lseek 9 n64 mmap sys_mips_mmap 10 n64 mprotect sys_mprotect 11 n64 munmap sys_munmap 12 n64 brk sys_brk 13 n64 rt_sigaction sys_rt_sigaction 14 n64 rt_sigprocmask sys_rt_sigprocmask 15 n64 ioctl sys_ioctl 16 n64 pread64 sys_pread64 17 n64 pwrite64 sys_pwrite64 18 n64 readv sys_readv 19 n64 writev sys_writev 20 n64 access sys_access 21 n64 pipe sysm_pipe 22 n64 _newselect sys_select 23 n64 sched_yield sys_sched_yield 24 n64 mremap sys_mremap 25 n64 msync sys_msync 26 n64 mincore sys_mincore 27 n64 madvise sys_madvise 28 n64 shmget sys_shmget 29 n64 shmat sys_shmat 30 n64 shmctl sys_old_shmctl 31 n64 dup sys_dup 32 n64 dup2 sys_dup2 33 n64 pause sys_pause 34 n64 nanosleep sys_nanosleep 35 n64 getitimer sys_getitimer 36 n64 setitimer sys_setitimer 37 n64 alarm sys_alarm 38 n64 getpid sys_getpid 39 n64 sendfile sys_sendfile64 40 n64 socket sys_socket 41 n64 connect sys_connect 42 n64 accept sys_accept 43 n64 sendto sys_sendto 44 n64 recvfrom sys_recvfrom 45 n64 sendmsg sys_sendmsg 46 n64 recvmsg sys_recvmsg 47 n64 shutdown sys_shutdown 48 n64 bind sys_bind 49 n64 listen sys_listen 50 n64 getsockname sys_getsockname 51 n64 getpeername sys_getpeername 52 n64 socketpair sys_socketpair 53 n64 setsockopt sys_setsockopt 54 n64 getsockopt sys_getsockopt 55 n64 clone __sys_clone 56 n64 fork __sys_fork 57 n64 execve sys_execve 58 n64 exit sys_exit 59 n64 wait4 sys_wait4 60 n64 kill sys_kill 61 n64 uname sys_newuname 62 n64 semget sys_semget 63 n64 semop sys_semop 64 n64 semctl sys_old_semctl 65 n64 shmdt sys_shmdt 66 n64 msgget sys_msgget 67 n64 msgsnd sys_msgsnd 68 n64 msgrcv sys_msgrcv 69 n64 msgctl sys_old_msgctl 70 n64 fcntl sys_fcntl 71 n64 flock sys_flock 72 n64 fsync sys_fsync 73 n64 fdatasync sys_fdatasync 74 n64 truncate sys_truncate 75 n64 ftruncate sys_ftruncate 76 n64 getdents sys_getdents 77 n64 getcwd sys_getcwd 78 n64 chdir sys_chdir 79 n64 fchdir sys_fchdir 80 n64 rename sys_rename 81 n64 mkdir sys_mkdir 82 n64 rmdir sys_rmdir 83 n64 creat sys_creat 84 n64 link sys_link 85 n64 unlink sys_unlink 86 n64 symlink sys_symlink 87 n64 readlink sys_readlink 88 n64 chmod sys_chmod 89 n64 fchmod sys_fchmod 90 n64 chown sys_chown 91 n64 fchown sys_fchown 92 n64 lchown sys_lchown 93 n64 umask sys_umask 94 n64 gettimeofday sys_gettimeofday 95 n64 getrlimit sys_getrlimit 96 n64 getrusage sys_getrusage 97 n64 sysinfo sys_sysinfo 98 n64 times sys_times 99 n64 ptrace sys_ptrace 100 n64 getuid sys_getuid 101 n64 syslog sys_syslog 102 n64 getgid sys_getgid 103 n64 setuid sys_setuid 104 n64 setgid sys_setgid 105 n64 geteuid sys_geteuid 106 n64 getegid sys_getegid 107 n64 setpgid sys_setpgid 108 n64 getppid sys_getppid 109 n64 getpgrp sys_getpgrp 110 n64 setsid sys_setsid 111 n64 setreuid sys_setreuid 112 n64 setregid sys_setregid 113 n64 getgroups sys_getgroups 114 n64 setgroups sys_setgroups 115 n64 setresuid sys_setresuid 116 n64 getresuid sys_getresuid 117 n64 setresgid sys_setresgid 118 n64 getresgid sys_getresgid 119 n64 getpgid sys_getpgid 120 n64 setfsuid sys_setfsuid 121 n64 setfsgid sys_setfsgid 122 n64 getsid sys_getsid 123 n64 capget sys_capget 124 n64 capset sys_capset 125 n64 rt_sigpending sys_rt_sigpending 126 n64 rt_sigtimedwait sys_rt_sigtimedwait 127 n64 rt_sigqueueinfo sys_rt_sigqueueinfo 128 n64 rt_sigsuspend sys_rt_sigsuspend 129 n64 sigaltstack sys_sigaltstack 130 n64 utime sys_utime 131 n64 mknod sys_mknod 132 n64 personality sys_personality 133 n64 ustat sys_ustat 134 n64 statfs sys_statfs 135 n64 fstatfs sys_fstatfs 136 n64 sysfs sys_sysfs 137 n64 getpriority sys_getpriority 138 n64 setpriority sys_setpriority 139 n64 sched_setparam sys_sched_setparam 140 n64 sched_getparam sys_sched_getparam 141 n64 sched_setscheduler sys_sched_setscheduler 142 n64 sched_getscheduler sys_sched_getscheduler 143 n64 sched_get_priority_max sys_sched_get_priority_max 144 n64 sched_get_priority_min sys_sched_get_priority_min 145 n64 sched_rr_get_interval sys_sched_rr_get_interval 146 n64 mlock sys_mlock 147 n64 munlock sys_munlock 148 n64 mlockall sys_mlockall 149 n64 munlockall sys_munlockall 150 n64 vhangup sys_vhangup 151 n64 pivot_root sys_pivot_root 152 n64 _sysctl sys_ni_syscall 153 n64 prctl sys_prctl 154 n64 adjtimex sys_adjtimex 155 n64 setrlimit sys_setrlimit 156 n64 chroot sys_chroot 157 n64 sync sys_sync 158 n64 acct sys_acct 159 n64 settimeofday sys_settimeofday 160 n64 mount sys_mount 161 n64 umount2 sys_umount 162 n64 swapon sys_swapon 163 n64 swapoff sys_swapoff 164 n64 reboot sys_reboot 165 n64 sethostname sys_sethostname 166 n64 setdomainname sys_setdomainname 167 n64 create_module sys_ni_syscall 168 n64 init_module sys_init_module 169 n64 delete_module sys_delete_module 170 n64 get_kernel_syms sys_ni_syscall 171 n64 query_module sys_ni_syscall 172 n64 quotactl sys_quotactl 173 n64 nfsservctl sys_ni_syscall 174 n64 getpmsg sys_ni_syscall 175 n64 putpmsg sys_ni_syscall 176 n64 afs_syscall sys_ni_syscall 177 n64 reserved177 sys_ni_syscall 178 n64 gettid sys_gettid 179 n64 readahead sys_readahead 180 n64 setxattr sys_setxattr 181 n64 lsetxattr sys_lsetxattr 182 n64 fsetxattr sys_fsetxattr 183 n64 getxattr sys_getxattr 184 n64 lgetxattr sys_lgetxattr 185 n64 fgetxattr sys_fgetxattr 186 n64 listxattr sys_listxattr 187 n64 llistxattr sys_llistxattr 188 n64 flistxattr sys_flistxattr 189 n64 removexattr sys_removexattr 190 n64 lremovexattr sys_lremovexattr 191 n64 fremovexattr sys_fremovexattr 192 n64 tkill sys_tkill 193 n64 reserved193 sys_ni_syscall 194 n64 futex sys_futex 195 n64 sched_setaffinity sys_sched_setaffinity 196 n64 sched_getaffinity sys_sched_getaffinity 197 n64 cacheflush sys_cacheflush 198 n64 cachectl sys_cachectl 199 n64 sysmips __sys_sysmips 200 n64 io_setup sys_io_setup 201 n64 io_destroy sys_io_destroy 202 n64 io_getevents sys_io_getevents 203 n64 io_submit sys_io_submit 204 n64 io_cancel sys_io_cancel 205 n64 exit_group sys_exit_group 206 n64 lookup_dcookie sys_ni_syscall 207 n64 epoll_create sys_epoll_create 208 n64 epoll_ctl sys_epoll_ctl 209 n64 epoll_wait sys_epoll_wait 210 n64 remap_file_pages sys_remap_file_pages 211 n64 rt_sigreturn sys_rt_sigreturn 212 n64 set_tid_address sys_set_tid_address 213 n64 restart_syscall sys_restart_syscall 214 n64 semtimedop sys_semtimedop 215 n64 fadvise64 sys_fadvise64_64 216 n64 timer_create sys_timer_create 217 n64 timer_settime sys_timer_settime 218 n64 timer_gettime sys_timer_gettime 219 n64 timer_getoverrun sys_timer_getoverrun 220 n64 timer_delete sys_timer_delete 221 n64 clock_settime sys_clock_settime 222 n64 clock_gettime sys_clock_gettime 223 n64 clock_getres sys_clock_getres 224 n64 clock_nanosleep sys_clock_nanosleep 225 n64 tgkill sys_tgkill 226 n64 utimes sys_utimes 227 n64 mbind sys_mbind 228 n64 get_mempolicy sys_get_mempolicy 229 n64 set_mempolicy sys_set_mempolicy 230 n64 mq_open sys_mq_open 231 n64 mq_unlink sys_mq_unlink 232 n64 mq_timedsend sys_mq_timedsend 233 n64 mq_timedreceive sys_mq_timedreceive 234 n64 mq_notify sys_mq_notify 235 n64 mq_getsetattr sys_mq_getsetattr 236 n64 vserver sys_ni_syscall 237 n64 waitid sys_waitid 239 n64 add_key sys_add_key 240 n64 request_key sys_request_key 241 n64 keyctl sys_keyctl 242 n64 set_thread_area sys_set_thread_area 243 n64 inotify_init sys_inotify_init 244 n64 inotify_add_watch sys_inotify_add_watch 245 n64 inotify_rm_watch sys_inotify_rm_watch 246 n64 migrate_pages sys_migrate_pages 247 n64 openat sys_openat 248 n64 mkdirat sys_mkdirat 249 n64 mknodat sys_mknodat 250 n64 fchownat sys_fchownat 251 n64 futimesat sys_futimesat 252 n64 newfstatat sys_newfstatat 253 n64 unlinkat sys_unlinkat 254 n64 renameat sys_renameat 255 n64 linkat sys_linkat 256 n64 symlinkat sys_symlinkat 257 n64 readlinkat sys_readlinkat 258 n64 fchmodat sys_fchmodat 259 n64 faccessat sys_faccessat 260 n64 pselect6 sys_pselect6 261 n64 ppoll sys_ppoll 262 n64 unshare sys_unshare 263 n64 splice sys_splice 264 n64 sync_file_range sys_sync_file_range 265 n64 tee sys_tee 266 n64 vmsplice sys_vmsplice 267 n64 move_pages sys_move_pages 268 n64 set_robust_list sys_set_robust_list 269 n64 get_robust_list sys_get_robust_list 270 n64 kexec_load sys_kexec_load 271 n64 getcpu sys_getcpu 272 n64 epoll_pwait sys_epoll_pwait 273 n64 ioprio_set sys_ioprio_set 274 n64 ioprio_get sys_ioprio_get 275 n64 utimensat sys_utimensat 276 n64 signalfd sys_signalfd 277 n64 timerfd sys_ni_syscall 278 n64 eventfd sys_eventfd 279 n64 fallocate sys_fallocate 280 n64 timerfd_create sys_timerfd_create 281 n64 timerfd_gettime sys_timerfd_gettime 282 n64 timerfd_settime sys_timerfd_settime 283 n64 signalfd4 sys_signalfd4 284 n64 eventfd2 sys_eventfd2 285 n64 epoll_create1 sys_epoll_create1 286 n64 dup3 sys_dup3 287 n64 pipe2 sys_pipe2 288 n64 inotify_init1 sys_inotify_init1 289 n64 preadv sys_preadv 290 n64 pwritev sys_pwritev 291 n64 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 292 n64 perf_event_open sys_perf_event_open 293 n64 accept4 sys_accept4 294 n64 recvmmsg sys_recvmmsg 295 n64 fanotify_init sys_fanotify_init 296 n64 fanotify_mark sys_fanotify_mark 297 n64 prlimit64 sys_prlimit64 298 n64 name_to_handle_at sys_name_to_handle_at 299 n64 open_by_handle_at sys_open_by_handle_at 300 n64 clock_adjtime sys_clock_adjtime 301 n64 syncfs sys_syncfs 302 n64 sendmmsg sys_sendmmsg 303 n64 setns sys_setns 304 n64 process_vm_readv sys_process_vm_readv 305 n64 process_vm_writev sys_process_vm_writev 306 n64 kcmp sys_kcmp 307 n64 finit_module sys_finit_module 308 n64 getdents64 sys_getdents64 309 n64 sched_setattr sys_sched_setattr 310 n64 sched_getattr sys_sched_getattr 311 n64 renameat2 sys_renameat2 312 n64 seccomp sys_seccomp 313 n64 getrandom sys_getrandom 314 n64 memfd_create sys_memfd_create 315 n64 bpf sys_bpf 316 n64 execveat sys_execveat 317 n64 userfaultfd sys_userfaultfd 318 n64 membarrier sys_membarrier 319 n64 mlock2 sys_mlock2 320 n64 copy_file_range sys_copy_file_range 321 n64 preadv2 sys_preadv2 322 n64 pwritev2 sys_pwritev2 323 n64 pkey_mprotect sys_pkey_mprotect 324 n64 pkey_alloc sys_pkey_alloc 325 n64 pkey_free sys_pkey_free 326 n64 statx sys_statx 327 n64 rseq sys_rseq 328 n64 io_pgetevents sys_io_pgetevents 424 n64 pidfd_send_signal sys_pidfd_send_signal 425 n64 io_uring_setup sys_io_uring_setup 426 n64 io_uring_enter sys_io_uring_enter 427 n64 io_uring_register sys_io_uring_register 428 n64 open_tree sys_open_tree 429 n64 move_mount sys_move_mount 430 n64 fsopen sys_fsopen 431 n64 fsconfig sys_fsconfig 432 n64 fsmount sys_fsmount 433 n64 fspick sys_fspick 434 n64 pidfd_open sys_pidfd_open 435 n64 clone3 __sys_clone3 436 n64 close_range sys_close_range 437 n64 openat2 sys_openat2 438 n64 pidfd_getfd sys_pidfd_getfd 439 n64 faccessat2 sys_faccessat2 440 n64 process_madvise sys_process_madvise 441 n64 epoll_pwait2 sys_epoll_pwait2 442 n64 mount_setattr sys_mount_setattr 443 n64 quotactl_fd sys_quotactl_fd 444 n64 landlock_create_ruleset sys_landlock_create_ruleset 445 n64 landlock_add_rule sys_landlock_add_rule 446 n64 landlock_restrict_self sys_landlock_restrict_self 448 n64 process_mrelease sys_process_mrelease 449 n64 futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 n64 cachestat sys_cachestat 452 n64 fchmodat2 sys_fchmodat2 453 n64 map_shadow_stack sys_map_shadow_stack 454 n64 futex_wake sys_futex_wake 455 n64 futex_wait sys_futex_wait 456 n64 futex_requeue sys_futex_requeue 457 n64 statmount sys_statmount 458 n64 listmount sys_listmount 459 n64 lsm_get_self_attr sys_lsm_get_self_attr 460 n64 lsm_set_self_attr sys_lsm_set_self_attr 461 n64 lsm_list_modules sys_lsm_list_modules 462 n64 mseal sys_mseal 463 n64 setxattrat sys_setxattrat 464 n64 getxattrat sys_getxattrat 465 n64 listxattrat sys_listxattrat 466 n64 removexattrat sys_removexattrat 467 n64 open_tree_attr sys_open_tree_attr 468 n64 file_getattr sys_file_getattr 469 n64 file_setattr sys_file_setattr 470 n64 listns sys_listns """ # PowerPC # - arch/powerpc/kernel/syscalls/syscall.tbl ppc_syscall_tbl = """ 0 nospu restart_syscall sys_restart_syscall 1 nospu exit sys_exit 2 nospu fork sys_fork 3 common read sys_read 4 common write sys_write 5 common open sys_open compat_sys_open 6 common close sys_close 7 common waitpid sys_waitpid 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 nospu execve sys_execve compat_sys_execve 12 common chdir sys_chdir 13 32 time sys_time32 13 64 time sys_time 13 spu time sys_time 14 common mknod sys_mknod 15 common chmod sys_chmod 16 common lchown sys_lchown 17 common break sys_ni_syscall 18 32 oldstat sys_stat sys_ni_syscall 18 64 oldstat sys_ni_syscall 18 spu oldstat sys_ni_syscall 19 common lseek sys_lseek compat_sys_lseek 20 common getpid sys_getpid 21 nospu mount sys_mount 22 32 umount sys_oldumount 22 64 umount sys_ni_syscall 22 spu umount sys_ni_syscall 23 common setuid sys_setuid 24 common getuid sys_getuid 25 32 stime sys_stime32 25 64 stime sys_stime 25 spu stime sys_stime 26 nospu ptrace sys_ptrace compat_sys_ptrace 27 common alarm sys_alarm 28 32 oldfstat sys_fstat sys_ni_syscall 28 64 oldfstat sys_ni_syscall 28 spu oldfstat sys_ni_syscall 29 nospu pause sys_pause 30 32 utime sys_utime32 30 64 utime sys_utime 31 common stty sys_ni_syscall 32 common gtty sys_ni_syscall 33 common access sys_access 34 common nice sys_nice 35 common ftime sys_ni_syscall 36 common sync sys_sync 37 common kill sys_kill 38 common rename sys_rename 39 common mkdir sys_mkdir 40 common rmdir sys_rmdir 41 common dup sys_dup 42 common pipe sys_pipe 43 common times sys_times compat_sys_times 44 common prof sys_ni_syscall 45 common brk sys_brk 46 common setgid sys_setgid 47 common getgid sys_getgid 48 nospu signal sys_signal 49 common geteuid sys_geteuid 50 common getegid sys_getegid 51 nospu acct sys_acct 52 nospu umount2 sys_umount 53 common lock sys_ni_syscall 54 common ioctl sys_ioctl compat_sys_ioctl 55 common fcntl sys_fcntl compat_sys_fcntl 56 common mpx sys_ni_syscall 57 common setpgid sys_setpgid 58 common ulimit sys_ni_syscall 59 32 oldolduname sys_olduname 59 64 oldolduname sys_ni_syscall 59 spu oldolduname sys_ni_syscall 60 common umask sys_umask 61 common chroot sys_chroot 62 nospu ustat sys_ustat compat_sys_ustat 63 common dup2 sys_dup2 64 common getppid sys_getppid 65 common getpgrp sys_getpgrp 66 common setsid sys_setsid 67 32 sigaction sys_sigaction compat_sys_sigaction 67 64 sigaction sys_ni_syscall 67 spu sigaction sys_ni_syscall 68 common sgetmask sys_sgetmask 69 common ssetmask sys_ssetmask 70 common setreuid sys_setreuid 71 common setregid sys_setregid 72 32 sigsuspend sys_sigsuspend 72 64 sigsuspend sys_ni_syscall 72 spu sigsuspend sys_ni_syscall 73 32 sigpending sys_sigpending compat_sys_sigpending 73 64 sigpending sys_ni_syscall 73 spu sigpending sys_ni_syscall 74 common sethostname sys_sethostname 75 common setrlimit sys_setrlimit compat_sys_setrlimit 76 32 getrlimit sys_old_getrlimit compat_sys_old_getrlimit 76 64 getrlimit sys_ni_syscall 76 spu getrlimit sys_ni_syscall 77 common getrusage sys_getrusage compat_sys_getrusage 78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday 79 common settimeofday sys_settimeofday compat_sys_settimeofday 80 common getgroups sys_getgroups 81 common setgroups sys_setgroups 82 32 select sys_old_select compat_sys_old_select 82 64 select sys_ni_syscall 82 spu select sys_ni_syscall 83 common symlink sys_symlink 84 32 oldlstat sys_lstat sys_ni_syscall 84 64 oldlstat sys_ni_syscall 84 spu oldlstat sys_ni_syscall 85 common readlink sys_readlink 86 nospu uselib sys_uselib 87 nospu swapon sys_swapon 88 nospu reboot sys_reboot 89 32 readdir sys_old_readdir compat_sys_old_readdir 89 64 readdir sys_ni_syscall 89 spu readdir sys_ni_syscall 90 common mmap sys_mmap 91 common munmap sys_munmap 92 common truncate sys_truncate compat_sys_truncate 93 common ftruncate sys_ftruncate compat_sys_ftruncate 94 common fchmod sys_fchmod 95 common fchown sys_fchown 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority 98 common profil sys_ni_syscall 99 nospu statfs sys_statfs compat_sys_statfs 100 nospu fstatfs sys_fstatfs compat_sys_fstatfs 101 common ioperm sys_ni_syscall 102 common socketcall sys_socketcall compat_sys_socketcall 103 common syslog sys_syslog 104 common setitimer sys_setitimer compat_sys_setitimer 105 common getitimer sys_getitimer compat_sys_getitimer 106 common stat sys_newstat compat_sys_newstat 107 common lstat sys_newlstat compat_sys_newlstat 108 common fstat sys_newfstat compat_sys_newfstat 109 32 olduname sys_uname 109 64 olduname sys_ni_syscall 109 spu olduname sys_ni_syscall 110 common iopl sys_ni_syscall 111 common vhangup sys_vhangup 112 common idle sys_ni_syscall 113 common vm86 sys_ni_syscall 114 common wait4 sys_wait4 compat_sys_wait4 115 nospu swapoff sys_swapoff 116 common sysinfo sys_sysinfo compat_sys_sysinfo 117 nospu ipc sys_ipc compat_sys_ipc 118 common fsync sys_fsync 119 32 sigreturn sys_sigreturn compat_sys_sigreturn 119 64 sigreturn sys_ni_syscall 119 spu sigreturn sys_ni_syscall 120 nospu clone sys_clone 121 common setdomainname sys_setdomainname 122 common uname sys_newuname 123 common modify_ldt sys_ni_syscall 124 32 adjtimex sys_adjtimex_time32 124 64 adjtimex sys_adjtimex 124 spu adjtimex sys_adjtimex 125 common mprotect sys_mprotect 126 32 sigprocmask sys_sigprocmask compat_sys_sigprocmask 126 64 sigprocmask sys_ni_syscall 126 spu sigprocmask sys_ni_syscall 127 common create_module sys_ni_syscall 128 nospu init_module sys_init_module 129 nospu delete_module sys_delete_module 130 common get_kernel_syms sys_ni_syscall 131 nospu quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir 134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 32 personality sys_personality compat_sys_ppc64_personality 136 64 personality sys_ppc64_personality 136 spu personality sys_ppc64_personality 137 common afs_syscall sys_ni_syscall 138 common setfsuid sys_setfsuid 139 common setfsgid sys_setfsgid 140 common _llseek sys_llseek 141 common getdents sys_getdents compat_sys_getdents 142 common _newselect sys_select compat_sys_select 143 common flock sys_flock 144 common msync sys_msync 145 common readv sys_readv 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync 149 nospu _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall 153 common munlockall sys_munlockall 154 common sched_setparam sys_sched_setparam 155 common sched_getparam sys_sched_getparam 156 common sched_setscheduler sys_sched_setscheduler 157 common sched_getscheduler sys_sched_getscheduler 158 common sched_yield sys_sched_yield 159 common sched_get_priority_max sys_sched_get_priority_max 160 common sched_get_priority_min sys_sched_get_priority_min 161 32 sched_rr_get_interval sys_sched_rr_get_interval_time32 161 64 sched_rr_get_interval sys_sched_rr_get_interval 161 spu sched_rr_get_interval sys_sched_rr_get_interval 162 32 nanosleep sys_nanosleep_time32 162 64 nanosleep sys_nanosleep 162 spu nanosleep sys_nanosleep 163 common mremap sys_mremap 164 common setresuid sys_setresuid 165 common getresuid sys_getresuid 166 common query_module sys_ni_syscall 167 common poll sys_poll 168 common nfsservctl sys_ni_syscall 169 common setresgid sys_setresgid 170 common getresgid sys_getresgid 171 common prctl sys_prctl 172 nospu rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn 173 nospu rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction 174 nospu rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask 175 nospu rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending 176 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 176 64 rt_sigtimedwait sys_rt_sigtimedwait 177 nospu rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 178 nospu rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 179 32 pread64 sys_ppc_pread64 compat_sys_ppc_pread64 179 64 pread64 sys_pread64 179 spu pread64 sys_pread64 180 32 pwrite64 sys_ppc_pwrite64 compat_sys_ppc_pwrite64 180 64 pwrite64 sys_pwrite64 180 spu pwrite64 sys_pwrite64 181 common chown sys_chown 182 common getcwd sys_getcwd 183 common capget sys_capget 184 common capset sys_capset 185 nospu sigaltstack sys_sigaltstack compat_sys_sigaltstack 186 32 sendfile sys_sendfile compat_sys_sendfile 186 64 sendfile sys_sendfile64 186 spu sendfile sys_sendfile64 187 common getpmsg sys_ni_syscall 188 common putpmsg sys_ni_syscall 189 nospu vfork sys_vfork 190 common ugetrlimit sys_getrlimit compat_sys_getrlimit 191 32 readahead sys_ppc_readahead compat_sys_ppc_readahead 191 64 readahead sys_readahead 191 spu readahead sys_readahead 192 32 mmap2 sys_mmap2 compat_sys_mmap2 193 32 truncate64 sys_ppc_truncate64 compat_sys_ppc_truncate64 194 32 ftruncate64 sys_ppc_ftruncate64 compat_sys_ppc_ftruncate64 195 32 stat64 sys_stat64 196 32 lstat64 sys_lstat64 197 32 fstat64 sys_fstat64 198 nospu pciconfig_read sys_pciconfig_read 199 nospu pciconfig_write sys_pciconfig_write 200 nospu pciconfig_iobase sys_pciconfig_iobase 201 common multiplexer sys_ni_syscall 202 common getdents64 sys_getdents64 203 common pivot_root sys_pivot_root 204 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 205 common madvise sys_madvise 206 common mincore sys_mincore 207 common gettid sys_gettid 208 common tkill sys_tkill 209 common setxattr sys_setxattr 210 common lsetxattr sys_lsetxattr 211 common fsetxattr sys_fsetxattr 212 common getxattr sys_getxattr 213 common lgetxattr sys_lgetxattr 214 common fgetxattr sys_fgetxattr 215 common listxattr sys_listxattr 216 common llistxattr sys_llistxattr 217 common flistxattr sys_flistxattr 218 common removexattr sys_removexattr 219 common lremovexattr sys_lremovexattr 220 common fremovexattr sys_fremovexattr 221 32 futex sys_futex_time32 221 64 futex sys_futex 221 spu futex sys_futex 222 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity 223 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity 225 common tuxcall sys_ni_syscall 226 32 sendfile64 sys_sendfile64 compat_sys_sendfile64 227 common io_setup sys_io_setup compat_sys_io_setup 228 common io_destroy sys_io_destroy 229 32 io_getevents sys_io_getevents_time32 229 64 io_getevents sys_io_getevents 229 spu io_getevents sys_io_getevents 230 common io_submit sys_io_submit compat_sys_io_submit 231 common io_cancel sys_io_cancel 232 nospu set_tid_address sys_set_tid_address 233 32 fadvise64 sys_ppc32_fadvise64 compat_sys_ppc32_fadvise64 233 64 fadvise64 sys_fadvise64 233 spu fadvise64 sys_fadvise64 234 nospu exit_group sys_exit_group 235 nospu lookup_dcookie sys_ni_syscall 236 common epoll_create sys_epoll_create 237 common epoll_ctl sys_epoll_ctl 238 common epoll_wait sys_epoll_wait 239 common remap_file_pages sys_remap_file_pages 240 common timer_create sys_timer_create compat_sys_timer_create 241 32 timer_settime sys_timer_settime32 241 64 timer_settime sys_timer_settime 241 spu timer_settime sys_timer_settime 242 32 timer_gettime sys_timer_gettime32 242 64 timer_gettime sys_timer_gettime 242 spu timer_gettime sys_timer_gettime 243 common timer_getoverrun sys_timer_getoverrun 244 common timer_delete sys_timer_delete 245 32 clock_settime sys_clock_settime32 245 64 clock_settime sys_clock_settime 245 spu clock_settime sys_clock_settime 246 32 clock_gettime sys_clock_gettime32 246 64 clock_gettime sys_clock_gettime 246 spu clock_gettime sys_clock_gettime 247 32 clock_getres sys_clock_getres_time32 247 64 clock_getres sys_clock_getres 247 spu clock_getres sys_clock_getres 248 32 clock_nanosleep sys_clock_nanosleep_time32 248 64 clock_nanosleep sys_clock_nanosleep 248 spu clock_nanosleep sys_clock_nanosleep 249 nospu swapcontext sys_swapcontext compat_sys_swapcontext 250 common tgkill sys_tgkill 251 32 utimes sys_utimes_time32 251 64 utimes sys_utimes 251 spu utimes sys_utimes 252 common statfs64 sys_statfs64 compat_sys_statfs64 253 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 254 32 fadvise64_64 sys_ppc_fadvise64_64 254 spu fadvise64_64 sys_ni_syscall 255 common rtas sys_rtas 256 32 sys_debug_setcontext sys_debug_setcontext sys_ni_syscall 256 64 sys_debug_setcontext sys_ni_syscall 256 spu sys_debug_setcontext sys_ni_syscall 258 nospu migrate_pages sys_migrate_pages 259 nospu mbind sys_mbind 260 nospu get_mempolicy sys_get_mempolicy 261 nospu set_mempolicy sys_set_mempolicy 262 nospu mq_open sys_mq_open compat_sys_mq_open 263 nospu mq_unlink sys_mq_unlink 264 32 mq_timedsend sys_mq_timedsend_time32 264 64 mq_timedsend sys_mq_timedsend 265 32 mq_timedreceive sys_mq_timedreceive_time32 265 64 mq_timedreceive sys_mq_timedreceive 266 nospu mq_notify sys_mq_notify compat_sys_mq_notify 267 nospu mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr 268 nospu kexec_load sys_kexec_load compat_sys_kexec_load 269 nospu add_key sys_add_key 270 nospu request_key sys_request_key 271 nospu keyctl sys_keyctl compat_sys_keyctl 272 nospu waitid sys_waitid compat_sys_waitid 273 nospu ioprio_set sys_ioprio_set 274 nospu ioprio_get sys_ioprio_get 275 nospu inotify_init sys_inotify_init 276 nospu inotify_add_watch sys_inotify_add_watch 277 nospu inotify_rm_watch sys_inotify_rm_watch 278 nospu spu_run sys_spu_run 279 nospu spu_create sys_spu_create 280 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 280 64 pselect6 sys_pselect6 281 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 281 64 ppoll sys_ppoll 282 common unshare sys_unshare 283 common splice sys_splice 284 common tee sys_tee 285 common vmsplice sys_vmsplice 286 common openat sys_openat compat_sys_openat 287 common mkdirat sys_mkdirat 288 common mknodat sys_mknodat 289 common fchownat sys_fchownat 290 32 futimesat sys_futimesat_time32 290 64 futimesat sys_futimesat 290 spu utimesat sys_futimesat 291 32 fstatat64 sys_fstatat64 291 64 newfstatat sys_newfstatat 291 spu newfstatat sys_newfstatat 292 common unlinkat sys_unlinkat 293 common renameat sys_renameat 294 common linkat sys_linkat 295 common symlinkat sys_symlinkat 296 common readlinkat sys_readlinkat 297 common fchmodat sys_fchmodat 298 common faccessat sys_faccessat 299 common get_robust_list sys_get_robust_list compat_sys_get_robust_list 300 common set_robust_list sys_set_robust_list compat_sys_set_robust_list 301 common move_pages sys_move_pages 302 common getcpu sys_getcpu 303 nospu epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 304 32 utimensat sys_utimensat_time32 304 64 utimensat sys_utimensat 304 spu utimensat sys_utimensat 305 common signalfd sys_signalfd compat_sys_signalfd 306 common timerfd_create sys_timerfd_create 307 common eventfd sys_eventfd 308 32 sync_file_range2 sys_ppc_sync_file_range2 compat_sys_ppc_sync_file_range2 308 64 sync_file_range2 sys_sync_file_range2 308 spu sync_file_range2 sys_sync_file_range2 309 32 fallocate sys_ppc_fallocate compat_sys_fallocate 309 64 fallocate sys_fallocate 310 nospu subpage_prot sys_subpage_prot 311 32 timerfd_settime sys_timerfd_settime32 311 64 timerfd_settime sys_timerfd_settime 311 spu timerfd_settime sys_timerfd_settime 312 32 timerfd_gettime sys_timerfd_gettime32 312 64 timerfd_gettime sys_timerfd_gettime 312 spu timerfd_gettime sys_timerfd_gettime 313 common signalfd4 sys_signalfd4 compat_sys_signalfd4 314 common eventfd2 sys_eventfd2 315 common epoll_create1 sys_epoll_create1 316 common dup3 sys_dup3 317 common pipe2 sys_pipe2 318 nospu inotify_init1 sys_inotify_init1 319 common perf_event_open sys_perf_event_open 320 common preadv sys_preadv compat_sys_preadv 321 common pwritev sys_pwritev compat_sys_pwritev 322 nospu rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 323 nospu fanotify_init sys_fanotify_init 324 nospu fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark 325 common prlimit64 sys_prlimit64 326 common socket sys_socket 327 common bind sys_bind 328 common connect sys_connect 329 common listen sys_listen 330 common accept sys_accept 331 common getsockname sys_getsockname 332 common getpeername sys_getpeername 333 common socketpair sys_socketpair 334 common send sys_send 335 common sendto sys_sendto 336 common recv sys_recv compat_sys_recv 337 common recvfrom sys_recvfrom compat_sys_recvfrom 338 common shutdown sys_shutdown 339 common setsockopt sys_setsockopt sys_setsockopt 340 common getsockopt sys_getsockopt sys_getsockopt 341 common sendmsg sys_sendmsg compat_sys_sendmsg 342 common recvmsg sys_recvmsg compat_sys_recvmsg 343 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 343 64 recvmmsg sys_recvmmsg 343 spu recvmmsg sys_recvmmsg 344 common accept4 sys_accept4 345 common name_to_handle_at sys_name_to_handle_at 346 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at 347 32 clock_adjtime sys_clock_adjtime32 347 64 clock_adjtime sys_clock_adjtime 347 spu clock_adjtime sys_clock_adjtime 348 common syncfs sys_syncfs 349 common sendmmsg sys_sendmmsg compat_sys_sendmmsg 350 common setns sys_setns 351 nospu process_vm_readv sys_process_vm_readv 352 nospu process_vm_writev sys_process_vm_writev 353 nospu finit_module sys_finit_module 354 nospu kcmp sys_kcmp 355 common sched_setattr sys_sched_setattr 356 common sched_getattr sys_sched_getattr 357 common renameat2 sys_renameat2 358 common seccomp sys_seccomp 359 common getrandom sys_getrandom 360 common memfd_create sys_memfd_create 361 common bpf sys_bpf 362 nospu execveat sys_execveat compat_sys_execveat 363 32 switch_endian sys_ni_syscall 363 64 switch_endian sys_switch_endian 363 spu switch_endian sys_ni_syscall 364 common userfaultfd sys_userfaultfd 365 common membarrier sys_membarrier 378 nospu mlock2 sys_mlock2 379 nospu copy_file_range sys_copy_file_range 380 common preadv2 sys_preadv2 compat_sys_preadv2 381 common pwritev2 sys_pwritev2 compat_sys_pwritev2 382 nospu kexec_file_load sys_kexec_file_load 383 nospu statx sys_statx 384 nospu pkey_alloc sys_pkey_alloc 385 nospu pkey_free sys_pkey_free 386 nospu pkey_mprotect sys_pkey_mprotect 387 nospu rseq sys_rseq 388 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents 388 64 io_pgetevents sys_io_pgetevents 392 64 semtimedop sys_semtimedop 393 common semget sys_semget 394 common semctl sys_semctl compat_sys_semctl 395 common shmget sys_shmget 396 common shmctl sys_shmctl compat_sys_shmctl 397 common shmat sys_shmat compat_sys_shmat 398 common shmdt sys_shmdt 399 common msgget sys_msgget 400 common msgsnd sys_msgsnd compat_sys_msgsnd 401 common msgrcv sys_msgrcv compat_sys_msgrcv 402 common msgctl sys_msgctl compat_sys_msgctl 403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime 404 32 clock_settime64 sys_clock_settime sys_clock_settime 405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime 406 32 clock_getres_time64 sys_clock_getres sys_clock_getres 407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep 408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime 409 32 timer_settime64 sys_timer_settime sys_timer_settime 410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime 411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime 412 32 utimensat_time64 sys_utimensat sys_utimensat 413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend 419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive 420 32 semtimedop_time64 sys_semtimedop sys_semtimedop 421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 422 32 futex_time64 sys_futex sys_futex 423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 nospu clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 nospu set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_ni_syscall 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # SPARC # - arch/sparc/kernel/syscalls/syscall.tbl sparc_syscall_tbl = """ 0 common restart_syscall sys_restart_syscall 1 32 exit sys_exit sparc_exit 1 64 exit sparc_exit 2 common fork sys_fork 3 common read sys_read 4 common write sys_write 5 common open sys_open compat_sys_open 6 common close sys_close 7 common wait4 sys_wait4 compat_sys_wait4 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 32 execv sunos_execv 11 64 execv sys_nis_syscall 12 common chdir sys_chdir 13 32 chown sys_chown16 13 64 chown sys_chown 14 common mknod sys_mknod 15 common chmod sys_chmod 16 32 lchown sys_lchown16 16 64 lchown sys_lchown 17 common brk sys_brk 18 common perfctr sys_nis_syscall 19 common lseek sys_lseek compat_sys_lseek 20 common getpid sys_getpid 21 common capget sys_capget 22 common capset sys_capset 23 32 setuid sys_setuid16 23 64 setuid sys_setuid 24 32 getuid sys_getuid16 24 64 getuid sys_getuid 25 common vmsplice sys_vmsplice 26 common ptrace sys_ptrace compat_sys_ptrace 27 common alarm sys_alarm 28 common sigaltstack sys_sigaltstack compat_sys_sigaltstack 29 32 pause sys_pause 29 64 pause sys_nis_syscall 30 32 utime sys_utime32 30 64 utime sys_utime 31 32 lchown32 sys_lchown 32 32 fchown32 sys_fchown 33 common access sys_access 34 common nice sys_nice 35 32 chown32 sys_chown 36 common sync sys_sync 37 common kill sys_kill 38 common stat sys_newstat compat_sys_newstat 39 32 sendfile sys_sendfile compat_sys_sendfile 39 64 sendfile sys_sendfile64 40 common lstat sys_newlstat compat_sys_newlstat 41 common dup sys_dup 42 common pipe sys_sparc_pipe 43 common times sys_times compat_sys_times 44 32 getuid32 sys_getuid 45 common umount2 sys_umount 46 32 setgid sys_setgid16 46 64 setgid sys_setgid 47 32 getgid sys_getgid16 47 64 getgid sys_getgid 48 common signal sys_signal 49 32 geteuid sys_geteuid16 49 64 geteuid sys_geteuid 50 32 getegid sys_getegid16 50 64 getegid sys_getegid 51 common acct sys_acct 52 64 memory_ordering sys_memory_ordering 53 32 getgid32 sys_getgid 54 common ioctl sys_ioctl compat_sys_ioctl 55 common reboot sys_reboot 56 32 mmap2 sys_mmap2 sys32_mmap2 57 common symlink sys_symlink 58 common readlink sys_readlink 59 32 execve sys_execve sys32_execve 59 64 execve sys64_execve 60 common umask sys_umask 61 common chroot sys_chroot 62 common fstat sys_newfstat compat_sys_newfstat 63 common fstat64 sys_fstat64 compat_sys_fstat64 64 common getpagesize sys_getpagesize 65 common msync sys_msync 66 common vfork sys_vfork 67 common pread64 sys_pread64 compat_sys_pread64 68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 69 32 geteuid32 sys_geteuid 70 32 getegid32 sys_getegid 71 common mmap sys_mmap 72 32 setreuid32 sys_setreuid 73 32 munmap sys_munmap 73 64 munmap sys_64_munmap 74 common mprotect sys_mprotect 75 common madvise sys_madvise 76 common vhangup sys_vhangup 77 32 truncate64 sys_truncate64 compat_sys_truncate64 78 common mincore sys_mincore 79 32 getgroups sys_getgroups16 79 64 getgroups sys_getgroups 80 32 setgroups sys_setgroups16 80 64 setgroups sys_setgroups 81 common getpgrp sys_getpgrp 82 32 setgroups32 sys_setgroups 83 common setitimer sys_setitimer compat_sys_setitimer 84 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 85 common swapon sys_swapon 86 common getitimer sys_getitimer compat_sys_getitimer 87 32 setuid32 sys_setuid 88 common sethostname sys_sethostname 89 32 setgid32 sys_setgid 90 common dup2 sys_dup2 91 32 setfsuid32 sys_setfsuid 92 common fcntl sys_fcntl compat_sys_fcntl 93 common select sys_select compat_sys_select 94 32 setfsgid32 sys_setfsgid 95 common fsync sys_fsync 96 common setpriority sys_setpriority 97 common socket sys_socket 98 common connect sys_connect 99 common accept sys_accept 100 common getpriority sys_getpriority 101 common rt_sigreturn sys_rt_sigreturn sys32_rt_sigreturn 102 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction 103 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask 104 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending 105 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 105 64 rt_sigtimedwait sys_rt_sigtimedwait 106 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 107 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 108 32 setresuid32 sys_setresuid 108 64 setresuid sys_setresuid 109 32 getresuid32 sys_getresuid 109 64 getresuid sys_getresuid 110 32 setresgid32 sys_setresgid 110 64 setresgid sys_setresgid 111 32 getresgid32 sys_getresgid 111 64 getresgid sys_getresgid 112 32 setregid32 sys_setregid 113 common recvmsg sys_recvmsg compat_sys_recvmsg 114 common sendmsg sys_sendmsg compat_sys_sendmsg 115 32 getgroups32 sys_getgroups 116 common gettimeofday sys_gettimeofday compat_sys_gettimeofday 117 common getrusage sys_getrusage compat_sys_getrusage 118 common getsockopt sys_getsockopt sys_getsockopt 119 common getcwd sys_getcwd 120 common readv sys_readv 121 common writev sys_writev 122 common settimeofday sys_settimeofday compat_sys_settimeofday 123 32 fchown sys_fchown16 123 64 fchown sys_fchown 124 common fchmod sys_fchmod 125 common recvfrom sys_recvfrom compat_sys_recvfrom 126 32 setreuid sys_setreuid16 126 64 setreuid sys_setreuid 127 32 setregid sys_setregid16 127 64 setregid sys_setregid 128 common rename sys_rename 129 common truncate sys_truncate compat_sys_truncate 130 common ftruncate sys_ftruncate compat_sys_ftruncate 131 common flock sys_flock 132 common lstat64 sys_lstat64 compat_sys_lstat64 133 common sendto sys_sendto 134 common shutdown sys_shutdown 135 common socketpair sys_socketpair 136 common mkdir sys_mkdir 137 common rmdir sys_rmdir 138 32 utimes sys_utimes_time32 138 64 utimes sys_utimes 139 common stat64 sys_stat64 compat_sys_stat64 140 common sendfile64 sys_sendfile64 141 common getpeername sys_getpeername 142 32 futex sys_futex_time32 142 64 futex sys_futex 143 common gettid sys_gettid 144 common getrlimit sys_getrlimit compat_sys_getrlimit 145 common setrlimit sys_setrlimit compat_sys_setrlimit 146 common pivot_root sys_pivot_root 147 common prctl sys_prctl 148 common pciconfig_read sys_pciconfig_read 149 common pciconfig_write sys_pciconfig_write 150 common getsockname sys_getsockname 151 common inotify_init sys_inotify_init 152 common inotify_add_watch sys_inotify_add_watch 153 common poll sys_poll 154 common getdents64 sys_getdents64 155 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 156 common inotify_rm_watch sys_inotify_rm_watch 157 common statfs sys_statfs compat_sys_statfs 158 common fstatfs sys_fstatfs compat_sys_fstatfs 159 common umount sys_oldumount 160 common sched_set_affinity sys_sched_setaffinity compat_sys_sched_setaffinity 161 common sched_get_affinity sys_sched_getaffinity compat_sys_sched_getaffinity 162 common getdomainname sys_getdomainname 163 common setdomainname sys_setdomainname 164 64 utrap_install sys_utrap_install 165 common quotactl sys_quotactl 166 common set_tid_address sys_set_tid_address 167 common mount sys_mount 168 common ustat sys_ustat compat_sys_ustat 169 common setxattr sys_setxattr 170 common lsetxattr sys_lsetxattr 171 common fsetxattr sys_fsetxattr 172 common getxattr sys_getxattr 173 common lgetxattr sys_lgetxattr 174 common getdents sys_getdents compat_sys_getdents 175 common setsid sys_setsid 176 common fchdir sys_fchdir 177 common fgetxattr sys_fgetxattr 178 common listxattr sys_listxattr 179 common llistxattr sys_llistxattr 180 common flistxattr sys_flistxattr 181 common removexattr sys_removexattr 182 common lremovexattr sys_lremovexattr 183 32 sigpending sys_sigpending compat_sys_sigpending 183 64 sigpending sys_nis_syscall 184 common query_module sys_ni_syscall 185 common setpgid sys_setpgid 186 common fremovexattr sys_fremovexattr 187 common tkill sys_tkill 188 32 exit_group sys_exit_group sparc_exit_group 188 64 exit_group sparc_exit_group 189 common uname sys_newuname 190 common init_module sys_init_module 191 32 personality sys_personality sys_sparc64_personality 191 64 personality sys_sparc64_personality 192 32 remap_file_pages sys_sparc_remap_file_pages sys_remap_file_pages 192 64 remap_file_pages sys_remap_file_pages 193 common epoll_create sys_epoll_create 194 common epoll_ctl sys_epoll_ctl 195 common epoll_wait sys_epoll_wait 196 common ioprio_set sys_ioprio_set 197 common getppid sys_getppid 198 32 sigaction sys_sparc_sigaction compat_sys_sparc_sigaction 198 64 sigaction sys_nis_syscall 199 common sgetmask sys_sgetmask 200 common ssetmask sys_ssetmask 201 32 sigsuspend sys_sigsuspend 201 64 sigsuspend sys_nis_syscall 202 common oldlstat sys_newlstat compat_sys_newlstat 203 common uselib sys_uselib 204 32 readdir sys_old_readdir compat_sys_old_readdir 204 64 readdir sys_nis_syscall 205 common readahead sys_readahead compat_sys_readahead 206 common socketcall sys_socketcall compat_sys_socketcall 207 common syslog sys_syslog 208 common lookup_dcookie sys_ni_syscall 209 common fadvise64 sys_fadvise64 compat_sys_fadvise64 210 common fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 211 common tgkill sys_tgkill 212 common waitpid sys_waitpid 213 common swapoff sys_swapoff 214 common sysinfo sys_sysinfo compat_sys_sysinfo 215 32 ipc sys_ipc compat_sys_ipc 215 64 ipc sys_sparc_ipc 216 32 sigreturn sys_sigreturn sys32_sigreturn 216 64 sigreturn sys_nis_syscall 217 common clone sys_clone 218 common ioprio_get sys_ioprio_get 219 32 adjtimex sys_adjtimex_time32 219 64 adjtimex sys_sparc_adjtimex 220 32 sigprocmask sys_sigprocmask compat_sys_sigprocmask 220 64 sigprocmask sys_nis_syscall 221 common create_module sys_ni_syscall 222 common delete_module sys_delete_module 223 common get_kernel_syms sys_ni_syscall 224 common getpgid sys_getpgid 225 common bdflush sys_ni_syscall 226 common sysfs sys_sysfs 227 common afs_syscall sys_nis_syscall 228 common setfsuid sys_setfsuid16 229 common setfsgid sys_setfsgid16 230 common _newselect sys_select compat_sys_select 231 32 time sys_time32 232 common splice sys_splice 233 32 stime sys_stime32 233 64 stime sys_stime 234 common statfs64 sys_statfs64 compat_sys_statfs64 235 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 236 common _llseek sys_llseek 237 common mlock sys_mlock 238 common munlock sys_munlock 239 common mlockall sys_mlockall 240 common munlockall sys_munlockall 241 common sched_setparam sys_sched_setparam 242 common sched_getparam sys_sched_getparam 243 common sched_setscheduler sys_sched_setscheduler 244 common sched_getscheduler sys_sched_getscheduler 245 common sched_yield sys_sched_yield 246 common sched_get_priority_max sys_sched_get_priority_max 247 common sched_get_priority_min sys_sched_get_priority_min 248 32 sched_rr_get_interval sys_sched_rr_get_interval_time32 248 64 sched_rr_get_interval sys_sched_rr_get_interval 249 32 nanosleep sys_nanosleep_time32 249 64 nanosleep sys_nanosleep 250 32 mremap sys_mremap 250 64 mremap sys_64_mremap 251 common _sysctl sys_ni_syscall 252 common getsid sys_getsid 253 common fdatasync sys_fdatasync 254 32 nfsservctl sys_ni_syscall sys_nis_syscall 254 64 nfsservctl sys_nis_syscall 255 common sync_file_range sys_sync_file_range compat_sys_sync_file_range 256 32 clock_settime sys_clock_settime32 256 64 clock_settime sys_clock_settime 257 32 clock_gettime sys_clock_gettime32 257 64 clock_gettime sys_clock_gettime 258 32 clock_getres sys_clock_getres_time32 258 64 clock_getres sys_clock_getres 259 32 clock_nanosleep sys_clock_nanosleep_time32 259 64 clock_nanosleep sys_clock_nanosleep 260 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity 261 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity 262 32 timer_settime sys_timer_settime32 262 64 timer_settime sys_timer_settime 263 32 timer_gettime sys_timer_gettime32 263 64 timer_gettime sys_timer_gettime 264 common timer_getoverrun sys_timer_getoverrun 265 common timer_delete sys_timer_delete 266 common timer_create sys_timer_create compat_sys_timer_create 267 common vserver sys_nis_syscall 268 common io_setup sys_io_setup compat_sys_io_setup 269 common io_destroy sys_io_destroy 270 common io_submit sys_io_submit compat_sys_io_submit 271 common io_cancel sys_io_cancel 272 32 io_getevents sys_io_getevents_time32 272 64 io_getevents sys_io_getevents 273 common mq_open sys_mq_open compat_sys_mq_open 274 common mq_unlink sys_mq_unlink 275 32 mq_timedsend sys_mq_timedsend_time32 275 64 mq_timedsend sys_mq_timedsend 276 32 mq_timedreceive sys_mq_timedreceive_time32 276 64 mq_timedreceive sys_mq_timedreceive 277 common mq_notify sys_mq_notify compat_sys_mq_notify 278 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr 279 common waitid sys_waitid compat_sys_waitid 280 common tee sys_tee 281 common add_key sys_add_key 282 common request_key sys_request_key 283 common keyctl sys_keyctl compat_sys_keyctl 284 common openat sys_openat compat_sys_openat 285 common mkdirat sys_mkdirat 286 common mknodat sys_mknodat 287 common fchownat sys_fchownat 288 32 futimesat sys_futimesat_time32 288 64 futimesat sys_futimesat 289 common fstatat64 sys_fstatat64 compat_sys_fstatat64 290 common unlinkat sys_unlinkat 291 common renameat sys_renameat 292 common linkat sys_linkat 293 common symlinkat sys_symlinkat 294 common readlinkat sys_readlinkat 295 common fchmodat sys_fchmodat 296 common faccessat sys_faccessat 297 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 297 64 pselect6 sys_pselect6 298 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 298 64 ppoll sys_ppoll 299 common unshare sys_unshare 300 common set_robust_list sys_set_robust_list compat_sys_set_robust_list 301 common get_robust_list sys_get_robust_list compat_sys_get_robust_list 302 common migrate_pages sys_migrate_pages 303 common mbind sys_mbind 304 common get_mempolicy sys_get_mempolicy 305 common set_mempolicy sys_set_mempolicy 306 common kexec_load sys_kexec_load compat_sys_kexec_load 307 common move_pages sys_move_pages 308 common getcpu sys_getcpu 309 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 310 32 utimensat sys_utimensat_time32 310 64 utimensat sys_utimensat 311 common signalfd sys_signalfd compat_sys_signalfd 312 common timerfd_create sys_timerfd_create 313 common eventfd sys_eventfd 314 common fallocate sys_fallocate compat_sys_fallocate 315 32 timerfd_settime sys_timerfd_settime32 315 64 timerfd_settime sys_timerfd_settime 316 32 timerfd_gettime sys_timerfd_gettime32 316 64 timerfd_gettime sys_timerfd_gettime 317 common signalfd4 sys_signalfd4 compat_sys_signalfd4 318 common eventfd2 sys_eventfd2 319 common epoll_create1 sys_epoll_create1 320 common dup3 sys_dup3 321 common pipe2 sys_pipe2 322 common inotify_init1 sys_inotify_init1 323 common accept4 sys_accept4 324 common preadv sys_preadv compat_sys_preadv 325 common pwritev sys_pwritev compat_sys_pwritev 326 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 327 common perf_event_open sys_perf_event_open 328 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 328 64 recvmmsg sys_recvmmsg 329 common fanotify_init sys_fanotify_init 330 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark 331 common prlimit64 sys_prlimit64 332 common name_to_handle_at sys_name_to_handle_at 333 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at 334 32 clock_adjtime sys_clock_adjtime32 334 64 clock_adjtime sys_sparc_clock_adjtime 335 common syncfs sys_syncfs 336 common sendmmsg sys_sendmmsg compat_sys_sendmmsg 337 common setns sys_setns 338 common process_vm_readv sys_process_vm_readv 339 common process_vm_writev sys_process_vm_writev 340 32 kern_features sys_ni_syscall sys_kern_features 340 64 kern_features sys_kern_features 341 common kcmp sys_kcmp 342 common finit_module sys_finit_module 343 common sched_setattr sys_sched_setattr 344 common sched_getattr sys_sched_getattr 345 common renameat2 sys_renameat2 346 common seccomp sys_seccomp 347 common getrandom sys_getrandom 348 common memfd_create sys_memfd_create 349 common bpf sys_bpf 350 32 execveat sys_execveat sys32_execveat 350 64 execveat sys64_execveat 351 common membarrier sys_membarrier 352 common userfaultfd sys_userfaultfd 353 common bind sys_bind 354 common listen sys_listen 355 common setsockopt sys_setsockopt sys_setsockopt 356 common mlock2 sys_mlock2 357 common copy_file_range sys_copy_file_range 358 common preadv2 sys_preadv2 compat_sys_preadv2 359 common pwritev2 sys_pwritev2 compat_sys_pwritev2 360 common statx sys_statx 361 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents 361 64 io_pgetevents sys_io_pgetevents 362 common pkey_mprotect sys_pkey_mprotect 363 common pkey_alloc sys_pkey_alloc 364 common pkey_free sys_pkey_free 365 common rseq sys_rseq 392 64 semtimedop sys_semtimedop 393 common semget sys_semget 394 common semctl sys_semctl compat_sys_semctl 395 common shmget sys_shmget 396 common shmctl sys_shmctl compat_sys_shmctl 397 common shmat sys_shmat compat_sys_shmat 398 common shmdt sys_shmdt 399 common msgget sys_msgget 400 common msgsnd sys_msgsnd compat_sys_msgsnd 401 common msgrcv sys_msgrcv compat_sys_msgrcv 402 common msgctl sys_msgctl compat_sys_msgctl 403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime 404 32 clock_settime64 sys_clock_settime sys_clock_settime 405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime 406 32 clock_getres_time64 sys_clock_getres sys_clock_getres 407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep 408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime 409 32 timer_settime64 sys_timer_settime sys_timer_settime 410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime 411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime 412 32 utimensat_time64 sys_utimensat sys_utimensat 413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend 419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive 420 32 semtimedop_time64 sys_semtimedop sys_semtimedop 421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 422 32 futex_time64 sys_futex sys_futex 423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # RISCV64 riscv64_syscall_tbl = arm64_syscall_tbl # RISCV32 riscv32_syscall_tbl = arm64_syscall_tbl # S390X # - arch/s390/kernel/syscalls/syscall.tbl s390x_syscall_tbl = """ 1 common exit sys_exit 2 common fork sys_fork 3 common read sys_read 4 common write sys_write 5 common open sys_open 6 common close sys_close 7 common restart_syscall sys_restart_syscall 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 common execve sys_execve 12 common chdir sys_chdir 14 common mknod sys_mknod 15 common chmod sys_chmod 19 common lseek sys_lseek 20 common getpid sys_getpid 21 common mount sys_mount 22 common umount sys_oldumount 26 common ptrace sys_ptrace 27 common alarm sys_alarm 29 common pause sys_pause 30 common utime sys_utime 33 common access sys_access 34 common nice sys_nice 36 common sync sys_sync 37 common kill sys_kill 38 common rename sys_rename 39 common mkdir sys_mkdir 40 common rmdir sys_rmdir 41 common dup sys_dup 42 common pipe sys_pipe 43 common times sys_times 45 common brk sys_brk 48 common signal sys_signal 51 common acct sys_acct 52 common umount2 sys_umount 54 common ioctl sys_ioctl 55 common fcntl sys_fcntl 57 common setpgid sys_setpgid 60 common umask sys_umask 61 common chroot sys_chroot 62 common ustat sys_ustat 63 common dup2 sys_dup2 64 common getppid sys_getppid 65 common getpgrp sys_getpgrp 66 common setsid sys_setsid 67 common sigaction sys_sigaction 72 common sigsuspend sys_sigsuspend 73 common sigpending sys_sigpending 74 common sethostname sys_sethostname 75 common setrlimit sys_setrlimit 77 common getrusage sys_getrusage 78 common gettimeofday sys_gettimeofday 79 common settimeofday sys_settimeofday 83 common symlink sys_symlink 85 common readlink sys_readlink 86 common uselib sys_uselib 87 common swapon sys_swapon 88 common reboot sys_reboot 89 common readdir sys_ni_syscall 90 common mmap sys_old_mmap 91 common munmap sys_munmap 92 common truncate sys_truncate 93 common ftruncate sys_ftruncate 94 common fchmod sys_fchmod 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority 99 common statfs sys_statfs 100 common fstatfs sys_fstatfs 102 common socketcall sys_socketcall 103 common syslog sys_syslog 104 common setitimer sys_setitimer 105 common getitimer sys_getitimer 106 common stat sys_newstat 107 common lstat sys_newlstat 108 common fstat sys_newfstat 110 common lookup_dcookie sys_ni_syscall 111 common vhangup sys_vhangup 112 common idle sys_ni_syscall 114 common wait4 sys_wait4 115 common swapoff sys_swapoff 116 common sysinfo sys_sysinfo 117 common ipc sys_s390_ipc 118 common fsync sys_fsync 119 common sigreturn sys_sigreturn 120 common clone sys_clone 121 common setdomainname sys_setdomainname 122 common uname sys_newuname 124 common adjtimex sys_adjtimex 125 common mprotect sys_mprotect 126 common sigprocmask sys_sigprocmask 127 common create_module sys_ni_syscall 128 common init_module sys_init_module 129 common delete_module sys_delete_module 130 common get_kernel_syms sys_ni_syscall 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir 134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_s390_personality 137 common afs_syscall sys_ni_syscall 141 common getdents sys_getdents 142 common select sys_select 143 common flock sys_flock 144 common msync sys_msync 145 common readv sys_readv 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync 149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall 153 common munlockall sys_munlockall 154 common sched_setparam sys_sched_setparam 155 common sched_getparam sys_sched_getparam 156 common sched_setscheduler sys_sched_setscheduler 157 common sched_getscheduler sys_sched_getscheduler 158 common sched_yield sys_sched_yield 159 common sched_get_priority_max sys_sched_get_priority_max 160 common sched_get_priority_min sys_sched_get_priority_min 161 common sched_rr_get_interval sys_sched_rr_get_interval 162 common nanosleep sys_nanosleep 163 common mremap sys_mremap 167 common query_module sys_ni_syscall 168 common poll sys_poll 169 common nfsservctl sys_ni_syscall 172 common prctl sys_prctl 173 common rt_sigreturn sys_rt_sigreturn 174 common rt_sigaction sys_rt_sigaction 175 common rt_sigprocmask sys_rt_sigprocmask 176 common rt_sigpending sys_rt_sigpending 177 common rt_sigtimedwait sys_rt_sigtimedwait 178 common rt_sigqueueinfo sys_rt_sigqueueinfo 179 common rt_sigsuspend sys_rt_sigsuspend 180 common pread64 sys_pread64 181 common pwrite64 sys_pwrite64 183 common getcwd sys_getcwd 184 common capget sys_capget 185 common capset sys_capset 186 common sigaltstack sys_sigaltstack 187 common sendfile sys_sendfile64 188 common getpmsg sys_ni_syscall 189 common putpmsg sys_ni_syscall 190 common vfork sys_vfork 191 common getrlimit sys_getrlimit 198 common lchown sys_lchown 199 common getuid sys_getuid 200 common getgid sys_getgid 201 common geteuid sys_geteuid 202 common getegid sys_getegid 203 common setreuid sys_setreuid 204 common setregid sys_setregid 205 common getgroups sys_getgroups 206 common setgroups sys_setgroups 207 common fchown sys_fchown 208 common setresuid sys_setresuid 209 common getresuid sys_getresuid 210 common setresgid sys_setresgid 211 common getresgid sys_getresgid 212 common chown sys_chown 213 common setuid sys_setuid 214 common setgid sys_setgid 215 common setfsuid sys_setfsuid 216 common setfsgid sys_setfsgid 217 common pivot_root sys_pivot_root 218 common mincore sys_mincore 219 common madvise sys_madvise 220 common getdents64 sys_getdents64 222 common readahead sys_readahead 224 common setxattr sys_setxattr 225 common lsetxattr sys_lsetxattr 226 common fsetxattr sys_fsetxattr 227 common getxattr sys_getxattr 228 common lgetxattr sys_lgetxattr 229 common fgetxattr sys_fgetxattr 230 common listxattr sys_listxattr 231 common llistxattr sys_llistxattr 232 common flistxattr sys_flistxattr 233 common removexattr sys_removexattr 234 common lremovexattr sys_lremovexattr 235 common fremovexattr sys_fremovexattr 236 common gettid sys_gettid 237 common tkill sys_tkill 238 common futex sys_futex 239 common sched_setaffinity sys_sched_setaffinity 240 common sched_getaffinity sys_sched_getaffinity 241 common tgkill sys_tgkill 243 common io_setup sys_io_setup 244 common io_destroy sys_io_destroy 245 common io_getevents sys_io_getevents 246 common io_submit sys_io_submit 247 common io_cancel sys_io_cancel 248 common exit_group sys_exit_group 249 common epoll_create sys_epoll_create 250 common epoll_ctl sys_epoll_ctl 251 common epoll_wait sys_epoll_wait 252 common set_tid_address sys_set_tid_address 253 common fadvise64 sys_fadvise64_64 254 common timer_create sys_timer_create 255 common timer_settime sys_timer_settime 256 common timer_gettime sys_timer_gettime 257 common timer_getoverrun sys_timer_getoverrun 258 common timer_delete sys_timer_delete 259 common clock_settime sys_clock_settime 260 common clock_gettime sys_clock_gettime 261 common clock_getres sys_clock_getres 262 common clock_nanosleep sys_clock_nanosleep 265 common statfs64 sys_statfs64 266 common fstatfs64 sys_fstatfs64 267 common remap_file_pages sys_remap_file_pages 268 common mbind sys_mbind 269 common get_mempolicy sys_get_mempolicy 270 common set_mempolicy sys_set_mempolicy 271 common mq_open sys_mq_open 272 common mq_unlink sys_mq_unlink 273 common mq_timedsend sys_mq_timedsend 274 common mq_timedreceive sys_mq_timedreceive 275 common mq_notify sys_mq_notify 276 common mq_getsetattr sys_mq_getsetattr 277 common kexec_load sys_kexec_load 278 common add_key sys_add_key 279 common request_key sys_request_key 280 common keyctl sys_keyctl 281 common waitid sys_waitid 282 common ioprio_set sys_ioprio_set 283 common ioprio_get sys_ioprio_get 284 common inotify_init sys_inotify_init 285 common inotify_add_watch sys_inotify_add_watch 286 common inotify_rm_watch sys_inotify_rm_watch 287 common migrate_pages sys_migrate_pages 288 common openat sys_openat 289 common mkdirat sys_mkdirat 290 common mknodat sys_mknodat 291 common fchownat sys_fchownat 292 common futimesat sys_futimesat 293 common newfstatat sys_newfstatat 294 common unlinkat sys_unlinkat 295 common renameat sys_renameat 296 common linkat sys_linkat 297 common symlinkat sys_symlinkat 298 common readlinkat sys_readlinkat 299 common fchmodat sys_fchmodat 300 common faccessat sys_faccessat 301 common pselect6 sys_pselect6 302 common ppoll sys_ppoll 303 common unshare sys_unshare 304 common set_robust_list sys_set_robust_list 305 common get_robust_list sys_get_robust_list 306 common splice sys_splice 307 common sync_file_range sys_sync_file_range 308 common tee sys_tee 309 common vmsplice sys_vmsplice 310 common move_pages sys_move_pages 311 common getcpu sys_getcpu 312 common epoll_pwait sys_epoll_pwait 313 common utimes sys_utimes 314 common fallocate sys_fallocate 315 common utimensat sys_utimensat 316 common signalfd sys_signalfd 317 common timerfd sys_ni_syscall 318 common eventfd sys_eventfd 319 common timerfd_create sys_timerfd_create 320 common timerfd_settime sys_timerfd_settime 321 common timerfd_gettime sys_timerfd_gettime 322 common signalfd4 sys_signalfd4 323 common eventfd2 sys_eventfd2 324 common inotify_init1 sys_inotify_init1 325 common pipe2 sys_pipe2 326 common dup3 sys_dup3 327 common epoll_create1 sys_epoll_create1 328 common preadv sys_preadv 329 common pwritev sys_pwritev 330 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 331 common perf_event_open sys_perf_event_open 332 common fanotify_init sys_fanotify_init 333 common fanotify_mark sys_fanotify_mark 334 common prlimit64 sys_prlimit64 335 common name_to_handle_at sys_name_to_handle_at 336 common open_by_handle_at sys_open_by_handle_at 337 common clock_adjtime sys_clock_adjtime 338 common syncfs sys_syncfs 339 common setns sys_setns 340 common process_vm_readv sys_process_vm_readv 341 common process_vm_writev sys_process_vm_writev 342 common s390_runtime_instr sys_s390_runtime_instr 343 common kcmp sys_kcmp 344 common finit_module sys_finit_module 345 common sched_setattr sys_sched_setattr 346 common sched_getattr sys_sched_getattr 347 common renameat2 sys_renameat2 348 common seccomp sys_seccomp 349 common getrandom sys_getrandom 350 common memfd_create sys_memfd_create 351 common bpf sys_bpf 352 common s390_pci_mmio_write sys_s390_pci_mmio_write 353 common s390_pci_mmio_read sys_s390_pci_mmio_read 354 common execveat sys_execveat 355 common userfaultfd sys_userfaultfd 356 common membarrier sys_membarrier 357 common recvmmsg sys_recvmmsg 358 common sendmmsg sys_sendmmsg 359 common socket sys_socket 360 common socketpair sys_socketpair 361 common bind sys_bind 362 common connect sys_connect 363 common listen sys_listen 364 common accept4 sys_accept4 365 common getsockopt sys_getsockopt 366 common setsockopt sys_setsockopt 367 common getsockname sys_getsockname 368 common getpeername sys_getpeername 369 common sendto sys_sendto 370 common sendmsg sys_sendmsg 371 common recvfrom sys_recvfrom 372 common recvmsg sys_recvmsg 373 common shutdown sys_shutdown 374 common mlock2 sys_mlock2 375 common copy_file_range sys_copy_file_range 376 common preadv2 sys_preadv2 377 common pwritev2 sys_pwritev2 378 common s390_guarded_storage sys_s390_guarded_storage 379 common statx sys_statx 380 common s390_sthyi sys_s390_sthyi 381 common kexec_file_load sys_kexec_file_load 382 common io_pgetevents sys_io_pgetevents 383 common rseq sys_rseq 384 common pkey_mprotect sys_pkey_mprotect 385 common pkey_alloc sys_pkey_alloc 386 common pkey_free sys_pkey_free 392 common semtimedop sys_semtimedop 393 common semget sys_semget 394 common semctl sys_semctl 395 common shmget sys_shmget 396 common shmctl sys_shmctl 397 common shmat sys_shmat 398 common shmdt sys_shmdt 399 common msgget sys_msgget 400 common msgsnd sys_msgsnd 401 common msgrcv sys_msgrcv 402 common msgctl sys_msgctl 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 447 common memfd_secret sys_memfd_secret 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # SH4 # - arch/sh/kernel/syscalls/syscall.tbl sh4_syscall_tbl = """ 0 common restart_syscall sys_restart_syscall 1 common exit sys_exit 2 common fork sys_fork 3 common read sys_read 4 common write sys_write 5 common open sys_open 6 common close sys_close 7 common waitpid sys_waitpid 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 common execve sys_execve 12 common chdir sys_chdir 13 common time sys_time32 14 common mknod sys_mknod 15 common chmod sys_chmod 16 common lchown sys_lchown16 18 common oldstat sys_stat 19 common lseek sys_lseek 20 common getpid sys_getpid 21 common mount sys_mount 22 common umount sys_oldumount 23 common setuid sys_setuid16 24 common getuid sys_getuid16 25 common stime sys_stime32 26 common ptrace sys_ptrace 27 common alarm sys_alarm 28 common oldfstat sys_fstat 29 common pause sys_pause 30 common utime sys_utime32 33 common access sys_access 34 common nice sys_nice 36 common sync sys_sync 37 common kill sys_kill 38 common rename sys_rename 39 common mkdir sys_mkdir 40 common rmdir sys_rmdir 41 common dup sys_dup 42 common pipe sys_sh_pipe 43 common times sys_times 45 common brk sys_brk 46 common setgid sys_setgid16 47 common getgid sys_getgid16 48 common signal sys_signal 49 common geteuid sys_geteuid16 50 common getegid sys_getegid16 51 common acct sys_acct 52 common umount2 sys_umount 54 common ioctl sys_ioctl 55 common fcntl sys_fcntl 57 common setpgid sys_setpgid 60 common umask sys_umask 61 common chroot sys_chroot 62 common ustat sys_ustat 63 common dup2 sys_dup2 64 common getppid sys_getppid 65 common getpgrp sys_getpgrp 66 common setsid sys_setsid 67 common sigaction sys_sigaction 68 common sgetmask sys_sgetmask 69 common ssetmask sys_ssetmask 70 common setreuid sys_setreuid16 71 common setregid sys_setregid16 72 common sigsuspend sys_sigsuspend 73 common sigpending sys_sigpending 74 common sethostname sys_sethostname 75 common setrlimit sys_setrlimit 76 common getrlimit sys_old_getrlimit 77 common getrusage sys_getrusage 78 common gettimeofday sys_gettimeofday 79 common settimeofday sys_settimeofday 80 common getgroups sys_getgroups16 81 common setgroups sys_setgroups16 83 common symlink sys_symlink 84 common oldlstat sys_lstat 85 common readlink sys_readlink 86 common uselib sys_uselib 87 common swapon sys_swapon 88 common reboot sys_reboot 89 common readdir sys_old_readdir 90 common mmap old_mmap 91 common munmap sys_munmap 92 common truncate sys_truncate 93 common ftruncate sys_ftruncate 94 common fchmod sys_fchmod 95 common fchown sys_fchown16 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority 99 common statfs sys_statfs 100 common fstatfs sys_fstatfs 102 common socketcall sys_socketcall 103 common syslog sys_syslog 104 common setitimer sys_setitimer 105 common getitimer sys_getitimer 106 common stat sys_newstat 107 common lstat sys_newlstat 108 common fstat sys_newfstat 109 common olduname sys_uname 111 common vhangup sys_vhangup 114 common wait4 sys_wait4 115 common swapoff sys_swapoff 116 common sysinfo sys_sysinfo 117 common ipc sys_ipc 118 common fsync sys_fsync 119 common sigreturn sys_sigreturn 120 common clone sys_clone 121 common setdomainname sys_setdomainname 122 common uname sys_newuname 123 common cacheflush sys_cacheflush 124 common adjtimex sys_adjtimex_time32 125 common mprotect sys_mprotect 126 common sigprocmask sys_sigprocmask 128 common init_module sys_init_module 129 common delete_module sys_delete_module 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir 134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality 138 common setfsuid sys_setfsuid16 139 common setfsgid sys_setfsgid16 140 common _llseek sys_llseek 141 common getdents sys_getdents 142 common _newselect sys_select 143 common flock sys_flock 144 common msync sys_msync 145 common readv sys_readv 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync 149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall 153 common munlockall sys_munlockall 154 common sched_setparam sys_sched_setparam 155 common sched_getparam sys_sched_getparam 156 common sched_setscheduler sys_sched_setscheduler 157 common sched_getscheduler sys_sched_getscheduler 158 common sched_yield sys_sched_yield 159 common sched_get_priority_max sys_sched_get_priority_max 160 common sched_get_priority_min sys_sched_get_priority_min 161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 162 common nanosleep sys_nanosleep_time32 163 common mremap sys_mremap 164 common setresuid sys_setresuid16 165 common getresuid sys_getresuid16 168 common poll sys_poll 169 common nfsservctl sys_ni_syscall 170 common setresgid sys_setresgid16 171 common getresgid sys_getresgid16 172 common prctl sys_prctl 173 common rt_sigreturn sys_rt_sigreturn 174 common rt_sigaction sys_rt_sigaction 175 common rt_sigprocmask sys_rt_sigprocmask 176 common rt_sigpending sys_rt_sigpending 177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 178 common rt_sigqueueinfo sys_rt_sigqueueinfo 179 common rt_sigsuspend sys_rt_sigsuspend 180 common pread64 sys_pread_wrapper 181 common pwrite64 sys_pwrite_wrapper 182 common chown sys_chown16 183 common getcwd sys_getcwd 184 common capget sys_capget 185 common capset sys_capset 186 common sigaltstack sys_sigaltstack 187 common sendfile sys_sendfile 190 common vfork sys_vfork 191 common ugetrlimit sys_getrlimit 192 common mmap2 sys_mmap2 193 common truncate64 sys_truncate64 194 common ftruncate64 sys_ftruncate64 195 common stat64 sys_stat64 196 common lstat64 sys_lstat64 197 common fstat64 sys_fstat64 198 common lchown32 sys_lchown 199 common getuid32 sys_getuid 200 common getgid32 sys_getgid 201 common geteuid32 sys_geteuid 202 common getegid32 sys_getegid 203 common setreuid32 sys_setreuid 204 common setregid32 sys_setregid 205 common getgroups32 sys_getgroups 206 common setgroups32 sys_setgroups 207 common fchown32 sys_fchown 208 common setresuid32 sys_setresuid 209 common getresuid32 sys_getresuid 210 common setresgid32 sys_setresgid 211 common getresgid32 sys_getresgid 212 common chown32 sys_chown 213 common setuid32 sys_setuid 214 common setgid32 sys_setgid 215 common setfsuid32 sys_setfsuid 216 common setfsgid32 sys_setfsgid 217 common pivot_root sys_pivot_root 218 common mincore sys_mincore 219 common madvise sys_madvise 220 common getdents64 sys_getdents64 221 common fcntl64 sys_fcntl64 224 common gettid sys_gettid 225 common readahead sys_readahead 226 common setxattr sys_setxattr 227 common lsetxattr sys_lsetxattr 228 common fsetxattr sys_fsetxattr 229 common getxattr sys_getxattr 230 common lgetxattr sys_lgetxattr 231 common fgetxattr sys_fgetxattr 232 common listxattr sys_listxattr 233 common llistxattr sys_llistxattr 234 common flistxattr sys_flistxattr 235 common removexattr sys_removexattr 236 common lremovexattr sys_lremovexattr 237 common fremovexattr sys_fremovexattr 238 common tkill sys_tkill 239 common sendfile64 sys_sendfile64 240 common futex sys_futex_time32 241 common sched_setaffinity sys_sched_setaffinity 242 common sched_getaffinity sys_sched_getaffinity 245 common io_setup sys_io_setup 246 common io_destroy sys_io_destroy 247 common io_getevents sys_io_getevents_time32 248 common io_submit sys_io_submit 249 common io_cancel sys_io_cancel 250 common fadvise64 sys_fadvise64 252 common exit_group sys_exit_group 253 common lookup_dcookie sys_ni_syscall 254 common epoll_create sys_epoll_create 255 common epoll_ctl sys_epoll_ctl 256 common epoll_wait sys_epoll_wait 257 common remap_file_pages sys_remap_file_pages 258 common set_tid_address sys_set_tid_address 259 common timer_create sys_timer_create 260 common timer_settime sys_timer_settime32 261 common timer_gettime sys_timer_gettime32 262 common timer_getoverrun sys_timer_getoverrun 263 common timer_delete sys_timer_delete 264 common clock_settime sys_clock_settime32 265 common clock_gettime sys_clock_gettime32 266 common clock_getres sys_clock_getres_time32 267 common clock_nanosleep sys_clock_nanosleep_time32 268 common statfs64 sys_statfs64 269 common fstatfs64 sys_fstatfs64 270 common tgkill sys_tgkill 271 common utimes sys_utimes_time32 272 common fadvise64_64 sys_fadvise64_64_wrapper 274 common mbind sys_mbind 275 common get_mempolicy sys_get_mempolicy 276 common set_mempolicy sys_set_mempolicy 277 common mq_open sys_mq_open 278 common mq_unlink sys_mq_unlink 279 common mq_timedsend sys_mq_timedsend_time32 280 common mq_timedreceive sys_mq_timedreceive_time32 281 common mq_notify sys_mq_notify 282 common mq_getsetattr sys_mq_getsetattr 283 common kexec_load sys_kexec_load 284 common waitid sys_waitid 285 common add_key sys_add_key 286 common request_key sys_request_key 287 common keyctl sys_keyctl 288 common ioprio_set sys_ioprio_set 289 common ioprio_get sys_ioprio_get 290 common inotify_init sys_inotify_init 291 common inotify_add_watch sys_inotify_add_watch 292 common inotify_rm_watch sys_inotify_rm_watch 294 common migrate_pages sys_migrate_pages 295 common openat sys_openat 296 common mkdirat sys_mkdirat 297 common mknodat sys_mknodat 298 common fchownat sys_fchownat 299 common futimesat sys_futimesat_time32 300 common fstatat64 sys_fstatat64 301 common unlinkat sys_unlinkat 302 common renameat sys_renameat 303 common linkat sys_linkat 304 common symlinkat sys_symlinkat 305 common readlinkat sys_readlinkat 306 common fchmodat sys_fchmodat 307 common faccessat sys_faccessat 308 common pselect6 sys_pselect6_time32 309 common ppoll sys_ppoll_time32 310 common unshare sys_unshare 311 common set_robust_list sys_set_robust_list 312 common get_robust_list sys_get_robust_list 313 common splice sys_splice 314 common sync_file_range sys_sh_sync_file_range6 315 common tee sys_tee 316 common vmsplice sys_vmsplice 317 common move_pages sys_move_pages 318 common getcpu sys_getcpu 319 common epoll_pwait sys_epoll_pwait 320 common utimensat sys_utimensat_time32 321 common signalfd sys_signalfd 322 common timerfd_create sys_timerfd_create 323 common eventfd sys_eventfd 324 common fallocate sys_fallocate 325 common timerfd_settime sys_timerfd_settime32 326 common timerfd_gettime sys_timerfd_gettime32 327 common signalfd4 sys_signalfd4 328 common eventfd2 sys_eventfd2 329 common epoll_create1 sys_epoll_create1 330 common dup3 sys_dup3 331 common pipe2 sys_pipe2 332 common inotify_init1 sys_inotify_init1 333 common preadv sys_preadv 334 common pwritev sys_pwritev 335 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 336 common perf_event_open sys_perf_event_open 337 common fanotify_init sys_fanotify_init 338 common fanotify_mark sys_fanotify_mark 339 common prlimit64 sys_prlimit64 340 common socket sys_socket 341 common bind sys_bind 342 common connect sys_connect 343 common listen sys_listen 344 common accept sys_accept 345 common getsockname sys_getsockname 346 common getpeername sys_getpeername 347 common socketpair sys_socketpair 348 common send sys_send 349 common sendto sys_sendto 350 common recv sys_recv 351 common recvfrom sys_recvfrom 352 common shutdown sys_shutdown 353 common setsockopt sys_setsockopt 354 common getsockopt sys_getsockopt 355 common sendmsg sys_sendmsg 356 common recvmsg sys_recvmsg 357 common recvmmsg sys_recvmmsg_time32 358 common accept4 sys_accept4 359 common name_to_handle_at sys_name_to_handle_at 360 common open_by_handle_at sys_open_by_handle_at 361 common clock_adjtime sys_clock_adjtime32 362 common syncfs sys_syncfs 363 common sendmmsg sys_sendmmsg 364 common setns sys_setns 365 common process_vm_readv sys_process_vm_readv 366 common process_vm_writev sys_process_vm_writev 367 common kcmp sys_kcmp 368 common finit_module sys_finit_module 369 common sched_getattr sys_sched_getattr 370 common sched_setattr sys_sched_setattr 371 common renameat2 sys_renameat2 372 common seccomp sys_seccomp 373 common getrandom sys_getrandom 374 common memfd_create sys_memfd_create 375 common bpf sys_bpf 376 common execveat sys_execveat 377 common userfaultfd sys_userfaultfd 378 common membarrier sys_membarrier 379 common mlock2 sys_mlock2 380 common copy_file_range sys_copy_file_range 381 common preadv2 sys_preadv2 382 common pwritev2 sys_pwritev2 383 common statx sys_statx 384 common pkey_mprotect sys_pkey_mprotect 385 common pkey_alloc sys_pkey_alloc 386 common pkey_free sys_pkey_free 387 common rseq sys_rseq 388 common sync_file_range2 sys_sync_file_range2 393 common semget sys_semget 394 common semctl sys_semctl 395 common shmget sys_shmget 396 common shmctl sys_shmctl 397 common shmat sys_shmat 398 common shmdt sys_shmdt 399 common msgget sys_msgget 400 common msgsnd sys_msgsnd 401 common msgrcv sys_msgrcv 402 common msgctl sys_msgctl 403 common clock_gettime64 sys_clock_gettime 404 common clock_settime64 sys_clock_settime 405 common clock_adjtime64 sys_clock_adjtime 406 common clock_getres_time64 sys_clock_getres 407 common clock_nanosleep_time64 sys_clock_nanosleep 408 common timer_gettime64 sys_timer_gettime 409 common timer_settime64 sys_timer_settime 410 common timerfd_gettime64 sys_timerfd_gettime 411 common timerfd_settime64 sys_timerfd_settime 412 common utimensat_time64 sys_utimensat 413 common pselect6_time64 sys_pselect6 414 common ppoll_time64 sys_ppoll 416 common io_pgetevents_time64 sys_io_pgetevents 417 common recvmmsg_time64 sys_recvmmsg 418 common mq_timedsend_time64 sys_mq_timedsend 419 common mq_timedreceive_time64 sys_mq_timedreceive 420 common semtimedop_time64 sys_semtimedop 421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait 422 common futex_time64 sys_futex 423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # m68k # - arch/m68k/kernel/syscalls/syscall.tbl m68k_syscall_tbl = """ 0 common restart_syscall sys_restart_syscall 1 common exit sys_exit 2 common fork __sys_fork 3 common read sys_read 4 common write sys_write 5 common open sys_open 6 common close sys_close 7 common waitpid sys_waitpid 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 common execve sys_execve 12 common chdir sys_chdir 13 common time sys_time32 14 common mknod sys_mknod 15 common chmod sys_chmod 16 common chown sys_chown16 18 common oldstat sys_stat 19 common lseek sys_lseek 20 common getpid sys_getpid 21 common mount sys_mount 22 common umount sys_oldumount 23 common setuid sys_setuid16 24 common getuid sys_getuid16 25 common stime sys_stime32 26 common ptrace sys_ptrace 27 common alarm sys_alarm 28 common oldfstat sys_fstat 29 common pause sys_pause 30 common utime sys_utime32 33 common access sys_access 34 common nice sys_nice 36 common sync sys_sync 37 common kill sys_kill 38 common rename sys_rename 39 common mkdir sys_mkdir 40 common rmdir sys_rmdir 41 common dup sys_dup 42 common pipe sys_pipe 43 common times sys_times 45 common brk sys_brk 46 common setgid sys_setgid16 47 common getgid sys_getgid16 48 common signal sys_signal 49 common geteuid sys_geteuid16 50 common getegid sys_getegid16 51 common acct sys_acct 52 common umount2 sys_umount 54 common ioctl sys_ioctl 55 common fcntl sys_fcntl 57 common setpgid sys_setpgid 60 common umask sys_umask 61 common chroot sys_chroot 62 common ustat sys_ustat 63 common dup2 sys_dup2 64 common getppid sys_getppid 65 common getpgrp sys_getpgrp 66 common setsid sys_setsid 67 common sigaction sys_sigaction 68 common sgetmask sys_sgetmask 69 common ssetmask sys_ssetmask 70 common setreuid sys_setreuid16 71 common setregid sys_setregid16 72 common sigsuspend sys_sigsuspend 73 common sigpending sys_sigpending 74 common sethostname sys_sethostname 75 common setrlimit sys_setrlimit 76 common getrlimit sys_old_getrlimit 77 common getrusage sys_getrusage 78 common gettimeofday sys_gettimeofday 79 common settimeofday sys_settimeofday 80 common getgroups sys_getgroups16 81 common setgroups sys_setgroups16 82 common select sys_old_select 83 common symlink sys_symlink 84 common oldlstat sys_lstat 85 common readlink sys_readlink 86 common uselib sys_uselib 87 common swapon sys_swapon 88 common reboot sys_reboot 89 common readdir sys_old_readdir 90 common mmap sys_old_mmap 91 common munmap sys_munmap 92 common truncate sys_truncate 93 common ftruncate sys_ftruncate 94 common fchmod sys_fchmod 95 common fchown sys_fchown16 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority 99 common statfs sys_statfs 100 common fstatfs sys_fstatfs 102 common socketcall sys_socketcall 103 common syslog sys_syslog 104 common setitimer sys_setitimer 105 common getitimer sys_getitimer 106 common stat sys_newstat 107 common lstat sys_newlstat 108 common fstat sys_newfstat 111 common vhangup sys_vhangup 114 common wait4 sys_wait4 115 common swapoff sys_swapoff 116 common sysinfo sys_sysinfo 117 common ipc sys_ipc 118 common fsync sys_fsync 119 common sigreturn sys_sigreturn 120 common clone __sys_clone 121 common setdomainname sys_setdomainname 122 common uname sys_newuname 123 common cacheflush sys_cacheflush 124 common adjtimex sys_adjtimex_time32 125 common mprotect sys_mprotect 126 common sigprocmask sys_sigprocmask 127 common create_module sys_ni_syscall 128 common init_module sys_init_module 129 common delete_module sys_delete_module 130 common get_kernel_syms sys_ni_syscall 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir 134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality 138 common setfsuid sys_setfsuid16 139 common setfsgid sys_setfsgid16 140 common _llseek sys_llseek 141 common getdents sys_getdents 142 common _newselect sys_select 143 common flock sys_flock 144 common msync sys_msync 145 common readv sys_readv 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync 149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall 153 common munlockall sys_munlockall 154 common sched_setparam sys_sched_setparam 155 common sched_getparam sys_sched_getparam 156 common sched_setscheduler sys_sched_setscheduler 157 common sched_getscheduler sys_sched_getscheduler 158 common sched_yield sys_sched_yield 159 common sched_get_priority_max sys_sched_get_priority_max 160 common sched_get_priority_min sys_sched_get_priority_min 161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 162 common nanosleep sys_nanosleep_time32 163 common mremap sys_mremap 164 common setresuid sys_setresuid16 165 common getresuid sys_getresuid16 166 common getpagesize sys_getpagesize 167 common query_module sys_ni_syscall 168 common poll sys_poll 169 common nfsservctl sys_ni_syscall 170 common setresgid sys_setresgid16 171 common getresgid sys_getresgid16 172 common prctl sys_prctl 173 common rt_sigreturn sys_rt_sigreturn 174 common rt_sigaction sys_rt_sigaction 175 common rt_sigprocmask sys_rt_sigprocmask 176 common rt_sigpending sys_rt_sigpending 177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 178 common rt_sigqueueinfo sys_rt_sigqueueinfo 179 common rt_sigsuspend sys_rt_sigsuspend 180 common pread64 sys_pread64 181 common pwrite64 sys_pwrite64 182 common lchown sys_lchown16 183 common getcwd sys_getcwd 184 common capget sys_capget 185 common capset sys_capset 186 common sigaltstack sys_sigaltstack 187 common sendfile sys_sendfile 188 common getpmsg sys_ni_syscall 189 common putpmsg sys_ni_syscall 190 common vfork __sys_vfork 191 common ugetrlimit sys_getrlimit 192 common mmap2 sys_mmap2 193 common truncate64 sys_truncate64 194 common ftruncate64 sys_ftruncate64 195 common stat64 sys_stat64 196 common lstat64 sys_lstat64 197 common fstat64 sys_fstat64 198 common chown32 sys_chown 199 common getuid32 sys_getuid 200 common getgid32 sys_getgid 201 common geteuid32 sys_geteuid 202 common getegid32 sys_getegid 203 common setreuid32 sys_setreuid 204 common setregid32 sys_setregid 205 common getgroups32 sys_getgroups 206 common setgroups32 sys_setgroups 207 common fchown32 sys_fchown 208 common setresuid32 sys_setresuid 209 common getresuid32 sys_getresuid 210 common setresgid32 sys_setresgid 211 common getresgid32 sys_getresgid 212 common lchown32 sys_lchown 213 common setuid32 sys_setuid 214 common setgid32 sys_setgid 215 common setfsuid32 sys_setfsuid 216 common setfsgid32 sys_setfsgid 217 common pivot_root sys_pivot_root 220 common getdents64 sys_getdents64 221 common gettid sys_gettid 222 common tkill sys_tkill 223 common setxattr sys_setxattr 224 common lsetxattr sys_lsetxattr 225 common fsetxattr sys_fsetxattr 226 common getxattr sys_getxattr 227 common lgetxattr sys_lgetxattr 228 common fgetxattr sys_fgetxattr 229 common listxattr sys_listxattr 230 common llistxattr sys_llistxattr 231 common flistxattr sys_flistxattr 232 common removexattr sys_removexattr 233 common lremovexattr sys_lremovexattr 234 common fremovexattr sys_fremovexattr 235 common futex sys_futex_time32 236 common sendfile64 sys_sendfile64 237 common mincore sys_mincore 238 common madvise sys_madvise 239 common fcntl64 sys_fcntl64 240 common readahead sys_readahead 241 common io_setup sys_io_setup 242 common io_destroy sys_io_destroy 243 common io_getevents sys_io_getevents_time32 244 common io_submit sys_io_submit 245 common io_cancel sys_io_cancel 246 common fadvise64 sys_fadvise64 247 common exit_group sys_exit_group 248 common lookup_dcookie sys_ni_syscall 249 common epoll_create sys_epoll_create 250 common epoll_ctl sys_epoll_ctl 251 common epoll_wait sys_epoll_wait 252 common remap_file_pages sys_remap_file_pages 253 common set_tid_address sys_set_tid_address 254 common timer_create sys_timer_create 255 common timer_settime sys_timer_settime32 256 common timer_gettime sys_timer_gettime32 257 common timer_getoverrun sys_timer_getoverrun 258 common timer_delete sys_timer_delete 259 common clock_settime sys_clock_settime32 260 common clock_gettime sys_clock_gettime32 261 common clock_getres sys_clock_getres_time32 262 common clock_nanosleep sys_clock_nanosleep_time32 263 common statfs64 sys_statfs64 264 common fstatfs64 sys_fstatfs64 265 common tgkill sys_tgkill 266 common utimes sys_utimes_time32 267 common fadvise64_64 sys_fadvise64_64 268 common mbind sys_mbind 269 common get_mempolicy sys_get_mempolicy 270 common set_mempolicy sys_set_mempolicy 271 common mq_open sys_mq_open 272 common mq_unlink sys_mq_unlink 273 common mq_timedsend sys_mq_timedsend_time32 274 common mq_timedreceive sys_mq_timedreceive_time32 275 common mq_notify sys_mq_notify 276 common mq_getsetattr sys_mq_getsetattr 277 common waitid sys_waitid 279 common add_key sys_add_key 280 common request_key sys_request_key 281 common keyctl sys_keyctl 282 common ioprio_set sys_ioprio_set 283 common ioprio_get sys_ioprio_get 284 common inotify_init sys_inotify_init 285 common inotify_add_watch sys_inotify_add_watch 286 common inotify_rm_watch sys_inotify_rm_watch 287 common migrate_pages sys_migrate_pages 288 common openat sys_openat 289 common mkdirat sys_mkdirat 290 common mknodat sys_mknodat 291 common fchownat sys_fchownat 292 common futimesat sys_futimesat_time32 293 common fstatat64 sys_fstatat64 294 common unlinkat sys_unlinkat 295 common renameat sys_renameat 296 common linkat sys_linkat 297 common symlinkat sys_symlinkat 298 common readlinkat sys_readlinkat 299 common fchmodat sys_fchmodat 300 common faccessat sys_faccessat 301 common pselect6 sys_pselect6_time32 302 common ppoll sys_ppoll_time32 303 common unshare sys_unshare 304 common set_robust_list sys_set_robust_list 305 common get_robust_list sys_get_robust_list 306 common splice sys_splice 307 common sync_file_range sys_sync_file_range 308 common tee sys_tee 309 common vmsplice sys_vmsplice 310 common move_pages sys_move_pages 311 common sched_setaffinity sys_sched_setaffinity 312 common sched_getaffinity sys_sched_getaffinity 313 common kexec_load sys_kexec_load 314 common getcpu sys_getcpu 315 common epoll_pwait sys_epoll_pwait 316 common utimensat sys_utimensat_time32 317 common signalfd sys_signalfd 318 common timerfd_create sys_timerfd_create 319 common eventfd sys_eventfd 320 common fallocate sys_fallocate 321 common timerfd_settime sys_timerfd_settime32 322 common timerfd_gettime sys_timerfd_gettime32 323 common signalfd4 sys_signalfd4 324 common eventfd2 sys_eventfd2 325 common epoll_create1 sys_epoll_create1 326 common dup3 sys_dup3 327 common pipe2 sys_pipe2 328 common inotify_init1 sys_inotify_init1 329 common preadv sys_preadv 330 common pwritev sys_pwritev 331 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 332 common perf_event_open sys_perf_event_open 333 common get_thread_area sys_get_thread_area 334 common set_thread_area sys_set_thread_area 335 common atomic_cmpxchg_32 sys_atomic_cmpxchg_32 336 common atomic_barrier sys_atomic_barrier 337 common fanotify_init sys_fanotify_init 338 common fanotify_mark sys_fanotify_mark 339 common prlimit64 sys_prlimit64 340 common name_to_handle_at sys_name_to_handle_at 341 common open_by_handle_at sys_open_by_handle_at 342 common clock_adjtime sys_clock_adjtime32 343 common syncfs sys_syncfs 344 common setns sys_setns 345 common process_vm_readv sys_process_vm_readv 346 common process_vm_writev sys_process_vm_writev 347 common kcmp sys_kcmp 348 common finit_module sys_finit_module 349 common sched_setattr sys_sched_setattr 350 common sched_getattr sys_sched_getattr 351 common renameat2 sys_renameat2 352 common getrandom sys_getrandom 353 common memfd_create sys_memfd_create 354 common bpf sys_bpf 355 common execveat sys_execveat 356 common socket sys_socket 357 common socketpair sys_socketpair 358 common bind sys_bind 359 common connect sys_connect 360 common listen sys_listen 361 common accept4 sys_accept4 362 common getsockopt sys_getsockopt 363 common setsockopt sys_setsockopt 364 common getsockname sys_getsockname 365 common getpeername sys_getpeername 366 common sendto sys_sendto 367 common sendmsg sys_sendmsg 368 common recvfrom sys_recvfrom 369 common recvmsg sys_recvmsg 370 common shutdown sys_shutdown 371 common recvmmsg sys_recvmmsg_time32 372 common sendmmsg sys_sendmmsg 373 common userfaultfd sys_userfaultfd 374 common membarrier sys_membarrier 375 common mlock2 sys_mlock2 376 common copy_file_range sys_copy_file_range 377 common preadv2 sys_preadv2 378 common pwritev2 sys_pwritev2 379 common statx sys_statx 380 common seccomp sys_seccomp 381 common pkey_mprotect sys_pkey_mprotect 382 common pkey_alloc sys_pkey_alloc 383 common pkey_free sys_pkey_free 384 common rseq sys_rseq 393 common semget sys_semget 394 common semctl sys_semctl 395 common shmget sys_shmget 396 common shmctl sys_shmctl 397 common shmat sys_shmat 398 common shmdt sys_shmdt 399 common msgget sys_msgget 400 common msgsnd sys_msgsnd 401 common msgrcv sys_msgrcv 402 common msgctl sys_msgctl 403 common clock_gettime64 sys_clock_gettime 404 common clock_settime64 sys_clock_settime 405 common clock_adjtime64 sys_clock_adjtime 406 common clock_getres_time64 sys_clock_getres 407 common clock_nanosleep_time64 sys_clock_nanosleep 408 common timer_gettime64 sys_timer_gettime 409 common timer_settime64 sys_timer_settime 410 common timerfd_gettime64 sys_timerfd_gettime 411 common timerfd_settime64 sys_timerfd_settime 412 common utimensat_time64 sys_utimensat 413 common pselect6_time64 sys_pselect6 414 common ppoll_time64 sys_ppoll 416 common io_pgetevents_time64 sys_io_pgetevents 417 common recvmmsg_time64 sys_recvmmsg 418 common mq_timedsend_time64 sys_mq_timedsend 419 common mq_timedreceive_time64 sys_mq_timedreceive 420 common semtimedop_time64 sys_semtimedop 421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait 422 common futex_time64 sys_futex 423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 __sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # alpha # - arch/alpha/kernel/syscalls/syscall.tbl alpha_syscall_tbl = """ 0 common osf_syscall alpha_syscall_zero 1 common exit sys_exit 2 common fork alpha_fork 3 common read sys_read 4 common write sys_write 5 common osf_old_open sys_ni_syscall 6 common close sys_close 7 common osf_wait4 sys_osf_wait4 8 common osf_old_creat sys_ni_syscall 9 common link sys_link 10 common unlink sys_unlink 11 common osf_execve sys_ni_syscall 12 common chdir sys_chdir 13 common fchdir sys_fchdir 14 common mknod sys_mknod 15 common chmod sys_chmod 16 common chown sys_chown 17 common brk sys_osf_brk 18 common osf_getfsstat sys_ni_syscall 19 common lseek sys_lseek 20 common getxpid sys_getxpid 21 common osf_mount sys_osf_mount 22 common umount2 sys_umount 23 common setuid sys_setuid 24 common getxuid sys_getxuid 25 common exec_with_loader sys_ni_syscall 26 common ptrace sys_ptrace 27 common osf_nrecvmsg sys_ni_syscall 28 common osf_nsendmsg sys_ni_syscall 29 common osf_nrecvfrom sys_ni_syscall 30 common osf_naccept sys_ni_syscall 31 common osf_ngetpeername sys_ni_syscall 32 common osf_ngetsockname sys_ni_syscall 33 common access sys_access 34 common osf_chflags sys_ni_syscall 35 common osf_fchflags sys_ni_syscall 36 common sync sys_sync 37 common kill sys_kill 38 common osf_old_stat sys_ni_syscall 39 common setpgid sys_setpgid 40 common osf_old_lstat sys_ni_syscall 41 common dup sys_dup 42 common pipe sys_alpha_pipe 43 common osf_set_program_attributes sys_osf_set_program_attributes 44 common osf_profil sys_ni_syscall 45 common open sys_open 46 common osf_old_sigaction sys_ni_syscall 47 common getxgid sys_getxgid 48 common osf_sigprocmask sys_osf_sigprocmask 49 common osf_getlogin sys_ni_syscall 50 common osf_setlogin sys_ni_syscall 51 common acct sys_acct 52 common sigpending sys_sigpending 54 common ioctl sys_ioctl 55 common osf_reboot sys_ni_syscall 56 common osf_revoke sys_ni_syscall 57 common symlink sys_symlink 58 common readlink sys_readlink 59 common execve sys_execve 60 common umask sys_umask 61 common chroot sys_chroot 62 common osf_old_fstat sys_ni_syscall 63 common getpgrp sys_getpgrp 64 common getpagesize sys_getpagesize 65 common osf_mremap sys_ni_syscall 66 common vfork alpha_vfork 67 common stat sys_newstat 68 common lstat sys_newlstat 69 common osf_sbrk sys_ni_syscall 70 common osf_sstk sys_ni_syscall 71 common mmap sys_osf_mmap 72 common osf_old_vadvise sys_ni_syscall 73 common munmap sys_munmap 74 common mprotect sys_mprotect 75 common madvise sys_madvise 76 common vhangup sys_vhangup 77 common osf_kmodcall sys_ni_syscall 78 common osf_mincore sys_ni_syscall 79 common getgroups sys_getgroups 80 common setgroups sys_setgroups 81 common osf_old_getpgrp sys_ni_syscall 82 common setpgrp sys_setpgid 83 common osf_setitimer compat_sys_setitimer 84 common osf_old_wait sys_ni_syscall 85 common osf_table sys_ni_syscall 86 common osf_getitimer compat_sys_getitimer 87 common gethostname sys_gethostname 88 common sethostname sys_sethostname 89 common getdtablesize sys_getdtablesize 90 common dup2 sys_dup2 91 common fstat sys_newfstat 92 common fcntl sys_fcntl 93 common osf_select sys_osf_select 94 common poll sys_poll 95 common fsync sys_fsync 96 common setpriority sys_setpriority 97 common socket sys_socket 98 common connect sys_connect 99 common accept sys_accept 100 common getpriority sys_osf_getpriority 101 common send sys_send 102 common recv sys_recv 103 common sigreturn sys_sigreturn 104 common bind sys_bind 105 common setsockopt sys_setsockopt 106 common listen sys_listen 107 common osf_plock sys_ni_syscall 108 common osf_old_sigvec sys_ni_syscall 109 common osf_old_sigblock sys_ni_syscall 110 common osf_old_sigsetmask sys_ni_syscall 111 common sigsuspend sys_sigsuspend 112 common osf_sigstack sys_osf_sigstack 113 common recvmsg sys_recvmsg 114 common sendmsg sys_sendmsg 115 common osf_old_vtrace sys_ni_syscall 116 common osf_gettimeofday sys_osf_gettimeofday 117 common osf_getrusage sys_osf_getrusage 118 common getsockopt sys_getsockopt 120 common readv sys_readv 121 common writev sys_writev 122 common osf_settimeofday sys_osf_settimeofday 123 common fchown sys_fchown 124 common fchmod sys_fchmod 125 common recvfrom sys_recvfrom 126 common setreuid sys_setreuid 127 common setregid sys_setregid 128 common rename sys_rename 129 common truncate sys_truncate 130 common ftruncate sys_ftruncate 131 common flock sys_flock 132 common setgid sys_setgid 133 common sendto sys_sendto 134 common shutdown sys_shutdown 135 common socketpair sys_socketpair 136 common mkdir sys_mkdir 137 common rmdir sys_rmdir 138 common osf_utimes sys_osf_utimes 139 common osf_old_sigreturn sys_ni_syscall 140 common osf_adjtime sys_ni_syscall 141 common getpeername sys_getpeername 142 common osf_gethostid sys_ni_syscall 143 common osf_sethostid sys_ni_syscall 144 common getrlimit sys_getrlimit 145 common setrlimit sys_setrlimit 146 common osf_old_killpg sys_ni_syscall 147 common setsid sys_setsid 148 common quotactl sys_quotactl 149 common osf_oldquota sys_ni_syscall 150 common getsockname sys_getsockname 153 common osf_pid_block sys_ni_syscall 154 common osf_pid_unblock sys_ni_syscall 156 common sigaction sys_osf_sigaction 157 common osf_sigwaitprim sys_ni_syscall 158 common osf_nfssvc sys_ni_syscall 159 common osf_getdirentries sys_osf_getdirentries 160 common osf_statfs sys_osf_statfs 161 common osf_fstatfs sys_osf_fstatfs 163 common osf_asynch_daemon sys_ni_syscall 164 common osf_getfh sys_ni_syscall 165 common osf_getdomainname sys_osf_getdomainname 166 common setdomainname sys_setdomainname 169 common osf_exportfs sys_ni_syscall 181 common osf_alt_plock sys_ni_syscall 184 common osf_getmnt sys_ni_syscall 187 common osf_alt_sigpending sys_ni_syscall 188 common osf_alt_setsid sys_ni_syscall 199 common osf_swapon sys_swapon 200 common msgctl sys_old_msgctl 201 common msgget sys_msgget 202 common msgrcv sys_msgrcv 203 common msgsnd sys_msgsnd 204 common semctl sys_old_semctl 205 common semget sys_semget 206 common semop sys_semop 207 common osf_utsname sys_osf_utsname 208 common lchown sys_lchown 209 common shmat sys_shmat 210 common shmctl sys_old_shmctl 211 common shmdt sys_shmdt 212 common shmget sys_shmget 213 common osf_mvalid sys_ni_syscall 214 common osf_getaddressconf sys_ni_syscall 215 common osf_msleep sys_ni_syscall 216 common osf_mwakeup sys_ni_syscall 217 common msync sys_msync 218 common osf_signal sys_ni_syscall 219 common osf_utc_gettime sys_ni_syscall 220 common osf_utc_adjtime sys_ni_syscall 222 common osf_security sys_ni_syscall 223 common osf_kloadcall sys_ni_syscall 224 common osf_stat sys_osf_stat 225 common osf_lstat sys_osf_lstat 226 common osf_fstat sys_osf_fstat 227 common osf_statfs64 sys_osf_statfs64 228 common osf_fstatfs64 sys_osf_fstatfs64 233 common getpgid sys_getpgid 234 common getsid sys_getsid 235 common sigaltstack sys_sigaltstack 236 common osf_waitid sys_ni_syscall 237 common osf_priocntlset sys_ni_syscall 238 common osf_sigsendset sys_ni_syscall 239 common osf_set_speculative sys_ni_syscall 240 common osf_msfs_syscall sys_ni_syscall 241 common osf_sysinfo sys_osf_sysinfo 242 common osf_uadmin sys_ni_syscall 243 common osf_fuser sys_ni_syscall 244 common osf_proplist_syscall sys_osf_proplist_syscall 245 common osf_ntp_adjtime sys_ni_syscall 246 common osf_ntp_gettime sys_ni_syscall 247 common osf_pathconf sys_ni_syscall 248 common osf_fpathconf sys_ni_syscall 250 common osf_uswitch sys_ni_syscall 251 common osf_usleep_thread sys_osf_usleep_thread 252 common osf_audcntl sys_ni_syscall 253 common osf_audgen sys_ni_syscall 254 common sysfs sys_sysfs 255 common osf_subsys_info sys_ni_syscall 256 common osf_getsysinfo sys_osf_getsysinfo 257 common osf_setsysinfo sys_osf_setsysinfo 258 common osf_afs_syscall sys_ni_syscall 259 common osf_swapctl sys_ni_syscall 260 common osf_memcntl sys_ni_syscall 261 common osf_fdatasync sys_ni_syscall 300 common bdflush sys_ni_syscall 301 common sethae sys_sethae 302 common mount sys_mount 303 common old_adjtimex sys_old_adjtimex 304 common swapoff sys_swapoff 305 common getdents sys_getdents 306 common create_module sys_ni_syscall 307 common init_module sys_init_module 308 common delete_module sys_delete_module 309 common get_kernel_syms sys_ni_syscall 310 common syslog sys_syslog 311 common reboot sys_reboot 312 common clone alpha_clone 313 common uselib sys_uselib 314 common mlock sys_mlock 315 common munlock sys_munlock 316 common mlockall sys_mlockall 317 common munlockall sys_munlockall 318 common sysinfo sys_sysinfo 319 common _sysctl sys_ni_syscall 321 common oldumount sys_oldumount 322 common swapon sys_swapon 323 common times sys_times 324 common personality sys_personality 325 common setfsuid sys_setfsuid 326 common setfsgid sys_setfsgid 327 common ustat sys_ustat 328 common statfs sys_statfs 329 common fstatfs sys_fstatfs 330 common sched_setparam sys_sched_setparam 331 common sched_getparam sys_sched_getparam 332 common sched_setscheduler sys_sched_setscheduler 333 common sched_getscheduler sys_sched_getscheduler 334 common sched_yield sys_sched_yield 335 common sched_get_priority_max sys_sched_get_priority_max 336 common sched_get_priority_min sys_sched_get_priority_min 337 common sched_rr_get_interval sys_sched_rr_get_interval 338 common afs_syscall sys_ni_syscall 339 common uname sys_newuname 340 common nanosleep sys_nanosleep 341 common mremap sys_mremap 342 common nfsservctl sys_ni_syscall 343 common setresuid sys_setresuid 344 common getresuid sys_getresuid 345 common pciconfig_read sys_pciconfig_read 346 common pciconfig_write sys_pciconfig_write 347 common query_module sys_ni_syscall 348 common prctl sys_prctl 349 common pread64 sys_pread64 350 common pwrite64 sys_pwrite64 351 common rt_sigreturn sys_rt_sigreturn 352 common rt_sigaction sys_rt_sigaction 353 common rt_sigprocmask sys_rt_sigprocmask 354 common rt_sigpending sys_rt_sigpending 355 common rt_sigtimedwait sys_rt_sigtimedwait 356 common rt_sigqueueinfo sys_rt_sigqueueinfo 357 common rt_sigsuspend sys_rt_sigsuspend 358 common select sys_select 359 common gettimeofday sys_gettimeofday 360 common settimeofday sys_settimeofday 361 common getitimer sys_getitimer 362 common setitimer sys_setitimer 363 common utimes sys_utimes 364 common getrusage sys_getrusage 365 common wait4 sys_wait4 366 common adjtimex sys_adjtimex 367 common getcwd sys_getcwd 368 common capget sys_capget 369 common capset sys_capset 370 common sendfile sys_sendfile64 371 common setresgid sys_setresgid 372 common getresgid sys_getresgid 373 common dipc sys_ni_syscall 374 common pivot_root sys_pivot_root 375 common mincore sys_mincore 376 common pciconfig_iobase sys_pciconfig_iobase 377 common getdents64 sys_getdents64 378 common gettid sys_gettid 379 common readahead sys_readahead 381 common tkill sys_tkill 382 common setxattr sys_setxattr 383 common lsetxattr sys_lsetxattr 384 common fsetxattr sys_fsetxattr 385 common getxattr sys_getxattr 386 common lgetxattr sys_lgetxattr 387 common fgetxattr sys_fgetxattr 388 common listxattr sys_listxattr 389 common llistxattr sys_llistxattr 390 common flistxattr sys_flistxattr 391 common removexattr sys_removexattr 392 common lremovexattr sys_lremovexattr 393 common fremovexattr sys_fremovexattr 394 common futex sys_futex 395 common sched_setaffinity sys_sched_setaffinity 396 common sched_getaffinity sys_sched_getaffinity 397 common tuxcall sys_ni_syscall 398 common io_setup sys_io_setup 399 common io_destroy sys_io_destroy 400 common io_getevents sys_io_getevents 401 common io_submit sys_io_submit 402 common io_cancel sys_io_cancel 405 common exit_group sys_exit_group 406 common lookup_dcookie sys_ni_syscall 407 common epoll_create sys_epoll_create 408 common epoll_ctl sys_epoll_ctl 409 common epoll_wait sys_epoll_wait 410 common remap_file_pages sys_remap_file_pages 411 common set_tid_address sys_set_tid_address 412 common restart_syscall sys_restart_syscall 413 common fadvise64 sys_fadvise64 414 common timer_create sys_timer_create 415 common timer_settime sys_timer_settime 416 common timer_gettime sys_timer_gettime 417 common timer_getoverrun sys_timer_getoverrun 418 common timer_delete sys_timer_delete 419 common clock_settime sys_clock_settime 420 common clock_gettime sys_clock_gettime 421 common clock_getres sys_clock_getres 422 common clock_nanosleep sys_clock_nanosleep 423 common semtimedop sys_semtimedop 424 common tgkill sys_tgkill 425 common stat64 sys_stat64 426 common lstat64 sys_lstat64 427 common fstat64 sys_fstat64 428 common vserver sys_ni_syscall 429 common mbind sys_ni_syscall 430 common get_mempolicy sys_ni_syscall 431 common set_mempolicy sys_ni_syscall 432 common mq_open sys_mq_open 433 common mq_unlink sys_mq_unlink 434 common mq_timedsend sys_mq_timedsend 435 common mq_timedreceive sys_mq_timedreceive 436 common mq_notify sys_mq_notify 437 common mq_getsetattr sys_mq_getsetattr 438 common waitid sys_waitid 439 common add_key sys_add_key 440 common request_key sys_request_key 441 common keyctl sys_keyctl 442 common ioprio_set sys_ioprio_set 443 common ioprio_get sys_ioprio_get 444 common inotify_init sys_inotify_init 445 common inotify_add_watch sys_inotify_add_watch 446 common inotify_rm_watch sys_inotify_rm_watch 447 common fdatasync sys_fdatasync 448 common kexec_load sys_kexec_load 449 common migrate_pages sys_migrate_pages 450 common openat sys_openat 451 common mkdirat sys_mkdirat 452 common mknodat sys_mknodat 453 common fchownat sys_fchownat 454 common futimesat sys_futimesat 455 common fstatat64 sys_fstatat64 456 common unlinkat sys_unlinkat 457 common renameat sys_renameat 458 common linkat sys_linkat 459 common symlinkat sys_symlinkat 460 common readlinkat sys_readlinkat 461 common fchmodat sys_fchmodat 462 common faccessat sys_faccessat 463 common pselect6 sys_pselect6 464 common ppoll sys_ppoll 465 common unshare sys_unshare 466 common set_robust_list sys_set_robust_list 467 common get_robust_list sys_get_robust_list 468 common splice sys_splice 469 common sync_file_range sys_sync_file_range 470 common tee sys_tee 471 common vmsplice sys_vmsplice 472 common move_pages sys_move_pages 473 common getcpu sys_getcpu 474 common epoll_pwait sys_epoll_pwait 475 common utimensat sys_utimensat 476 common signalfd sys_signalfd 477 common timerfd sys_ni_syscall 478 common eventfd sys_eventfd 479 common recvmmsg sys_recvmmsg 480 common fallocate sys_fallocate 481 common timerfd_create sys_timerfd_create 482 common timerfd_settime sys_timerfd_settime 483 common timerfd_gettime sys_timerfd_gettime 484 common signalfd4 sys_signalfd4 485 common eventfd2 sys_eventfd2 486 common epoll_create1 sys_epoll_create1 487 common dup3 sys_dup3 488 common pipe2 sys_pipe2 489 common inotify_init1 sys_inotify_init1 490 common preadv sys_preadv 491 common pwritev sys_pwritev 492 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 493 common perf_event_open sys_perf_event_open 494 common fanotify_init sys_fanotify_init 495 common fanotify_mark sys_fanotify_mark 496 common prlimit64 sys_prlimit64 497 common name_to_handle_at sys_name_to_handle_at 498 common open_by_handle_at sys_open_by_handle_at 499 common clock_adjtime sys_clock_adjtime 500 common syncfs sys_syncfs 501 common setns sys_setns 502 common accept4 sys_accept4 503 common sendmmsg sys_sendmmsg 504 common process_vm_readv sys_process_vm_readv 505 common process_vm_writev sys_process_vm_writev 506 common kcmp sys_kcmp 507 common finit_module sys_finit_module 508 common sched_setattr sys_sched_setattr 509 common sched_getattr sys_sched_getattr 510 common renameat2 sys_renameat2 511 common getrandom sys_getrandom 512 common memfd_create sys_memfd_create 513 common execveat sys_execveat 514 common seccomp sys_seccomp 515 common bpf sys_bpf 516 common userfaultfd sys_userfaultfd 517 common membarrier sys_membarrier 518 common mlock2 sys_mlock2 519 common copy_file_range sys_copy_file_range 520 common preadv2 sys_preadv2 521 common pwritev2 sys_pwritev2 522 common statx sys_statx 523 common io_pgetevents sys_io_pgetevents 524 common pkey_mprotect sys_pkey_mprotect 525 common pkey_alloc sys_pkey_alloc 526 common pkey_free sys_pkey_free 527 common rseq sys_rseq 528 common statfs64 sys_statfs64 529 common fstatfs64 sys_fstatfs64 530 common getegid sys_getegid 531 common geteuid sys_geteuid 532 common getppid sys_getppid 534 common pidfd_send_signal sys_pidfd_send_signal 535 common io_uring_setup sys_io_uring_setup 536 common io_uring_enter sys_io_uring_enter 537 common io_uring_register sys_io_uring_register 538 common open_tree sys_open_tree 539 common move_mount sys_move_mount 540 common fsopen sys_fsopen 541 common fsconfig sys_fsconfig 542 common fsmount sys_fsmount 543 common fspick sys_fspick 544 common pidfd_open sys_pidfd_open 545 common clone3 alpha_clone3 546 common close_range sys_close_range 547 common openat2 sys_openat2 548 common pidfd_getfd sys_pidfd_getfd 549 common faccessat2 sys_faccessat2 550 common process_madvise sys_process_madvise 551 common epoll_pwait2 sys_epoll_pwait2 552 common mount_setattr sys_mount_setattr 553 common quotactl_fd sys_quotactl_fd 554 common landlock_create_ruleset sys_landlock_create_ruleset 555 common landlock_add_rule sys_landlock_add_rule 556 common landlock_restrict_self sys_landlock_restrict_self 558 common process_mrelease sys_process_mrelease 559 common futex_waitv sys_futex_waitv 560 common set_mempolicy_home_node sys_ni_syscall 561 common cachestat sys_cachestat 562 common fchmodat2 sys_fchmodat2 563 common map_shadow_stack sys_map_shadow_stack 564 common futex_wake sys_futex_wake 565 common futex_wait sys_futex_wait 566 common futex_requeue sys_futex_requeue 567 common statmount sys_statmount 568 common listmount sys_listmount 569 common lsm_get_self_attr sys_lsm_get_self_attr 570 common lsm_set_self_attr sys_lsm_set_self_attr 571 common lsm_list_modules sys_lsm_list_modules 572 common mseal sys_mseal 573 common setxattrat sys_setxattrat 574 common getxattrat sys_getxattrat 575 common listxattrat sys_listxattrat 576 common removexattrat sys_removexattrat 577 common open_tree_attr sys_open_tree_attr 578 common file_getattr sys_file_getattr 579 common file_setattr sys_file_setattr 580 common listns sys_listns """ # HPPA # - arch/parisc/kernel/syscalls/syscall.tbl hppa_syscall_tbl = """ 0 common restart_syscall sys_restart_syscall 1 common exit sys_exit 2 common fork sys_fork_wrapper 3 common read sys_read 4 common write sys_write 5 common open sys_open compat_sys_open 6 common close sys_close 7 common waitpid sys_waitpid 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 common execve sys_execve compat_sys_execve 12 common chdir sys_chdir 13 32 time sys_time32 13 64 time sys_time 14 common mknod sys_mknod 15 common chmod sys_chmod 16 common lchown sys_lchown 17 common socket sys_socket 18 common stat sys_newstat compat_sys_newstat 19 common lseek sys_lseek compat_sys_lseek 20 common getpid sys_getpid 21 common mount sys_mount 22 common bind sys_bind 23 common setuid sys_setuid 24 common getuid sys_getuid 25 32 stime sys_stime32 25 64 stime sys_stime 26 common ptrace sys_ptrace compat_sys_ptrace 27 common alarm sys_alarm 28 common fstat sys_newfstat compat_sys_newfstat 29 common pause sys_pause 30 32 utime sys_utime32 30 64 utime sys_utime 31 common connect sys_connect 32 common listen sys_listen 33 common access sys_access 34 common nice sys_nice 35 common accept sys_accept 36 common sync sys_sync 37 common kill sys_kill 38 common rename sys_rename 39 common mkdir sys_mkdir 40 common rmdir sys_rmdir 41 common dup sys_dup 42 common pipe sys_pipe 43 common times sys_times compat_sys_times 44 common getsockname sys_getsockname 45 common brk sys_brk 46 common setgid sys_setgid 47 common getgid sys_getgid 48 common signal sys_signal 49 common geteuid sys_geteuid 50 common getegid sys_getegid 51 common acct sys_acct 52 common umount2 sys_umount 53 common getpeername sys_getpeername 54 common ioctl sys_ioctl compat_sys_ioctl 55 common fcntl sys_fcntl compat_sys_fcntl 56 common socketpair sys_socketpair 57 common setpgid sys_setpgid 58 common send sys_send 59 common uname sys_newuname 60 common umask sys_umask 61 common chroot sys_chroot 62 common ustat sys_ustat compat_sys_ustat 63 common dup2 sys_dup2 64 common getppid sys_getppid 65 common getpgrp sys_getpgrp 66 common setsid sys_setsid 67 common pivot_root sys_pivot_root 68 common sgetmask sys_sgetmask sys32_unimplemented 69 common ssetmask sys_ssetmask sys32_unimplemented 70 common setreuid sys_setreuid 71 common setregid sys_setregid 72 common mincore sys_mincore 73 common sigpending sys_sigpending compat_sys_sigpending 74 common sethostname sys_sethostname 75 common setrlimit sys_setrlimit compat_sys_setrlimit 76 common getrlimit sys_getrlimit compat_sys_getrlimit 77 common getrusage sys_getrusage compat_sys_getrusage 78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday 79 common settimeofday sys_settimeofday compat_sys_settimeofday 80 common getgroups sys_getgroups 81 common setgroups sys_setgroups 82 common sendto sys_sendto 83 common symlink sys_symlink 84 common lstat sys_newlstat compat_sys_newlstat 85 common readlink sys_readlink 86 common uselib sys_ni_syscall 87 common swapon sys_swapon 88 common reboot sys_reboot 89 common mmap2 sys_mmap2 90 common mmap sys_mmap 91 common munmap sys_munmap 92 common truncate sys_truncate compat_sys_truncate 93 common ftruncate sys_ftruncate compat_sys_ftruncate 94 common fchmod sys_fchmod 95 common fchown sys_fchown 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority 98 common recv sys_recv compat_sys_recv 99 common statfs sys_statfs compat_sys_statfs 100 common fstatfs sys_fstatfs compat_sys_fstatfs 101 common stat64 sys_stat64 103 common syslog sys_syslog 104 common setitimer sys_setitimer compat_sys_setitimer 105 common getitimer sys_getitimer compat_sys_getitimer 106 common capget sys_capget 107 common capset sys_capset 108 32 pread64 parisc_pread64 108 64 pread64 sys_pread64 109 32 pwrite64 parisc_pwrite64 109 64 pwrite64 sys_pwrite64 110 common getcwd sys_getcwd 111 common vhangup sys_vhangup 112 common fstat64 sys_fstat64 113 common vfork sys_vfork_wrapper 114 common wait4 sys_wait4 compat_sys_wait4 115 common swapoff sys_swapoff 116 common sysinfo sys_sysinfo compat_sys_sysinfo 117 common shutdown sys_shutdown 118 common fsync sys_fsync 119 common madvise parisc_madvise 120 common clone sys_clone_wrapper 121 common setdomainname sys_setdomainname 122 common sendfile sys_sendfile compat_sys_sendfile 123 common recvfrom sys_recvfrom compat_sys_recvfrom 124 32 adjtimex sys_adjtimex_time32 124 64 adjtimex sys_adjtimex 125 common mprotect sys_mprotect 126 common sigprocmask sys_sigprocmask compat_sys_sigprocmask 128 common init_module sys_init_module 129 common delete_module sys_delete_module 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir 134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 32 personality parisc_personality 136 64 personality sys_personality 138 common setfsuid sys_setfsuid 139 common setfsgid sys_setfsgid 140 common _llseek sys_llseek 141 common getdents sys_getdents compat_sys_getdents 142 common _newselect sys_select compat_sys_select 143 common flock sys_flock 144 common msync sys_msync 145 common readv sys_readv 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync 149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall 153 common munlockall sys_munlockall 154 common sched_setparam sys_sched_setparam 155 common sched_getparam sys_sched_getparam 156 common sched_setscheduler sys_sched_setscheduler 157 common sched_getscheduler sys_sched_getscheduler 158 common sched_yield sys_sched_yield 159 common sched_get_priority_max sys_sched_get_priority_max 160 common sched_get_priority_min sys_sched_get_priority_min 161 32 sched_rr_get_interval sys_sched_rr_get_interval_time32 161 64 sched_rr_get_interval sys_sched_rr_get_interval 162 32 nanosleep sys_nanosleep_time32 162 64 nanosleep sys_nanosleep 163 common mremap sys_mremap 164 common setresuid sys_setresuid 165 common getresuid sys_getresuid 166 common sigaltstack sys_sigaltstack compat_sys_sigaltstack 168 common poll sys_poll 170 common setresgid sys_setresgid 171 common getresgid sys_getresgid 172 common prctl sys_prctl 173 common rt_sigreturn sys_rt_sigreturn_wrapper 174 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction 175 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask 176 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending 177 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 177 64 rt_sigtimedwait sys_rt_sigtimedwait 178 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 179 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 180 common chown sys_chown 181 common setsockopt sys_setsockopt sys_setsockopt 182 common getsockopt sys_getsockopt sys_getsockopt 183 common sendmsg sys_sendmsg compat_sys_sendmsg 184 common recvmsg sys_recvmsg compat_sys_recvmsg 185 common semop sys_semop 186 common semget sys_semget 187 common semctl sys_semctl compat_sys_semctl 188 common msgsnd sys_msgsnd compat_sys_msgsnd 189 common msgrcv sys_msgrcv compat_sys_msgrcv 190 common msgget sys_msgget 191 common msgctl sys_msgctl compat_sys_msgctl 192 common shmat sys_shmat compat_sys_shmat 193 common shmdt sys_shmdt 194 common shmget sys_shmget 195 common shmctl sys_shmctl compat_sys_shmctl 198 common lstat64 sys_lstat64 199 32 truncate64 parisc_truncate64 199 64 truncate64 sys_truncate64 200 32 ftruncate64 parisc_ftruncate64 200 64 ftruncate64 sys_ftruncate64 201 common getdents64 sys_getdents64 202 common fcntl64 sys_fcntl64 compat_sys_fcntl64 206 common gettid sys_gettid 207 32 readahead parisc_readahead 207 64 readahead sys_readahead 208 common tkill sys_tkill 209 common sendfile64 sys_sendfile64 compat_sys_sendfile64 210 32 futex sys_futex_time32 210 64 futex sys_futex 211 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity 212 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity 215 common io_setup sys_io_setup compat_sys_io_setup 216 common io_destroy sys_io_destroy 217 32 io_getevents sys_io_getevents_time32 217 64 io_getevents sys_io_getevents 218 common io_submit sys_io_submit compat_sys_io_submit 219 common io_cancel sys_io_cancel 222 common exit_group sys_exit_group 223 common lookup_dcookie sys_ni_syscall 224 common epoll_create sys_epoll_create 225 common epoll_ctl sys_epoll_ctl 226 common epoll_wait sys_epoll_wait 227 common remap_file_pages sys_remap_file_pages 228 32 semtimedop sys_semtimedop_time32 228 64 semtimedop sys_semtimedop 229 common mq_open sys_mq_open compat_sys_mq_open 230 common mq_unlink sys_mq_unlink 231 32 mq_timedsend sys_mq_timedsend_time32 231 64 mq_timedsend sys_mq_timedsend 232 32 mq_timedreceive sys_mq_timedreceive_time32 232 64 mq_timedreceive sys_mq_timedreceive 233 common mq_notify sys_mq_notify compat_sys_mq_notify 234 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr 235 common waitid sys_waitid compat_sys_waitid 236 32 fadvise64_64 parisc_fadvise64_64 236 64 fadvise64_64 sys_fadvise64_64 237 common set_tid_address sys_set_tid_address 238 common setxattr sys_setxattr 239 common lsetxattr sys_lsetxattr 240 common fsetxattr sys_fsetxattr 241 common getxattr sys_getxattr 242 common lgetxattr sys_lgetxattr 243 common fgetxattr sys_fgetxattr 244 common listxattr sys_listxattr 245 common llistxattr sys_llistxattr 246 common flistxattr sys_flistxattr 247 common removexattr sys_removexattr 248 common lremovexattr sys_lremovexattr 249 common fremovexattr sys_fremovexattr 250 common timer_create sys_timer_create compat_sys_timer_create 251 32 timer_settime sys_timer_settime32 251 64 timer_settime sys_timer_settime 252 32 timer_gettime sys_timer_gettime32 252 64 timer_gettime sys_timer_gettime 253 common timer_getoverrun sys_timer_getoverrun 254 common timer_delete sys_timer_delete 255 32 clock_settime sys_clock_settime32 255 64 clock_settime sys_clock_settime 256 32 clock_gettime sys_clock_gettime32 256 64 clock_gettime sys_clock_gettime 257 32 clock_getres sys_clock_getres_time32 257 64 clock_getres sys_clock_getres 258 32 clock_nanosleep sys_clock_nanosleep_time32 258 64 clock_nanosleep sys_clock_nanosleep 259 common tgkill sys_tgkill 260 common mbind sys_mbind 261 common get_mempolicy sys_get_mempolicy 262 common set_mempolicy sys_set_mempolicy 264 common add_key sys_add_key 265 common request_key sys_request_key 266 common keyctl sys_keyctl compat_sys_keyctl 267 common ioprio_set sys_ioprio_set 268 common ioprio_get sys_ioprio_get 269 common inotify_init sys_inotify_init 270 common inotify_add_watch sys_inotify_add_watch 271 common inotify_rm_watch sys_inotify_rm_watch 272 common migrate_pages sys_migrate_pages 273 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 273 64 pselect6 sys_pselect6 274 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 274 64 ppoll sys_ppoll 275 common openat sys_openat compat_sys_openat 276 common mkdirat sys_mkdirat 277 common mknodat sys_mknodat 278 common fchownat sys_fchownat 279 32 futimesat sys_futimesat_time32 279 64 futimesat sys_futimesat 280 common fstatat64 sys_fstatat64 281 common unlinkat sys_unlinkat 282 common renameat sys_renameat 283 common linkat sys_linkat 284 common symlinkat sys_symlinkat 285 common readlinkat sys_readlinkat 286 common fchmodat sys_fchmodat 287 common faccessat sys_faccessat 288 common unshare sys_unshare 289 common set_robust_list sys_set_robust_list compat_sys_set_robust_list 290 common get_robust_list sys_get_robust_list compat_sys_get_robust_list 291 common splice sys_splice 292 32 sync_file_range parisc_sync_file_range 292 64 sync_file_range sys_sync_file_range 293 common tee sys_tee 294 common vmsplice sys_vmsplice 295 common move_pages sys_move_pages 296 common getcpu sys_getcpu 297 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 298 common statfs64 sys_statfs64 compat_sys_statfs64 299 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 300 common kexec_load sys_kexec_load compat_sys_kexec_load 301 32 utimensat sys_utimensat_time32 301 64 utimensat sys_utimensat 302 common signalfd sys_signalfd compat_sys_signalfd 304 common eventfd sys_eventfd 305 32 fallocate parisc_fallocate 305 64 fallocate sys_fallocate 306 common timerfd_create parisc_timerfd_create 307 32 timerfd_settime sys_timerfd_settime32 307 64 timerfd_settime sys_timerfd_settime 308 32 timerfd_gettime sys_timerfd_gettime32 308 64 timerfd_gettime sys_timerfd_gettime 309 common signalfd4 parisc_signalfd4 parisc_compat_signalfd4 310 common eventfd2 parisc_eventfd2 311 common epoll_create1 sys_epoll_create1 312 common dup3 sys_dup3 313 common pipe2 parisc_pipe2 314 common inotify_init1 parisc_inotify_init1 315 common preadv sys_preadv compat_sys_preadv 316 common pwritev sys_pwritev compat_sys_pwritev 317 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 318 common perf_event_open sys_perf_event_open 319 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 319 64 recvmmsg sys_recvmmsg 320 common accept4 sys_accept4 321 common prlimit64 sys_prlimit64 322 common fanotify_init sys_fanotify_init 323 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark 324 32 clock_adjtime sys_clock_adjtime32 324 64 clock_adjtime sys_clock_adjtime 325 common name_to_handle_at sys_name_to_handle_at 326 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at 327 common syncfs sys_syncfs 328 common setns sys_setns 329 common sendmmsg sys_sendmmsg compat_sys_sendmmsg 330 common process_vm_readv sys_process_vm_readv 331 common process_vm_writev sys_process_vm_writev 332 common kcmp sys_kcmp 333 common finit_module sys_finit_module 334 common sched_setattr sys_sched_setattr 335 common sched_getattr sys_sched_getattr 336 32 utimes sys_utimes_time32 336 64 utimes sys_utimes 337 common renameat2 sys_renameat2 338 common seccomp sys_seccomp 339 common getrandom sys_getrandom 340 common memfd_create sys_memfd_create 341 common bpf sys_bpf 342 common execveat sys_execveat compat_sys_execveat 343 common membarrier sys_membarrier 344 common userfaultfd parisc_userfaultfd 345 common mlock2 sys_mlock2 346 common copy_file_range sys_copy_file_range 347 common preadv2 sys_preadv2 compat_sys_preadv2 348 common pwritev2 sys_pwritev2 compat_sys_pwritev2 349 common statx sys_statx 350 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents 350 64 io_pgetevents sys_io_pgetevents 351 common pkey_mprotect sys_pkey_mprotect 352 common pkey_alloc sys_pkey_alloc 353 common pkey_free sys_pkey_free 354 common rseq sys_rseq 355 common kexec_file_load sys_kexec_file_load sys_kexec_file_load 356 common cacheflush sys_cacheflush 403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime 404 32 clock_settime64 sys_clock_settime sys_clock_settime 405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime 406 32 clock_getres_time64 sys_clock_getres sys_clock_getres 407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep 408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime 409 32 timer_settime64 sys_timer_settime sys_timer_settime 410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime 411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime 412 32 utimensat_time64 sys_utimensat sys_utimensat 413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend 419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive 420 32 semtimedop_time64 sys_semtimedop sys_semtimedop 421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 422 32 futex_time64 sys_futex sys_futex 423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3_wrapper 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # OpenRISC or1k_syscall_tbl = arm64_syscall_tbl # Nios II nios2_syscall_tbl = arm64_syscall_tbl # MicroBlaze # - arch/microblaze/kernel/syscalls/syscall.tbl microblaze_syscall_tbl = """ 0 common restart_syscall sys_restart_syscall 1 common exit sys_exit 2 common fork sys_fork 3 common read sys_read 4 common write sys_write 5 common open sys_open 6 common close sys_close 7 common waitpid sys_waitpid 8 common creat sys_creat 9 common link sys_link 10 common unlink sys_unlink 11 common execve sys_execve 12 common chdir sys_chdir 13 common time sys_time32 14 common mknod sys_mknod 15 common chmod sys_chmod 16 common lchown sys_lchown 17 common break sys_ni_syscall 18 common oldstat sys_ni_syscall 19 common lseek sys_lseek 20 common getpid sys_getpid 21 common mount sys_mount 22 common umount sys_oldumount 23 common setuid sys_setuid 24 common getuid sys_getuid 25 common stime sys_stime32 26 common ptrace sys_ptrace 27 common alarm sys_alarm 28 common oldfstat sys_ni_syscall 29 common pause sys_pause 30 common utime sys_utime32 31 common stty sys_ni_syscall 32 common gtty sys_ni_syscall 33 common access sys_access 34 common nice sys_nice 35 common ftime sys_ni_syscall 36 common sync sys_sync 37 common kill sys_kill 38 common rename sys_rename 39 common mkdir sys_mkdir 40 common rmdir sys_rmdir 41 common dup sys_dup 42 common pipe sys_pipe 43 common times sys_times 44 common prof sys_ni_syscall 45 common brk sys_brk 46 common setgid sys_setgid 47 common getgid sys_getgid 48 common signal sys_signal 49 common geteuid sys_geteuid 50 common getegid sys_getegid 51 common acct sys_acct 52 common umount2 sys_umount 53 common lock sys_ni_syscall 54 common ioctl sys_ioctl 55 common fcntl sys_fcntl 56 common mpx sys_ni_syscall 57 common setpgid sys_setpgid 58 common ulimit sys_ni_syscall 59 common oldolduname sys_ni_syscall 60 common umask sys_umask 61 common chroot sys_chroot 62 common ustat sys_ustat 63 common dup2 sys_dup2 64 common getppid sys_getppid 65 common getpgrp sys_getpgrp 66 common setsid sys_setsid 67 common sigaction sys_ni_syscall 68 common sgetmask sys_sgetmask 69 common ssetmask sys_ssetmask 70 common setreuid sys_setreuid 71 common setregid sys_setregid 72 common sigsuspend sys_ni_syscall 73 common sigpending sys_sigpending 74 common sethostname sys_sethostname 75 common setrlimit sys_setrlimit 76 common getrlimit sys_ni_syscall 77 common getrusage sys_getrusage 78 common gettimeofday sys_gettimeofday 79 common settimeofday sys_settimeofday 80 common getgroups sys_getgroups 81 common setgroups sys_setgroups 82 common select sys_ni_syscall 83 common symlink sys_symlink 84 common oldlstat sys_ni_syscall 85 common readlink sys_readlink 86 common uselib sys_uselib 87 common swapon sys_swapon 88 common reboot sys_reboot 89 common readdir sys_ni_syscall 90 common mmap sys_mmap 91 common munmap sys_munmap 92 common truncate sys_truncate 93 common ftruncate sys_ftruncate 94 common fchmod sys_fchmod 95 common fchown sys_fchown 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority 98 common profil sys_ni_syscall 99 common statfs sys_statfs 100 common fstatfs sys_fstatfs 101 common ioperm sys_ni_syscall 102 common socketcall sys_socketcall 103 common syslog sys_syslog 104 common setitimer sys_setitimer 105 common getitimer sys_getitimer 106 common stat sys_newstat 107 common lstat sys_newlstat 108 common fstat sys_newfstat 109 common olduname sys_ni_syscall 110 common iopl sys_ni_syscall 111 common vhangup sys_vhangup 112 common idle sys_ni_syscall 113 common vm86old sys_ni_syscall 114 common wait4 sys_wait4 115 common swapoff sys_swapoff 116 common sysinfo sys_sysinfo 117 common ipc sys_ni_syscall 118 common fsync sys_fsync 119 common sigreturn sys_ni_syscall 120 common clone sys_clone 121 common setdomainname sys_setdomainname 122 common uname sys_newuname 123 common modify_ldt sys_ni_syscall 124 common adjtimex sys_adjtimex_time32 125 common mprotect sys_mprotect 126 common sigprocmask sys_sigprocmask 127 common create_module sys_ni_syscall 128 common init_module sys_init_module 129 common delete_module sys_delete_module 130 common get_kernel_syms sys_ni_syscall 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir 134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality 137 common afs_syscall sys_ni_syscall 138 common setfsuid sys_setfsuid 139 common setfsgid sys_setfsgid 140 common _llseek sys_llseek 141 common getdents sys_getdents 142 common _newselect sys_select 143 common flock sys_flock 144 common msync sys_msync 145 common readv sys_readv 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync 149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall 153 common munlockall sys_munlockall 154 common sched_setparam sys_sched_setparam 155 common sched_getparam sys_sched_getparam 156 common sched_setscheduler sys_sched_setscheduler 157 common sched_getscheduler sys_sched_getscheduler 158 common sched_yield sys_sched_yield 159 common sched_get_priority_max sys_sched_get_priority_max 160 common sched_get_priority_min sys_sched_get_priority_min 161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 162 common nanosleep sys_nanosleep_time32 163 common mremap sys_mremap 164 common setresuid sys_setresuid 165 common getresuid sys_getresuid 166 common vm86 sys_ni_syscall 167 common query_module sys_ni_syscall 168 common poll sys_poll 169 common nfsservctl sys_ni_syscall 170 common setresgid sys_setresgid 171 common getresgid sys_getresgid 172 common prctl sys_prctl 173 common rt_sigreturn sys_rt_sigreturn_wrapper 174 common rt_sigaction sys_rt_sigaction 175 common rt_sigprocmask sys_rt_sigprocmask 176 common rt_sigpending sys_rt_sigpending 177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 178 common rt_sigqueueinfo sys_rt_sigqueueinfo 179 common rt_sigsuspend sys_rt_sigsuspend 180 common pread64 sys_pread64 181 common pwrite64 sys_pwrite64 182 common chown sys_chown 183 common getcwd sys_getcwd 184 common capget sys_capget 185 common capset sys_capset 186 common sigaltstack sys_ni_syscall 187 common sendfile sys_sendfile 188 common getpmsg sys_ni_syscall 189 common putpmsg sys_ni_syscall 190 common vfork sys_vfork 191 common ugetrlimit sys_getrlimit 192 common mmap2 sys_mmap2 193 common truncate64 sys_truncate64 194 common ftruncate64 sys_ftruncate64 195 common stat64 sys_stat64 196 common lstat64 sys_lstat64 197 common fstat64 sys_fstat64 198 common lchown32 sys_lchown 199 common getuid32 sys_getuid 200 common getgid32 sys_getgid 201 common geteuid32 sys_geteuid 202 common getegid32 sys_getegid 203 common setreuid32 sys_setreuid 204 common setregid32 sys_setregid 205 common getgroups32 sys_getgroups 206 common setgroups32 sys_setgroups 207 common fchown32 sys_fchown 208 common setresuid32 sys_setresuid 209 common getresuid32 sys_getresuid 210 common setresgid32 sys_setresgid 211 common getresgid32 sys_getresgid 212 common chown32 sys_chown 213 common setuid32 sys_setuid 214 common setgid32 sys_setgid 215 common setfsuid32 sys_setfsuid 216 common setfsgid32 sys_setfsgid 217 common pivot_root sys_pivot_root 218 common mincore sys_mincore 219 common madvise sys_madvise 220 common getdents64 sys_getdents64 221 common fcntl64 sys_fcntl64 224 common gettid sys_gettid 225 common readahead sys_readahead 226 common setxattr sys_setxattr 227 common lsetxattr sys_lsetxattr 228 common fsetxattr sys_fsetxattr 229 common getxattr sys_getxattr 230 common lgetxattr sys_lgetxattr 231 common fgetxattr sys_fgetxattr 232 common listxattr sys_listxattr 233 common llistxattr sys_llistxattr 234 common flistxattr sys_flistxattr 235 common removexattr sys_removexattr 236 common lremovexattr sys_lremovexattr 237 common fremovexattr sys_fremovexattr 238 common tkill sys_tkill 239 common sendfile64 sys_sendfile64 240 common futex sys_futex_time32 241 common sched_setaffinity sys_sched_setaffinity 242 common sched_getaffinity sys_sched_getaffinity 243 common set_thread_area sys_ni_syscall 244 common get_thread_area sys_ni_syscall 245 common io_setup sys_io_setup 246 common io_destroy sys_io_destroy 247 common io_getevents sys_io_getevents_time32 248 common io_submit sys_io_submit 249 common io_cancel sys_io_cancel 250 common fadvise64 sys_fadvise64 252 common exit_group sys_exit_group 253 common lookup_dcookie sys_ni_syscall 254 common epoll_create sys_epoll_create 255 common epoll_ctl sys_epoll_ctl 256 common epoll_wait sys_epoll_wait 257 common remap_file_pages sys_remap_file_pages 258 common set_tid_address sys_set_tid_address 259 common timer_create sys_timer_create 260 common timer_settime sys_timer_settime32 261 common timer_gettime sys_timer_gettime32 262 common timer_getoverrun sys_timer_getoverrun 263 common timer_delete sys_timer_delete 264 common clock_settime sys_clock_settime32 265 common clock_gettime sys_clock_gettime32 266 common clock_getres sys_clock_getres_time32 267 common clock_nanosleep sys_clock_nanosleep_time32 268 common statfs64 sys_statfs64 269 common fstatfs64 sys_fstatfs64 270 common tgkill sys_tgkill 271 common utimes sys_utimes_time32 272 common fadvise64_64 sys_fadvise64_64 273 common vserver sys_ni_syscall 274 common mbind sys_mbind 275 common get_mempolicy sys_get_mempolicy 276 common set_mempolicy sys_set_mempolicy 277 common mq_open sys_mq_open 278 common mq_unlink sys_mq_unlink 279 common mq_timedsend sys_mq_timedsend_time32 280 common mq_timedreceive sys_mq_timedreceive_time32 281 common mq_notify sys_mq_notify 282 common mq_getsetattr sys_mq_getsetattr 283 common kexec_load sys_kexec_load 284 common waitid sys_waitid 286 common add_key sys_add_key 287 common request_key sys_request_key 288 common keyctl sys_keyctl 289 common ioprio_set sys_ioprio_set 290 common ioprio_get sys_ioprio_get 291 common inotify_init sys_inotify_init 292 common inotify_add_watch sys_inotify_add_watch 293 common inotify_rm_watch sys_inotify_rm_watch 294 common migrate_pages sys_ni_syscall 295 common openat sys_openat 296 common mkdirat sys_mkdirat 297 common mknodat sys_mknodat 298 common fchownat sys_fchownat 299 common futimesat sys_futimesat_time32 300 common fstatat64 sys_fstatat64 301 common unlinkat sys_unlinkat 302 common renameat sys_renameat 303 common linkat sys_linkat 304 common symlinkat sys_symlinkat 305 common readlinkat sys_readlinkat 306 common fchmodat sys_fchmodat 307 common faccessat sys_faccessat 308 common pselect6 sys_pselect6_time32 309 common ppoll sys_ppoll_time32 310 common unshare sys_unshare 311 common set_robust_list sys_set_robust_list 312 common get_robust_list sys_get_robust_list 313 common splice sys_splice 314 common sync_file_range sys_sync_file_range 315 common tee sys_tee 316 common vmsplice sys_vmsplice 317 common move_pages sys_move_pages 318 common getcpu sys_getcpu 319 common epoll_pwait sys_epoll_pwait 320 common utimensat sys_utimensat_time32 321 common signalfd sys_signalfd 322 common timerfd_create sys_timerfd_create 323 common eventfd sys_eventfd 324 common fallocate sys_fallocate 325 common semtimedop sys_semtimedop_time32 326 common timerfd_settime sys_timerfd_settime32 327 common timerfd_gettime sys_timerfd_gettime32 328 common semctl sys_old_semctl 329 common semget sys_semget 330 common semop sys_semop 331 common msgctl sys_old_msgctl 332 common msgget sys_msgget 333 common msgrcv sys_msgrcv 334 common msgsnd sys_msgsnd 335 common shmat sys_shmat 336 common shmctl sys_old_shmctl 337 common shmdt sys_shmdt 338 common shmget sys_shmget 339 common signalfd4 sys_signalfd4 340 common eventfd2 sys_eventfd2 341 common epoll_create1 sys_epoll_create1 342 common dup3 sys_dup3 343 common pipe2 sys_pipe2 344 common inotify_init1 sys_inotify_init1 345 common socket sys_socket 346 common socketpair sys_socketpair 347 common bind sys_bind 348 common listen sys_listen 349 common accept sys_accept 350 common connect sys_connect 351 common getsockname sys_getsockname 352 common getpeername sys_getpeername 353 common sendto sys_sendto 354 common send sys_send 355 common recvfrom sys_recvfrom 356 common recv sys_recv 357 common setsockopt sys_setsockopt 358 common getsockopt sys_getsockopt 359 common shutdown sys_shutdown 360 common sendmsg sys_sendmsg 361 common recvmsg sys_recvmsg 362 common accept4 sys_accept4 363 common preadv sys_preadv 364 common pwritev sys_pwritev 365 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 366 common perf_event_open sys_perf_event_open 367 common recvmmsg sys_recvmmsg_time32 368 common fanotify_init sys_fanotify_init 369 common fanotify_mark sys_fanotify_mark 370 common prlimit64 sys_prlimit64 371 common name_to_handle_at sys_name_to_handle_at 372 common open_by_handle_at sys_open_by_handle_at 373 common clock_adjtime sys_clock_adjtime32 374 common syncfs sys_syncfs 375 common setns sys_setns 376 common sendmmsg sys_sendmmsg 377 common process_vm_readv sys_process_vm_readv 378 common process_vm_writev sys_process_vm_writev 379 common kcmp sys_kcmp 380 common finit_module sys_finit_module 381 common sched_setattr sys_sched_setattr 382 common sched_getattr sys_sched_getattr 383 common renameat2 sys_renameat2 384 common seccomp sys_seccomp 385 common getrandom sys_getrandom 386 common memfd_create sys_memfd_create 387 common bpf sys_bpf 388 common execveat sys_execveat 389 common userfaultfd sys_userfaultfd 390 common membarrier sys_membarrier 391 common mlock2 sys_mlock2 392 common copy_file_range sys_copy_file_range 393 common preadv2 sys_preadv2 394 common pwritev2 sys_pwritev2 395 common pkey_mprotect sys_pkey_mprotect 396 common pkey_alloc sys_pkey_alloc 397 common pkey_free sys_pkey_free 398 common statx sys_statx 399 common io_pgetevents sys_io_pgetevents_time32 400 common rseq sys_rseq 403 common clock_gettime64 sys_clock_gettime 404 common clock_settime64 sys_clock_settime 405 common clock_adjtime64 sys_clock_adjtime 406 common clock_getres_time64 sys_clock_getres 407 common clock_nanosleep_time64 sys_clock_nanosleep 408 common timer_gettime64 sys_timer_gettime 409 common timer_settime64 sys_timer_settime 410 common timerfd_gettime64 sys_timerfd_gettime 411 common timerfd_settime64 sys_timerfd_settime 412 common utimensat_time64 sys_utimensat 413 common pselect6_time64 sys_pselect6 414 common ppoll_time64 sys_ppoll 416 common io_pgetevents_time64 sys_io_pgetevents 417 common recvmmsg_time64 sys_recvmmsg 418 common mq_timedsend_time64 sys_mq_timedsend 419 common mq_timedreceive_time64 sys_mq_timedreceive 420 common semtimedop_time64 sys_semtimedop 421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait 422 common futex_time64 sys_futex 423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # Xtensa # arch/xtensa/kernel/syscalls/syscall.tbl xtensa_syscall_tbl = """ 0 common spill sys_ni_syscall 1 common xtensa sys_ni_syscall 2 common available4 sys_ni_syscall 3 common available5 sys_ni_syscall 4 common available6 sys_ni_syscall 5 common available7 sys_ni_syscall 6 common available8 sys_ni_syscall 7 common available9 sys_ni_syscall 8 common open sys_open 9 common close sys_close 10 common dup sys_dup 11 common dup2 sys_dup2 12 common read sys_read 13 common write sys_write 14 common select sys_select 15 common lseek sys_lseek 16 common poll sys_poll 17 common _llseek sys_llseek 18 common epoll_wait sys_epoll_wait 19 common epoll_ctl sys_epoll_ctl 20 common epoll_create sys_epoll_create 21 common creat sys_creat 22 common truncate sys_truncate 23 common ftruncate sys_ftruncate 24 common readv sys_readv 25 common writev sys_writev 26 common fsync sys_fsync 27 common fdatasync sys_fdatasync 28 common truncate64 sys_truncate64 29 common ftruncate64 sys_ftruncate64 30 common pread64 sys_pread64 31 common pwrite64 sys_pwrite64 32 common link sys_link 33 common rename sys_rename 34 common symlink sys_symlink 35 common readlink sys_readlink 36 common mknod sys_mknod 37 common pipe sys_pipe 38 common unlink sys_unlink 39 common rmdir sys_rmdir 40 common mkdir sys_mkdir 41 common chdir sys_chdir 42 common fchdir sys_fchdir 43 common getcwd sys_getcwd 44 common chmod sys_chmod 45 common chown sys_chown 46 common stat sys_newstat 47 common stat64 sys_stat64 48 common lchown sys_lchown 49 common lstat sys_newlstat 50 common lstat64 sys_lstat64 51 common available51 sys_ni_syscall 52 common fchmod sys_fchmod 53 common fchown sys_fchown 54 common fstat sys_newfstat 55 common fstat64 sys_fstat64 56 common flock sys_flock 57 common access sys_access 58 common umask sys_umask 59 common getdents sys_getdents 60 common getdents64 sys_getdents64 61 common fcntl64 sys_fcntl64 62 common fallocate sys_fallocate 63 common fadvise64_64 xtensa_fadvise64_64 64 common utime sys_utime32 65 common utimes sys_utimes_time32 66 common ioctl sys_ioctl 67 common fcntl sys_fcntl 68 common setxattr sys_setxattr 69 common getxattr sys_getxattr 70 common listxattr sys_listxattr 71 common removexattr sys_removexattr 72 common lsetxattr sys_lsetxattr 73 common lgetxattr sys_lgetxattr 74 common llistxattr sys_llistxattr 75 common lremovexattr sys_lremovexattr 76 common fsetxattr sys_fsetxattr 77 common fgetxattr sys_fgetxattr 78 common flistxattr sys_flistxattr 79 common fremovexattr sys_fremovexattr 80 common mmap2 sys_mmap_pgoff 81 common munmap sys_munmap 82 common mprotect sys_mprotect 83 common brk sys_brk 84 common mlock sys_mlock 85 common munlock sys_munlock 86 common mlockall sys_mlockall 87 common munlockall sys_munlockall 88 common mremap sys_mremap 89 common msync sys_msync 90 common mincore sys_mincore 91 common madvise sys_madvise 92 common shmget sys_shmget 93 common shmat xtensa_shmat 94 common shmctl sys_old_shmctl 95 common shmdt sys_shmdt 96 common socket sys_socket 97 common setsockopt sys_setsockopt 98 common getsockopt sys_getsockopt 99 common shutdown sys_shutdown 100 common bind sys_bind 101 common connect sys_connect 102 common listen sys_listen 103 common accept sys_accept 104 common getsockname sys_getsockname 105 common getpeername sys_getpeername 106 common sendmsg sys_sendmsg 107 common recvmsg sys_recvmsg 108 common send sys_send 109 common recv sys_recv 110 common sendto sys_sendto 111 common recvfrom sys_recvfrom 112 common socketpair sys_socketpair 113 common sendfile sys_sendfile 114 common sendfile64 sys_sendfile64 115 common sendmmsg sys_sendmmsg 116 common clone sys_clone 117 common execve sys_execve 118 common exit sys_exit 119 common exit_group sys_exit_group 120 common getpid sys_getpid 121 common wait4 sys_wait4 122 common waitid sys_waitid 123 common kill sys_kill 124 common tkill sys_tkill 125 common tgkill sys_tgkill 126 common set_tid_address sys_set_tid_address 127 common gettid sys_gettid 128 common setsid sys_setsid 129 common getsid sys_getsid 130 common prctl sys_prctl 131 common personality sys_personality 132 common getpriority sys_getpriority 133 common setpriority sys_setpriority 134 common setitimer sys_setitimer 135 common getitimer sys_getitimer 136 common setuid sys_setuid 137 common getuid sys_getuid 138 common setgid sys_setgid 139 common getgid sys_getgid 140 common geteuid sys_geteuid 141 common getegid sys_getegid 142 common setreuid sys_setreuid 143 common setregid sys_setregid 144 common setresuid sys_setresuid 145 common getresuid sys_getresuid 146 common setresgid sys_setresgid 147 common getresgid sys_getresgid 148 common setpgid sys_setpgid 149 common getpgid sys_getpgid 150 common getppid sys_getppid 151 common getpgrp sys_getpgrp 152 common reserved152 sys_ni_syscall 153 common reserved153 sys_ni_syscall 154 common times sys_times 155 common acct sys_acct 156 common sched_setaffinity sys_sched_setaffinity 157 common sched_getaffinity sys_sched_getaffinity 158 common capget sys_capget 159 common capset sys_capset 160 common ptrace sys_ptrace 161 common semtimedop sys_semtimedop_time32 162 common semget sys_semget 163 common semop sys_semop 164 common semctl sys_old_semctl 165 common available165 sys_ni_syscall 166 common msgget sys_msgget 167 common msgsnd sys_msgsnd 168 common msgrcv sys_msgrcv 169 common msgctl sys_old_msgctl 170 common available170 sys_ni_syscall 171 common umount2 sys_umount 172 common mount sys_mount 173 common swapon sys_swapon 174 common chroot sys_chroot 175 common pivot_root sys_pivot_root 176 common umount sys_oldumount 177 common swapoff sys_swapoff 178 common sync sys_sync 179 common syncfs sys_syncfs 180 common setfsuid sys_setfsuid 181 common setfsgid sys_setfsgid 182 common sysfs sys_sysfs 183 common ustat sys_ustat 184 common statfs sys_statfs 185 common fstatfs sys_fstatfs 186 common statfs64 sys_statfs64 187 common fstatfs64 sys_fstatfs64 188 common setrlimit sys_setrlimit 189 common getrlimit sys_getrlimit 190 common getrusage sys_getrusage 191 common futex sys_futex_time32 192 common gettimeofday sys_gettimeofday 193 common settimeofday sys_settimeofday 194 common adjtimex sys_adjtimex_time32 195 common nanosleep sys_nanosleep_time32 196 common getgroups sys_getgroups 197 common setgroups sys_setgroups 198 common sethostname sys_sethostname 199 common setdomainname sys_setdomainname 200 common syslog sys_syslog 201 common vhangup sys_vhangup 202 common uselib sys_uselib 203 common reboot sys_reboot 204 common quotactl sys_quotactl 205 common nfsservctl sys_ni_syscall 206 common _sysctl sys_ni_syscall 207 common bdflush sys_ni_syscall 208 common uname sys_newuname 209 common sysinfo sys_sysinfo 210 common init_module sys_init_module 211 common delete_module sys_delete_module 212 common sched_setparam sys_sched_setparam 213 common sched_getparam sys_sched_getparam 214 common sched_setscheduler sys_sched_setscheduler 215 common sched_getscheduler sys_sched_getscheduler 216 common sched_get_priority_max sys_sched_get_priority_max 217 common sched_get_priority_min sys_sched_get_priority_min 218 common sched_rr_get_interval sys_sched_rr_get_interval_time32 219 common sched_yield sys_sched_yield 222 common available222 sys_ni_syscall 223 common restart_syscall sys_restart_syscall 224 common sigaltstack sys_sigaltstack 225 common rt_sigreturn xtensa_rt_sigreturn 226 common rt_sigaction sys_rt_sigaction 227 common rt_sigprocmask sys_rt_sigprocmask 228 common rt_sigpending sys_rt_sigpending 229 common rt_sigtimedwait sys_rt_sigtimedwait_time32 230 common rt_sigqueueinfo sys_rt_sigqueueinfo 231 common rt_sigsuspend sys_rt_sigsuspend 232 common mq_open sys_mq_open 233 common mq_unlink sys_mq_unlink 234 common mq_timedsend sys_mq_timedsend_time32 235 common mq_timedreceive sys_mq_timedreceive_time32 236 common mq_notify sys_mq_notify 237 common mq_getsetattr sys_mq_getsetattr 238 common available238 sys_ni_syscall 239 common io_setup sys_io_setup 240 common io_destroy sys_io_destroy 241 common io_submit sys_io_submit 242 common io_getevents sys_io_getevents_time32 243 common io_cancel sys_io_cancel 244 common clock_settime sys_clock_settime32 245 common clock_gettime sys_clock_gettime32 246 common clock_getres sys_clock_getres_time32 247 common clock_nanosleep sys_clock_nanosleep_time32 248 common timer_create sys_timer_create 249 common timer_delete sys_timer_delete 250 common timer_settime sys_timer_settime32 251 common timer_gettime sys_timer_gettime32 252 common timer_getoverrun sys_timer_getoverrun 253 common reserved253 sys_ni_syscall 254 common lookup_dcookie sys_ni_syscall 255 common available255 sys_ni_syscall 256 common add_key sys_add_key 257 common request_key sys_request_key 258 common keyctl sys_keyctl 259 common available259 sys_ni_syscall 260 common readahead sys_readahead 261 common remap_file_pages sys_remap_file_pages 262 common migrate_pages sys_migrate_pages 263 common mbind sys_mbind 264 common get_mempolicy sys_get_mempolicy 265 common set_mempolicy sys_set_mempolicy 266 common unshare sys_unshare 267 common move_pages sys_move_pages 268 common splice sys_splice 269 common tee sys_tee 270 common vmsplice sys_vmsplice 271 common available271 sys_ni_syscall 272 common pselect6 sys_pselect6_time32 273 common ppoll sys_ppoll_time32 274 common epoll_pwait sys_epoll_pwait 275 common epoll_create1 sys_epoll_create1 276 common inotify_init sys_inotify_init 277 common inotify_add_watch sys_inotify_add_watch 278 common inotify_rm_watch sys_inotify_rm_watch 279 common inotify_init1 sys_inotify_init1 280 common getcpu sys_getcpu 281 common kexec_load sys_ni_syscall 282 common ioprio_set sys_ioprio_set 283 common ioprio_get sys_ioprio_get 284 common set_robust_list sys_set_robust_list 285 common get_robust_list sys_get_robust_list 286 common available286 sys_ni_syscall 287 common available287 sys_ni_syscall 288 common openat sys_openat 289 common mkdirat sys_mkdirat 290 common mknodat sys_mknodat 291 common unlinkat sys_unlinkat 292 common renameat sys_renameat 293 common linkat sys_linkat 294 common symlinkat sys_symlinkat 295 common readlinkat sys_readlinkat 296 common utimensat sys_utimensat_time32 297 common fchownat sys_fchownat 298 common futimesat sys_futimesat_time32 299 common fstatat64 sys_fstatat64 300 common fchmodat sys_fchmodat 301 common faccessat sys_faccessat 302 common available302 sys_ni_syscall 303 common available303 sys_ni_syscall 304 common signalfd sys_signalfd 306 common eventfd sys_eventfd 307 common recvmmsg sys_recvmmsg_time32 308 common setns sys_setns 309 common signalfd4 sys_signalfd4 310 common dup3 sys_dup3 311 common pipe2 sys_pipe2 312 common timerfd_create sys_timerfd_create 313 common timerfd_settime sys_timerfd_settime32 314 common timerfd_gettime sys_timerfd_gettime32 315 common available315 sys_ni_syscall 316 common eventfd2 sys_eventfd2 317 common preadv sys_preadv 318 common pwritev sys_pwritev 319 common available319 sys_ni_syscall 320 common fanotify_init sys_fanotify_init 321 common fanotify_mark sys_fanotify_mark 322 common process_vm_readv sys_process_vm_readv 323 common process_vm_writev sys_process_vm_writev 324 common name_to_handle_at sys_name_to_handle_at 325 common open_by_handle_at sys_open_by_handle_at 326 common sync_file_range2 sys_sync_file_range2 327 common perf_event_open sys_perf_event_open 328 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 329 common clock_adjtime sys_clock_adjtime32 330 common prlimit64 sys_prlimit64 331 common kcmp sys_kcmp 332 common finit_module sys_finit_module 333 common accept4 sys_accept4 334 common sched_setattr sys_sched_setattr 335 common sched_getattr sys_sched_getattr 336 common renameat2 sys_renameat2 337 common seccomp sys_seccomp 338 common getrandom sys_getrandom 339 common memfd_create sys_memfd_create 340 common bpf sys_bpf 341 common execveat sys_execveat 342 common userfaultfd sys_userfaultfd 343 common membarrier sys_membarrier 344 common mlock2 sys_mlock2 345 common copy_file_range sys_copy_file_range 346 common preadv2 sys_preadv2 347 common pwritev2 sys_pwritev2 348 common pkey_mprotect sys_pkey_mprotect 349 common pkey_alloc sys_pkey_alloc 350 common pkey_free sys_pkey_free 351 common statx sys_statx 352 common rseq sys_rseq 403 common clock_gettime64 sys_clock_gettime 404 common clock_settime64 sys_clock_settime 405 common clock_adjtime64 sys_clock_adjtime 406 common clock_getres_time64 sys_clock_getres 407 common clock_nanosleep_time64 sys_clock_nanosleep 408 common timer_gettime64 sys_timer_gettime 409 common timer_settime64 sys_timer_settime 410 common timerfd_gettime64 sys_timerfd_gettime 411 common timerfd_settime64 sys_timerfd_settime 412 common utimensat_time64 sys_utimensat 413 common pselect6_time64 sys_pselect6 414 common ppoll_time64 sys_ppoll 416 common io_pgetevents_time64 sys_io_pgetevents 417 common recvmmsg_time64 sys_recvmmsg 418 common mq_timedsend_time64 sys_mq_timedsend 419 common mq_timedreceive_time64 sys_mq_timedreceive 420 common semtimedop_time64 sys_semtimedop 421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait 422 common futex_time64 sys_futex 423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval 424 common pidfd_send_signal sys_pidfd_send_signal 425 common io_uring_setup sys_io_uring_setup 426 common io_uring_enter sys_io_uring_enter 427 common io_uring_register sys_io_uring_register 428 common open_tree sys_open_tree 429 common move_mount sys_move_mount 430 common fsopen sys_fsopen 431 common fsconfig sys_fsconfig 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd 439 common faccessat2 sys_faccessat2 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr 443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self 448 common process_mrelease sys_process_mrelease 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue 457 common statmount sys_statmount 458 common listmount sys_listmount 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal 463 common setxattrat sys_setxattrat 464 common getxattrat sys_getxattrat 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr 468 common file_getattr sys_file_getattr 469 common file_setattr sys_file_setattr 470 common listns sys_listns """ # CRIS # [How to make] # cd /path/to/linux-4.16.18/ # awk '/sys_call_table:/,/^$/' arch/cris/arch-v10/kernel/entry.S \ # | grep -o "\.long \w*" | nl -v0 | awk '{print $1" cris "substr($3,5)" "$3}' |column -t cris_syscall_tbl = """ 0 cris restart_syscall sys_restart_syscall 1 cris exit sys_exit 2 cris fork sys_fork 3 cris read sys_read 4 cris write sys_write 5 cris open sys_open 6 cris close sys_close 7 cris waitpid sys_waitpid 8 cris creat sys_creat 9 cris link sys_link 10 cris unlink sys_unlink 11 cris execve sys_execve 12 cris chdir sys_chdir 13 cris time sys_time 14 cris mknod sys_mknod 15 cris chmod sys_chmod 16 cris lchown16 sys_lchown16 17 cris ni_syscall sys_ni_syscall 18 cris stat sys_stat 19 cris lseek sys_lseek 20 cris getpid sys_getpid 21 cris mount sys_mount 22 cris oldumount sys_oldumount 23 cris setuid16 sys_setuid16 24 cris getuid16 sys_getuid16 25 cris stime sys_stime 26 cris ptrace sys_ptrace 27 cris alarm sys_alarm 28 cris fstat sys_fstat 29 cris pause sys_pause 30 cris utime sys_utime 31 cris ni_syscall sys_ni_syscall 32 cris ni_syscall sys_ni_syscall 33 cris access sys_access 34 cris nice sys_nice 35 cris ni_syscall sys_ni_syscall 36 cris sync sys_sync 37 cris kill sys_kill 38 cris rename sys_rename 39 cris mkdir sys_mkdir 40 cris rmdir sys_rmdir 41 cris dup sys_dup 42 cris pipe sys_pipe 43 cris times sys_times 44 cris ni_syscall sys_ni_syscall 45 cris brk sys_brk 46 cris setgid16 sys_setgid16 47 cris getgid16 sys_getgid16 48 cris signal sys_signal 49 cris geteuid16 sys_geteuid16 50 cris getegid16 sys_getegid16 51 cris acct sys_acct 52 cris umount sys_umount 53 cris ni_syscall sys_ni_syscall 54 cris ioctl sys_ioctl 55 cris fcntl sys_fcntl 56 cris ni_syscall sys_ni_syscall 57 cris setpgid sys_setpgid 58 cris ni_syscall sys_ni_syscall 59 cris ni_syscall sys_ni_syscall 60 cris umask sys_umask 61 cris chroot sys_chroot 62 cris ustat sys_ustat 63 cris dup2 sys_dup2 64 cris getppid sys_getppid 65 cris getpgrp sys_getpgrp 66 cris setsid sys_setsid 67 cris sigaction sys_sigaction 68 cris sgetmask sys_sgetmask 69 cris ssetmask sys_ssetmask 70 cris setreuid16 sys_setreuid16 71 cris setregid16 sys_setregid16 72 cris sigsuspend sys_sigsuspend 73 cris sigpending sys_sigpending 74 cris sethostname sys_sethostname 75 cris setrlimit sys_setrlimit 76 cris old_getrlimit sys_old_getrlimit 77 cris getrusage sys_getrusage 78 cris gettimeofday sys_gettimeofday 79 cris settimeofday sys_settimeofday 80 cris getgroups16 sys_getgroups16 81 cris setgroups16 sys_setgroups16 82 cris select sys_select 83 cris symlink sys_symlink 84 cris lstat sys_lstat 85 cris readlink sys_readlink 86 cris uselib sys_uselib 87 cris swapon sys_swapon 88 cris reboot sys_reboot 89 cris old_readdir sys_old_readdir 90 cris old_mmap sys_old_mmap 91 cris munmap sys_munmap 92 cris truncate sys_truncate 93 cris ftruncate sys_ftruncate 94 cris fchmod sys_fchmod 95 cris fchown16 sys_fchown16 96 cris getpriority sys_getpriority 97 cris setpriority sys_setpriority 98 cris ni_syscall sys_ni_syscall 99 cris statfs sys_statfs 100 cris fstatfs sys_fstatfs 101 cris ni_syscall sys_ni_syscall 102 cris socketcall sys_socketcall 103 cris syslog sys_syslog 104 cris setitimer sys_setitimer 105 cris getitimer sys_getitimer 106 cris newstat sys_newstat 107 cris newlstat sys_newlstat 108 cris newfstat sys_newfstat 109 cris ni_syscall sys_ni_syscall 110 cris ni_syscall sys_ni_syscall 111 cris vhangup sys_vhangup 112 cris ni_syscall sys_ni_syscall 113 cris ni_syscall sys_ni_syscall 114 cris wait4 sys_wait4 115 cris swapoff sys_swapoff 116 cris sysinfo sys_sysinfo 117 cris ipc sys_ipc 118 cris fsync sys_fsync 119 cris sigreturn sys_sigreturn 120 cris clone sys_clone 121 cris setdomainname sys_setdomainname 122 cris newuname sys_newuname 123 cris ni_syscall sys_ni_syscall 124 cris adjtimex sys_adjtimex 125 cris mprotect sys_mprotect 126 cris sigprocmask sys_sigprocmask 127 cris ni_syscall sys_ni_syscall 128 cris init_module sys_init_module 129 cris delete_module sys_delete_module 130 cris ni_syscall sys_ni_syscall 131 cris quotactl sys_quotactl 132 cris getpgid sys_getpgid 133 cris fchdir sys_fchdir 134 cris bdflush sys_bdflush 135 cris sysfs sys_sysfs 136 cris personality sys_personality 137 cris ni_syscall sys_ni_syscall 138 cris setfsuid16 sys_setfsuid16 139 cris setfsgid16 sys_setfsgid16 140 cris llseek sys_llseek 141 cris getdents sys_getdents 142 cris select sys_select 143 cris flock sys_flock 144 cris msync sys_msync 145 cris readv sys_readv 146 cris writev sys_writev 147 cris getsid sys_getsid 148 cris fdatasync sys_fdatasync 149 cris sysctl sys_sysctl 150 cris mlock sys_mlock 151 cris munlock sys_munlock 152 cris mlockall sys_mlockall 153 cris munlockall sys_munlockall 154 cris sched_setparam sys_sched_setparam 155 cris sched_getparam sys_sched_getparam 156 cris sched_setscheduler sys_sched_setscheduler 157 cris sched_getscheduler sys_sched_getscheduler 158 cris sched_yield sys_sched_yield 159 cris sched_get_priority_max sys_sched_get_priority_max 160 cris sched_get_priority_min sys_sched_get_priority_min 161 cris sched_rr_get_interval sys_sched_rr_get_interval 162 cris nanosleep sys_nanosleep 163 cris mremap sys_mremap 164 cris setresuid16 sys_setresuid16 165 cris getresuid16 sys_getresuid16 166 cris ni_syscall sys_ni_syscall 167 cris ni_syscall sys_ni_syscall 168 cris poll sys_poll 169 cris ni_syscall sys_ni_syscall 170 cris setresgid16 sys_setresgid16 171 cris getresgid16 sys_getresgid16 172 cris prctl sys_prctl 173 cris rt_sigreturn sys_rt_sigreturn 174 cris rt_sigaction sys_rt_sigaction 175 cris rt_sigprocmask sys_rt_sigprocmask 176 cris rt_sigpending sys_rt_sigpending 177 cris rt_sigtimedwait sys_rt_sigtimedwait 178 cris rt_sigqueueinfo sys_rt_sigqueueinfo 179 cris rt_sigsuspend sys_rt_sigsuspend 180 cris pread64 sys_pread64 181 cris pwrite64 sys_pwrite64 182 cris chown16 sys_chown16 183 cris getcwd sys_getcwd 184 cris capget sys_capget 185 cris capset sys_capset 186 cris sigaltstack sys_sigaltstack 187 cris sendfile sys_sendfile 188 cris ni_syscall sys_ni_syscall 189 cris ni_syscall sys_ni_syscall 190 cris vfork sys_vfork 191 cris getrlimit sys_getrlimit 192 cris mmap2 sys_mmap2 193 cris truncate64 sys_truncate64 194 cris ftruncate64 sys_ftruncate64 195 cris stat64 sys_stat64 196 cris lstat64 sys_lstat64 197 cris fstat64 sys_fstat64 198 cris lchown sys_lchown 199 cris getuid sys_getuid 200 cris getgid sys_getgid 201 cris geteuid sys_geteuid 202 cris getegid sys_getegid 203 cris setreuid sys_setreuid 204 cris setregid sys_setregid 205 cris getgroups sys_getgroups 206 cris setgroups sys_setgroups 207 cris fchown sys_fchown 208 cris setresuid sys_setresuid 209 cris getresuid sys_getresuid 210 cris setresgid sys_setresgid 211 cris getresgid sys_getresgid 212 cris chown sys_chown 213 cris setuid sys_setuid 214 cris setgid sys_setgid 215 cris setfsuid sys_setfsuid 216 cris setfsgid sys_setfsgid 217 cris pivot_root sys_pivot_root 218 cris mincore sys_mincore 219 cris madvise sys_madvise 220 cris getdents64 sys_getdents64 221 cris fcntl64 sys_fcntl64 222 cris ni_syscall sys_ni_syscall 223 cris ni_syscall sys_ni_syscall 224 cris gettid sys_gettid 225 cris readahead sys_readahead 226 cris setxattr sys_setxattr 227 cris lsetxattr sys_lsetxattr 228 cris fsetxattr sys_fsetxattr 229 cris getxattr sys_getxattr 230 cris lgetxattr sys_lgetxattr 231 cris fgetxattr sys_fgetxattr 232 cris listxattr sys_listxattr 233 cris llistxattr sys_llistxattr 234 cris flistxattr sys_flistxattr 235 cris removexattr sys_removexattr 236 cris lremovexattr sys_lremovexattr 237 cris fremovexattr sys_fremovexattr 238 cris tkill sys_tkill 239 cris sendfile64 sys_sendfile64 240 cris futex sys_futex 241 cris sched_setaffinity sys_sched_setaffinity 242 cris sched_getaffinity sys_sched_getaffinity 243 cris ni_syscall sys_ni_syscall 244 cris ni_syscall sys_ni_syscall 245 cris io_setup sys_io_setup 246 cris io_destroy sys_io_destroy 247 cris io_getevents sys_io_getevents 248 cris io_submit sys_io_submit 249 cris io_cancel sys_io_cancel 250 cris fadvise64 sys_fadvise64 251 cris ni_syscall sys_ni_syscall 252 cris exit_group sys_exit_group 253 cris lookup_dcookie sys_lookup_dcookie 254 cris epoll_create sys_epoll_create 255 cris epoll_ctl sys_epoll_ctl 256 cris epoll_wait sys_epoll_wait 257 cris remap_file_pages sys_remap_file_pages 258 cris set_tid_address sys_set_tid_address 259 cris timer_create sys_timer_create 260 cris timer_settime sys_timer_settime 261 cris timer_gettime sys_timer_gettime 262 cris timer_getoverrun sys_timer_getoverrun 263 cris timer_delete sys_timer_delete 264 cris clock_settime sys_clock_settime 265 cris clock_gettime sys_clock_gettime 266 cris clock_getres sys_clock_getres 267 cris clock_nanosleep sys_clock_nanosleep 268 cris statfs64 sys_statfs64 269 cris fstatfs64 sys_fstatfs64 270 cris tgkill sys_tgkill 271 cris utimes sys_utimes 272 cris fadvise64_64 sys_fadvise64_64 273 cris ni_syscall sys_ni_syscall 274 cris ni_syscall sys_ni_syscall 275 cris ni_syscall sys_ni_syscall 276 cris ni_syscall sys_ni_syscall 277 cris mq_open sys_mq_open 278 cris mq_unlink sys_mq_unlink 279 cris mq_timedsend sys_mq_timedsend 280 cris mq_timedreceive sys_mq_timedreceive 281 cris mq_notify sys_mq_notify 282 cris mq_getsetattr sys_mq_getsetattr 283 cris ni_syscall sys_ni_syscall 284 cris waitid sys_waitid 285 cris ni_syscall sys_ni_syscall 286 cris add_key sys_add_key 287 cris request_key sys_request_key 288 cris keyctl sys_keyctl 289 cris ioprio_set sys_ioprio_set 290 cris ioprio_get sys_ioprio_get 291 cris inotify_init sys_inotify_init 292 cris inotify_add_watch sys_inotify_add_watch 293 cris inotify_rm_watch sys_inotify_rm_watch 294 cris migrate_pages sys_migrate_pages 295 cris openat sys_openat 296 cris mkdirat sys_mkdirat 297 cris mknodat sys_mknodat 298 cris fchownat sys_fchownat 299 cris futimesat sys_futimesat 300 cris fstatat64 sys_fstatat64 301 cris unlinkat sys_unlinkat 302 cris renameat sys_renameat 303 cris linkat sys_linkat 304 cris symlinkat sys_symlinkat 305 cris readlinkat sys_readlinkat 306 cris fchmodat sys_fchmodat 307 cris faccessat sys_faccessat 308 cris pselect6 sys_pselect6 309 cris ppoll sys_ppoll 310 cris unshare sys_unshare 311 cris set_robust_list sys_set_robust_list 312 cris get_robust_list sys_get_robust_list 313 cris splice sys_splice 314 cris sync_file_range sys_sync_file_range 315 cris tee sys_tee 316 cris vmsplice sys_vmsplice 317 cris move_pages sys_move_pages 318 cris getcpu sys_getcpu 319 cris epoll_pwait sys_epoll_pwait 320 cris utimensat sys_utimensat 321 cris signalfd sys_signalfd 322 cris timerfd_create sys_timerfd_create 323 cris eventfd sys_eventfd 324 cris fallocate sys_fallocate 325 cris timerfd_settime sys_timerfd_settime 326 cris timerfd_gettime sys_timerfd_gettime 327 cris signalfd4 sys_signalfd4 328 cris eventfd2 sys_eventfd2 329 cris epoll_create1 sys_epoll_create1 330 cris dup3 sys_dup3 331 cris pipe2 sys_pipe2 332 cris inotify_init1 sys_inotify_init1 333 cris preadv sys_preadv 334 cris pwritev sys_pwritev 335 cris setns sys_setns 336 cris name_to_handle_at sys_name_to_handle_at 337 cris open_by_handle_at sys_open_by_handle_at 338 cris rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 339 cris perf_event_open sys_perf_event_open 340 cris recvmmsg sys_recvmmsg 341 cris accept4 sys_accept4 342 cris fanotify_init sys_fanotify_init 343 cris fanotify_mark sys_fanotify_mark 344 cris prlimit64 sys_prlimit64 345 cris clock_adjtime sys_clock_adjtime 346 cris syncfs sys_syncfs 347 cris sendmmsg sys_sendmmsg 348 cris process_vm_readv sys_process_vm_readv 349 cris process_vm_writev sys_process_vm_writev 350 cris kcmp sys_kcmp 351 cris finit_module sys_finit_module 352 cris sched_setattr sys_sched_setattr 353 cris sched_getattr sys_sched_getattr 354 cris renameat2 sys_renameat2 355 cris seccomp sys_seccomp 356 cris getrandom sys_getrandom 357 cris memfd_create sys_memfd_create 358 cris bpf sys_bpf 359 cris execveat sys_execveat """ # Loongarch # # [How to make for 6.10.14] # cd /path/to/linux-6.10.14/ # gcc -I `pwd`/include/uapi/ -E -D__SYSCALL=SYSCALL arch/loongarch/include/uapi/asm/unistd.h | grep ^SYSCALL \ # | sed -e 's/SYSCALL(//;s/[,)]//g' > /tmp/a # grep -oP "__NR\S+\s+\d+$" include/uapi/asm-generic/unistd.h | grep -v __NR_sync_file_range2 > /tmp/b # join -2 2 -o 1.1,1.10,2.1,1.2 -e loongarch /tmp/a /tmp/b | sed -e 's/\(__NR_\|__NR3264_\)//g' | column -t loongarch_syscall_tbl = """ 0 loongarch io_setup sys_io_setup 1 loongarch io_destroy sys_io_destroy 2 loongarch io_submit sys_io_submit 3 loongarch io_cancel sys_io_cancel 4 loongarch io_getevents sys_io_getevents 5 loongarch setxattr sys_setxattr 6 loongarch lsetxattr sys_lsetxattr 7 loongarch fsetxattr sys_fsetxattr 8 loongarch getxattr sys_getxattr 9 loongarch lgetxattr sys_lgetxattr 10 loongarch fgetxattr sys_fgetxattr 11 loongarch listxattr sys_listxattr 12 loongarch llistxattr sys_llistxattr 13 loongarch flistxattr sys_flistxattr 14 loongarch removexattr sys_removexattr 15 loongarch lremovexattr sys_lremovexattr 16 loongarch fremovexattr sys_fremovexattr 17 loongarch getcwd sys_getcwd 18 loongarch lookup_dcookie sys_ni_syscall 19 loongarch eventfd2 sys_eventfd2 20 loongarch epoll_create1 sys_epoll_create1 21 loongarch epoll_ctl sys_epoll_ctl 22 loongarch epoll_pwait sys_epoll_pwait 23 loongarch dup sys_dup 24 loongarch dup3 sys_dup3 25 loongarch fcntl sys_fcntl 26 loongarch inotify_init1 sys_inotify_init1 27 loongarch inotify_add_watch sys_inotify_add_watch 28 loongarch inotify_rm_watch sys_inotify_rm_watch 29 loongarch ioctl sys_ioctl 30 loongarch ioprio_set sys_ioprio_set 31 loongarch ioprio_get sys_ioprio_get 32 loongarch flock sys_flock 33 loongarch mknodat sys_mknodat 34 loongarch mkdirat sys_mkdirat 35 loongarch unlinkat sys_unlinkat 36 loongarch symlinkat sys_symlinkat 37 loongarch linkat sys_linkat 39 loongarch umount2 sys_umount 40 loongarch mount sys_mount 41 loongarch pivot_root sys_pivot_root 42 loongarch nfsservctl sys_ni_syscall 43 loongarch statfs sys_statfs 44 loongarch fstatfs sys_fstatfs 45 loongarch truncate sys_truncate 46 loongarch ftruncate sys_ftruncate 47 loongarch fallocate sys_fallocate 48 loongarch faccessat sys_faccessat 49 loongarch chdir sys_chdir 50 loongarch fchdir sys_fchdir 51 loongarch chroot sys_chroot 52 loongarch fchmod sys_fchmod 53 loongarch fchmodat sys_fchmodat 54 loongarch fchownat sys_fchownat 55 loongarch fchown sys_fchown 56 loongarch openat sys_openat 57 loongarch close sys_close 58 loongarch vhangup sys_vhangup 59 loongarch pipe2 sys_pipe2 60 loongarch quotactl sys_quotactl 61 loongarch getdents64 sys_getdents64 62 loongarch lseek sys_lseek 63 loongarch read sys_read 64 loongarch write sys_write 65 loongarch readv sys_readv 66 loongarch writev sys_writev 67 loongarch pread64 sys_pread64 68 loongarch pwrite64 sys_pwrite64 69 loongarch preadv sys_preadv 70 loongarch pwritev sys_pwritev 71 loongarch sendfile sys_sendfile64 72 loongarch pselect6 sys_pselect6 73 loongarch ppoll sys_ppoll 74 loongarch signalfd4 sys_signalfd4 75 loongarch vmsplice sys_vmsplice 76 loongarch splice sys_splice 77 loongarch tee sys_tee 78 loongarch readlinkat sys_readlinkat 81 loongarch sync sys_sync 82 loongarch fsync sys_fsync 83 loongarch fdatasync sys_fdatasync 84 loongarch sync_file_range sys_sync_file_range 85 loongarch timerfd_create sys_timerfd_create 86 loongarch timerfd_settime sys_timerfd_settime 87 loongarch timerfd_gettime sys_timerfd_gettime 88 loongarch utimensat sys_utimensat 89 loongarch acct sys_acct 90 loongarch capget sys_capget 91 loongarch capset sys_capset 92 loongarch personality sys_personality 93 loongarch exit sys_exit 94 loongarch exit_group sys_exit_group 95 loongarch waitid sys_waitid 96 loongarch set_tid_address sys_set_tid_address 97 loongarch unshare sys_unshare 98 loongarch futex sys_futex 99 loongarch set_robust_list sys_set_robust_list 100 loongarch get_robust_list sys_get_robust_list 101 loongarch nanosleep sys_nanosleep 102 loongarch getitimer sys_getitimer 103 loongarch setitimer sys_setitimer 104 loongarch kexec_load sys_kexec_load 105 loongarch init_module sys_init_module 106 loongarch delete_module sys_delete_module 107 loongarch timer_create sys_timer_create 108 loongarch timer_gettime sys_timer_gettime 109 loongarch timer_getoverrun sys_timer_getoverrun 110 loongarch timer_settime sys_timer_settime 111 loongarch timer_delete sys_timer_delete 112 loongarch clock_settime sys_clock_settime 113 loongarch clock_gettime sys_clock_gettime 114 loongarch clock_getres sys_clock_getres 115 loongarch clock_nanosleep sys_clock_nanosleep 116 loongarch syslog sys_syslog 117 loongarch ptrace sys_ptrace 118 loongarch sched_setparam sys_sched_setparam 119 loongarch sched_setscheduler sys_sched_setscheduler 120 loongarch sched_getscheduler sys_sched_getscheduler 121 loongarch sched_getparam sys_sched_getparam 122 loongarch sched_setaffinity sys_sched_setaffinity 123 loongarch sched_getaffinity sys_sched_getaffinity 124 loongarch sched_yield sys_sched_yield 125 loongarch sched_get_priority_max sys_sched_get_priority_max 126 loongarch sched_get_priority_min sys_sched_get_priority_min 127 loongarch sched_rr_get_interval sys_sched_rr_get_interval 128 loongarch restart_syscall sys_restart_syscall 129 loongarch kill sys_kill 130 loongarch tkill sys_tkill 131 loongarch tgkill sys_tgkill 132 loongarch sigaltstack sys_sigaltstack 133 loongarch rt_sigsuspend sys_rt_sigsuspend 134 loongarch rt_sigaction sys_rt_sigaction 135 loongarch rt_sigprocmask sys_rt_sigprocmask 136 loongarch rt_sigpending sys_rt_sigpending 137 loongarch rt_sigtimedwait sys_rt_sigtimedwait 138 loongarch rt_sigqueueinfo sys_rt_sigqueueinfo 139 loongarch rt_sigreturn sys_rt_sigreturn 140 loongarch setpriority sys_setpriority 141 loongarch getpriority sys_getpriority 142 loongarch reboot sys_reboot 143 loongarch setregid sys_setregid 144 loongarch setgid sys_setgid 145 loongarch setreuid sys_setreuid 146 loongarch setuid sys_setuid 147 loongarch setresuid sys_setresuid 148 loongarch getresuid sys_getresuid 149 loongarch setresgid sys_setresgid 150 loongarch getresgid sys_getresgid 151 loongarch setfsuid sys_setfsuid 152 loongarch setfsgid sys_setfsgid 153 loongarch times sys_times 154 loongarch setpgid sys_setpgid 155 loongarch getpgid sys_getpgid 156 loongarch getsid sys_getsid 157 loongarch setsid sys_setsid 158 loongarch getgroups sys_getgroups 159 loongarch setgroups sys_setgroups 160 loongarch uname sys_newuname 161 loongarch sethostname sys_sethostname 162 loongarch setdomainname sys_setdomainname 165 loongarch getrusage sys_getrusage 166 loongarch umask sys_umask 167 loongarch prctl sys_prctl 168 loongarch getcpu sys_getcpu 169 loongarch gettimeofday sys_gettimeofday 170 loongarch settimeofday sys_settimeofday 171 loongarch adjtimex sys_adjtimex 172 loongarch getpid sys_getpid 173 loongarch getppid sys_getppid 174 loongarch getuid sys_getuid 175 loongarch geteuid sys_geteuid 176 loongarch getgid sys_getgid 177 loongarch getegid sys_getegid 178 loongarch gettid sys_gettid 179 loongarch sysinfo sys_sysinfo 180 loongarch mq_open sys_mq_open 181 loongarch mq_unlink sys_mq_unlink 182 loongarch mq_timedsend sys_mq_timedsend 183 loongarch mq_timedreceive sys_mq_timedreceive 184 loongarch mq_notify sys_mq_notify 185 loongarch mq_getsetattr sys_mq_getsetattr 186 loongarch msgget sys_msgget 187 loongarch msgctl sys_msgctl 188 loongarch msgrcv sys_msgrcv 189 loongarch msgsnd sys_msgsnd 190 loongarch semget sys_semget 191 loongarch semctl sys_semctl 192 loongarch semtimedop sys_semtimedop 193 loongarch semop sys_semop 194 loongarch shmget sys_shmget 195 loongarch shmctl sys_shmctl 196 loongarch shmat sys_shmat 197 loongarch shmdt sys_shmdt 198 loongarch socket sys_socket 199 loongarch socketpair sys_socketpair 200 loongarch bind sys_bind 201 loongarch listen sys_listen 202 loongarch accept sys_accept 203 loongarch connect sys_connect 204 loongarch getsockname sys_getsockname 205 loongarch getpeername sys_getpeername 206 loongarch sendto sys_sendto 207 loongarch recvfrom sys_recvfrom 208 loongarch setsockopt sys_setsockopt 209 loongarch getsockopt sys_getsockopt 210 loongarch shutdown sys_shutdown 211 loongarch sendmsg sys_sendmsg 212 loongarch recvmsg sys_recvmsg 213 loongarch readahead sys_readahead 214 loongarch brk sys_brk 215 loongarch munmap sys_munmap 216 loongarch mremap sys_mremap 217 loongarch add_key sys_add_key 218 loongarch request_key sys_request_key 219 loongarch keyctl sys_keyctl 220 loongarch clone sys_clone 221 loongarch execve sys_execve 222 loongarch mmap sys_mmap 223 loongarch fadvise64 sys_fadvise64_64 224 loongarch swapon sys_swapon 225 loongarch swapoff sys_swapoff 226 loongarch mprotect sys_mprotect 227 loongarch msync sys_msync 228 loongarch mlock sys_mlock 229 loongarch munlock sys_munlock 230 loongarch mlockall sys_mlockall 231 loongarch munlockall sys_munlockall 232 loongarch mincore sys_mincore 233 loongarch madvise sys_madvise 234 loongarch remap_file_pages sys_remap_file_pages 235 loongarch mbind sys_mbind 236 loongarch get_mempolicy sys_get_mempolicy 237 loongarch set_mempolicy sys_set_mempolicy 238 loongarch migrate_pages sys_migrate_pages 239 loongarch move_pages sys_move_pages 240 loongarch rt_tgsigqueueinfo sys_rt_tgsigqueueinfo 241 loongarch perf_event_open sys_perf_event_open 242 loongarch accept4 sys_accept4 243 loongarch recvmmsg sys_recvmmsg 260 loongarch wait4 sys_wait4 261 loongarch prlimit64 sys_prlimit64 262 loongarch fanotify_init sys_fanotify_init 263 loongarch fanotify_mark sys_fanotify_mark 264 loongarch name_to_handle_at sys_name_to_handle_at 265 loongarch open_by_handle_at sys_open_by_handle_at 266 loongarch clock_adjtime sys_clock_adjtime 267 loongarch syncfs sys_syncfs 268 loongarch setns sys_setns 269 loongarch sendmmsg sys_sendmmsg 270 loongarch process_vm_readv sys_process_vm_readv 271 loongarch process_vm_writev sys_process_vm_writev 272 loongarch kcmp sys_kcmp 273 loongarch finit_module sys_finit_module 274 loongarch sched_setattr sys_sched_setattr 275 loongarch sched_getattr sys_sched_getattr 276 loongarch renameat2 sys_renameat2 277 loongarch seccomp sys_seccomp 278 loongarch getrandom sys_getrandom 279 loongarch memfd_create sys_memfd_create 280 loongarch bpf sys_bpf 281 loongarch execveat sys_execveat 282 loongarch userfaultfd sys_userfaultfd 283 loongarch membarrier sys_membarrier 284 loongarch mlock2 sys_mlock2 285 loongarch copy_file_range sys_copy_file_range 286 loongarch preadv2 sys_preadv2 287 loongarch pwritev2 sys_pwritev2 288 loongarch pkey_mprotect sys_pkey_mprotect 289 loongarch pkey_alloc sys_pkey_alloc 290 loongarch pkey_free sys_pkey_free 291 loongarch statx sys_statx 292 loongarch io_pgetevents sys_io_pgetevents 293 loongarch rseq sys_rseq 294 loongarch kexec_file_load sys_kexec_file_load 424 loongarch pidfd_send_signal sys_pidfd_send_signal 425 loongarch io_uring_setup sys_io_uring_setup 426 loongarch io_uring_enter sys_io_uring_enter 427 loongarch io_uring_register sys_io_uring_register 428 loongarch open_tree sys_open_tree 429 loongarch move_mount sys_move_mount 430 loongarch fsopen sys_fsopen 431 loongarch fsconfig sys_fsconfig 432 loongarch fsmount sys_fsmount 433 loongarch fspick sys_fspick 434 loongarch pidfd_open sys_pidfd_open 435 loongarch clone3 sys_clone3 436 loongarch close_range sys_close_range 437 loongarch openat2 sys_openat2 438 loongarch pidfd_getfd sys_pidfd_getfd 439 loongarch faccessat2 sys_faccessat2 440 loongarch process_madvise sys_process_madvise 441 loongarch epoll_pwait2 sys_epoll_pwait2 442 loongarch mount_setattr sys_mount_setattr 443 loongarch quotactl_fd sys_quotactl_fd 444 loongarch landlock_create_ruleset sys_landlock_create_ruleset 445 loongarch landlock_add_rule sys_landlock_add_rule 446 loongarch landlock_restrict_self sys_landlock_restrict_self 448 loongarch process_mrelease sys_process_mrelease 449 loongarch futex_waitv sys_futex_waitv 450 loongarch set_mempolicy_home_node sys_set_mempolicy_home_node 451 loongarch cachestat sys_cachestat 452 loongarch fchmodat2 sys_fchmodat2 453 loongarch map_shadow_stack sys_map_shadow_stack 454 loongarch futex_wake sys_futex_wake 455 loongarch futex_wait sys_futex_wait 456 loongarch futex_requeue sys_futex_requeue 457 loongarch statmount sys_statmount 458 loongarch listmount sys_listmount 459 loongarch lsm_get_self_attr sys_lsm_get_self_attr 460 loongarch lsm_set_self_attr sys_lsm_set_self_attr 461 loongarch lsm_list_modules sys_lsm_list_modules 462 loongarch mseal sys_mseal 463 loongarch setxattrat sys_setxattrat 464 loongarch getxattrat sys_getxattrat 465 loongarch listxattrat sys_listxattrat 466 loongarch removexattrat sys_removexattrat 467 loongarch open_tree_attr sys_open_tree_attr """ # arc arc_syscall_tbl = arm64_syscall_tbl # csky csky_syscall_tbl = arm64_syscall_tbl # ARM/ARM64 OP-TEE (at secure world) # - core/include/tee/tee_svc.h # - core/include/tee/tee_svc_cryp.h # - core/include/tee/tee_svc_storage.h # - core/include/tee/svc_cache.h arm_OPTEE_syscall_list = [ [0x00, "syscall_sys_return", ["unsigned long ret"]], [0x01, "syscall_log", ["const void *buf", "size_t len"]], [0x02, "syscall_panic", ["unsigned long code"]], [0x03, "syscall_get_property", ["unsigned long prop_set", "unsigned long index", "void *name", "uint32_t *name_len", "void *buf", "uint32_t *blen", "uint32_t *prop_type"]], [0x04, "syscall_get_property_name_to_index", ["unsigned long prop_set", "void *name", "unsigned long name_len", "uint32_t *index"]], [0x05, "syscall_open_ta_session", ["const TEE_UUID *dest", "unsigned long cancel_req_to", "struct utee_params *params", "uint32_t *sess", "uint32_t *ret_orig"]], [0x06, "syscall_close_ta_session", ["unsigned long sess"]], [0x07, "syscall_invoke_ta_command", ["unsigned long sess", "unsigned long cancel_req_to", "unsigned long cmd_id", "struct utee_params *params", "uint32_t *ret_orig"]], [0x08, "syscall_check_access_rights", ["unsigned long flags", "const void *buf", "size_t len"]], [0x09, "syscall_get_cancellation_flag", ["uint32_t *cancel"]], [0x0a, "syscall_unmask_cancellation", ["uint32_t *old_mask"]], [0x0b, "syscall_mask_cancellation", ["uint32_t *old_mask"]], [0x0c, "syscall_wait", ["unsigned long timeout"]], [0x0d, "syscall_get_time", ["unsigned long cat", "TEE_Time *time"]], [0x0e, "syscall_set_ta_time", ["const TEE_Time *time"]], [0x0f, "syscall_cryp_state_alloc", ["unsigned long algo", "unsigned long op_mode", "unsigned long key1", "unsigned long key2", "uint32_t *state"]], [0x10, "syscall_cryp_state_copy", ["unsigned long dst", "unsigned long src"]], [0x11, "syscall_cryp_state_free", ["unsigned long state"]], [0x12, "syscall_hash_init", ["unsigned long state", "const void *iv", "size_t iv_len"]], [0x13, "syscall_hash_update", ["unsigned long state", "const void *chunk", "size_t chunk_size"]], [0x14, "syscall_hash_final", ["unsigned long state", "const void *chunk", "size_t chunk_size", "void *hash", "uint64_t *hash_len"]], [0x15, "syscall_cipher_init", ["unsigned long state", "const void *iv", "size_t iv_len"]], [0x16, "syscall_cipher_update", ["unsigned long state", "const void *src", "size_t src_len", "void *dest", "uint64_t *dest_len"]], [0x17, "syscall_cipher_final", ["unsigned long state", "const void *src", "size_t src_len", "void *dest", "uint64_t *dest_len"]], [0x18, "syscall_cryp_obj_get_info", ["unsigned long obj", "TEE_ObjectInfo *info"]], [0x19, "syscall_cryp_obj_restrict_usage", ["unsigned long obj", "unsigned long usage"]], [0x1a, "syscall_cryp_obj_get_attr", ["unsigned long obj", "unsigned long attr_id", "void *buffer", "uint64_t *size"]], [0x1b, "syscall_cryp_obj_alloc", ["unsigned long obj_type", "unsigned long max_key_size", "uint32_t *obj"]], [0x1c, "syscall_cryp_obj_close", ["unsigned long obj"]], [0x1d, "syscall_cryp_obj_reset", ["unsigned long obj"]], [0x1e, "syscall_cryp_obj_populate", ["unsigned long obj", "struct utee_attribute *attrs", "unsigned long attr_count"]], [0x1f, "syscall_cryp_obj_copy", ["unsigned long dst_obj", "unsigned long src_obj"]], [0x20, "syscall_cryp_derive_key", ["unsigned long state", "const struct utee_attribute *params", "unsigned long param_count", "unsigned long derived_key"]], [0x21, "syscall_cryp_random_number_generate", ["void *buf", "size_t blen"]], [0x22, "syscall_authenc_init", ["unsigned long state", "const void *nonce", "size_t nonce_len", "size_t tag_len", "size_t aad_len", "size_t payload_len"]], [0x23, "syscall_authenc_update_aad", ["unsigned long state", "const void *aad_data", "size_t aad_data_len"]], [0x24, "syscall_authenc_update_payload", ["unsigned long state", "const void *src_data", "size_t src_len", "void *dest_data", "uint64_t *dest_len"]], [0x25, "syscall_authenc_enc_final", ["unsigned long state", "const void *src_data", "size_t src_len", "void *dest_data", "uint64_t *dest_len", "void *tag", "uint64_t *tag_len"]], [0x26, "syscall_authenc_dec_final", ["unsigned long state", "const void *src_data", "size_t src_len", "void *dest_data", "uint64_t *dest_len", "const void *tag", "uint64_t *tag_len"]], [0x27, "syscall_asymm_operate", ["unsigned long state", "const struct utee_attribute *usr_params", "size_t num_params", "const void *src_data", "size_t src_len", "void *dest_data", "uint64_t *dest_len"]], [0x28, "syscall_asymm_verify", ["unsigned long state", "const struct utee_attribute *usr_params", "size_t num_params", "const void *data", "size_t data_len", "const void *sig", "size_t sig_len"]], [0x29, "syscall_storage_obj_open", ["unsigned long storage_id", "void *object_id", "size_t object_id_len", "unsigned long flags", "uint32_t *obj"]], [0x2a, "syscall_storage_obj_create", ["unsigned long storage_id", "void *object_id", "size_t object_id_len", "unsigned long flags", "unsigned long attr", "void *data", "size_t len", "uint32_t *obj"]], [0x2b, "syscall_storage_obj_del", ["unsigned long obj"]], [0x2c, "syscall_storage_obj_rename", ["unsigned long obj", "void *object_id", "size_t object_id_len"]], [0x2d, "syscall_storage_alloc_enum", ["uint32_t *obj_enum"]], [0x2e, "syscall_storage_free_enum", ["nsigned long obj_enum"]], [0x2f, "syscall_storage_reset_enum", ["unsigned long obj_enum"]], [0x30, "syscall_storage_start_enum", ["unsigned long obj_enum", "unsigned long storage_id"]], [0x31, "syscall_storage_next_enum", ["unsigned long obj_enum", "TEE_ObjectInfo *info", "void *obj_id", "uint64_t *len"]], [0x32, "syscall_storage_obj_read", ["unsigned long obj", "void *data", "size_t len", "uint64_t *count"]], [0x33, "syscall_storage_obj_write", ["unsigned long obj", "void *data", "size_t len"]], [0x34, "syscall_storage_obj_trunc", ["unsigned long obj, size_t len"]], [0x35, "syscall_storage_obj_seek", ["unsigned long obj", "int32_t offset", "unsigned long whence"]], [0x36, "syscall_obj_generate_key", ["unsigned long obj", "unsigned long key_size", "const struct utee_attribute *params", "unsigned long param_count"]], [0x37, "syscall_not_supported", []], [0x38, "syscall_not_supported", []], [0x39, "syscall_not_supported", []], [0x3a, "syscall_not_supported", []], [0x3b, "syscall_not_supported", []], [0x3c, "syscall_not_supported", []], [0x3d, "syscall_not_supported", []], [0x3e, "syscall_not_supported", []], [0x3f, "syscall_not_supported", []], [0x40, "syscall_not_supported", []], [0x41, "syscall_not_supported", []], [0x42, "syscall_not_supported", []], [0x43, "syscall_not_supported", []], [0x44, "syscall_not_supported", []], [0x45, "syscall_not_supported", []], [0x46, "syscall_cache_operation", ["void *va, size_t len", "unsigned long op"]], ] # ARM/ARM64 OP-TEE ldelf (at secure world) # - core/include/tee/tee_svc.h # - core/include/kernel/ldelf_syscalls.h arm_ldelf_syscall_list = [ # noqa: F841 [0x00, "syscall_sys_return", ["unsigned long ret"]], [0x01, "syscall_log", ["const void *buf", "size_t len"]], [0x02, "syscall_panic", ["unsigned long code"]], [0x03, "ldelf_syscall_map_zi", ["vaddr_t *va", "size_t num_bytes", "size_t pad_begin", "size_t pad_end", "unsigned long flags"]], [0x04, "ldelf_syscall_unmap", ["vaddr_t va", "size_t num_bytes"]], [0x05, "ldelf_syscall_open_bin", ["const TEE_UUID *uuid", "size_t uuid_size", "uint32_t *handle"]], [0x06, "ldelf_syscall_close_bin", ["unsigned long handle"]], [0x07, "ldelf_syscall_map_bin", ["vaddr_t *va", "size_t num_bytes", "unsigned long handle", "size_t offs_bytes", "size_t pad_begin", "size_t pad_end", "unsigned long flags"]], [0x08, "ldelf_syscall_copy_from_bin", ["void *dst", "size_t offs", "size_t num_bytes", "unsigned long handle"]], [0x09, "ldelf_syscall_set_prot", ["unsigned long va", "size_t num_bytes", "unsigned long flags"]], [0x0a, "ldelf_syscall_remap", ["unsigned long old_va", "addr_t *new_va", "size_t num_bytes", "size_t pad_begin", "size_t pad_end"]], [0x0b, "ldelf_syscall_gen_rnd_num", ["void *buf", "size_t num_bytes"]], ] # x86_16 FreeDOS int 0x21 # https://en.wikipedia.org/wiki/DOS_API # https://stanislavs.org/helppc/int_21.html # http://www2.ift.ulaval.ca/~marchand/ift17583/dosints.pdf x86_16_dos_syscall_list = [ # nr, syscall name, return registers, args, arg registers # 1.0+ [0x00, "ProgramTerminate", [], [], []], [0x01, "CharacterInput", ["$al"], [], []], [0x02, "CharacterOutput", [], ["character"], ["$dl"]], [0x03, "AuxiliaryInput", ["$al"], [], []], [0x04, "AuxiliaryOutput", [], ["character"], ["$dl"]], [0x05, "PrinterOutput", [], ["character"], ["$dl"]], [0x06, "DirectConsoleIo", ["$al", "$eflags.zf"], ["character"], ["$dl"]], [0x07, "DirectStdinInputNoEcho", ["$al"], [], []], [0x08, "ConsoleInputNoEcho", ["$al"], [], []], [0x09, "DisplayString", [], ["string"], ["$ds:$dx"]], [0x0a, "BufferedKeyboardInput", [], ["buffer"], ["$ds:$dx"]], [0x0b, "GetInputStatus", ["$al"], [], []], [0x0c, "FlushInputBufferAndInput", ["$al"], ["function"], ["$al"]], [0x0d, "DiskReset", [], [], []], [0x0e, "SetDefaultDrive", ["$al"], ["drive_number"], ["$dl"]], [0x0f, "OpenFile", ["$al"], ["FCB"], ["$ds:$dx"]], [0x10, "CloseFile", ["$al"], ["FCB"], ["$ds:$dx"]], [0x11, "FindFirstFile", ["$al"], ["FCB"], ["$ds:$dx"]], [0x12, "FindNextFile", ["$al"], ["FCB"], ["$ds:$dx"]], [0x13, "DeleteFile", ["$al"], ["FCB"], ["$ds:$dx"]], [0x14, "SequentialRead", ["$al"], ["FCB"], ["$ds:$dx"]], [0x15, "SequentialWrite", ["$al"], ["FCB"], ["$ds:$dx"]], [0x16, "CreateFile", ["$al"], ["FCB"], ["$ds:$dx"]], [0x17, "RenameFile", ["$al"], ["FCB"], ["$ds:$dx"]], # 0x18: reserved [0x19, "GetDefaultDrive", ["$al"], [], []], [0x1a, "SetDiskTransferAddress", [], ["DTA"], ["$ds:$dx"]], [0x1b, "GetAllocationInfoForDefaultDrive", ["$al", "$cx", "$dx", "$ds:$bx"], [], []], [0x1c, "GetAllocationInfoForSpecifiedDrive", ["$al", "$cx", "$dx", "$ds:$bx"], ["drive_number"], ["$dl"]], # 0x1d: reserved # 0x1e: reserved [0x1f, "GetDiskParameterBlockForDefaultDrive", ["$al"], ["drive_number"], ["$dl"]], # 0x20: reserved [0x21, "RandomRead", ["$al"], ["FCB"], ["$ds:$dx"]], [0x22, "RandomWrite", ["$al"], ["FCB"], ["$ds:$dx"]], [0x23, "GetFileSizeInRecords", ["$al"], ["FCB"], ["$ds:$dx"]], [0x24, "SetRandomRecordNumber", [], ["FCB"], ["$ds:$dx"]], [0x25, "SetInterruptVector", [], ["interrupt_number", "handler"], ["$al", "$ds:$dx"]], [0x26, "CreatePSP", [], ["segment_number"], ["$dx"]], [0x27, "RandomBlockRead", ["$al", "$cx"], ["FCB", "record_count"], ["$ds:$dx", "$cx"]], [0x28, "RandomBlockWrite", ["$al", "$cx"], ["FCB", "record_count"], ["$ds:$dx", "$cx"]], [0x29, "ParseFilename", ["$al", "$ds:$si", "$es:$di"], ["control", "string", "buffer"], ["$al", "$ds:$si", "$es:$di"]], [0x2a, "GetDate", ["$al", "$cx", "$dh", "$dl"], [], []], [0x2b, "SetDate", ["$al"], ["year", "month", "day"], ["$cx", "$dh", "$dl"]], [0x2c, "GetTime", ["$ch", "$cl", "$dh", "$dl"], [], []], [0x2d, "SetTime", ["$al"], ["hour", "minutes", "seconds", "hundredths"], ["$ch", "$cl", "$dh", "$dl"]], [0x2e, "SetVerifyFlag", [], ["verify_flag", "0"], ["$al", "$dl"]], # 2.0+ [0x2f, "GetDiskTransferAddress", ["$es:$bx"], [], []], [0x30, "GetDosVersion", ["$al", "$ah", "$bh", "$bl", "$cx"], [], []], [0x31, "TerminateAndStayResident", [], ["exit_code", "program_size"], ["$al", "$dx"]], [0x32, "GetDiskParameterBlock", ["$al", "$ds:$bx"], ["drive"], ["$dl"]], [0x33, "GetOrSetCtrlBreak", ["$al", "$dl"], ["subfunction", "value"], ["$al", "$dl"]], [0x34, "GetDosCriticalFlagPointer", ["$es:$bx"], [], []], [0x35, "GetInterruptVector", ["$es:$bx"], ["interrupt_number"], ["$al"]], [0x36, "GetFreeDiskSpace", ["$ax", "$bx", "$cx", "$dx"], ["drive_number"], ["$dl"]], [0x37, "GetOrSetSwitchCharacter", ["$al", "$dl"], ["subfunction", "value"], ["$al", "$dl"]], [0x38, "GetOrSetCountryInfo", ["$ax", "$bx", "$ds:$dx"], ["subfunction", "country_code", "buffer"], ["$al", "$bx", "$ds:dx"]], [0x39, "CreateSubDirectory", ["$ax"], ["pathname"], ["$ds:$dx"]], [0x3a, "RemoveSubDirectory", ["$ax"], ["pathname"], ["$ds:$dx"]], [0x3b, "ChangeCurrentDirectory", ["$ax"], ["pathname"], ["$ds:$dx"]], [0x3c, "CreateFile", ["$ax"], ["pathname", "attribute"], ["$ds:$dx", "$cx"]], [0x3d, "OpenFile", ["$ax"], ["mode", "pathname"], ["$al", "$ds:$dx"]], [0x3e, "CloseFile", ["$ax"], ["handle"], ["$bx"]], [0x3f, "ReadFileOrDevice", ["$ax"], ["handle", "size"], ["$bx", "$cx", "$ds:$dx"]], [0x40, "WriteFileOrDevice", ["$ax"], ["handle", "size", "buffer"], ["$bx", "$cx", "$ds:$dx"]], [0x41, "DeleteFile", ["$ax"], ["pathname"], ["$ds:$dx"]], [0x42, "SeekFile", ["$dx", "$ax"], ["origin", "handle", "move_size_high", "move_size_low"], ["$al", "$bx", "$cx", "$dx"]], [0x43, "GetOrSetFileAttributes", ["$ax", "$cx"], ["subfunction", "pathname", "attribute"], ["$al", "$ds:$dx", "$cx"]], [0x44, "IoControlForDevices", ["$ax", "$dx"], ["subfunction", "arg1", "arg2", "arg3"], ["$al", "$bx", "$cx", "$ds:$dx"]], [0x45, "DuplicateHandle", ["$ax"], ["handle"], ["$bx"]], [0x46, "RedirectHandle", ["$ax"], ["old_handle", "new_handle"], ["$bx", "$cx"]], [0x47, "GetCurrentDirectory", ["$ds:$si", "$ax"], ["drive_number", "buffer"], ["$dl", "$ds:$si"]], [0x48, "AllocateMemory", ["$ax", "$bx"], ["block_size"], ["$bx"]], [0x49, "ReleaseMemory", ["$ax"], ["segment"], ["$es"]], [0x4a, "ReallocateMemory", ["$ax", "$bx"], ["new_block_size", "segment"], ["$bx", "$es"]], [0x4b, "ExecuteProgram", ["$ax", "$es:$bx"], ["subfunction", "pathname", "parameter"], ["$al", "$ds:$dx", "$es:$bx"]], [0x4c, "TerminateWithReturnCode", [], ["return_code"], ["$al"]], [0x4d, "GetProgramReturnCode", ["$ah", "$al"], [], []], [0x4e, "FindFirstFile", ["$ax"], ["pathname", "attribute"], ["$ds:$dx", "$cx"]], # DTA omitted [0x4f, "FindNextFile", ["$ax"], ["pathname"], ["$ds:$dx"]], # DTA omitted [0x50, "SetCurrentPSP", [], ["segment"], ["$bx"]], [0x51, "GetCurrentPSP", ["$bx"], [], []], [0x52, "GetListOfLists", ["$es:$bx"], [], []], [0x53, "CreateDiskParameterBlock", ["$es:$bp"], ["bios_parameter", "buffer"], ["$ds:si", "$es:$bp"]], [0x54, "GetVerifyFlag", ["$al"], [], []], [0x55, "CreateProgramPSP", [], ["segment", "size"], ["$dx", "$si"]], [0x56, "RenameFile", ["$ax"], ["old_pathname", "new_pathname"], ["$ds:$dx", "$es:$di"]], [0x57, "GetOrSetFileDateAndTime", ["$ax", "$cx", "$dx"], ["subfunction", "handle", "time", "date", "buffer"], ["$al", "$bx", "$cx", "$dx", "$es:$di"]], # 2.11+ [0x58, "GetOrSetAllocationStrategy", ["$ax"], ["subfunction", "strategy"], ["$al", "$bx"]], # 3.0+ [0x59, "GetExtendedErrorInfo", ["$ax", "$bh", "$bl", "$ch"], ["0"], ["$bx"]], [0x5a, "CreateTempFile", ["$ax", "$ds:$dx"], ["pathname", "attribute"], ["$ds:$dx", "$cx"]], [0x5b, "CreateNewFile", ["$ax"], ["pathname", "attribute"], ["$ds:$dx", "$cx"]], [0x5c, "LockOrUnlockFile", ["$ax"], ["subfunction", "handle", "offset_high", "offset_low", "length_high", "length_low"], ["$al", "$bx", "$cx", "$dx", "$si", "$di"]], [0x5d, "FileSharingFunctions", ["$ds:$si"], ["subfunction", "arg1"], ["$al", "$ds:$dx"]], [0x5e, "NetworkFunctions", ["$ax"], ["subfunction"], ["$al"]], # too complicated [0x5f, "NetworkRedirectionFunctions", ["$ax"], ["subfunction"], ["$al"]], # too complicated [0x60, "QualifyFilename", ["$es:$di", "$ah"], ["pathname", "buffer"], ["$ds:$si", "$es:$di"]], # 0x61: reserved [0x62, "GetCurrentPSP", ["$bx"], [], []], [0x63, "GetLeadByteTable", ["$ax", "$ds:$si", "$dl"], ["subfunction", "flag"], ["$al", "$dl"]], # 3.2+ [0x64, "SetDeviceDriverLookAhead", ["$dl"], ["subfunction", "arg1"], ["$al", "$dl"]], # 3.3+ [0x65, "GetExtendedCountryInfo", ["$ax"], ["subfunction"], ["$al"]], # too complicated [0x66, "GetOrSetGlobalCodePage", ["$ax", "$bx", "$cx"], ["subfunction", "active_codepage", "system_codepage"], ["$al", "$bx", "$dx"]], [0x67, "SetHandleCount", ["$ax"], ["max_handle_count"], ["$bx"]], [0x68, "CommitFile", ["$ax"], ["handle"], ["$bx"]], # 4.0+ [0x69, "GetOrSetMediaId", ["$ax", "$ds:$dx"], ["subfunction", "drive_number", "buffer"], ["$al", "$bl", "$ds:$dx"]], # 0x6a: reserved # 0x6b: reserved [0x6c, "ExtendedOpenCreateFile", ["$ax", "$cx"], ["0", "mode", "attribute", "control", "spec"], ["$al", "$bx", "$cx", "$dx", "$ds:$si"]], ] class Syscall: """A collection of utility functions that are related to syscall tables.""" @staticmethod @Cache.cache_this_session def parse_common_syscall_defs(): """Parse and return a common definition of a syscall, common to all architectures.""" sc_defs = [ syscall_defs, syscall_defs_compat, ] dic = {} for defs in sc_defs: for line in defs.splitlines(): if line == "": continue if line.startswith("#"): continue # ignore `!` m = re.search(r"asmlinkage\s+(?:long|ssize_t)\s+(\S+)\((.+?)\);", line) if not m: continue name, args = m.group(1), m.group(2) args = [x.strip() for x in args.split(",")] if name in dic: err("Duplicate: {:s}".format(name)) raise if len(args) == 1 and args[0] == "void": dic[name] = [] else: dic[name] = args return dic @staticmethod def parse_syscall_table_defs(table_defs): """Parse and return syscall table defines for a specified architecture.""" table = [] for line in table_defs.splitlines(): if line == "": continue if line.startswith("#"): continue entry = line.split() if len(entry) == 3: # it is unimplemented continue entry[0] = int(entry[0]) table.append(entry) return table @staticmethod def make_syscall_list_x86_64(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(x64_syscall_tbl) arch_specific_dic = { "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c "sys_modify_ldt": [ "int func", "void __user *ptr", "unsigned long bytecount", ], # arch/x86/kernel/ldt.c "sys_arch_prctl": [ "int option", "unsigned long arg2", ], # arch/x86/kernel/process_64.c "sys_iopl": [ "unsigned int level", ], # arch/x86/kernel/ioport.c "compat_sys_x32_rt_sigreturn": [], # arch/x86/kernel/signal.c "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long off", ], # arch/x86/kernel/sys_x86_64.c "sys_rt_sigreturn": [], # arch/x86/kernel/signal.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # include/linux/syscalls.h } syscall_list = [] __X32_SYSCALL_BIT = 0x4000_0000 for entry in tbl: nr, abi, name, func = entry[:4] if abi not in ["common", "64", "x32"]: continue # special case if func in arch_specific_dic: if abi in ["common", "64"]: syscall_list.append([nr, name, arch_specific_dic[func]]) if abi in ["common", "x32"]: syscall_list.append([nr + __X32_SYSCALL_BIT, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise if abi in ["common", "64"]: syscall_list.append([nr, name, sc_def[func]]) if abi in ["common", "x32"]: syscall_list.append([nr + __X32_SYSCALL_BIT, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_x86_32_emulated(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(x86_syscall_tbl) arch_specific_dic = { "compat_sys_sigreturn": [], # arch/x86/ia32/ia32_signal.c "compat_sys_rt_sigreturn": [], # arch/x86/ia32/ia32_signal.c "compat_sys_old_getrlimit": [ "unsigned int resource", "struct compat_rlimit *rlim", ], # kernel/sys.c "compat_sys_ia32_mmap": [ "struct mmap_arg_struct32 __user *arg", ], # arch/x86/kernel/sys_ia32.c "sys_iopl": [ "unsigned int level", ], # arch/x86/kernel/ioport.c "compat_sys_ia32_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls_val", "int __user *child_tidptr", ], # arch/x86/kernel/sys_ia32.c (CONFIG_CLONE_BACKWARDS) "sys_modify_ldt": [ "int func", "void __user *ptr", "unsigned long bytecount", ], # arch/x86/kernel/ldt.c "sys_ia32_pread64": [ "unsigned int fd", "char __user *ubuf", "u32 count", "u32 poslo", "u32 poshi", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_pwrite64": [ "unsigned int fd", "const char __user *ubuf", "u32 count", "u32 poslo", "u32 poshi", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_truncate64": [ "const char __user *filename", "unsigned long offset_low", "unsigned long offset_high", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_ftruncate64": [ "unsigned int fd", "unsigned long offset_low", "unsigned long offset_high", ], # arch/x86/kernel/sys_ia32.c "compat_sys_ia32_stat64": [ "const char __user *filename", "struct stat64 __user *statbuf", ], # arch/x86/kernel/sys_ia32.c "compat_sys_ia32_lstat64": [ "const char __user *filename", "struct stat64 __user *statbuf", ], # arch/x86/kernel/sys_ia32.c "compat_sys_ia32_fstat64": [ "unsigned long fd", "struct stat64 __user *statbuf", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_readahead": [ "int fd", "unsigned int off_lo", "unsigned int off_high", "size_t count", ], # arch/x86/kernel/sys_ia32.c "sys_set_thread_area": [ "struct user_desc __user *u_info", ], # arch/x86/kernel/tls.c "sys_get_thread_area": [ "struct user_desc __user *u_info", ], # arch/x86/kernel/tls.c "sys_ia32_fadvise64": [ "int fd", "unsigned int offset_lo", "unsigned int offset_hi", "size_t len", "int advice", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_fadvise64_64": [ "int fd", "__u32 offset_low", "__u32 offset_high", "__u32 len_low", "__u32 len_high", "int advice", ], # arch/x86/kernel/sys_ia32.c "compat_sys_ia32_fstatat64": [ "unsigned int dfd", "const char __user *filename", "struct stat64 __user *statbuf", "int flag", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_sync_file_range": [ "int fd", "unsigned int off_low", "unsigned int off_hi", "unsigned int n_low", "unsigned int n_hi", "unsigned int flags", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_fallocate": [ "int fd", "int mode", "unsigned int offset_lo", "unsigned int offset_hi", "unsigned int len_lo", "unsigned int len_hi", ], # arch/x86/kernel/sys_ia32.c "sys_arch_prctl": [ "int option", "unsigned long arg2", ], # arch/x86/kernel/process_64.c } syscall_list = [] for entry in tbl: if len(entry) == 5: nr, abi, name, _, func = entry # use compat else: nr, abi, name, func = entry[:4] if abi != "i386": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_x86_32_native(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(x86_syscall_tbl) arch_specific_dic = { "sys_iopl": [ "unsigned int level", ], # arch/x86/kernel/ioport.c "sys_vm86old": [ "struct vm86_struct __user *user_vm86", ], # arch/x86/kernel/vm86_32.c "sys_sigreturn": [], # arch/x86/kernel/signal.c "sys_rt_sigreturn": [], # arch/x86/kernel/signal.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_modify_ldt": [ "int func", "void __user *ptr", "unsigned long bytecount", ], # arch/x86/kernel/ldt.c "sys_vm86": [ "unsigned long cmd", "unsigned long arg", ], # arch/x86/kernel/vm86_32.c "sys_ia32_pread64": [ "unsigned int fd", "char __user *ubuf", "u32 count", "u32 poslo", "u32 poshi", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_pwrite64": [ "unsigned int fd", "const char __user *ubuf", "u32 count", "u32 poslo", "u32 poshi", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_truncate64": [ "const char __user *filename", "unsigned long offset_low", "unsigned long offset_high", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_ftruncate64": [ "unsigned int fd", "unsigned long offset_low", "unsigned long offset_high", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_readahead": [ "int fd", "unsigned int off_lo", "unsigned int off_high", "size_t count", ], # arch/x86/kernel/sys_ia32.c "sys_set_thread_area": [ "struct user_desc __user *u_info", ], # arch/x86/kernel/tls.c "sys_get_thread_area": [ "struct user_desc __user *u_info", ], # arch/x86/kernel/tls.c "sys_ia32_fadvise64": [ "int fd", "unsigned int offset_lo", "unsigned int offset_hi", "size_t len", "int advice", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_fadvise64_64": [ "int fd", "__u32 offset_low", "__u32 offset_high", "__u32 len_low", "__u32 len_high", "int advice", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_sync_file_range": [ "int fd", "unsigned int off_low", "unsigned int off_hi", "unsigned int n_low", "unsigned int n_hi", "unsigned int flags", ], # arch/x86/kernel/sys_ia32.c "sys_ia32_fallocate": [ "int fd", "int mode", "unsigned int offset_lo", "unsigned int offset_hi", "unsigned int len_lo", "unsigned int len_hi", ], # arch/x86/kernel/sys_ia32.c "sys_arch_prctl": [ "int option", "unsigned long arg2", ], # arch/x86/kernel/process_32.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "unsigned int mask_1", "unsigned int mask_2", "int dfd", "const char __user *pathname", ], # include/linux/syscalls.h } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi != "i386": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_arm64(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(arm64_syscall_tbl) arch_specific_dic = { "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int __user *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_rt_sigreturn": [], # arch/arm64/kernel/signal.c "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long off", ], # arch/arm64/kernel/sys.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # include/linux/syscalls.h } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # arch/arm64/kernel/Makefile.syscalls if abi not in ["common", "64", "renameat", "rlimit", "memfd_secret"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_arm32_emulated(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(arm_compat_syscall_tbl) arch_specific_dic = { "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int __user *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "compat_sys_aarch32_pread64": [ "unsigned int fd", "char *buf", "size_t count", "u32 __pad", "arg_u32p(pos)", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_pwrite64": [ "unsigned int fd", "const char *buf", "size_t count", "u32 __pad", "arg_u32p(pos)", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long off_4k", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_truncate64": [ "const char *path", "u32 __pad", "arg_u32p(length)", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_ftruncate64": [ "unsigned int fd", "u32 __pad", "arg_u32p(length)", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_readahead": [ "int fd", "u32 __pad", "arg_u32(offset)", "size_t count", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_statfs64": [ "const char *pathname", "compat_size_t sz", "struct compat_statfs64 *buf", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_fstatfs64": [ "unsigned int fd", "compat_size_t sz", "struct compat_statfs64 *buf", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_fadvise64_64": [ "int fd", "int advice", "arg_u32p(offset)", "arg_u32p(len)", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_sync_file_range2": [ "int fd", "unsigned int flags", "arg_u32p(offset)", "arg_u32p(nbytes)", ], # arch/arm64/kernel/sys32.c "compat_sys_aarch32_fallocate": [ "int fd", "int mode", "arg_u32p(offset)", "arg_u32p(len)", ], # arch/arm64/kernel/sys32.c "compat_sys_old_semctl": [ "int semid", "int semnum", "int cmd", "int arg", ], # ipc/sem.c "compat_sys_old_msgctl": [ "int msqid", "int cmd", "void *uptr", ], # ipc/msg.c "compat_sys_old_shmctl": [ "int shmid", "int cmd", "void *uptr", ], # ipc/shm.c "compat_sys_sigreturn": [], # arch/arm64/kernel/signal32.c "compat_sys_rt_sigreturn": [], # arch/arm64/kernel/signal32.c } syscall_list = [] for entry in tbl: if len(entry) == 5: nr, abi, name, _, func = entry # use compat else: nr, abi, name, func = entry[:4] if abi != "common": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) arch_specific_extra = [ [0xf0002, "cacheflush", [ "unsigned long start", "unsigned long end", "int flags", ]], # arch/arm64/kernel/sys_compat.c [0xf0005, "set_tls", [ "unsigned long val", ]], # arch/arm64/kernel/sys_compat.c ] syscall_list += arch_specific_extra return syscall_list @staticmethod def make_syscall_list_arm32_native(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(arm_native_syscall_tbl) arch_specific_dic = { "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int __user *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # include/asm-generic/syscalls.h "sys_sigreturn_wrapper": [], # arch/arm/kernel/entry-common.S "sys_rt_sigreturn_wrapper": [], # arch/arm/kernel/entry-common.S "sys_statfs64_wrapper": [ "const char __user *path", "size_t sz", "struct statfs64 __user *buf", ], # arch/arm/kernel/entry-common.S "sys_fstatfs64_wrapper": [ "unsigned int fd", "size_t sz", "struct statfs64 __user *buf", ], # arch/arm/kernel/entry-common.S "sys_arm_fadvise64_64": [ "int fd", "int advice", "loff_t offset", "loff_t len", ], # arch/arm/kernel/sys_arm.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use OABI if abi not in ["common", "eabi"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) arch_specific_extra = [ [0xf0001, "breakpoint", []], # arch/arm/kernel/traps.c [0xf0002, "cacheflush", [ "unsigned long start", "unsigned long end", "int flags", ]], # arch/arm/kernel/traps.c [0xf0003, "usr26", []], # arch/arm/kernel/traps.c [0xf0004, "usr32", []], # arch/arm/kernel/traps.c [0xf0005, "set_tls", [ "unsigned long val", ]], # arch/arm/kernel/traps.c [0xf0006, "get_tls", []], # arch/arm/kernel/traps.c ] syscall_list += arch_specific_extra return syscall_list @staticmethod def make_syscall_list_mips32(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(mips_o32_syscall_tbl) arch_specific_dic = { "sys_syscall": ["..."], # "__sys_fork": [], # "sys_rt_sigreturn": [], # arch/mips/kernel/signal.c "sysm_pipe": [], # arch/mips/kernel/syscall.c "sys_mips_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "off_t offset", ], # arch/mips/kernel/syscall.c "sys_sigreturn": [], # "__sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int __user *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_cacheflush": [ "unsigned long addr", "unsigned long bytes", "unsigned int cache", ], # arch/mips/mm/cache.c "sys_cachectl": [ "char *addr", "int nbytes", "int op", ], # arch/mips/kernel/syscall.c "__sys_sysmips": [ "long cmd", "long arg1", "long arg2", ], # arch/mips/kernel/syscall.c "sys_mips_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/mips/kernel/syscall.c "sys_set_thread_area": [ "unsigned long addr", ], # arch/mips/kernel/syscall.c "__sys_clone3": [ "struct clone_args __user *uargs", "size_t size", ], # "sys_sigsuspend": [ "sigset_t __user *uset", ], # arch/mips/kernel/signal.c "sys_sigaction": [ "int sig2", "const struct sigaction __user *act", "struct sigaction __user *oact", ], # arch/mips/kernel/signal.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi != "o32": continue nr += 4000 # arch/mips/include/asm/unistd.h # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_mipsn32(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(mips_n32_syscall_tbl) arch_specific_dic = { "__sys_fork": [], # "sysm_pipe": [], # arch/mips/kernel/syscall.c "sys_mips_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "off_t offset", ], # arch/mips/kernel/syscall.c "__sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int __user *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_cacheflush": [ "unsigned long addr", "unsigned long bytes", "unsigned int cache", ], # arch/mips/mm/cache.c "sys_cachectl": [ "char *addr", "int nbytes", "int op", ], # arch/mips/kernel/syscall.c "__sys_sysmips": [ "long cmd", "long arg1", "long arg2", ], # arch/mips/kernel/syscall.c "sys_set_thread_area": [ "unsigned long addr", ], # arch/mips/kernel/syscall.c "__sys_clone3": [ "struct clone_args __user *uargs", "size_t size", ], # "compat_sys_old_shmctl": [ "int shmid", "int cmd", "void *uptr", ], # ipc/shm.c "compat_sys_old_semctl": [ "int semid", "int semnum", "int cmd", "int arg", ], # ipc/sem.c "compat_sys_old_msgctl": [ "int msqid", "int cmd", "void *uptr", ], # ipc/msg.c "sys_32_personality": [ "unsigned long personality", ], # arch/mips/kernel/linux32.c "sysn32_rt_sigreturn": [], # arch/mips/kernel/signal_n32.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi != "n32": continue nr += 6000 # arch/mips/include/asm/unistd.h # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_mips64(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(mips_n64_syscall_tbl) arch_specific_dic = { "sys_mips_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "off_t offset", ], # arch/mips/kernel/syscall.c "sysm_pipe": [], # arch/mips/kernel/syscall.c "__sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int __user *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "__sys_fork": [], # "sys_rt_sigreturn": [], # arch/mips/kernel/signal.c "sys_cacheflush": [ "unsigned long addr", "unsigned long bytes", "unsigned int cache", ], # arch/mips/mm/cache.c "sys_cachectl": [ "char *addr", "int nbytes", "int op", ], # arch/mips/kernel/syscall.c "__sys_sysmips": [ "long cmd", "long arg1", "long arg2", ], # arch/mips/kernel/syscall.c "sys_set_thread_area": [ "unsigned long addr", ], # arch/mips/kernel/syscall.c "__sys_clone3": [ "struct clone_args __user *uargs", "size_t size", ], # "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi != "n64": continue nr += 5000 # arch/mips/include/asm/unistd.h # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_ppc32(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(ppc_syscall_tbl) arch_specific_dic = { "sys_sigreturn": [], # arch/powerpc/kernel/signal_32.c "sys_rt_sigreturn": [], # arch/powerpc/kernel/signal_32.c "sys_mmap": [ "unsigned long addr", "size_t len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "off_t offset", ], # arch/powerpc/kernel/syscalls.c "sys_mmap2": [ "unsigned long addr", "size_t len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/powerpc/kernel/syscalls.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int __user *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_swapcontext": [ "struct ucontext __user *old_ctx", "struct ucontext __user *new_ctx", "long ctx_size", ], # arch/powerpc/kernel/signal_32.c "ppc_fadvise64_64": [ "int fd", "int advice", "u32 offset_high", "u32 offset_low", "u32 len_high", "u32 len_low", ], # arch/poerpc/kernel/syscalls.c "sys_rtas": [ "struct rtas_args __user *uargs", ], # arch/powerpc/include/asm/syscalls.h "sys_debug_setcontext": [ "struct ucontext __user *ctx", "int ndbg", "struct sig_dbg_op __user *dbg", ], # arch/powerpc/kernel/signal_32.c "sys_subpage_prot": [ "unsigned long addr", "unsigned long len", "u32 __user *map", ], # arch/powerpc/mm/book3s64/subpage_prot.c "sys_ppc_pread64": [ "unsigned int fd", "char __user *ubuf", "compat_size_t count", "u32 reg6", "u32 pos1", "u32 pos2", ], # arch/powerpc/kernel/sys_ppc32.c "sys_ppc_pwrite64": [ "unsigned int fd", "const char __user *ubuf", "compat_size_t count", "u32 reg6", "u32 pos1", "u32 pos2", ], # arch/powerpc/kernel/sys_ppc32.c "sys_ppc_readahead": [ "int fd", "u32 r4", "u32 offset1", "u32 offset2", "u32 count", ], # arch/powerpc/kernel/sys_ppc32.c "sys_ppc_truncate64": [ "const char __user *path", "u32 reg4", "unsigned long len1", "unsigned long len2", ], # arch/powerpc/kernel/sys_ppc32.c "sys_ppc_ftruncate64": [ "unsigned int fd", "u32 reg4", "unsigned long len1", "unsigned long len2", ], # arch/powerpc/kernel/sys_ppc32.c "sys_ppc32_fadvise64": [ "int fd", "u32 unused", "u32 offset1", "u32 offset2", "size_t len", "int advice", ], # arch/powerpc/kernel/sys_ppc32.c "sys_ppc_fadvise64_64": [ "int fd", "int advice", "u32 offset_high", "u32 offset_low", "u32 len_high", "u32 len_low", ], # arch/powerpc/kernel/syscalls.c "sys_ppc_sync_file_range2": [ "int fd", "unsigned int flags", "unsigned int offset1", "unsigned int offset2", "unsigned int nbytes1", "unsigned int nbytes2", ], # arch/powerpc/kernel/sys_ppc32.c "sys_ppc_fallocate": [ "int fd", "int mode", "u32 offset1", "u32 offset2", "u32 len1", "u32 len2", ], # arch/powerpc/kernel/sys_ppc32.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "unsigned int mask_1", "unsigned int mask_2", "int dfd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi not in ["common", "32", "nospu"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_ppc64(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(ppc_syscall_tbl) arch_specific_dic = { "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int __user *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_rt_sigreturn": [], # arch/powerpc/kernel/signal_64.c "sys_mmap": [ "unsigned long addr", "size_t len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "off_t offset", ], # arch/powerpc/kernel/syscalls.c "sys_mmap2": [ "unsigned long addr", "size_t len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/powerpc/kernel/syscalls.c "sys_ppc64_personality": [ "unsigned long personality", ], # arch/powerpc/kernel/syscalls.c "sys_swapcontext": [ "struct ucontext __user *old_ctx", "struct ucontext __user *new_ctx", "long ctx_size", ], # arch/powerpc/kernel/signal_64.c "sys_rtas": [ "struct rtas_args __user *uargs", ], # arch/powerpc/include/asm/syscalls.h "sys_subpage_prot": [ "unsigned long addr", "unsigned long len", "u32 __user *map", ], # arch/powerpc/mm/book3s64/subpage_prot.c "sys_switch_endian": [], # arch/powerpc/kernel/syscalls.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi not in ["common", "64", "nospu"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_sparc32(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(sparc_syscall_tbl) arch_specific_dic = { "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long off" ], # arch/sparc/kernel/sys_sparc_32.c "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff" ], # arch/sparc/kernel/sys_sparc_32.c "sunos_execv": [ "const char __user *filename", "const char __user *const __user *argv", "const char __user *const __user *envp", ], # arch/sparc/kernel/entry.S "sys_sparc_pipe": [], # arch/sparc/kernel/sys_sparc_32.c "sys_getpagesize": [], # arch/sparc/kernel/sys_sparc_32.c "sys_getdomainname": [ "char __user *name", "int len" ], # arch/sparc/kernel/sys_sparc_32.c "sys_sparc_remap_file_pages": [ "unsigned long start", "unsigned long size", "unsigned long prot", "unsigned long pgoff", "unsigned long flags", ], # kernel/sys_sparc_32.c "sys_sparc_sigaction": [ "int, sig", "struct old_sigaction __user *act", "struct old_sigaction __user *oact", ], # arch/sparc/kernel/sys_sparc_32.c "sys_sigreturn": [], # arch/sparc/kernel/syscalls.S "sys_rt_sigreturn": [], # arch/sparc/kernel/syscalls.S "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi not in ["common", "32"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func in ["sys_ni_syscall", "sys_nis_syscall"]: continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_sparc64(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(sparc_syscall_tbl) arch_specific_dic = { "sparc_exit": [ "int error_code", ], # arch/sparc/kernel/syscalls.S "sys_sparc_pipe": [], # arch/sparc/kernel/sys_sparc_64.c "sys_memory_ordering": [ "unsigned long model", ], # arch/sparc/kernel/sys_sparc_64.c "sys64_execve": [ "const char __user *filename", "const char __user *const __user *argv", "const char __user *const __user *envp", ], # arch/sparc/kernel/syscalls.S "sys_getpagesize": [], # arch/sparc/kernel/sys_sparc_64.c "sys_64_munmap": [ "unsigned long addr", "size_t len", ], # arch/sparc/kernel/sys_sparc_64.c "sys_getdomainname": [ "char __user *name", "int len" ], # arch/sparc/kernel/sys_sparc_64.c "sys_utrap_install": [ "utrap_entry_t type", "utrap_handler_t new_p", "utrap_handler_t new_d", "utrap_handler_t __user * old_p", "utrap_handler_t __user *old_d", ], # arch/sparc/kernel/sys_sparc_64.c "sparc_exit_group": [ "int error_code", ], # arch/sparc/kernel/syscalls.S "sys_sparc64_personality": [ "unsigned long personality", ], # arch/sparc/kernel/sys_sparc_64.c "sys_sparc_ipc": [ "unsigned int call", "int first", "unsigned long second", "unsigned long third", "void __user *ptr", "long fifth", ], # arch/sparc/kernel/sys_sparc_64.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c "sys_sparc_adjtimex": [ "struct __kernel_timex __user *txc_p", ], # arch/sparc/kernel/sys_sparc_64.c "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long off" ], # arch/sparc/kernel/sys_sparc_64.c "sys_64_mremap": [ "unsigned long addr", "unsigned long old_len", "unsigned long new_len", "unsigned long flags", "unsigned long new_addr", ], # arch/sparc/kernel/sys_sparc_64.c "sys_sparc_clock_adjtime": [ "const clockid_t which_clock", "struct __kernel_timex __user *txc_p", ], # arch/sparc/kernel/sys_sparc_64.c "sys_kern_features": [], # arch/sparc/kernel/sys_sparc_64.c "sys64_execveat": [ "int dfd", "const char __user *filename", "const char __user *const __user *argv", "const char __user *const __user *envp", "int flags", ] ,# arch/sparc/kernel/syscalls.S "sys_rt_sigreturn": [ "struct pt_regs *regs", ], # arch/sparc/kernel/signal_64.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi not in ["common", "64"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func in ["sys_ni_syscall", "sys_nis_syscall"]: continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_riscv32(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(riscv32_syscall_tbl) arch_specific_dic = { "sys_rt_sigreturn": [], # arch/riscv/kernel/signal.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "off_t offset", ], # arch/riscv/kernel/sys_riscv.c" "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c "sys_riscv_flush_icache": [ "uintptr_t start", "uintptr_t end", "uintptr_t flags", ], # arch/riscv/kernel/sys_riscv.c "sys_riscv_hwprobe": [ "struct riscv_hwprobe __user *pairs", "size_t pair_count", "size_t cpusetsize", "unsigned long __user *cpus", "unsigned int flags", ], # arch/riscv/kernel/sys_hwprobe.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # arch/riscv/kernel/Makefile.syscalls if abi not in ["common", "32", "riscv", "memfd_secret"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_riscv64(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(riscv64_syscall_tbl) arch_specific_dic = { "sys_rt_sigreturn": [], # arch/riscv/kernel/signal.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "off_t offset", ], # arch/riscv/kernel/sys_riscv.c" "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c "sys_riscv_flush_icache": [ "uintptr_t start", "uintptr_t end", "uintptr_t flags", ], # arch/riscv/kernel/sys_riscv.c "sys_riscv_hwprobe": [ "struct riscv_hwprobe __user *pairs", "size_t pair_count", "size_t cpusetsize", "unsigned long __user *cpus", "unsigned int flags", ], # arch/riscv/kernel/sys_hwprobe.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # arch/riscv/kernel/Makefile.syscalls if abi not in ["common", "64", "riscv", "rlimit", "memfd_secret"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_s390x(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(s390x_syscall_tbl) arch_specific_dic = { "sys_s390_ipc": [ "uint, call", "int first", "unsigned long second", "unsigned long third", "void __user *ptr", ], # arch/s390/kernel/syscall.c "sys_sigreturn": [], # arch/s390/kernel/signal.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int stack_size", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS2) "sys_s390_personality": [ "unsigned int personality", ], # arch/s390/kernel/syscall.c "sys_rt_sigreturn": [], # arch/s390/kernel/signal.c "sys_s390_runtime_instr": [ "int, command", "int signum", ], # arch/s390/kernel/runtime_instr.c "sys_s390_pci_mmio_write": [ "unsigned long mmio_addr", "const void __user *user_buffer", "size_t length", ], # arch/s390/pci/pci_mmio.c "sys_s390_pci_mmio_read": [ "unsigned long mmio_addr", "void __user *user_buffer", "size_t length", ], # arch/s390/pci/pci_mmio.c "sys_s390_guarded_storage": [ "int command", "struct gs_cb __user *gs_cb", ], # arch/s390/kernel/guarded_storage.c "sys_s390_sthyi": [ "unsigned long function_code", "void __user *buffer", "u64 __user *return_code", "unsigned long flags", ], # arch/s390/kernel/sthyi.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi not in ["common", "64"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func in ["sys_ni_syscall", "-"]: continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_sh4(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(sh4_syscall_tbl) arch_specific_dic = { "sys_sh_pipe": [], # arch/sh/kernel/sys_sh32.c "old_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "int fd", "unsigned long off", ], # arch/sh/kernel/sys_sh.c "sys_sigreturn": [], # arch/sh/kernel/signal_32.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c "sys_cacheflush": [ "unsigned long addr", "unsigned long len", "int op", ], # arch/sh/kernel/sys_sh.c "sys_rt_sigreturn": [], # arch/sh/kernel/signal_32.c "sys_pread_wrapper": [ "unsigned int fd", "char __user *buf", "size_t count", "long dummy", "loff_t pos", ], # arch/sh/kernel/sys_sh32.c "sys_pwrite_wrapper": [ "unsigned int fd", "const char __user *buf", "size_t count", "long dummy", "loff_t pos", ], # arch/sh/kernel/sys_sh32.c "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/sh/kernel/sys_sh.c "sys_fadvise64_64_wrapper": [ "int fd", "u32 offset0", "u32 offset1", "u32 len0", "u32 len1", "int advice", ], # arch/sh/kernel/sys_sh32.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c "sys_sh_sync_file_range6": [ "int fd", "u64 offset", "u64 nbytes", "unsigned int flags", ], # sh/kernel/sys_sh32.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry if abi != "common": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_m68k(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(m68k_syscall_tbl) arch_specific_dic = { "__sys_fork": [], # kernel/fork.c "sys_sigreturn": [], # arch/m68k/kernel/entry.S "__sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c "sys_cacheflush": [ "unsigned long addr", "int scope", "int cache", "unsigned long len", ], # "sys_getpagesize": [], # arch/m68k/kernel/sys_m68k.c "sys_rt_sigreturn": [], # arch/m68k/kernel/entry.S "__sys_vfork": [], # kernel/fork.c "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/m68k/kernel/sys_m68k.c "sys_get_thread_area": [], # arch/m68k/kernel/sys_m68k.c "sys_set_thread_area": [ "unsigned long tp", ], # arch/m68k/kernel/sys_m68k.c "sys_atomic_cmpxchg_32": [ "unsigned long newval", "int oldval", "int d3", "int d4", "int d5", "unsigned long __user *mem", ], # arch/m68k/kernel/sys_m68k.c "sys_atomic_barrier": [], # arch/m68k/kernel/sys_m68k.c "__sys_clone3": [ "struct clone_args __user *uargs", "size_t size", ], # "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry if abi != "common": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_alpha(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(alpha_syscall_tbl) arch_specific_dic = { "alpha_syscall_zero": [], # arch/alpha/kernel/entry.S "alpha_fork": [], # arch/alpha/kernel/entry.S (fork_like macro) "sys_osf_wait4": [ "pid_t pid", "int __user *ustatus", "int options", "struct rusage32 __user *ur", ], # arch/alpha/kernel/osf_sys.c "sys_osf_brk": [ "unsigned long brk", ], # arch/alpha/kernel/osf_sys.c "sys_getxpid": [], # arch/alpha/kernel/osf_sys.c "sys_osf_mount": [ "unsigned long typenr", "const char __user *path", "int flag", "void __user *data" ], # arch/alpha/kernel/osf_sys.c "sys_getxuid": [], # arch/alpha/kernel/osf_sys.c "sys_alpha_pipe": [], # arch/alpha/kernel/osf_sys.c "sys_osf_set_program_attributes": [ "unsigned long text_start", "unsigned long text_len", "unsigned long bss_start", "unsigned long bss_len", ], # arch/alpha/kernel/osf_sys.c "sys_getxgid": [], # arch/alpha/kernel/osf_sys.c "sys_osf_sigprocmask": [ "int how", "unsigned long newmask", ], # arch/alpha/kernel/signal.c "sys_getpagesize": [], # arch/alpha/kernel/osf_sys.c "alpha_vfork": [], # arch/alpha/kernel/entry.S (fork_like macro) "sys_osf_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long off", ], # arch/alpha/kernel/osf_sys.c "sys_getdtablesize": [], # arch/alpha/kernel/osf_sys.c "sys_osf_select": [ "int, n, fd_set __user *inp", "fd_set __user *outp", "fd_set __user *exp", "struct timeval32 __user *tvp", ], # arch/alpha/kernel/osf_sys.c "sys_osf_getpriority": [ "int which", "int who", ], # arch/alpha/kernel/osf_sys.c "sys_sigreturn": [], # arch/alpha/kernel/entry.S (sigreturn_like macro) "sys_osf_sigstack": [ "struct sigstack __user *uss", "struct sigstack __user *uoss", ], # arch/alpha/kernel/osf_sys.c "sys_osf_gettimeofday": [ "struct timeval32 __user *tv", "struct timezone __user *tz", ], # arch/alpha/kernel/osf_sys.c "sys_osf_getrusage": [ "int who", "struct rusage32 __user *ru", ], # arch/alpha/kernel/osf_sys.c "sys_osf_settimeofday": [ "struct timeval32 __user *tv", "struct timezone __user *tz", ], # arch/alpha/kernel/osf_sys.c "sys_osf_utimes": [ "const char __user *filename", "struct timeval32 __user *tvs", ], # arch/alpha/kernel/osf_sys.c "sys_osf_sigaction": [ "int, sig", "const struct osf_sigaction __user *act", "struct osf_sigaction __user *oact", ], # arch/alpha/kernel/signal.c "sys_osf_getdirentries": [ "unsigned int fd", "struct osf_dirent __user *dirent", "unsigned int count", "long __user *basep", ], # arch/alpha/kernel/osf_sys.c "sys_osf_statfs": [ "const char __user *pathname", "struct osf_statfs __user *buffer", "unsigned long bufsiz", ], # arch/alpha/kernel/osf_sys.c "sys_osf_fstatfs": [ "unsigned long fd", "struct osf_statfs __user *buffer", "unsigned long bufsiz", ], # arch/alpha/kernel/osf_sys.c "sys_osf_getdomainname": [ "char __user *name", "int namelen", ], # arch/alpha/kernel/osf_sys.c "sys_osf_utsname": [ "char __user *name", ], # arch/alpha/kernel/osf_sys.c "sys_osf_stat": [ "char __user *name", "struct osf_stat __user *buf", ], # arch/alpha/kernel/osf_sys.c "sys_osf_lstat": [ "char __user *name", "struct osf_stat __user *buf", ], # arch/alpha/kernel/osf_sys.c "sys_osf_fstat": [ "int fd", "struct osf_stat __user *buf", ], # arch/alpha/kernel/osf_sys.c "sys_osf_statfs64": [ "char __user *pathname", "struct osf_statfs64 __user *buffer", "unsigned long bufsiz", ], # arch/alpha/kernel/osf_sys.c "sys_osf_fstatfs64": [ "unsigned long fd", "struct osf_statfs64 __user *buffer", "unsigned long bufsiz", ], # arch/alpha/kernel/osf_sys.c "sys_osf_sysinfo": [ "int command", "char __user *buf", "long count", ], # arch/alpha/kernel/osf_sys.c "sys_osf_proplist_syscall": [ "enum pl_code code", "union pl_args __user *args", ], # arch/alpha/kernel/osf_sys.c "sys_osf_usleep_thread": [ "struct timeval32 __user *sleep", "struct timeval32 __user *remain", ], # arch/alpha/kernel/osf_sys.c "sys_osf_getsysinfo": [ "unsigned long op", "void __user *buffer", "unsigned long nbytes", "int __user *start", "void __user *arg", ], # arch/alpha/kernel/osf_sys.c "sys_osf_setsysinfo": [ "unsigned long op", "void __user *buffer", "unsigned long nbytes", "int __user *start", "void __user *arg", ], # arch/alpha/kernel/osf_sys.c "sys_sethae": [ "unsigned long val", ], # arch/alpha/kernel/osf_sys.c "sys_old_adjtimex": [ "struct timex32 __user *txc_p", ], # arch/alpha/kernel/osf_sys.c "alpha_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # arch/alpha/kernel/entry.S (fork_like macro) "alpha_clone3": [ "struct clone_args __user *uargs", "size_t size", ], # arch/alpha/kernel/entry.S (fork_like macro) "sys_rt_sigreturn": [], # arch/alpha/kernel/entry.S (sigreturn_like macro) "sys_rt_sigaction": [ "int sig", "const struct sigaction __user *act", "struct sigaction __user *oact", "size_t sigsetsize", "void __user *restorer", ], # arch/alpha/kernel/signal.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry if abi != "common": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_hppa32(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(hppa_syscall_tbl) arch_specific_dic = { "sys_fork_wrapper": [], # arch/parisc/kernel/entry.S (fork_like macro) "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/parisc/kernel/sys_parisc.c "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long offset", ], # arch/parisc/kernel/sys_parisc.c "parisc_pread64": [ "unsigned int fd", "char __user *buf", "size_t count", "unsigned int high", "unsigned int low", ], # arch/parisc/kernel/sys_parisc.c "parisc_pwrite64": [ "unsigned int fd", "const char __user *buf", "size_t count", "unsigned int high", "unsigned int low", ], # arch/parisc/kernel/sys_parisc.c "sys_vfork_wrapper": [], # arch/parisc/kernel/entry.S (fork_like macro) "sys_clone_wrapper": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int *child_tidptr", ], # arch/parisc/kernel/entry.S (fork_like macro, CONFIG_CLONE_BACKWARDS) "parisc_personality": [ "unsigned long personality", ], # arch/parisc/kernel/sys_parisc.c "sys_rt_sigreturn_wrapper": [], # arch/parisc/kernel/entry.S "parisc_truncate64": [ "const char __user * path", "unsigned int high", "unsigned int low", ], # arch/parisc/kernel/sys_parisc.c "parisc_ftruncate64": [ "unsigned int fd", "unsigned int high", "unsigned int low", ], # arch/parisc/kernel/sys_parisc.c "parisc_readahead": [ "int fd", "unsigned int high", "unsigned int low", "size_t count", ], # arch/parisc/kernel/sys_parisc.c "parisc_fadvise64_64": [ "int fd", "unsigned int high_off", "unsigned int low_off", "unsigned int high_len", "unsigned int low_len", "int advice", ], # arch/parisc/kernel/sys_parisc.c "parisc_sync_file_range": [ "int fd", "u32 hi_off", "u32 lo_off", "u32 hi_nbytes", "u32 lo_nbytes", "unsigned int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_fallocate": [ "int fd", "int mode", "u32 offhi", "u32 offlo", "u32 lenhi", "u32 lenlo", ], # arch/parisc/kernel/sys_parisc.c "parisc_timerfd_create": [ "int clockid", "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_signalfd4": [ "int ufd", "sigset_t __user *user_mask", "size_t sizemask", "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_eventfd2": [ "unsigned int count", "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_pipe2": [ "int __user *fildes", "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_inotify_init1": [ "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_userfaultfd": [ "int flags", ], # arch/parisc/kernel/sys_parisc.c "sys_clone3_wrapper": [ "struct clone_args __user *uargs", "size_t size", ], # arch/parisc/kernel/entry.S (fork_like macro) "parisc_madvise": [ "unsigned long start", "size_t len_in", "int behavior", ], # arch/parisc/kernel/sys_parisc.c "sys_cacheflush": [ "unsigned long addr", "unsigned long bytes", "unsigned int cache", ], # arch/parisc/kernel/cache.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "unsigned int mask_1", "unsigned int mask_2", "int dfd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi not in ["common", "32"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_hppa64(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(hppa_syscall_tbl) arch_specific_dic = { "sys_fork_wrapper": [], # arch/parisc/kernel/entry.S (fork_like macro) "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/parisc/kernel/sys_parisc.c "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long offset", ], # arch/parisc/kernel/sys_parisc.c "sys_vfork_wrapper": [], # arch/parisc/kernel/entry.S (fork_like macro) "sys_clone_wrapper": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int *child_tidptr", ], # arch/parisc/kernel/entry.S (fork_like macro, CONFIG_CLONE_BACKWARDS) "sys_rt_sigreturn_wrapper": [], # arch/parisc/kernel/entry.S "parisc_timerfd_create": [ "int clockid", "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_signalfd4": [ "int ufd", "sigset_t __user *user_mask", "size_t sizemask", "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_eventfd2": [ "unsigned int count", "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_pipe2": [ "int __user *fildes", "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_inotify_init1": [ "int flags", ], # arch/parisc/kernel/sys_parisc.c "parisc_userfaultfd": [ "int flags", ], # arch/parisc/kernel/sys_parisc.c "sys_clone3_wrapper": [ "struct clone_args __user *uargs", "size_t size", ], # arch/parisc/kernel/entry.S (fork_like macro) "parisc_madvise": [ "unsigned long start", "size_t len_in", "int behavior", ], # arch/parisc/kernel/sys_parisc.c "sys_cacheflush": [ "unsigned long addr", "unsigned long bytes", "unsigned int cache", ], # arch/parisc/kernel/cache.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # don't use compat if abi not in ["common", "64"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_or1k(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(or1k_syscall_tbl) arch_specific_dic = { "sys_rt_sigreturn": [], # arch/openrisc/kernel/entry.S "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "void __user *parent_tid", "void __user *child_tid", "int tls", ], # arch/openrisc/include/syscalls.h "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # include/asm-generic/syscalls.h "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c "sys_or1k_atomic": [ "unsigned long type", "unsigned long *v1", "unsigned long *v2", ], # arch/openrisc/include/asm/syscalls.h } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # arch/openrisc/kernel/Makefile.syscalls if abi not in ["common", "32", "or1k", "time32", "stat64", "rlimit", "renameat"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_nios2(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(nios2_syscall_tbl) arch_specific_dic = { "sys_rt_sigreturn": [], # arch/nios2/kernel/entry.S "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "int tls_val", ], # arch/nios2/kernel/entry.S "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # include/asm-generic/syscalls.h "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c "sys_cacheflush": [ "unsigned long addr", "unsigned long len", "unsigned int op", ], # arch/nios2/include/asm/syscalls.h } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # arch/nios2/kernel/Makefile.syscalls if abi not in ["common", "32", "nios2", "time32", "stat64", "renameat", "rlimit"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func in ["sys_clone3"]: # __ARCH_BROKEN_SYS_CLONE3 continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_microblaze(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(microblaze_syscall_tbl) arch_specific_dic = { "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/microblaze/kernel/sys_microblaze.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int stack_size", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS3) "sys_rt_sigreturn_wrapper": [], # arch/microblaze/kernel/entry.S "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/microblaze/kernel/sys_microblaze.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry if abi != "common": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_xtensa(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(xtensa_syscall_tbl) arch_specific_dic = { "xtensa_fadvise64_64": [ "int fd", "int advice", "unsigned long long offset", "unsigned long long len", ], # arch/xtensa/kernel/syscall.c "xtensa_shmat": [ "int shmid", "char __user *shmaddr", "int shmflg", ], # arch/xtensa/kernel/syscall.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int *child_tidptr", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS) "xtensa_rt_sigreturn": [], # arch/xtensa/kernel/signal.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry if abi != "common": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_cris(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(cris_syscall_tbl) arch_specific_dic = { "sys_sigreturn": [], # arch/cris/arch-v10/kernel/signal.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int stack_size", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c (CONFIG_CLONE_BACKWARDS2) "sys_bdflush": [ "int func", "long data", ], # include/linux/syscalls.h "sys_sysctl": [ "struct __sysctl_args __user *args", ], # include/linux/syscalls.h "sys_rt_sigreturn": [], # arch/cris/arch-v10/kernel/signal.c "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/cris/kernel/sys_cris.c "sys_lookup_dcookie": [ "u64 cookie64", "char __user *buf", "size_t, len", ], # fs/dcookies.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry if abi != "cris": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_loongarch64(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(loongarch_syscall_tbl) arch_specific_dic = { "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c "sys_rt_sigreturn": [], # arch/loongarch/kernel/signal.c "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long offset", ], # arch/loongarch/kernel/syscall.c "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry if abi != "loongarch": continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_arc(bit_str): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(arc_syscall_tbl) arch_specific_dic = { "sys_rt_sigreturn": [], # arch/arc/kernel/signal.c "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "unsigned long tls", "int *child_tidptr", ], # arch/arc/kernel/entry.S (sys_clone_wrapper, CONFIG_CLONE_BACKWARDS) "sys_clone3": [ "struct clone_args __user *uargs", "size_t size", ], # arch/arc/kernel/entry.S (sys_clone3_wrapper) "sys_mmap": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long off", ], # include/uapi/asm/unistd.h "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long pgoff", ], # arch/arc/kernel/sys.c (sys_mmap_pgoff) "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c "sys_cacheflush": [ "uint32_t start", "uint32_t sz", "uint32_t flags", ], # arch/arc/mm/cache.c "sys_arc_settls": [ "void* user_tls_data_ptr", ], # arch/arc/kernel/process.c "sys_arc_gettls": [], # arch/arc/kernel/process.c "sys_sysfs": [ "int option", "unsigned long arg1", "unsigned long arg2", ], # fs/filesystems.c "sys_arc_usr_cmpxchg": [ "int __user *uaddr", "int expected", "int new", ], # arch/arc/kernel/process.c } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # arch/arc/kernel/Makefile.syscalls if abi not in ["common", bit_str, "arc", "time32", "renameat", "stat64", "rlimit"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod def make_syscall_list_csky(): sc_def = Syscall.parse_common_syscall_defs() tbl = Syscall.parse_syscall_table_defs(csky_syscall_tbl) arch_specific_dic = { "sys_clone": [ "unsigned long clone_flags", "unsigned long newsp", "int __user *parent_tidptr", "int __user *child_tidptr", "unsigned long tls", ], # kernel/fork.c "sys_rt_sigreturn": [], # arch/csky/kernel/signal.c "sys_mmap2": [ "unsigned long addr", "unsigned long len", "unsigned long prot", "unsigned long flags", "unsigned long fd", "unsigned long offset", ], # arch/csky/kernel/syscall.c "sys_fadvise64_64": [ "int fd", "int advice", "loff_t offset", "loff_t len", ], # arch/csky/include/asm/syscalls.h "sys_fanotify_mark": [ "int fanotify_fd", "unsigned int flags", "u64 mask", "int fd", "const char __user *pathname", ], # fs/notify/fanotify/fanotify_user.c "sys_set_thread_area": [ "unsigned long addr", ], # arch/csky/kernel/signal.c "sys_cacheflush": [ "void __user *addr", "unsigned long len", "int op", ], # arch/csky/include/asm/syscalls.h } syscall_list = [] for entry in tbl: nr, abi, name, func = entry[:4] # arch/csky/kernel/Makefile.syscalls if abi not in ["common", "32", "csky", "time32", "stat64", "rlimit"]: continue # special case if func in arch_specific_dic: syscall_list.append([nr, name, arch_specific_dic[func]]) continue # common case if func == "sys_ni_syscall": continue if func not in sc_def: err("Not found: {:s}".format(func)) raise syscall_list.append([nr, name, sc_def[func]]) return syscall_list @staticmethod @Cache.cache_this_session def make_syscall_table(arch, mode): if arch == "X86" and mode == "64": return_register = X86_64.return_register args_register = X86_64.syscall_parameters syscall_list = Syscall.make_syscall_list_x86_64() elif arch == "X86" and mode == "Emulated-32": return_register = X86.return_register args_register = X86.syscall_parameters syscall_list = Syscall.make_syscall_list_x86_32_emulated() elif arch == "X86" and mode == "Native-32": return_register = X86.return_register args_register = X86.syscall_parameters syscall_list = Syscall.make_syscall_list_x86_32_native() elif arch == "X86" and mode == "16": syscall_list = [] return_register = {} args_register = {} for nr, name, ret_regs, args, arg_regs in x86_16_dos_syscall_list: syscall_list.append([nr, name, args]) return_register[nr] = ret_regs args_register[nr] = arg_regs elif arch == "ARM64" and mode == "ARM": return_register = AARCH64.return_register args_register = AARCH64.syscall_parameters syscall_list = Syscall.make_syscall_list_arm64() elif arch == "ARM" and mode == "Emulated-32": return_register = ARM.return_register args_register = ARM.syscall_parameters syscall_list = Syscall.make_syscall_list_arm32_emulated() # only support EABI elif arch == "ARM" and mode == "Native-32": return_register = ARM.return_register args_register = ARM.syscall_parameters syscall_list = Syscall.make_syscall_list_arm32_native() # only support EABI elif arch == "ARM64" and mode == "Secure-World": return_register = AARCH64.return_register args_register = AARCH64.syscall_parameters + ["$x6"] # OPTEE uses 7 args syscall_list = arm_OPTEE_syscall_list.copy() elif arch == "ARM" and mode == "Secure-World": return_register = ARM.return_register args_register = ARM.syscall_parameters syscall_list = arm_OPTEE_syscall_list.copy() elif arch == "MIPS" and mode == "32": return_register = MIPS.return_register args_register = MIPS.syscall_parameters syscall_list = Syscall.make_syscall_list_mips32() elif arch == "MIPS" and mode == "n32": return_register = MIPSN32.return_register args_register = MIPSN32.syscall_parameters syscall_list = Syscall.make_syscall_list_mipsn32() elif arch == "MIPS" and mode == "64": return_register = MIPS64.return_register args_register = MIPS64.syscall_parameters syscall_list = Syscall.make_syscall_list_mips64() elif arch == "PPC" and mode == "32": return_register = PPC.return_register args_register = PPC.syscall_parameters syscall_list = Syscall.make_syscall_list_ppc32() elif arch == "PPC" and mode == "64": return_register = PPC64.return_register args_register = PPC64.syscall_parameters syscall_list = Syscall.make_syscall_list_ppc64() elif arch == "SPARC" and mode == "32": return_register = SPARC.return_register args_register = SPARC.syscall_parameters syscall_list = Syscall.make_syscall_list_sparc32() elif arch == "SPARC" and mode == "32PLUS": return_register = SPARC32PLUS.return_register args_register = SPARC32PLUS.syscall_parameters syscall_list = Syscall.make_syscall_list_sparc32() # same sparc32 elif arch == "SPARC" and mode == "64": return_register = SPARC64.return_register args_register = SPARC64.syscall_parameters syscall_list = Syscall.make_syscall_list_sparc64() elif arch == "RISCV" and mode == "32": return_register = RISCV.return_register args_register = RISCV.syscall_parameters syscall_list = Syscall.make_syscall_list_riscv32() elif arch == "RISCV" and mode == "64": return_register = RISCV64.return_register args_register = RISCV64.syscall_parameters syscall_list = Syscall.make_syscall_list_riscv64() elif arch == "S390X" and mode == "64": return_register = S390X.return_register args_register = S390X.syscall_parameters syscall_list = Syscall.make_syscall_list_s390x() elif arch == "SH4" and mode == "SH4": return_register = SH4.return_register args_register = SH4.syscall_parameters syscall_list = Syscall.make_syscall_list_sh4() elif arch == "M68K" and mode == "32": return_register = M68K.return_register args_register = M68K.syscall_parameters syscall_list = Syscall.make_syscall_list_m68k() elif arch == "ALPHA" and mode == "ALPHA": return_register = ALPHA.return_register args_register = ALPHA.syscall_parameters syscall_list = Syscall.make_syscall_list_alpha() elif arch == "HPPA" and mode == "32": return_register = HPPA.return_register args_register = HPPA.syscall_parameters syscall_list = Syscall.make_syscall_list_hppa32() elif arch == "HPPA" and mode == "64": return_register = HPPA64.return_register args_register = HPPA64.syscall_parameters syscall_list = Syscall.make_syscall_list_hppa64() elif arch == "OR1K" and mode == "OR1K": return_register = OR1K.return_register args_register = OR1K.syscall_parameters syscall_list = Syscall.make_syscall_list_or1k() elif arch == "NIOS2" and mode == "NIOS2": return_register = NIOS2.return_register args_register = NIOS2.syscall_parameters syscall_list = Syscall.make_syscall_list_nios2() elif arch == "MICROBLAZE" and mode == "MICROBLAZE": return_register = MICROBLAZE.return_register args_register = MICROBLAZE.syscall_parameters syscall_list = Syscall.make_syscall_list_microblaze() elif arch == "XTENSA" and mode == "XTENSA": return_register = XTENSA.return_register args_register = XTENSA.syscall_parameters syscall_list = Syscall.make_syscall_list_xtensa() elif arch == "CRIS" and mode == "CRIS": return_register = CRIS.return_register args_register = CRIS.syscall_parameters syscall_list = Syscall.make_syscall_list_cris() elif arch == "LOONGARCH" and mode == "64": return_register = LOONGARCH64.return_register args_register = LOONGARCH64.syscall_parameters syscall_list = Syscall.make_syscall_list_loongarch64() elif arch == "ARC" and mode in ["32v2", "32"]: return_register = ARC.return_register args_register = ARC.syscall_parameters syscall_list = Syscall.make_syscall_list_arc("32") elif arch == "ARC" and mode in ["32v3"]: return_register = ARCv3.return_register args_register = ARCv3.syscall_parameters syscall_list = Syscall.make_syscall_list_arc("32") elif arch == "ARC" and mode in ["64v3", "64"]: return_register = ARC64.return_register args_register = ARC64.syscall_parameters syscall_list = Syscall.make_syscall_list_arc("64") elif arch == "CSKY" and mode == "CSKY": return_register = CSKY.return_register args_register = CSKY.syscall_parameters syscall_list = Syscall.make_syscall_list_csky() else: return None Table = collections.namedtuple("Table", "arch mode nr_table name_table") syscall_table = Table(arch, mode, {}, {}) # example: # syscall_table.arch: 'X86' # syscall_table.mode: '64' # syscall_table.nr_table[0].nr: 0 # syscall_table.nr_table[0].name: 'read' # syscall_table.nr_table[0].ret_regs: ['$rax'] # syscall_table.nr_table[0].arg_regs: ['$rdi', '$rsi', ...] # syscall_table.nr_table[0].args_full: ['unsigned int fd', ...] # syscall_table.nr_table[0].args: ['fd', ...] # syscall_table.nr_table[1] ... # syscall_table.name_table["read"].nr: 0 # syscall_table.name_table["read"].name: 'read' # syscall_table.name_table["read"].ret_regs: ['$rax'] # syscall_table.name_table["read"].arg_regs: ['$rdi', '$rsi', ...] # syscall_table.name_table["read"].args_full: ['unsigned int fd', ...] # syscall_table.name_table["read"].args: ['fd', ...] # syscall_table.name_table["write"] ... Entry = collections.namedtuple("Entry", "nr name ret_regs arg_regs args_full args") for nr, name, args_full in sorted(syscall_list, key=lambda x: x[0]): # make entry args = [re.split(r" |\*", p)[-1] for p in args_full] if (arch, mode) == ("X86", "16"): entry = Entry(nr, name, return_register[nr], args_register[nr], args_full, args) else: entry = Entry(nr, name, [return_register], args_register[:len(args)], args_full, args) # nr_table syscall_table.nr_table[nr] = entry # name_table if name not in syscall_table.name_table: syscall_table.name_table[name] = entry return syscall_table def get_syscall_table(arch=None, mode=None): if arch is None and mode is None : if is_x86_64(): arch, mode = "X86", "64" elif is_x86_32(): if is_emulated32(): arch, mode = "X86", "Emulated-32" else: arch, mode = "X86", "Native-32" elif is_arm64(): if is_in_secure(): arch, mode = "ARM64", "Secure-World" else: arch, mode = "ARM64", "ARM" elif is_arm32(): if is_in_secure(): arch, mode = "ARM", "Secure-World" elif is_emulated32(): arch, mode = "ARM", "Emulated-32" else: arch, mode = "ARM", "Native-32" elif current_arch: arch = current_arch.arch mode = current_arch.mode else: arch, mode = None, None if arch in ["ARM", "ARM64"] and mode == "S": mode = "Secure-World" if arch in ["X86", "ARM"] and mode == "32": mode = "Emulated-32" elif arch in ["X86", "ARM"] and mode == "N32": mode = "Native-32" return Syscall.make_syscall_table(arch, mode) @register_command class SyscallArgsCommand(GenericCommand): """Get the syscall name and arguments based on the register values in the current state.""" _cmdline_ = "syscall-args" _category_ = "01-a. Debugging Support - Context" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("nr", metavar="SYSCALL_NUM", nargs="?", type=AddressUtil.parse_address, help="syscall number to search.") _syntax_ = parser.format_help() @staticmethod def get_nr(): # str or list syscall_register = current_arch.syscall_register # hppa specific. hppa syscall instruction has a delay slot and _NR may be set there. if is_hppa32() or is_hppa64(): next_insn = Disasm.gef_instruction_n(current_arch.pc, 1) if next_insn.mnemonic == "ldi" and next_insn.operands[1] == "r20": nr = int(next_insn.operands[0], 16) else: # already set nr = get_register(syscall_register) # s390x specific. s390x syscall number may be embedded in the instruction. elif is_s390x(): insn = get_insn() r = re.search(syscall_register[0], str(insn)) nr = int(r.group(1), 0) if nr == 0: syscall_register = syscall_register[1] # use $r1 nr = get_register(syscall_register) else: syscall_register = syscall_register[0] # normal pattern else: nr = get_register(syscall_register) return syscall_register, nr def get_values(self, registers): values = [] for reg in registers: if "+" in reg: # `$sp + 0x10` reg_n, off_n = reg.split("+") values.append(read_int_from_memory(get_register(reg_n) + int(off_n, 0))) elif is_x86_16() and ":" in reg: # $ds:$dx seg, reg = reg.split(":") values.append(current_arch.real2phys(seg, reg)) else: values.append(get_register(reg)) return values def print_syscall(self, syscall_table, syscall_register, nr): if syscall_table: entry = syscall_table.nr_table[nr] syscall_name = entry.name ret_regs = entry.ret_regs arg_regs = entry.arg_regs args_full = entry.args_full args = entry.args arch = syscall_table.arch mode = syscall_table.mode else: syscall_name = None ret_regs = [current_arch.return_register] arg_regs = current_arch.syscall_parameters args_full = None args = ["?"] * len(arg_regs) arch = current_arch.arch mode = current_arch.mode # header info("Detected syscall (arch:{:s}, mode:{:s})".format(arch, mode)) if syscall_name and args_full is not None: gef_print(" " + Color.colorify("{}({})".format(syscall_name, ", ".join(args_full)), "bold yellow")) fmt = "{:<20} {:<20} {}" legend = ["Parameter", "Register", "Value"] info(GefUtil.make_legend(fmt.format(*legend))) # ret for ret in ret_regs: gef_print(" {:<20} {:<20} {:<20}".format("RET", ret, "-")) # syscall number gef_print(" {:<20} {:<20} {:#x}".format("NR", syscall_register, nr)) # syscall args values = self.get_values(arg_regs) for name, register, value in zip(args, arg_regs, values): line = " {:<20} {:<20} ".format(name, register) if value is not None: line += AddressUtil.recursive_dereference_to_string(value) gef_print(line) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("wine",)) @require_arch_set def do_invoke(self, args): if args.nr is not None: syscall_register, nr = "-", args.nr else: syscall_register, nr = SyscallArgsCommand.get_nr() syscall_table = get_syscall_table() if syscall_table and nr not in syscall_table.nr_table: warn("There is no system call for {:#x}".format(nr)) return self.print_syscall(syscall_table, syscall_register, nr) return @register_command class CodebaseCommand(GenericCommand): """Display various base addresses.""" _cmdline_ = "codebase" _category_ = "02-b. Process Information - Base Address" _aliases_ = ["base"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() def define_section_variable(self, elf, bin_base, section_name): sec = elf.get_shdr(section_name) if not sec: return if elf.is_pie(): addr = sec.sh_addr + bin_base else: addr = sec.sh_addr self.quiet_print(titlify(section_name)) var_name = section_name.lstrip(".") self.quiet_print("${:s} = {:#x}".format(var_name, addr)) gdb.execute("set ${:s} = {:#x}".format(var_name, addr)) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) def do_invoke(self, args): # The codebase may be heuristically determined from the memory map. bin_base = ProcessMap.get_codebase() if bin_base is None: self.quiet_err("Could not find the binary base") return self.quiet_print(titlify("code base")) gdb.execute(f"set $codebase = {bin_base:#x}") self.quiet_print(f"$codebase = {bin_base:#x}") gdb.execute(f"set $binbase = {bin_base:#x}") self.quiet_print(f"$binbase = {bin_base:#x}") # Any other area should use a section header. elf = Elf.get_elf() if elf is None or not elf.is_valid(): self.quiet_err("Failed to load an ELF") return self.define_section_variable(elf, bin_base, ".text") self.define_section_variable(elf, bin_base, ".rodata") self.define_section_variable(elf, bin_base, ".data") self.define_section_variable(elf, bin_base, ".bss") return @register_command class HeapbaseCommand(GenericCommand): """Display heap base address.""" _cmdline_ = "heapbase" _category_ = "02-b. Process Information - Base Address" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() @staticmethod def heap_base_from_symbol(force_heuristic): # The value of mp_->sbrk_base is correct in x86 or x64. # However, for architectures that have TLS in the bss area (such as ARM or ARM64), # the start position of the heap seems to shift by the amount of the area used as the TLS variable. # This method should not be used on ARM or ARM64, as there seems to be no way to predetermine the TLS size. if not is_x86(): return None if force_heuristic: return None try: # symbol and type are defined return AddressUtil.parse_address("mp_->sbrk_base") except gdb.error: return None @staticmethod def heap_base_from_info_proc_map(force_heuristic): # For non-static binaries, this is mostly sufficient. if force_heuristic: return None try: codebase = ProcessMap.get_codebase() elf = Elf(codebase) except Exception: return None if elf.is_static(): return None return ProcessMap.get_section_base_address("[heap]") @staticmethod def heap_base_from_tcache(): # If glibc has tcache, there is tcache_perthread_struct* in TLS. # This structure is always allocated in the first chunk, # and can be used to find the starting address of the heap. # This path is useful for old qemu-user emulation, etc. if get_libc_version() < (2, 26): return None # In 2.42 and later, tcache_perthread_struct is not necessarily the first chunk, # so this detection method does not work. # However, glibc 2.42 is used in an Ubuntu 25.10 environment, and in this environment qemu-user is 10.1. # This version of qemu-user can obtain the exact heap base address via `info proc map`, # so the heuristic method should not be necessary. if get_libc_version() >= (2, 42): return None if not current_arch.tls_supported: return None main_arena_addr = GlibcHeap.search_for_main_arena() if main_arena_addr is None: return None tcache_perthread_struct = GlibcHeap.search_for_tcache_from_tls(main_arena_addr) if tcache_perthread_struct is None: return None if is_x86_32() or is_riscv32() or is_ppc32(): chunk_offset = 0x10 else: chunk_offset = current_arch.ptrsize * 2 heap_base = tcache_perthread_struct - chunk_offset return heap_base @staticmethod def heap_base_from_mp(): # However, in the case of qemu-user runs static binary, the heapbase cannot be obtained even with # `info proc map`, and this way may be necessary. main_arena_addr = GlibcHeap.search_for_main_arena() if main_arena_addr is None: return None try: codebase = ProcessMap.get_codebase() elf = Elf(codebase) except Exception: return None if not elf.is_static(): return None """ 0x0000004dd160|+0x0000|+000: trim_threshold : 0x0000000000020000 0x0000004dd168|+0x0008|+001: top_pad : 0x0000000000020000 0x0000004dd170|+0x0010|+002: mmap_threshold : 0x0000000000020000 0x0000004dd178|+0x0018|+003: arena_test : 0x0000000000000008 0x0000004dd180|+0x0020|+004: arena_max : 0x0000000000000000 0x0000004dd188|+0x0028|+005: thp_pagesize : 0x0000000000000000 0x0000004dd190|+0x0030|+006: hp_pagesize : 0x0000000000000000 0x0000004dd198|+0x0038|+007: n_mmaps+hp_flags : 0x0000000000000000 0x0000004dd1a0|+0x0040|+008: max_n_mmaps+n_mmaps_max: 0x0000000000010000 0x0000004dd1a8|+0x0048|+009: no_dyn_threshold : 0x0000000000000000 0x0000004dd1b0|+0x0050|+010: mmaped_mem : 0x0000000000000000 0x0000004dd1b8|+0x0058|+011: max_mmaped_mem : 0x0000000000000000 0x0000004dd1c0|+0x0060|+012: sbrk_base : 0x00000000004e5d40 <----- here 0x0000004dd1c8|+0x0068|+013: tcache_small_bins : 0x0000000000000040 0x0000004dd1d0|+0x0070|+014: tcache_max_bytes : 0x0000000000000411 0x0000004dd1d8|+0x0078|+015: tcache_count : 0x0000000000000007 0x0000004dd1e0|+0x0080|+016: tcache_unsorted_limit : 0x0000000000000000 0x0000004dd1e8|+0x0088|+017: : 0x0000000000000000 0x0000004dd1f0|+0x0090|+018: : 0x0000000000000000 0x0000004dd1f8|+0x0098|+019: : 0x0000000000000000 0x0000004dd200|+0x00a0|+020: main_arena : 0x0000000000000000 0x0000004dd208|+0x00a8|+021: : 0x0000000000000001 0x0000004dd210|+0x00b0|+022: : 0x00000000005165e0 0x0000004dd218|+0x00b8|+023: : 0x0000000000000000 0x0000004dd220|+0x00c0|+024: : 0x0000000000000000 0x0000004dd228|+0x00c8|+025: : 0x0000000000000000 """ for i in range(1, 20): x = main_arena_addr - current_arch.ptrsize * i if not is_valid_addr(x): break y = read_int_from_memory(x) if is_valid_addr(y): return y return None @staticmethod @Cache.cache_this_session_skip_None_cache def heap_base(force_heuristic=False): heap_base = HeapbaseCommand.heap_base_from_symbol(force_heuristic) if is_valid_addr(heap_base): return heap_base heap_base = HeapbaseCommand.heap_base_from_info_proc_map(force_heuristic) if is_valid_addr(heap_base): return heap_base heap_base = HeapbaseCommand.heap_base_from_tcache() if is_valid_addr(heap_base): return heap_base heap_base = HeapbaseCommand.heap_base_from_mp() if is_valid_addr(heap_base): return heap_base return None @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): heap = HeapbaseCommand.heap_base() if heap is None: err("Could not find the heap") return self.quiet_print(titlify("Heap base")) gdb.execute(f"set $heapbase = {heap:#x}") self.quiet_print(f"$heapbase = {heap:#x}") return @register_command class LibcCommand(GenericCommand): """Display libc base address.""" _cmdline_ = "libc" _category_ = "02-b. Process Information - Base Address" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): super().__init__() self.add_setting("assume_version", "()", "The default libc version") return @staticmethod def is_same_filename(a, b): # If the file names match, they are considered to be the same file. if os.path.basename(a) == os.path.basename(b): return True # If `a` is the destination of a symbolic link, compare whether resolving `b` results in `a`. a_dir = os.path.join(os.path.dirname(a)) b_file = os.path.basename(b) ab_path = os.path.join(a_dir, b_file) while os.path.islink(ab_path): ab_path = os.path.normpath(os.path.join(os.path.dirname(ab_path), os.readlink(ab_path))) return os.path.basename(a) == os.path.basename(ab_path) def libc_calc_hash(self, libc_targets): libc = ProcessMap.process_lookup_path(libc_targets) real_libc_path = None if is_container_attach(): real_libc_path = Path.append_proc_root(libc.path) if not os.path.exists(real_libc_path): return data = open(real_libc_path, "rb").read() elif is_remote_debug(): if is_qemu_user(): data = None for maps in ProcessMap.get_process_maps(outer=True): if not LibcCommand.is_same_filename(maps.path, libc.path): continue real_libc_path = maps.path data = open(real_libc_path, "rb").read() break else: data = Path.read_remote_file(libc.path) if not data: return else: if not os.path.exists(libc.path): return data = open(libc.path, "rb").read() gef_print("path:\t{:s}{:s}".format(libc.path, " (remote)" if is_remote_debug() else "")) if real_libc_path: gef_print("path:\t{:s} (real)".format(real_libc_path)) gef_print("sha512:\t{:s}".format(hashlib.sha512(data).hexdigest())) gef_print("sha256:\t{:s}".format(hashlib.sha256(data).hexdigest())) gef_print("sha1:\t{:s}".format(hashlib.sha1(data).hexdigest())) gef_print("md5:\t{:s}".format(hashlib.md5(data).hexdigest())) pos = re.search(b"(GNU C Library|uClibc-ng release) [\x20-\x7e]*", data) if pos: gef_print("ver:\t{:s}".format(String.bytes2str(pos.group(0)))) return def show_libc_assume_version(self): self.quiet_print(titlify("GEF libc info")) try: v = get_libc_version(verbose=True) gef_print("GEF recognized: {}".format(v)) except Exception: gef_print("GEF recognized: None") info("If version detection is failing, you can fix it with: `gef config libc.assume_version (2,39)`") return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) def do_invoke(self, args): Cache.reset_gef_caches(all=True) # get_process_maps may be caching old information libc_targets = ( "libc-2.", "libc.so.6", # glibc "libuClibc-", "/libc.so.0", # uClibc ) libc = ProcessMap.get_section_base_address_by_list(libc_targets) if libc is None: err("Could not find the libc") if not args.quiet: self.show_libc_assume_version() return self.quiet_print(titlify("libc info")) gdb.execute(f"set $libc = {libc:#x}") self.quiet_print(f"$libc = {libc:#x}") if not args.quiet: self.libc_calc_hash(libc_targets) self.show_libc_assume_version() return @register_command class LdCommand(GenericCommand): """Display ld base address.""" _cmdline_ = "ld" _category_ = "02-b. Process Information - Base Address" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() def ld_calc_hash(self, ld_targets): ld = ProcessMap.process_lookup_path(ld_targets) real_ld_path = None if is_container_attach(): real_ld_path = Path.append_proc_root(ld.path) if not os.path.exists(real_ld_path): return data = open(real_ld_path, "rb").read() elif is_remote_debug(): if is_qemu_user(): data = None for maps in ProcessMap.get_process_maps(outer=True): if not LibcCommand.is_same_filename(maps.path, ld.path): continue real_ld_path = maps.path data = open(real_ld_path, "rb").read() break else: data = Path.read_remote_file(ld.path) if not data: return else: if not os.path.exists(ld.path): return data = open(ld.path, "rb").read() gef_print("path:\t{:s}{:s}".format(ld.path, " (remote)" if is_remote_debug() else "")) if real_ld_path: gef_print("path:\t{:s} (real)".format(real_ld_path)) gef_print("sha512:\t{:s}".format(hashlib.sha512(data).hexdigest())) gef_print("sha256:\t{:s}".format(hashlib.sha256(data).hexdigest())) gef_print("sha1:\t{:s}".format(hashlib.sha1(data).hexdigest())) gef_print("md5:\t{:s}".format(hashlib.md5(data).hexdigest())) pos = re.search(b"ld.so [\x20-\x7e]+ version [\x20-\x7e]*", data) if pos: gef_print("ver:\t{:s}".format(String.bytes2str(pos.group(0)))) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) def do_invoke(self, args): Cache.reset_gef_caches(all=True) # get_process_maps may be caching old information ld_targets = ( "ld-2.", "ld-linux-", "ld-linux.", # glibc "ld64-uClibc-", "ld-uClibc-", "ld64-uClibc.", "ld-uClibc.", # uClibc ) ld = ProcessMap.get_section_base_address_by_list(ld_targets) if ld is None: err("Could not find the ld") return self.quiet_print(titlify("ld info")) gdb.execute(f"set $ld = {ld:#x}") self.quiet_print(f"$ld = {ld:#x}") if not args.quiet: self.ld_calc_hash(ld_targets) return @register_command class MagicCommand(GenericCommand): """Display useful userland addresses and offsets.""" _cmdline_ = "magic" _category_ = "02-g. Process Information - Symbol" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-s", "--smart", action="store_true", help="show only the most frequently used items.") parser.add_argument("-j", "--print-file-jumps", action="store_true", help="print _IO_xxx_jumps functions.") parser.add_argument("filter", metavar="FILTER", nargs="*", help="filter string.") _syntax_ = parser.format_help() def should_be_print(self, sym): if not self.args.filter: return True for filt in self.args.filter: if filt in sym: return True return False def resolve_and_print(self, sym, base): if not self.should_be_print(sym): return width = AddressUtil.get_format_address_width() try: addr = int(gdb.parse_and_eval(f"&{sym}")) except gdb.error: gef_print("{:45s} {:>{:d}s}".format(sym, "Not found", width)) return addr = ProcessMap.lookup_address(addr) perm = addr.section.permission if is_ascii_string(addr.value): val = read_cstring_from_memory(addr.value) gef_print("{:45s} {!s} [{!s}] (+{:#010x}) -> {:s}".format( sym, addr, perm, addr.value - base, val, )) else: val = ProcessMap.lookup_address(read_int_from_memory(addr.value)) val_sym = Symbol.get_symbol_string(val.value) gef_print("{:45s} {!s} [{!s}] (+{:#010x}) -> {:s}{:s}".format( sym, addr, perm, addr.value - base, val.long_fmt(), val_sym, )) return def resolve_and_print_fj(self, sym, base): self.resolve_and_print(sym, base) if not self.should_be_print(sym): return if not self.args.print_file_jumps: return try: vtable = int(gdb.parse_and_eval(f"&{sym}")) except Exception: return gdb.execute("dereference {:#x} 22 --no-pager".format(vtable)) return def magic(self): codebase = ProcessMap.get_codebase() libc = ProcessMap.get_section_base_address_by_list(("libc-2.", "libc.so.6")) ld = ProcessMap.get_section_base_address_by_list(("ld-2.", "ld-linux-", "ld-linux.so.2")) if libc is None or ld is None: gef_print("libc/ld not found") return gef_print(titlify("Legend")) fmt = "{:45s} {:{:d}s} {:5s} (+{:10s}) -> {:{:d}s}" width = AddressUtil.get_format_address_width() legend = ["Symbol", "Addr", width, "Perm", "Offset", "Value", width] gef_print(GefUtil.make_legend(fmt.format(*legend))) gef_print(titlify("Heap")) if not self.args.smart: self.resolve_and_print("main_arena", libc) self.resolve_and_print("mp_", libc) self.resolve_and_print("__malloc_hook", libc) self.resolve_and_print("__free_hook", libc) self.resolve_and_print("__realloc_hook", libc) if not self.args.smart: self.resolve_and_print("__memalign_hook", libc) self.resolve_and_print("__after_morecore_hook", libc) self.resolve_and_print("_dl_open_hook", libc) self.resolve_and_print("global_max_fast", libc) self.resolve_and_print("malloc", libc) self.resolve_and_print("free", libc) self.resolve_and_print("calloc", libc) self.resolve_and_print("realloc", libc) gef_print(titlify("I/O")) self.resolve_and_print("*stdin", libc) self.resolve_and_print("*stdout", libc) self.resolve_and_print("*stderr", libc) self.resolve_and_print("_IO_list_all", libc) if not self.args.smart: if get_libc_version() < (2, 38): self.resolve_and_print_fj("_IO_file_jumps", libc) self.resolve_and_print_fj("_IO_file_jumps_mmap", libc) self.resolve_and_print_fj("_IO_file_jumps_maybe_mmap", libc) self.resolve_and_print_fj("_IO_wfile_jumps", libc) self.resolve_and_print_fj("_IO_wfile_jumps_mmap", libc) self.resolve_and_print_fj("_IO_wfile_jumps_maybe_mmap", libc) self.resolve_and_print_fj("_IO_old_file_jumps", libc) self.resolve_and_print_fj("_IO_mem_jumps", libc) self.resolve_and_print_fj("_IO_wmem_jumps", libc) self.resolve_and_print_fj("_IO_str_jumps", libc) self.resolve_and_print_fj("_IO_strn_jumps", libc) self.resolve_and_print_fj("_IO_str_chk_jumps", libc) self.resolve_and_print_fj("_IO_wstr_jumps", libc) self.resolve_and_print_fj("_IO_wstrn_jumps", libc) self.resolve_and_print_fj("_IO_streambuf_jumps", libc) self.resolve_and_print_fj("_IO_proc_jumps", libc) self.resolve_and_print_fj("_IO_old_proc_jumps", libc) self.resolve_and_print_fj("_IO_helper_jumps", libc) self.resolve_and_print_fj("_IO_cookie_jumps", libc) self.resolve_and_print_fj("_IO_obstack_jumps", libc) else: self.resolve_and_print_fj("__io_vtables[IO_STR_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_WSTR_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_FILE_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_FILE_JUMPS_MMAP]", libc) self.resolve_and_print_fj("__io_vtables[IO_FILE_JUMPS_MAYBE_MMAP]", libc) self.resolve_and_print_fj("__io_vtables[IO_WFILE_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_WFILE_JUMPS_MMAP]", libc) self.resolve_and_print_fj("__io_vtables[IO_WFILE_JUMPS_MAYBE_MMAP]", libc) self.resolve_and_print_fj("__io_vtables[IO_COOKIE_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_PROC_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_MEM_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_WMEM_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_PRINTF_BUFFER_AS_FILE_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_WPRINTF_BUFFER_AS_FILE_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_OLD_FILE_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_OLD_PROC_JUMPS]", libc) self.resolve_and_print_fj("__io_vtables[IO_OLD_COOKIED_JUMPS]", libc) self.resolve_and_print("open", libc) self.resolve_and_print("read", libc) self.resolve_and_print("write", libc) if not self.args.smart: self.resolve_and_print("dup", libc) self.resolve_and_print("dup2", libc) self.resolve_and_print("dup3", libc) self.resolve_and_print("puts", libc) self.resolve_and_print("gets", libc) self.resolve_and_print("fputs", libc) self.resolve_and_print("fgets", libc) self.resolve_and_print("printf", libc) self.resolve_and_print("fprintf", libc) self.resolve_and_print("dprintf", libc) self.resolve_and_print("sprintf", libc) self.resolve_and_print("snprintf", libc) self.resolve_and_print("__printf_chk", libc) self.resolve_and_print("__fprintf_chk", libc) self.resolve_and_print("__dprintf_chk", libc) self.resolve_and_print("__sprintf_chk", libc) self.resolve_and_print("__snprintf_chk", libc) self.resolve_and_print("__printf_function_table", libc) self.resolve_and_print("__printf_arginfo_table", libc) self.resolve_and_print("scanf", libc) self.resolve_and_print("fscanf", libc) self.resolve_and_print("sscanf", libc) gef_print(titlify("Process")) self.resolve_and_print("system", libc) if not self.args.smart: self.resolve_and_print("do_system", libc) self.resolve_and_print("execve", libc) self.resolve_and_print("setcontext", libc) if not self.args.smart: self.resolve_and_print("__libc_start_main", libc) self.resolve_and_print("syscall", libc) self.resolve_and_print("ptrace", libc) self.resolve_and_print("prctl", libc) if not self.args.smart: gef_print(titlify("Memory")) self.resolve_and_print("mmap", libc) self.resolve_and_print("munmap", libc) self.resolve_and_print("mremap", libc) self.resolve_and_print("mprotect", libc) gef_print(titlify("Stack")) self.resolve_and_print("__libc_argv", libc) self.resolve_and_print("__environ", libc) gef_print(titlify("Destructor")) self.resolve_and_print("_rtld_global->_dl_rtld_lock_recursive", ld) self.resolve_and_print("_rtld_global->_dl_rtld_unlock_recursive", ld) self.resolve_and_print("error_print_progname", libc) gef_print(titlify("Unwind")) self.resolve_and_print("'DW.ref.__gxx_personality_v0'", codebase) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("wine", "kgdb")) @require_arch_set def do_invoke(self, args): if is_qemu_system() or is_vmware(): info("Redirect to kmagic") gdb.execute("kmagic {:s}".format(" ".join(args.filter))) return self.magic() return @register_command class KernelMagicCommand(GenericCommand): """Display useful kernel addresses and offsets.""" _cmdline_ = "kmagic" _category_ = "06-c. Qemu-system/KGDB Cooperation - Linux Basic" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("filter", metavar="FILTER", nargs="*", help="filter string.") _syntax_ = parser.format_help() def should_be_print(self, sym): if not self.args.filter: return True for filt in self.args.filter: if filt in sym: return True return False def resolve_and_print_kernel(self, sym, base, maps, external_func=None, to_string=False): def get_permission(addr, maps): if maps is None: return "???" for vaddr, size, perm in maps: if vaddr <= addr and addr < vaddr + size: return perm return "???" if not self.should_be_print(sym): return width = AddressUtil.get_format_address_width() if external_func: try: addr = external_func() except Exception: gef_print("{:42s} {:>{:d}s}".format(sym, "Not found", width)) return if addr is None: gef_print("{:42s} {:>{:d}s}".format(sym, "Not found", width)) return else: if isinstance(sym, str): addr = Symbol.get_ksymaddr(sym) if addr is None: gef_print("{:42s} {:>{:d}s}".format(sym, "Not found", width)) return elif isinstance(sym, list): for s in sym: addr = Symbol.get_ksymaddr(s) if addr: sym = s break else: sym = sym[0] gef_print("{:42s} {:>{:d}s}".format(sym, "Not found", width)) return if not is_valid_addr(addr): gef_print("{:42s} {:#0{:d}x} [---] -> Inaccessible".format( sym, addr, width, )) return perm = get_permission(addr, maps) if base is None: val = read_int_from_memory(addr) gef_print("{:42s} {:#0{:d}x} [{:3s}] -> {:#0{:d}x}".format( sym, addr, width, perm, val, width, )) elif to_string: val = read_cstring_from_memory(addr) or "???" gef_print("{:42s} {:#0{:d}x} [{:3s}] (+{:#010x}) -> {:s}".format( sym, addr, width, perm, addr - base, val, )) else: val = read_int_from_memory(addr) gef_print("{:42s} {:#0{:d}x} [{:3s}] (+{:#010x}) -> {:#0{:d}x}".format( sym, addr, width, perm, addr - base, val, width, )) return def magic_kernel(self): info("Wait for memory scan") kversion = Kernel.kernel_version() kinfo = Kernel.get_kernel_layout() maps = kinfo.maps text_base = kinfo.text_base text_size = kinfo.text_size if text_base is None or text_size is None: return gef_print("{:42s} {:#x} ({:#x} bytes)".format("kernel_base", text_base, text_size)) gef_print(titlify("Legend")) fmt = "{:42s} {:{:d}s} {:5s} (+{:10s}) -> {:{:d}s}" width = AddressUtil.get_format_address_width() legend = ["Symbol", "Addr", width, "Perm", "Offset", "Value", width] gef_print(GefUtil.make_legend(fmt.format(*legend))) gef_print(titlify("Credential")) self.resolve_and_print_kernel("commit_creds", text_base, maps) self.resolve_and_print_kernel("prepare_kernel_cred", text_base, maps) self.resolve_and_print_kernel( "init_cred", text_base, maps, KernelAddressHeuristicFinder.get_init_cred, ) self.resolve_and_print_kernel(["sys_setuid", "__sys_setuid"], text_base, maps) self.resolve_and_print_kernel( "init_task", text_base, maps, KernelAddressHeuristicFinder.get_init_task, ) gef_print(titlify("Usermode helper")) self.resolve_and_print_kernel("call_usermodehelper", text_base, maps) self.resolve_and_print_kernel("run_cmd", text_base, maps) self.resolve_and_print_kernel( "modprobe_path", text_base, maps, KernelAddressHeuristicFinder.get_modprobe_path, to_string=True, ) self.resolve_and_print_kernel("orderly_poweroff", text_base, maps) self.resolve_and_print_kernel( "poweroff_cmd", text_base, maps, KernelAddressHeuristicFinder.get_poweroff_cmd, to_string=True, ) self.resolve_and_print_kernel( "core_pattern", text_base, maps, KernelAddressHeuristicFinder.get_core_pattern, to_string=True, ) gef_print(titlify("ROP finalizer")) if is_x86_64(): self.resolve_and_print_kernel( "swapgs_restore_regs_and_return_to_usermode", text_base, maps, ) self.resolve_and_print_kernel("msleep", text_base, maps) gef_print(titlify("Memory protection modifier")) if is_x86(): self.resolve_and_print_kernel("native_write_cr0", text_base, maps) self.resolve_and_print_kernel("native_write_cr4", text_base, maps) self.resolve_and_print_kernel("set_memory_rw", text_base, maps) self.resolve_and_print_kernel("set_memory_x", text_base, maps) gef_print(titlify("Memory patcher")) if is_x86(): self.resolve_and_print_kernel("text_poke", text_base, maps) self.resolve_and_print_kernel("memcpy", text_base, maps) if is_x86(): self.resolve_and_print_kernel(["_copy_to_user", "copy_to_user"], text_base, maps) self.resolve_and_print_kernel(["_copy_from_user", "copy_from_user"], text_base, maps) elif is_arm32(): self.resolve_and_print_kernel(["arm_copy_to_user", "__copy_to_user"], text_base, maps) self.resolve_and_print_kernel(["arm_copy_from_user", "__copy_from_user"], text_base, maps) elif is_arm64(): self.resolve_and_print_kernel("__arch_copy_to_user", text_base, maps) self.resolve_and_print_kernel("__arch_copy_from_user", text_base, maps) gef_print(titlify("Memory remapper")) self.resolve_and_print_kernel(["ioremap", "__ioremap", "ioremap_cache"], text_base, maps) self.resolve_and_print_kernel(["iounmap", "__iounmap"], text_base, maps) if is_x86(): gef_print(titlify("Automatically called function pointer")) self.resolve_and_print_kernel("kvm_clock", text_base, maps) self.resolve_and_print_kernel( "clocksource_tsc", text_base, maps, KernelAddressHeuristicFinder.get_clocksource_tsc, ) gef_print(titlify("Function pointer table")) self.resolve_and_print_kernel( "capability_hooks", text_base, maps, KernelAddressHeuristicFinder.get_capability_hooks, ) self.resolve_and_print_kernel( "n_tty_ops", text_base, maps, KernelAddressHeuristicFinder.get_n_tty_ops, ) gef_print(titlify("Function pointer table array")) self.resolve_and_print_kernel( "tty_ldiscs", text_base, maps, KernelAddressHeuristicFinder.get_tty_ldiscs, ) gef_print(titlify("Allocator")) self.resolve_and_print_kernel("__kmalloc", text_base, maps) self.resolve_and_print_kernel(["kzalloc", "kzalloc.constprop.0"], text_base, maps) self.resolve_and_print_kernel("kfree", text_base, maps) self.resolve_and_print_kernel(["kzfree", "kfree_sensitive"], text_base, maps) self.resolve_and_print_kernel( "slab_caches", text_base, maps, KernelAddressHeuristicFinder.get_slab_caches, ) gef_print(titlify("Dynamic resolver")) self.resolve_and_print_kernel("kallsyms_lookup_name", text_base, maps) if is_x86_64(): if kversion and "3.16" <= kversion: gef_print(titlify("vDSO")) self.resolve_and_print_kernel( "vdso_image_64", text_base, maps, KernelAddressHeuristicFinder.get_vdso_image_64, ) self.resolve_and_print_kernel( "vdso_image_32", text_base, maps, KernelAddressHeuristicFinder.get_vdso_image_32, ) self.resolve_and_print_kernel( "vdso_image_x32", text_base, maps, KernelAddressHeuristicFinder.get_vdso_image_x32, ) elif is_x86_32(): if kversion and "3.16" <= kversion: gef_print(titlify("vDSO")) self.resolve_and_print_kernel( "vdso_image_32", text_base, maps, KernelAddressHeuristicFinder.get_vdso_image_32, ) elif is_arm64(): if kversion and "5.8" <= kversion: gef_print(titlify("vDSO")) self.resolve_and_print_kernel( "vdso_info", text_base, maps, KernelAddressHeuristicFinder.get_vdso_info, ) self.resolve_and_print_kernel( "vdso_start", text_base, maps, KernelAddressHeuristicFinder.get_vdso_start, ) self.resolve_and_print_kernel( "vdso32_start", text_base, maps, KernelAddressHeuristicFinder.get_vdso32_start, ) elif kversion and "5.3" <= kversion: gef_print(titlify("vDSO")) self.resolve_and_print_kernel( "vdso_lookup", text_base, maps, KernelAddressHeuristicFinder.get_vdso_lookup, ) self.resolve_and_print_kernel( "vdso_start", text_base, maps, KernelAddressHeuristicFinder.get_vdso_start, ) self.resolve_and_print_kernel( "vdso32_start", text_base, maps, KernelAddressHeuristicFinder.get_vdso32_start, ) elif kversion and "3.7" <= kversion: gef_print(titlify("vDSO")) self.resolve_and_print_kernel( "vdso_start", text_base, maps, KernelAddressHeuristicFinder.get_vdso_start, ) elif is_arm32(): if kversion and "4.1" <= kversion: gef_print(titlify("vDSO")) self.resolve_and_print_kernel( "vdso_start", text_base, maps, KernelAddressHeuristicFinder.get_vdso_start, ) gef_print(titlify("Others")) self.resolve_and_print_kernel(["do_fchmodat", "sys_fchmodat"], text_base, maps) self.resolve_and_print_kernel( "mmap_min_addr", text_base, maps, KernelAddressHeuristicFinder.get_mmap_min_addr, ) self.resolve_and_print_kernel( "__per_cpu_offset", text_base, maps, KernelAddressHeuristicFinder.get_per_cpu_offset, ) if is_x86(): gef_print(titlify("Descriptor Table")) self.resolve_and_print_kernel( "IDT base (fixed address?)", None, maps, KernelAddressHeuristicFinder.get_idt_base, ) self.resolve_and_print_kernel( "GDT base (fixed address?)", None, maps, KernelAddressHeuristicFinder.get_gdt_base, ) self.resolve_and_print_kernel( "LDT base (fixed address?)", None, maps, KernelAddressHeuristicFinder.get_ldt_base, ) self.resolve_and_print_kernel( "TSS base (fixed address?)", None, maps, KernelAddressHeuristicFinder.get_tss_base, ) gef_print(titlify("Memory base")) self.resolve_and_print_kernel( "PAGE_OFFSET (physmem direct map)", None, maps, KernelAddressHeuristicFinder.get_PAGE_OFFSET, ) self.resolve_and_print_kernel( "PAGE_OFFSET_END", None, maps, KernelAddressHeuristicFinder.get_PAGE_OFFSET_END, ) self.resolve_and_print_kernel( "VMALLOC_START", None, maps, KernelAddressHeuristicFinder.get_VMALLOC_START, ) self.resolve_and_print_kernel( "VMALLOC_END", None, maps, KernelAddressHeuristicFinder.get_VMALLOC_END, ) if is_x86_64() or is_arm64(): self.resolve_and_print_kernel( "VMEMMAP_START (struct page[])", None, maps, KernelAddressHeuristicFinder.get_VMEMMAP_START, ) self.resolve_and_print_kernel( "VMEMMAP_END", None, maps, KernelAddressHeuristicFinder.get_VMEMMAP_END, ) if is_x86_64(): self.resolve_and_print_kernel( "phys_base (kbase@phys)", text_base, maps, KernelAddressHeuristicFinder.get_phys_base, ) if is_x86_32() or is_arm32(): self.resolve_and_print_kernel( "mem_map (struct page[])", None, maps, KernelAddressHeuristicFinder.get_mem_map, ) self.resolve_and_print_kernel( "mem_section (struct page[][])", None, maps, KernelAddressHeuristicFinder.get_mem_section, ) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.magic_kernel() return @register_command class OneGadgetCommand(GenericCommand): """Invoke `one_gadget`.""" _cmdline_ = "onegadget" _category_ = "01-i. Debugging Support - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-s", "--apply-smart-filter", action="store_true", help="filter valid gadgets for the current register and memory values (x64 only).") _syntax_ = parser.format_help() def parse_exp(self, exp): orig_exp = exp[::] # for debug # preparation exp = re.sub(r"(xmm\d+)", "$\\1.uint128", exp) exp = exp.replace("NULL", "0") exp = exp.replace("(u16)", "(unsigned short)") exp = exp.replace("(s16)", "(signed short)") exp = exp.replace("(u32)", "(unsigned int)") exp = exp.replace("(s32)", "(signed int)") exp = exp.replace("(u64)", "(unsigned long long)") exp = exp.replace("(s64)", "(signed long long)") # fix register name for regname in current_arch.general_registers: exp = exp.replace(regname[1:], "((unsigned long)" + regname + ")") # enclose both sides in parentheses if "==" in exp: exp = ["(" + e + ")" for e in exp.split("==")] exp = "==".join(exp) # fix memory accessing while True: m = re.search(r"(\[[^\[\]]+\])", exp) # find innermost [...] if not m: break prefix = exp[:m.regs[1][0]] target = m.group(1)[1:-1] # skip "[", "]" suffix = exp[m.regs[1][1]:] target = "(*(unsigned long*)(" + target + "))" exp = prefix + target + suffix # evaluate try: return AddressUtil.parse_address(exp) except (gdb.MemoryError, MemoryError): pass except Exception: err("Could not handle") err("before: " + orig_exp) err("after : " + exp) return None def get_filtered_result(self, one_gadget_command, libc_path): res = GefUtil.gef_execute_external([one_gadget_command, libc_path, "-l", "1"], as_list=True) res_groups = "\n".join(res).split("\n\n") gadgets = [line.split("\n") for line in res_groups] valid_lines = [] for g in gadgets: valid = True for constraints in g[2:]: constraints = constraints.strip() # pattern 1: address rsp+0x60 is writable m = re.match(r"address (\S+) is writable", constraints) if m: ret = self.parse_exp(m.group(1)) if ret is None: continue addr = ProcessMap.lookup_address(ret) if not addr.valid or not addr.section.is_writable(): valid &= False break continue # pattern 2: A || B sub_valid = False for sub_constraints in constraints.split(" || "): # pattern 2-1: {"sh", rax, rip+0x17302e, r12, ...} is a valid argv if "is a valid" in sub_constraints: # Accurate evaluation is impossible. # This condition is rarely met, so it is always considered invalid. sub_valid |= False continue # pattern 2-2: writable: rdi if "writable:" in sub_constraints: exp = sub_constraints.split(": ")[-1] ret = self.parse_exp(exp) if ret is None: continue addr = ProcessMap.lookup_address(ret) if addr.valid and addr.section.is_writable(): sub_valid = True break continue # pattern 2-3: rsp & 0xf == 0 # (u64)xmm0 == NULL # rdx == NULL # (s32)[rdx+0x4] <= 0 exp = sub_constraints if self.parse_exp(sub_constraints): sub_valid = True break continue if sub_valid is False: valid = False continue if valid: valid_lines.extend(g) valid_lines.append("") return "\n".join(valid_lines) @parse_args @only_if_gdb_running @only_if_gdb_target_local @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): try: one_gadget_command = GefUtil.which("one_gadget") except FileNotFoundError: err("Missing `one_gadget`, install with: `gem install one_gadget`") return if args.apply_smart_filter and not is_x86_64(): err("Unsupported (x64 only)") return libc = ProcessMap.process_lookup_path(("libc-2.", "libc.so.6")) if libc is None: err("Could not find the libc") return gef_print(titlify("{!r} {!r} -l 1".format(one_gadget_command, libc.path))) if args.apply_smart_filter: condition = Color.boldify("`... is a valid ...`") false = Color.boldify("false") info("The condition {:s} is always assumed to be {:s}".format(condition, false)) res = self.get_filtered_result(one_gadget_command, libc.path) gef_print(res) else: os.system("{!r} {!r} -l 1".format(one_gadget_command, libc.path)) return @register_command class SeccompCommand(GenericCommand): """Invoke `ceccomp` or `seccomp-tools`.""" _cmdline_ = "seccomp" _category_ = "01-i. Debugging Support - Other" _aliases_ = ["ceccomp"] parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=False) group.add_argument("-c", "--force-ceccomp", action="store_true", help="force use ceccomp.") group.add_argument("-s", "--force-seccomp-tools", action="store_true", help="force use seccomp-tools.") _syntax_ = parser.format_help() _note_ = [ "Default: Search `ceccomp` -> `seccomp-tools`, and use found one.", "With `-c` or `-s`: Forces GEF to use the specified one.", ] _note_ = "\n".join(_note_) def get_ceccomp_command(self): try: comm = GefUtil.which("ceccomp") return [f"{comm!r} trace --quiet ", f"{comm!r} probe --quiet "] except FileNotFoundError: err("Missing `ceccomp`, install from https://github.com/dbgbgtf1/Ceccomp") return None def get_seccomp_tools_command(self): try: comm = GefUtil.which("seccomp-tools") return [f"{comm!r} dump "] except FileNotFoundError: err("Missing `seccomp-tools`, install with: `gem install seccomp-tools`") return None def get_either_command(self): try: comm = GefUtil.which("ceccomp") return [f"{comm!r} trace --quiet ", f"{comm!r} probe --quiet "] except FileNotFoundError: try: comm = GefUtil.which("seccomp-tools") return [f"{comm!r} dump "] except FileNotFoundError: err("Missing both `ceccomp` and `seccomp-tools`") err("install with `gem install seccomp-tools` or build `ceccomp`") return None @parse_args @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): if args.force_seccomp_tools: ret = self.get_seccomp_tools_command() elif args.force_ceccomp: ret = self.get_ceccomp_command() else: ret = self.get_either_command() if ret is None: return commands = ret path = Path.get_filepath() if path is None: err("Could not find the target binary") return for comm in commands: comm += f"{path!r}" gef_print(titlify(comm)) os.system(comm) return @register_command class SysregCommand(GenericCommand): """Pretty-print system registers (not general purpose) from `info register`.""" _cmdline_ = "sysreg" _category_ = "04-a. Register - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("filter", metavar="FILTER", nargs="*", help="filter string.") parser.add_argument("--exact", action="store_true", help="use exact match.") _syntax_ = parser.format_help() def get_non_generic_regs(self): if is_riscv64() or is_riscv32(): res = gdb.execute("info registers system", to_string=True) else: res = gdb.execute("info registers", to_string=True) res = res.strip() regs = {} for line in res.splitlines(): m = re.match(r"^(\S+)\s*(0x\S+)", line) if not m: continue regname, regvalue = m.group(1), m.group(2) if self.args.filter: if self.args.exact: if not any(f.lower() == regname.lower() for f in self.args.filter): continue else: if not any(f.lower() in regname.lower() for f in self.args.filter): continue regs[regname] = int(regvalue, 16) regs = list(filter(lambda x: "$" + x[0] not in current_arch.all_registers, sorted(regs.items()))) return regs # [[regname, regvalue], ...] def print_sysreg_compact(self): regs = self.get_non_generic_regs() if regs: gef_print(titlify("System registers")) else: gef_print("Could not find non generic regs") return COLUMN = 3 length = len(regs) length_of_each_bank = (length + COLUMN - 1) // COLUMN for i in range(length_of_each_bank): out = [] for j in range(COLUMN): if len(regs) > i + j * length_of_each_bank: msg = "{:25s} = {:#18x}".format(*regs[i + j * length_of_each_bank]) if regs[i + j * length_of_each_bank][1] > 0: msg = Color.boldify(msg) out.append(msg) else: out.append("") gef_print(" | ".join(out)) return @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): if is_kgdb() and not kgdb_has_system_registers(): err("Unsupported in kgdb mode without access to system registers") return self.print_sysreg_compact() return @register_command class MmxSetCommand(GenericCommand): """Simply set the value to mm register.""" _cmdline_ = "mmxset" _category_ = "04-b. Register - Modify" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("reg_and_value", metavar="REG=VALUE", help="MMX register and value to set.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $mm0=0x1122334455667788", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Disable `-enable-kvm` option for qemu-system.", ] _note_ = "\n".join(_note_) def execute_movq_mm(self, value, reg): REG_CODE = { "$mm0": b"\x0f\x6f\x00", # movq mm0, qword ptr [rax] "$mm1": b"\x0f\x6f\x08", # movq mm1, qword ptr [rax] "$mm2": b"\x0f\x6f\x10", # movq mm2, qword ptr [rax] "$mm3": b"\x0f\x6f\x08", # movq mm3, qword ptr [rax] "$mm4": b"\x0f\x6f\x20", # movq mm4, qword ptr [rax] "$mm5": b"\x0f\x6f\x28", # movq mm5, qword ptr [rax] "$mm6": b"\x0f\x6f\x30", # movq mm6, qword ptr [rax] "$mm7": b"\x0f\x6f\x38", # movq mm7, qword ptr [rax] } codes = [REG_CODE[reg] + p64(value)] # movq mm0, [rax]; db value if is_x86_64(): regs = {"$rax": current_arch.pc + 5} # points to value else: regs = {"$eax": current_arch.pc + 5} # points to value ExecAsm(codes, regs=regs).exec_code() return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64")) @only_if_kvm_disabled def do_invoke(self, args): # arg parse try: reg, value = args.reg_and_value.split("=") value = int(value, 0) except ValueError: self.usage() return # check register is valid or not if reg not in ["$mm{:d}".format(i) for i in range(8)]: err("Invalid register name") return # modify self.execute_movq_mm(value, reg) return @register_command class MmxCommand(GenericCommand): """Display MMX registers.""" _cmdline_ = "mmx" _category_ = "04-a. Register - View" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def print_mmx(self): gef_print(titlify("MMX Register (from fpu register)")) regs = [] for i in range(8): regname = "$st{:d}".format(i) result = gdb.execute(f"info registers $st{i}", to_string=True) r = re.findall(r"\(raw (0x[0-9a-f]+)\)", result) if r: reg = int(r[0], 16) & 0xffff_ffff_ffff_ffff regs.append(reg) fstat = get_register("$fstat") top_of_stack = (fstat >> 11) & 0b111 regs = regs[-top_of_stack:] + regs[:-top_of_stack] # need rotate. because mmx0 != st(0) fmt = "{:5s}: {:s}" legend = ["Name", "64-bit hex"] gef_print(GefUtil.make_legend(fmt.format(*legend))) red = lambda x: Color.colorify("{:s}".format(x), "bold red") for i in range(len(regs)): regname = "$mm{:d}".format(i) reghex = "" for j in range(8): c = (regs[i] >> (8 * j)) & 0xff reghex += chr(c) if 0x20 <= c < 0x7f else "." gef_print("{:s} : {:#018x} | {:s} |".format(red(regname), regs[i], reghex)) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb",)) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): self.print_mmx() return @register_command class XmmSetCommand(GenericCommand): """Simply set the value to xmm or ymm register.""" _cmdline_ = "xmmset" _category_ = "04-b. Register - Modify" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("reg_and_value", metavar="REG=VALUE", help="XMM/YMM/ZMM register and value to set.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $ymm0=0x11223344556677889900aabbccddeeff9876543210", ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): # arg parse try: reg, value = args.reg_and_value.split("=") value = int(value, 0) except Exception: self.usage() return # check register is valid or not try: gdb.execute(f"info registers {reg}", to_string=True) except gdb.error: err("Invalid register name") return # modify if "$xmm" in reg: for i in range(2): v = (value >> (64 * i)) & ((1 << 64) - 1) gdb.execute(f"set {reg}.v2_int64[{i}]={v:#x}", to_string=True) elif "$ymm" in reg: for i in range(4): v = (value >> (64 * i)) & ((1 << 64) - 1) gdb.execute(f"set {reg}.v4_int64[{i}]={v:#x}", to_string=True) elif "$zmm" in reg: for i in range(8): v = (value >> (64 * i)) & ((1 << 64) - 1) gdb.execute(f"set {reg}.v8_int64[{i}]={v:#x}", to_string=True) else: err("Unsupported") return @register_command class SseCommand(GenericCommand): """Display SSE registers.""" _cmdline_ = "sse" _category_ = "04-a. Register - View" _aliases_ = ["xmm"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-v", "--verbose", action="store_true", help="also display bit information of mxcsr registers.") _syntax_ = parser.format_help() def print_sse(self): gef_print(titlify("SSE Data Register")) # xmm0-15 regs = [] for i in range(16 if is_x86_64() else 8): result = gdb.execute(f"info registers $xmm{i}", to_string=True) r = re.findall(r"uint128 = (0x[0-9a-f]+)", result) if r: reg = int(r[0], 16) regs.append(reg) fmt = "{:7s}: {:s}" legend = ["Name", "128-bit hex"] gef_print(GefUtil.make_legend(fmt.format(*legend))) red = lambda x: Color.colorify("{:s}".format(x), "bold red") for i in range(len(regs)): if i == 8: gef_print("* xmm8-15 are introduced by AVX") reghex = "" for j in range(16): c = (regs[i] >> (8 * j)) & 0xff reghex += chr(c) if 0x20 <= c < 0x7f else "." regname = "$xmm{:<2d}".format(i) gef_print("{:s} : {:#034x} | {:s} |".format(red(regname), regs[i], reghex)) return def print_sse_other(self): # mxcsr gef_print(titlify("MXCSR (MXCSR Control and Status Register)")) bit_info = [ [15, "FZ", "Flush To Zero"], [[13, 14], "RC", "Rounding Control", "00: Round To Nearest, 01: Round Negative, 10: Round Positive, 11: Round To Zero"], [12, "PM", "Precision Exception Mask"], [11, "UM", "Underflow Exception Mask"], [10, "OM", "Overflow Exception Mask"], [9, "ZM", "Zero Divide Exception Mask"], [8, "DM", "Denormalized Opernad Exception Mask"], [7, "IM", "Invalid Operation Exception Mask"], [6, "DAZ", "Use as 0.0 if input data is denormalized"], [5, "PE", "Precision Exception"], [4, "UE", "Underflow Exception"], # codespell:ignore [3, "OE", "Overflow Exception"], [2, "ZE", "Zero Divide Exception"], [1, "DE", "Denormalized Operand Exception"], [0, "IE", "Invalid Operation Exception"], ] reg = int(gdb.execute("info registers $mxcsr", to_string=True).split()[1], 16) BitInfo("$mxcsr", 32, bit_info).print(reg) return @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb",)) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, argv): if "-h" in argv: self.usage() return self.print_sse() if "-v" in argv: self.print_sse_other() else: info("for $mxcsr flags description, use `-v`") return @register_command class AvxCommand(GenericCommand): """Display AVX registers.""" _cmdline_ = "avx" _category_ = "04-a. Register - View" _aliases_ = ["ymm"] parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def print_avx(self): regs = [] for i in range(16 if is_x86_64() else 8): try: result = gdb.execute(f"info registers $ymm{i}", to_string=True) except gdb.error: continue result = result.replace("\n", "") r = re.findall(r"v2_int128 = \{" r".*?\[0x0\] = (0x[0-9a-f]+)," r".*?\[0x1\] = (0x[0-9a-f]+)" r".*?\}", result) if r: reg = (int(r[0][1], 16) << 128) + int(r[0][0], 16) regs.append(reg) if regs: gef_print(titlify("AVX Register")) fmt = "{:7s}: {:s}" legend = ["Name", "256-bit hex"] gef_print(GefUtil.make_legend(fmt.format(*legend))) red = lambda x: Color.colorify("{:s}".format(x), "bold red") for i in range(len(regs)): regname = "$ymm{:<2d}".format(i) reghex = "" for j in range(32): c = (regs[i] >> (8 * j)) & 0xff reghex += chr(c) if 0x20 <= c < 0x7f else "." gef_print("{:s} : {:#066x} | {:s} |".format(red(regname), regs[i], reghex)) else: err("Could not find avx registers") return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb",)) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): self.print_avx() return @register_command class Avx512Command(GenericCommand): """Display AVX512 registers.""" _cmdline_ = "avx512" _category_ = "04-a. Register - View" _aliases_ = ["zmm"] parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def print_avx512(self): regs = [] for i in range(32): try: result = gdb.execute(f"info registers $zmm{i}", to_string=True) except gdb.error: continue result = result.replace("\n", "") r = re.findall(r"v4_int128 = \{" r".*?\[0x0\] = (0x[0-9a-f]+)," r".*?\[0x1\] = (0x[0-9a-f]+)," r".*?\[0x2\] = (0x[0-9a-f]+)," r".*?\[0x3\] = (0x[0-9a-f]+)" r".*?\}", result) if r: reg = int(r[0][0], 16) reg += (int(r[0][1], 16) << 128) reg += (int(r[0][2], 16) << 256) reg += (int(r[0][3], 16) << 384) regs.append(reg) if regs: gef_print(titlify("AVX Register")) fmt = "{:7s}: {:s}" legend = ["Name", "512-bit hex"] gef_print(GefUtil.make_legend(fmt.format(*legend))) red = lambda x: Color.colorify("{:s}".format(x), "bold red") for i in range(len(regs)): regname = "$zmm{:<2d}".format(i) reghex = "" for j in range(64): c = (regs[i] >> (8 * j)) & 0xff reghex += chr(c) if 0x20 <= c < 0x7f else "." gef_print("{:s} : {:#0130x} | {:s} |".format(red(regname), regs[i], reghex)) else: err("Could not find avx512 registers") return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb",)) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): self.print_avx512() return @register_command class FpuCommand(GenericCommand): """Display fpu registers (x86/x64:x87-fpu, ARM/ARM64:vfp-d16).""" _cmdline_ = "fpu" _category_ = "04-a. Register - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-v", "--verbose", action="store_true", help="also display bit information of fpu control registers.") _syntax_ = parser.format_help() def f2u(self, a): u = lambda a: struct.unpack("> 11) & 0b111 regs = ["mm{:d}".format(i) for i in range(8)] regs = regs[top_of_stack:] + regs[:top_of_stack] # need rotate. because mmx0 != st(0) for i in range(8): regname = "$st{:d}".format(i) result = gdb.execute("info registers {}".format(regname), to_string=True) if "invalid" in result: r = re.findall(r"\(raw (0x[0-9a-f]+)\)", result) u80 = int(r[0], 16) u64 = 0xfff8_0000_0000_0000 # nan u32 = 0xffc0_0000 # nan gef_print("{:4s}({:3s}) : {:<27s}\t{:<#24x} {:<#18x} {:<#10x}".format( red(regname), regs[i], "", u80, u64, u32, )) else: reg = float(result.split()[1]) u80 = self.d2u80(reg) u64 = self.d2u(reg) u32 = self.f2u(reg) gef_print("{:4s}({:3s}) : {:<+.20e}\t{:<#24x} {:<#18x} {:<#10x}".format( red(regname), regs[i], reg, u80, u64, u32, )) info('XWORD: Real register value; Used at "fstp xword ptr [rax]".') info('QWORD: Used at "fst/fstp qword ptr [rax]".') info('DWORD: Used at "fst/fstp dword ptr [rax]".') return def print_fpu_arm_other(self): # fpscr gef_print(titlify("FPSCR (Floating-Point Status and Control Register)")) bit_info = [ [31, "N", "Negative condition flag"], [30, "Z", "Zero condition flag"], [29, "C", "Carry condition flag"], [28, "V", "Overflow condition flag"], [27, "QC", "Cumulative saturation bit"], [26, "AHP", "Alternative Half-Precision Control"], [25, "DN", "Default NaN mode Control"], [24, "FZ", "Flush-to-zero mode Control"], [[22, 23], "RMode", "Rounding Control", "00: Round To Nearest, 01: Round Positive, 10: Round Negative, 11: Round To Zero"], [[20, 21], "Stride", "", "IMPLEMENTATION DEFINED"], [19, "FZ16", "Flush-to-zero mode Control", "When FEAT_FP16 is implemented"], [[16, 17, 18], "Len", "", "IMPLEMENTATION DEFINED"], [15, "IDE", "Input Denormal floating-point Exception trap enable"], [12, "IXE", "Inexact floating-point Exception trap enable"], [11, "UFE", "Underflow floating-point Exception trap enable"], [10, "OFE", "Overflow floating-point Exception trap enable"], [9, "DZE", "Divide by Zero floating-point Exception trap enable"], [8, "IOE", "Invalid Operation floating-point Exception trap enable"], [7, "IDC", "Input Denormal Cumulative floating-point exception bit"], [4, "IXC", "Inexact Cumulative floating-point exception bit"], [3, "UFC", "Underflow Cumulative floating-point exception bit"], [2, "OFC", "Overflow Cumulative floating-point exception bit"], [1, "DZC", "Divide by Zero Cumulative floating-point exception bit"], [0, "IOC", "Invalid Operation Cumulative floating-point exception bit"], ] reg = get_register("$fpscr") if reg is not None: BitInfo("$fpscr", 32, bit_info).print(reg) else: warn("Failed to get the value") # fpsid gef_print(titlify("FPSID (Floating-Point System ID Register)")) bit_info = [ [range(24, 32), "Implementer", "Implementer code"], [23, "SW", "Software bit", "Implementation of floating point instructions, 0:HW, 1:SW"], [range(16, 23), "Subarchitecture", "Subarchitecture version number"], [range(8, 16), "PartNum", "Part number", "IMPLEMENTATION DEFINED"], [[4, 5, 6, 7], "Variant", "Variant number", "IMPLEMENTATION DEFINED"], [[0, 1, 2, 3], "Revision", "Revisino number", "IMPLEMENTATION DEFINED"], ] impl = { 0x00: "Reserved for software use", 0xc0: "Ampere Computing", 0x41: "Arm Limited", 0x42: "Broadcom Corporation", 0x43: "Cavium Inc.", 0x44: "Digital Equipment Corporation", 0x46: "Fujitsu Ltd.", 0x49: "Infineon Technologies AG", 0x4d: "Motorola or Freescale Semiconductor Inc.", 0x4e: "NVIDIA Corporation", 0x50: "Applied Micro Circuits Corporation", 0x51: "Qualcomm Inc.", 0x56: "Marvell International Ltd.", 0x69: "Intel Corporation", } reg = get_register("$fpsid") if reg is not None: BitInfo("$fpsid", 32, bit_info).print(reg) gef_print("Implementer code") for k, v in impl.items(): gef_print(" {:#02x}: {:s}".format(k, v)) else: warn("Failed to get the value") # fpexc gef_print(titlify("FPEXC (Floating-Point Exception Control Register)")) bit_info = [ [31, "EX", "Exception bit"], [30, "EN", "Enables access to the Advanced SIMD and floating-point functionality from all Exception levels"], [29, "DEX", "Defined synchronous exception on floating-point execution"], [28, "FP2V", "FPINST2 instruction valid bit"], [27, "VV", "VECITR valid bit"], [26, "TFV", "Trapped Fault Valid bit"], [[8, 9, 10], "VECITR", "Vector iteration count"], [7, "IDF", "Input Denormal trapped exception bit"], [4, "IXF", "Inexact trapped exception bit"], [3, "UFF", "Underflow trapped exception bit"], [2, "OFF", "Overflow trapped exception bit"], [1, "DZF", "Divide by Zero trapped exception bit"], [0, "IOF", "Invalid Operation trapped exception bit"], ] reg = get_register("$fpexc") if reg is not None: BitInfo("$fpexc", 32, bit_info).print(reg) else: warn("Failed to get the value") return def print_fpu_arm64_other(self): # fpcr gef_print(titlify("FPCR (Floating-Point Control Register)")) bit_info = [ [26, "AHP", "Alternative Half-Precision Control"], [25, "DN", "Default NaN mode Control"], [24, "FZ", "Flush-to-zero mode Control"], [[22, 23], "RMode", "Rounding Control", "00: Round To Nearest, 01: Round Positive, 10: Round Negative, 11: Round To Zero"], [[20, 21], "Stride", "", "Unused"], [19, "FZ16", "Flush-to-zero mode Control", "When FEAT_FP16 is implemented"], [[16, 17, 18], "Len", "", "Unused"], [15, "IDE", "Input Denormal floating-point Exception trap enable"], [12, "IXE", "Inexact floating-point Exception trap enable"], [11, "UFE", "Underflow floating-point Exception trap enable"], [10, "OFE", "Overflow floating-point Exception trap enable"], [9, "DZE", "Divide by Zero floating-point Exception trap enable"], [8, "IOE", "Invalid Operation floating-point Exception trap enable"], ] reg = get_register("$fpcr") if reg is not None: BitInfo("$fpcr", 32, bit_info).print(reg) # fpsr gef_print(titlify("FPCR (Floating-Point Status Register)")) bit_info = [ [31, "N", "", "Unused, see $cpsr"], [30, "Z", "", "Unused, see $cpsr"], [29, "C", "", "Unused, see $cpsr"], [28, "V", "", "Unused, see $cpsr"], [27, "QC", "Cumulative saturation bit"], [7, "IDC", "Input Denormal Cumulative floating-point exception bit"], [4, "IXC", "Inexact Cumulative floating-point exception bit"], [3, "UFC", "Underflow Cumulative floating-point exception bit"], [2, "OFC", "Overflow Cumulative floating-point exception bit"], [1, "DZC", "Divide by Zero Cumulative floating-point exception bit"], [0, "IOC", "Invalid Operation Cumulative floating-point exception bit"], ] reg = get_register("$fpsr") if reg is not None: BitInfo("$fpsr", 32, bit_info).print(reg) return def print_fpu_x86_other(self): # fctrl gef_print(titlify("FCTRL (x87 FPU Control Word)")) bit_info = [ [12, "X", "Infinity Control"], [[10, 11], "RC", "Rounding Control", "00: Round To Nearest, 01: Round Negative, 10: Round Positive, 11: Round To Zero"], [[8, 9], "PC", "Precision Control", "00: Single Precision, 01: Reserved, 10: Double Precision, 11: Double-Extended Precision"], [5, "PM", "Precision Exception Mask"], [4, "UM", "Underflow Exception Mask"], [3, "OM", "Overflow Exception Mask"], [2, "ZM", "Zero Divide Exception Mask"], [1, "DM", "Denormalized Opernd Exception Mask"], [0, "IM", "Invalid Operation Exception Mask"], ] reg = get_register("$fctrl") BitInfo("$fctrl", 16, bit_info).print(reg) # fstat gef_print(titlify("FSTAT (x87 FPU Status Word)")) bit_info = [ [15, "B", "FPU Busy"], [14, "C3", "Condition Code"], [[11, 12, 13], "TOP", "Top of Stack Pointer"], [10, "C2", "Condition Code"], [9, "C1", "Condition Code"], [8, "C0", "Condition Code"], [7, "ES", "Exception Summary Status"], [6, "SF", "Stack Fault"], [5, "PE", "Precision Exception"], [4, "UE", "Underflow Exception"], # codespell:ignore [3, "OE", "Overflow Exception"], [2, "ZE", "Zero Divide Exception"], [1, "DE", "Denormalized Operand Exception"], [0, "IE", "Invalid Operation Exception"], ] reg = get_register("$fstat") BitInfo("$fstat", 16, bit_info).print(reg) # ftag gef_print(titlify("FTAG (x87 FPU Tag Word)")) bit_info = [ [[14, 15], "TAG(7)", "Reg7 Tag", "00: Valid, 01: Zero, 10: Invalid/Nan/Inf/Denormal, 11: Blank"], [[12, 13], "TAG(6)", "Reg6 Tag", "00: Valid, 01: Zero, 10: Invalid/Nan/Inf/Denormal, 11: Blank"], [[10, 11], "TAG(5)", "Reg5 Tag", "00: Valid, 01: Zero, 10: Invalid/Nan/Inf/Denormal, 11: Blank"], [[8, 9], "TAG(4)", "Reg4 Tag", "00: Valid, 01: Zero, 10: Invalid/Nan/Inf/Denormal, 11: Blank"], [[6, 7], "TAG(3)", "Reg3 Tag", "00: Valid, 01: Zero, 10: Invalid/Nan/Inf/Denormal, 11: Blank"], [[4, 5], "TAG(2)", "Reg2 Tag", "00: Valid, 01: Zero, 10: Invalid/Nan/Inf/Denormal, 11: Blank"], [[2, 3], "TAG(1)", "Reg1 Tag", "00: Valid, 01: Zero, 10: Invalid/Nan/Inf/Denormal, 11: Blank"], [[0, 1], "TAG(0)", "Reg0 Tag", "00: Valid, 01: Zero, 10: Invalid/Nan/Inf/Denormal, 11: Blank"], ] reg = get_register("$ftag") BitInfo("$ftag", 16, bit_info).print(reg) # $fiseg, $fioff gef_print(titlify("FCS:FIP (x87 FPU Last Instruction Pointer)")) reg = get_register("$fiseg") BitInfo("$fiseg(FCS)", 16).print(reg) reg = get_register("$fioff") BitInfo("$fioff(FIP)", 32).print(reg) # $foseg, $fooff gef_print(titlify("FDS:FDP (x87 FPU Last Data(Operand) Pointer)")) reg = get_register("$foseg") BitInfo("$foseg(FDS)", 16).print(reg) reg = get_register("$fooff") BitInfo("$fooff(FDP)", 32).print(reg) # $fop gef_print(titlify("FOP (x87 FPU Last Instruction Opcode)")) reg = get_register("$fop") BitInfo("$fop", 11).print(reg) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("kgdb",)) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): if is_x86(): self.print_fpu_x86() elif is_arm32() or is_arm64(): self.print_fpu_arm() if args.verbose: if is_x86(): self.print_fpu_x86_other() elif is_arm32(): self.print_fpu_arm_other() elif is_arm64(): self.print_fpu_arm64_other() else: info("for fpu other register's flags description, use `-v`") return @register_command class ErrnoCommand(GenericCommand, BufferingOutput): """Convert errno (or argument) to its string representation.""" _cmdline_ = "errno" _category_ = "02-d. Process Information - Trivial Information" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("errno", metavar="ERRNO", nargs="?", type=AddressUtil.parse_address, help="show specific errno definitions.") parser.add_argument("-a", "--all", action="store_true", help="show all errno definitions.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @staticmethod def get_errno_dict(): ERRNO_BASE_DICT = { 0 : ["-", "No error"], # include/uapi/asm-generic/errno-base.h 1 : ["EPERM", "Operation not permitted"], 2 : ["ENOENT", "No such file or directory"], 3 : ["ESRCH", "No such process"], 4 : ["EINTR", "Interrupted system call"], 5 : ["EIO", "I/O error"], 6 : ["ENXIO", "No such device or address"], 7 : ["E2BIG", "Argument list too long"], 8 : ["ENOEXEC", "Exec format error"], 9 : ["EBADF", "Bad file number"], 10 : ["ECHILD", "No child processes"], 11 : ["EAGAIN", "Try again"], 12 : ["ENOMEM", "Out of memory"], 13 : ["EACCES", "Permission denied"], 14 : ["EFAULT", "Bad address"], 15 : ["ENOTBLK", "Block device required"], 16 : ["EBUSY", "Device or resource busy"], 17 : ["EEXIST", "File exists"], 18 : ["EXDEV", "Cross-device link"], 19 : ["ENODEV", "No such device"], 20 : ["ENOTDIR", "Not a directory"], 21 : ["EISDIR", "Is a directory"], 22 : ["EINVAL", "Invalid argument"], 23 : ["ENFILE", "File table overflow"], 24 : ["EMFILE", "Too many open files"], 25 : ["ENOTTY", "Not a typewriter"], 26 : ["ETXTBSY", "Text file busy"], 27 : ["EFBIG", "File too large"], 28 : ["ENOSPC", "No space left on device"], 29 : ["ESPIPE", "Illegal seek"], 30 : ["EROFS", "Read-only file system"], 31 : ["EMLINK", "Too many links"], 32 : ["EPIPE", "Broken pipe"], 33 : ["EDOM", "Math argument out of domain of func"], 34 : ["ERANGE", "Math result not representable"], } ERRNO_DICT = { # include/uapi/asm-generic/errno.h 35 : ["EDEADLK", "Resource deadlock would occur"], 36 : ["ENAMETOOLONG", "File name too long"], 37 : ["ENOLCK", "No record locks available"], 38 : ["ENOSYS", "Invalid system call number"], 39 : ["ENOTEMPTY", "Directory not empty"], 40 : ["ELOOP", "Too many symbolic links encountered"], # 41 42 : ["ENOMSG", "No message of desired type"], 43 : ["EIDRM", "Identifier removed"], 44 : ["ECHRNG", "Channel number out of range"], 45 : ["EL2NSYNC", "Level 2 not synchronized"], 46 : ["EL3HLT", "Level 3 halted"], 47 : ["EL3RST", "Level 3 reset"], 48 : ["ELNRNG", "Link number out of range"], 49 : ["EUNATCH", "Protocol driver not attached"], 50 : ["ENOCSI", "No CSI structure available"], 51 : ["EL2HLT", "Level 2 halted"], 52 : ["EBADE", "Invalid exchange"], 53 : ["EBADR", "Invalid request descriptor"], 54 : ["EXFULL", "Exchange full"], 55 : ["ENOANO", "No anode"], 56 : ["EBADRQC", "Invalid request code"], 57 : ["EBADSLT", "Invalid slot"], # 58 59 : ["EBFONT", "Bad font file format"], 60 : ["ENOSTR", "Device not a stream"], 61 : ["ENODATA", "No data available"], 62 : ["ETIME", "Timer expired"], 63 : ["ENOSR", "Out of streams resources"], 64 : ["ENONET", "Machine is not on the network"], 65 : ["ENOPKG", "Package not installed"], 66 : ["EREMOTE", "Object is remote"], 67 : ["ENOLINK", "Link has been severed"], 68 : ["EADV", "Advertise error"], 69 : ["ESRMNT", "Srmount error"], 70 : ["ECOMM", "Communication error on send"], 71 : ["EPROTO", "Protocol error"], 72 : ["EMULTIHOP", "Multihop attempted"], 73 : ["EDOTDOT", "RFS specific error"], 74 : ["EBADMSG", "Not a data message"], 75 : ["EOVERFLOW", "Value too large for defined data type"], 76 : ["ENOTUNIQ", "Name not unique on network"], 77 : ["EBADFD", "File descriptor in bad state"], 78 : ["EREMCHG", "Remote address changed"], 79 : ["ELIBACC", "Can not access a needed shared library"], 80 : ["ELIBBAD", "Accessing a corrupted shared library"], 81 : ["ELIBSCN", ".lib section in a.out corrupted"], 82 : ["ELIBMAX", "Attempting to link in too many shared libraries"], 83 : ["ELIBEXEC", "Cannot exec a shared library directly"], 84 : ["EILSEQ", "Illegal byte sequence"], 85 : ["ERESTART", "Interrupted system call should be restarted"], 86 : ["ESTRPIPE", "Streams pipe error"], 87 : ["EUSERS", "Too many users"], 88 : ["ENOTSOCK", "Socket operation on non-socket"], 89 : ["EDESTADDRREQ", "Destination address required"], 90 : ["EMSGSIZE", "Message too long"], 91 : ["EPROTOTYPE", "Protocol wrong type for socket"], 92 : ["ENOPROTOOPT", "Protocol not available"], 93 : ["EPROTONOSUPPORT", "Protocol not supported"], 94 : ["ESOCKTNOSUPPORT", "Socket type not supported"], 95 : ["EOPNOTSUPP", "Operation not supported on transport endpoint"], 96 : ["EPFNOSUPPORT", "Protocol family not supported"], 97 : ["EAFNOSUPPORT", "Address family not supported by protocol"], 98 : ["EADDRINUSE", "Address already in use"], 99 : ["EADDRNOTAVAIL", "Cannot assign requested address"], 100 : ["ENETDOWN", "Network is down"], 101 : ["ENETUNREACH", "Network is unreachable"], 102 : ["ENETRESET", "Network dropped connection because of reset"], 103 : ["ECONNABORTED", "Software caused connection abort"], 104 : ["ECONNRESET", "Connection reset by peer"], 105 : ["ENOBUFS", "No buffer space available"], 106 : ["EISCONN", "Transport endpoint is already connected"], 107 : ["ENOTCONN", "Transport endpoint is not connected"], 108 : ["ESHUTDOWN", "Cannot send after transport endpoint shutdown"], 109 : ["ETOOMANYREFS", "Too many references: cannot splice"], 110 : ["ETIMEDOUT", "Connection timed out"], 111 : ["ECONNREFUSED", "Connection refused"], 112 : ["EHOSTDOWN", "Host is down"], 113 : ["EHOSTUNREACH", "No route to host"], 114 : ["EALREADY", "Operation already in progress"], 115 : ["EINPROGRESS", "Operation now in progress"], 116 : ["ESTALE", "Stale file handle"], 117 : ["EUCLEAN", "Structure needs cleaning"], 118 : ["ENOTNAM", "Not a XENIX named type file"], 119 : ["ENAVAIL", "No XENIX semaphores available"], 120 : ["EISNAM", "Is a named type file"], 121 : ["EREMOTEIO", "Remote I/O error"], 122 : ["EDQUOT", "Quota exceeded"], 123 : ["ENOMEDIUM", "No medium found"], 124 : ["EMEDIUMTYPE", "Wrong medium type"], 125 : ["ECANCELED", "Operation Canceled"], 126 : ["ENOKEY", "Required key not available"], 127 : ["EKEYEXPIRED", "Key has expired"], 128 : ["EKEYREVOKED", "Key has been revoked"], 129 : ["EKEYREJECTED", "Key was rejected by service"], 130 : ["EOWNERDEAD", "Owner died"], 131 : ["ENOTRECOVERABLE", "State not recoverable"], 132 : ["ERFKILL", "Operation not possible due to RF-kill"], 133 : ["EHWPOISON", "Memory page has hardware error"], } if is_alpha(): ERRNO_DICT = { # arch/alpha/include/uapi/asm/errno.h 11 : ["EDEADLK", "Resource deadlock would occur"], # override # 35 : ["EAGAIN", "Try again"], 36 : ["EINPROGRESS", "Operation now in progress"], 37 : ["EALREADY", "Operation already in progress"], 38 : ["ENOTSOCK", "Socket operation on non-socket"], 39 : ["EDESTADDRREQ", "Destination address required"], 40 : ["EMSGSIZE", "Message too long"], 41 : ["EPROTOTYPE", "Protocol wrong type for socket"], 42 : ["ENOPROTOOPT", "Protocol not available"], 43 : ["EPROTONOSUPPORT", "Protocol not supported"], 44 : ["ESOCKTNOSUPPORT", "Socket type not supported"], 45 : ["EOPNOTSUPP", "Operation not supported on transport endpoint"], 46 : ["EPFNOSUPPORT", "Protocol family not supported"], 47 : ["EAFNOSUPPORT", "Address family not supported by protocol"], 48 : ["EADDRINUSE", "Address already in use"], 49 : ["EADDRNOTAVAIL", "Cannot assign requested address"], 50 : ["ENETDOWN", "Network is down"], 51 : ["ENETUNREACH", "Network is unreachable"], 52 : ["ENETRESET", "Network dropped connection because of reset"], 53 : ["ECONNABORTED", "Software caused connection abort"], 54 : ["ECONNRESET", "Connection reset by peer"], 55 : ["ENOBUFS", "No buffer space available"], 56 : ["EISCONN", "Transport endpoint is already connected"], 57 : ["ENOTCONN", "Transport endpoint is not connected"], 58 : ["ESHUTDOWN", "Cannot send after transport endpoint shutdown"], 59 : ["ETOOMANYREFS", "Too many references: cannot splice"], 60 : ["ETIMEDOUT", "Connection timed out"], 61 : ["ECONNREFUSED", "Connection refused"], 62 : ["ELOOP", "Too many symbolic links encountered"], 63 : ["ENAMETOOLONG", "File name too long"], 64 : ["EHOSTDOWN", "Host is down"], 65 : ["EHOSTUNREACH", "No route to host"], 66 : ["ENOTEMPTY", "Directory not empty"], # 67 68 : ["EUSERS", "Too many users"], 69 : ["EDQUOT", "Quota exceeded"], 70 : ["ESTALE", "Stale file handle"], 71 : ["EREMOTE", "Object is remote"], # 72-76 77 : ["ENOLCK", "No record locks available"], 78 : ["ENOSYS", "Function not implemented"], # 79 80 : ["ENOMSG", "No message of desired type"], 81 : ["EIDRM", "Identifier removed"], 82 : ["ENOSR", "Out of streams resources"], 83 : ["ETIME", "Timer expired"], 84 : ["EBADMSG", "Not a data message"], 85 : ["EPROTO", "Protocol error"], 86 : ["ENODATA", "No data available"], 87 : ["ENOSTR", "Device not a stream"], 88 : ["ECHRNG", "Channel number out of range"], 89 : ["EL2NSYNC", "Level 2 not synchronized"], 90 : ["EL3HLT", "Level 3 halted"], 91 : ["EL3RST", "Level 3 reset"], 92 : ["ENOPKG", "Package not installed"], 93 : ["ELNRNG", "Link number out of range"], 94 : ["EUNATCH", "Protocol driver not attached"], 95 : ["ENOCSI", "No CSI structure available"], 96 : ["EL2HLT", "Level 2 halted"], 97 : ["EBADE", "Invalid exchange"], 98 : ["EBADR", "Invalid request descriptor"], 99 : ["EXFULL", "Exchange full"], 100 : ["ENOANO", "No anode"], 101 : ["EBADRQC", "Invalid request code"], 102 : ["EBADSLT", "Invalid slot"], # 103 104 : ["EBFONT", "Bad font file format"], 105 : ["ENONET", "Machine is not on the network"], 106 : ["ENOLINK", "Link has been severed"], 107 : ["EADV", "Advertise error"], 108 : ["ESRMNT", "Srmount error"], 109 : ["ECOMM", "Communication error on send"], 110 : ["EMULTIHOP", "Multihop attempted"], 111 : ["EDOTDOT", "RFS specific error"], 112 : ["EOVERFLOW", "Value too large for defined data type"], 113 : ["ENOTUNIQ", "Name not unique on network"], 114 : ["EBADFD", "File descriptor in bad state"], 115 : ["EREMCHG", "Remote address changed"], 116 : ["EILSEQ", "Illegal byte sequence"], 117 : ["EUCLEAN", "Structure needs cleaning"], 118 : ["ENOTNAM", "Not a XENIX named type file"], 119 : ["ENAVAIL", "No XENIX semaphores available"], 120 : ["EISNAM", "Is a named type file"], 121 : ["EREMOTEIO", "Remote I/O error"], 122 : ["ELIBACC", "Can not access a needed shared library"], 123 : ["ELIBBAD", "Accessing a corrupted shared library"], 124 : ["ELIBSCN", ".lib section in a.out corrupted"], 125 : ["ELIBMAX", "Attempting to link in too many shared libraries"], 126 : ["ELIBEXEC", "Cannot exec a shared library directly"], 127 : ["ERESTART", "Interrupted system call should be restarted"], 128 : ["ESTRPIPE", "Streams pipe error"], 129 : ["ENOMEDIUM", "No medium found"], 130 : ["EMEDIUMTYPE", "Wrong medium type"], 131 : ["ECANCELED", "Operation Canceled"], 132 : ["ENOKEY", "Required key not available"], 133 : ["EKEYEXPIRED", "Key has expired"], 134 : ["EKEYREVOKED", "Key has been revoked"], 135 : ["EKEYREJECTED", "Key was rejected by service"], 136 : ["EOWNERDEAD", "Owner died"], 137 : ["ENOTRECOVERABLE", "State not recoverable"], 138 : ["ERFKILL", "Operation not possible due to RF-kill"], 139 : ["EHWPOISON", "Memory page has hardware error"], } elif is_mips32() or is_mips64() or is_mipsn32(): ERRNO_DICT = { 35 : ["ENOMSG", "No message of desired type"], 36 : ["EIDRM", "Identifier removed"], 37 : ["ECHRNG", "Channel number out of range"], 38 : ["EL2NSYNC", "Level 2 not synchronized"], 39 : ["EL3HLT", "Level 3 halted"], 40 : ["EL3RST", "Level 3 reset"], 41 : ["ELNRNG", "Link number out of range"], 42 : ["EUNATCH", "Protocol driver not attached"], 43 : ["ENOCSI", "No CSI structure available"], 44 : ["EL2HLT", "Level 2 halted"], 45 : ["EDEADLK", "Resource deadlock would occur"], 46 : ["ENOLCK", "No record locks available"], # 47-49 50 : ["EBADE", "Invalid exchange"], 51 : ["EBADR", "Invalid request descriptor"], 52 : ["EXFULL", "Exchange full"], 53 : ["ENOANO", "No anode"], 54 : ["EBADRQC", "Invalid request code"], 55 : ["EBADSLT", "Invalid slot"], 56 : ["EDEADLOCK", "File locking deadlock error"], # 57-58 59 : ["EBFONT", "Bad font file format"], 60 : ["ENOSTR", "Device not a stream"], 61 : ["ENODATA", "No data available"], 62 : ["ETIME", "Timer expired"], 63 : ["ENOSR", "Out of streams resources"], 64 : ["ENONET", "Machine is not on the network"], 65 : ["ENOPKG", "Package not installed"], 66 : ["EREMOTE", "Object is remote"], 67 : ["ENOLINK", "Link has been severed"], 68 : ["EADV", "Advertise error"], 69 : ["ESRMNT", "Srmount error"], 70 : ["ECOMM", "Communication error on send"], 71 : ["EPROTO", "Protocol error"], # 72 73 : ["EDOTDOT", "RFS specific error"], 74 : ["EMULTIHOP", "Multihop attempted"], # 75-76 77 : ["EBADMSG", "Not a data message"], 78 : ["ENAMETOOLONG", "File name too long"], 79 : ["EOVERFLOW", "Value too large for defined data type"], 80 : ["ENOTUNIQ", "Name not unique on network"], 81 : ["EBADFD", "File descriptor in bad state"], 82 : ["EREMCHG", "Remote address changed"], 83 : ["ELIBACC", "Can not access a needed shared library"], 84 : ["ELIBBAD", "Accessing a corrupted shared library"], 85 : ["ELIBSCN", ".lib section in a.out corrupted"], 86 : ["ELIBMAX", "Attempting to link in too many shared libraries"], 87 : ["ELIBEXEC", "Cannot exec a shared library directly"], 88 : ["EILSEQ", "Illegal byte sequence"], 89 : ["ENOSYS", "Function not implemented"], 90 : ["ELOOP", "Too many symbolic links encountered"], 91 : ["ERESTART", "Interrupted system call should be restarted"], 92 : ["ESTRPIPE", "Streams pipe error"], 93 : ["ENOTEMPTY", "Directory not empty"], 94 : ["EUSERS", "Too many users"], 95 : ["ENOTSOCK", "Socket operation on non-socket"], 96 : ["EDESTADDRREQ", "Destination address required"], 97 : ["EMSGSIZE", "Message too long"], 98 : ["EPROTOTYPE", "Protocol wrong type for socket"], 99 : ["ENOPROTOOPT", "Protocol not available"], # 100-119 120 : ["EPROTONOSUPPORT", "Protocol not supported"], 121 : ["ESOCKTNOSUPPORT", "Socket type not supported"], 122 : ["EOPNOTSUPP", "Operation not supported on transport endpoint"], 123 : ["EPFNOSUPPORT", "Protocol family not supported"], 124 : ["EAFNOSUPPORT", "Address family not supported by protocol"], 125 : ["EADDRINUSE", "Address already in use"], 126 : ["EADDRNOTAVAIL", "Cannot assign requested address"], 127 : ["ENETDOWN", "Network is down"], 128 : ["ENETUNREACH", "Network is unreachable"], 129 : ["ENETRESET", "Network dropped connection because of reset"], 130 : ["ECONNABORTED", "Software caused connection abort"], 131 : ["ECONNRESET", "Connection reset by peer"], 132 : ["ENOBUFS", "No buffer space available"], 133 : ["EISCONN", "Transport endpoint is already connected"], 134 : ["ENOTCONN", "Transport endpoint is not connected"], 135 : ["EUCLEAN", "Structure needs cleaning"], # 136 137 : ["ENOTNAM", "Not a XENIX named type file"], 138 : ["ENAVAIL", "No XENIX semaphores available"], 139 : ["EISNAM", "Is a named type file"], 140 : ["EREMOTEIO", "Remote I/O error"], 141 : ["EINIT", "Reserved"], 142 : ["EREMDEV", "Error 142"], 143 : ["ESHUTDOWN", "Cannot send after transport endpoint shutdown"], 144 : ["ETOOMANYREFS", "Too many references: cannot splice"], 145 : ["ETIMEDOUT", "Connection timed out"], 146 : ["ECONNREFUSED", "Connection refused"], 147 : ["EHOSTDOWN", "Host is down"], 148 : ["EHOSTUNREACH", "No route to host"], 149 : ["EALREADY", "Operation already in progress"], 150 : ["EINPROGRESS", "Operation now in progress"], 151 : ["ESTALE", "Stale file handle"], # 152-157 158 : ["ECANCELED", "AIO operation canceled"], 159 : ["ENOMEDIUM", "No medium found"], 160 : ["EMEDIUMTYPE", "Wrong medium type"], 161 : ["ENOKEY", "Required key not available"], 162 : ["EKEYEXPIRED", "Key has expired"], 163 : ["EKEYREVOKED", "Key has been revoked"], 164 : ["EKEYREJECTED", "Key was rejected by service"], 165 : ["EOWNERDEAD", "Owner died"], 166 : ["ENOTRECOVERABLE", "State not recoverable"], 167 : ["ERFKILL", "Operation not possible due to RF-kill"], 168 : ["EHWPOISON", "Memory page has hardware error"], # 1133: ["EDQUOT", "Quota exceeded"], } elif is_hppa32() or is_hppa64(): ERRNO_DICT = { 35 : ["ENOMSG", "No message of desired type"], 36 : ["EIDRM", "Identifier removed"], 37 : ["ECHRNG", "Channel number out of range"], 38 : ["EL2NSYNC", "Level 2 not synchronized"], 39 : ["EL3HLT", "Level 3 halted"], 40 : ["EL3RST", "Level 3 reset"], 41 : ["ELNRNG", "Link number out of range"], 42 : ["EUNATCH", "Protocol driver not attached"], 43 : ["ENOCSI", "No CSI structure available"], 44 : ["EL2HLT", "Level 2 halted"], 45 : ["EDEADLK", "Resource deadlock would occur"], 46 : ["ENOLCK", "No record locks available"], 47 : ["EILSEQ", "Illegal byte sequence"], # 48-49 50 : ["ENONET", "Machine is not on the network"], 51 : ["ENODATA", "No data available"], 52 : ["ETIME", "Timer expired"], 53 : ["ENOSR", "Out of streams resources"], 54 : ["ENOSTR", "Device not a stream"], 55 : ["ENOPKG", "Package not installed"], # 56 57 : ["ENOLINK", "Link has been severed"], 58 : ["EADV", "Advertise error"], 59 : ["ESRMNT", "Srmount error"], 60 : ["ECOMM", "Communication error on send"], 61 : ["EPROTO", "Protocol error"], # 62-63 64 : ["EMULTIHOP", "Multihop attempted"], # 65 66 : ["EDOTDOT", "RFS specific error"], 67 : ["EBADMSG", "Not a data message"], 68 : ["EUSERS", "Too many users"], 69 : ["EDQUOT", "Quota exceeded"], 70 : ["ESTALE", "Stale file handle"], 71 : ["EREMOTE", "Object is remote"], 72 : ["EOVERFLOW", "Value too large for defined data type"], # 73-159 160 : ["EBADE", "Invalid exchange"], 161 : ["EBADR", "Invalid request descriptor"], 162 : ["EXFULL", "Exchange full"], 163 : ["ENOANO", "No anode"], 164 : ["EBADRQC", "Invalid request code"], 165 : ["EBADSLT", "Invalid slot"], 166 : ["EBFONT", "Bad font file format"], 167 : ["ENOTUNIQ", "Name not unique on network"], 168 : ["EBADFD", "File descriptor in bad state"], 169 : ["EREMCHG", "Remote address changed"], 170 : ["ELIBACC", "Can not access a needed shared library"], 171 : ["ELIBBAD", "Accessing a corrupted shared library"], 172 : ["ELIBSCN", ".lib section in a.out corrupted"], 173 : ["ELIBMAX", "Attempting to link in too many shared libraries"], 174 : ["ELIBEXEC", "Cannot exec a shared library directly"], 175 : ["ERESTART", "Interrupted system call should be restarted"], 176 : ["ESTRPIPE", "Streams pipe error"], 177 : ["EUCLEAN", "Structure needs cleaning"], 178 : ["ENOTNAM", "Not a XENIX named type file"], 179 : ["ENAVAIL", "No XENIX semaphores available"], 180 : ["EISNAM", "Is a named type file"], 181 : ["EREMOTEIO", "Remote I/O error"], 182 : ["ENOMEDIUM", "No medium found"], 183 : ["EMEDIUMTYPE", "Wrong medium type"], 184 : ["ENOKEY", "Required key not available"], 185 : ["EKEYEXPIRED", "Key has expired"], 186 : ["EKEYREVOKED", "Key has been revoked"], 187 : ["EKEYREJECTED", "Key was rejected by service"], # 188-215 216 : ["ENOTSOCK", "Socket operation on non-socket"], 217 : ["EDESTADDRREQ", "Destination address required"], 218 : ["EMSGSIZE", "Message too long"], 219 : ["EPROTOTYPE", "Protocol wrong type for socket"], 220 : ["ENOPROTOOPT", "Protocol not available"], 221 : ["EPROTONOSUPPORT", "Protocol not supported"], 222 : ["ESOCKTNOSUPPORT", "Socket type not supported"], 223 : ["EOPNOTSUPP", "Operation not supported on transport endpoint"], 224 : ["EPFNOSUPPORT", "Protocol family not supported"], 225 : ["EAFNOSUPPORT", "Address family not supported by protocol"], 226 : ["EADDRINUSE", "Address already in use"], 227 : ["EADDRNOTAVAIL", "Cannot assign requested address"], 228 : ["ENETDOWN", "Network is down"], 229 : ["ENETUNREACH", "Network is unreachable"], 230 : ["ENETRESET", "Network dropped connection because of reset"], 231 : ["ECONNABORTED", "Software caused connection abort"], 232 : ["ECONNRESET", "Connection reset by peer"], 233 : ["ENOBUFS", "No buffer space available"], 234 : ["EISCONN", "Transport endpoint is already connected"], 235 : ["ENOTCONN", "Transport endpoint is not connected"], 236 : ["ESHUTDOWN", "Cannot send after transport endpoint shutdown"], 237 : ["ETOOMANYREFS", "Too many references: cannot splice"], 238 : ["ETIMEDOUT", "Connection timed out"], 239 : ["ECONNREFUSED", "Connection refused"], # 240 241 : ["EHOSTDOWN", "Host is down"], 242 : ["EHOSTUNREACH", "No route to host"], # 243 244 : ["EALREADY", "Operation already in progress"], 245 : ["EINPROGRESS", "Operation now in progress"], # 246 247 : ["ENOTEMPTY", "Directory not empty"], 248 : ["ENAMETOOLONG", "File name too long"], 249 : ["ELOOP", "Too many symbolic links encountered"], # 250 251 : ["ENOSYS", "Function not implemented"], # 252 253 : ["ECANCELLED", "aio request was canceled before complete (POSIX.4 / HPUX)"], 254 : ["EOWNERDEAD", "Owner died"], 255 : ["ENOTRECOVERABLE", "State not recoverable"], 256 : ["ERFKILL", "Operation not possible due to RF-kill"], 257 : ["EHWPOISON", "Memory page has hardware error"], } elif is_sparc32() or is_sparc64(): ERRNO_DICT = { 36 : ["EINPROGRESS", "Operation now in progress"], 37 : ["EALREADY", "Operation already in progress"], 38 : ["ENOTSOCK", "Socket operation on non-socket"], 39 : ["EDESTADDRREQ", "Destination address required"], 40 : ["EMSGSIZE", "Message too long"], 41 : ["EPROTOTYPE", "Protocol wrong type for socket"], 42 : ["ENOPROTOOPT", "Protocol not available"], 43 : ["EPROTONOSUPPORT", "Protocol not supported"], 44 : ["ESOCKTNOSUPPORT", "Socket type not supported"], 45 : ["EOPNOTSUPP", "Op not supported on transport endpoint"], 46 : ["EPFNOSUPPORT", "Protocol family not supported"], 47 : ["EAFNOSUPPORT", "Address family not supported by protocol"], 48 : ["EADDRINUSE", "Address already in use"], 49 : ["EADDRNOTAVAIL", "Cannot assign requested address"], 50 : ["ENETDOWN", "Network is down"], 51 : ["ENETUNREACH", "Network is unreachable"], 52 : ["ENETRESET", "Net dropped connection because of reset"], 53 : ["ECONNABORTED", "Software caused connection abort"], 54 : ["ECONNRESET", "Connection reset by peer"], 55 : ["ENOBUFS", "No buffer space available"], 56 : ["EISCONN", "Transport endpoint is already connected"], 57 : ["ENOTCONN", "Transport endpoint is not connected"], 58 : ["ESHUTDOWN", "No send after transport endpoint shutdown"], 59 : ["ETOOMANYREFS", "Too many references: cannot splice"], 60 : ["ETIMEDOUT", "Connection timed out"], 61 : ["ECONNREFUSED", "Connection refused"], 62 : ["ELOOP", "Too many symbolic links encountered"], 63 : ["ENAMETOOLONG", "File name too long"], 64 : ["EHOSTDOWN", "Host is down"], 65 : ["EHOSTUNREACH", "No route to host"], 66 : ["ENOTEMPTY", "Directory not empty"], 67 : ["EPROCLIM", "SUNOS: Too many processes"], 68 : ["EUSERS", "Too many users"], 69 : ["EDQUOT", "Quota exceeded"], 70 : ["ESTALE", "Stale file handle"], 71 : ["EREMOTE", "Object is remote"], 72 : ["ENOSTR", "Device not a stream"], 73 : ["ETIME", "Timer expired"], 74 : ["ENOSR", "Out of streams resources"], 75 : ["ENOMSG", "No message of desired type"], 76 : ["EBADMSG", "Not a data message"], 77 : ["EIDRM", "Identifier removed"], 78 : ["EDEADLK", "Resource deadlock would occur"], 79 : ["ENOLCK", "No record locks available"], 80 : ["ENONET", "Machine is not on the network"], 81 : ["ERREMOTE", "SunOS: Too many lvls of remote in path"], 82 : ["ENOLINK", "Link has been severed"], 83 : ["EADV", "Advertise error"], 84 : ["ESRMNT", "Srmount error"], 85 : ["ECOMM", "Communication error on send"], 86 : ["EPROTO", "Protocol error"], 87 : ["EMULTIHOP", "Multihop attempted"], 88 : ["EDOTDOT", "RFS specific error"], 89 : ["EREMCHG", "Remote address changed"], 90 : ["ENOSYS", "Function not implemented"], 91 : ["ESTRPIPE", "Streams pipe error"], 92 : ["EOVERFLOW", "Value too large for defined data type"], 93 : ["EBADFD", "File descriptor in bad state"], 94 : ["ECHRNG", "Channel number out of range"], 95 : ["EL2NSYNC", "Level 2 not synchronized"], 96 : ["EL3HLT", "Level 3 halted"], 97 : ["EL3RST", "Level 3 reset"], 98 : ["ELNRNG", "Link number out of range"], 99 : ["EUNATCH", "Protocol driver not attached"], 100 : ["ENOCSI", "No CSI structure available"], 101 : ["EL2HLT", "Level 2 halted"], 102 : ["EBADE", "Invalid exchange"], 103 : ["EBADR", "Invalid request descriptor"], 104 : ["EXFULL", "Exchange full"], 105 : ["ENOANO", "No anode"], 106 : ["EBADRQC", "Invalid request code"], 107 : ["EBADSLT", "Invalid slot"], 108 : ["EDEADLOCK", "File locking deadlock error"], 109 : ["EBFONT", "Bad font file format"], 110 : ["ELIBEXEC", "Cannot exec a shared library directly"], 111 : ["ENODATA", "No data available"], 112 : ["ELIBBAD", "Accessing a corrupted shared library"], 113 : ["ENOPKG", "Package not installed"], 114 : ["ELIBACC", "Can not access a needed shared library"], 115 : ["ENOTUNIQ", "Name not unique on network"], 116 : ["ERESTART", "Interrupted syscall should be restarted"], 117 : ["EUCLEAN", "Structure needs cleaning"], 118 : ["ENOTNAM", "Not a XENIX named type file"], 119 : ["ENAVAIL", "No XENIX semaphores available"], 120 : ["EISNAM", "Is a named type file"], 121 : ["EREMOTEIO", "Remote I/O error"], 122 : ["EILSEQ", "Illegal byte sequence"], 123 : ["ELIBMAX", "Atmpt to link in too many shared libs"], 124 : ["ELIBSCN", ".lib section in a.out corrupted"], 125 : ["ENOMEDIUM", "No medium found"], 126 : ["EMEDIUMTYPE", "Wrong medium type"], 127 : ["ECANCELED", "Operation Cancelled"], 128 : ["ENOKEY", "Required key not available"], 129 : ["EKEYEXPIRED", "Key has expired"], 130 : ["EKEYREVOKED", "Key has been revoked"], 131 : ["EKEYREJECTED", "Key was rejected by service"], 132 : ["EOWNERDEAD", "Owner died"], 133 : ["ENOTRECOVERABLE", "State not recoverable"], 134 : ["ERFKILL", "Operation not possible due to RF-kill"], 135 : ["EHWPOISON", "Memory page has hardware error"], } return ERRNO_BASE_DICT | ERRNO_DICT @parse_args @exclude_specific_gdb_mode(mode=("wine",)) @require_arch_set def do_invoke(self, args): ERRNO_DICT = ErrnoCommand.get_errno_dict() if args.all: self.out = [] for val, (sym, desc) in sorted(ERRNO_DICT.items()): self.out.append('{:3d} (={:#4x}): {:<15s}: "{:s}"'.format(val, val, sym, desc)) self.print_output(check_terminal_size=True) return if args.errno is None: if not is_alive(): warn("No debugging session active") return try: val = AddressUtil.parse_address("*__errno_location()") except gdb.error: err("Failed to get *__errno_location()") return else: val = args.errno if val > 0xffff: if current_arch and current_arch.ptrsize == 4: val = struct.unpack(" double/float).""" _cmdline_ = "u2d" _category_ = "07-a. Misc - Conversion" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the hex value or double value.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0xdeadbeef", "{0:s} 0.12345", "{0:s} 1.2345e-1", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Only ~64bit supported (Unsupported 80bit, 128bit)", ] _note_ = "\n".join(_note_) def f2u(self, x): u = lambda a: struct.unpack(" unsigned long long")) gef_print(Color.cyanify("double -> ull (reinterpret_cast)")) gef_print(" {:.20e} ---> {:#018x}".format(n, self.d2u(n))) gef_print(titlify("float -> float")) gef_print(Color.cyanify("float -> uint (reinterpret_cast)")) gef_print(" {:.20e} ---> {:#010x}".format(n, self.f2u(n))) return def convert_from_int(self, n): n &= 0xffff_ffff_ffff_ffff gef_print(titlify("unsigned long long <-> double")) gef_print(Color.cyanify("ull -> double (reinterpret_cast)")) gef_print(" {:#018x} ---> {:.20e}".format(n, self.u2d(n))) gef_print(Color.cyanify("ull -> double -> ull (static_cast)")) gef_print(" {:#018x} ---> {:#018x} ---> {:#018x}".format(n, self.d2u(float(n)), int(self.u2d(self.d2u(float(n)))))) gef_print(Color.cyanify("double -> ull (reinterpret_cast)")) try: gef_print(" {:#018x} ---> {:#018x}".format(n, int(self.u2d(n)))) except ValueError: gef_print(" {:18s} ---> ???".format("nan")) n &= 0xffff_ffff gef_print(titlify("unsigned int <-> float")) gef_print(Color.cyanify("uint -> float (reinterpret_cast)")) gef_print(" {:#010x} ---> {:.20e}".format(n, self.u2f(n))) gef_print(Color.cyanify("uint -> float -> uint (static_cast)")) gef_print(" {:#010x} ---> {:#010x} ---> {:#010x}".format(n, self.f2u(float(n)), int(self.u2f(self.f2u(float(n)))))) gef_print(Color.cyanify("float -> uint (reinterpret_cast)")) try: gef_print(" {:#010x} ---> {:#010x}".format(n, int(self.u2f(n)))) except ValueError: gef_print(" {:10s} ---> ???".format("nan")) return @parse_args def do_invoke(self, args): try: if "." in args.value: n = float(args.value) self.convert_from_float(n) else: n = int(args.value, 0) self.convert_from_int(n) except Exception: self.usage() return @register_command class UnsignedCommand(GenericCommand): """Convert the negative number to unsigned.""" _cmdline_ = "unsigned" _category_ = "07-a. Misc - Conversion" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", type=AddressUtil.parse_address, help="the value to convert.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -- -0xa0", ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args def do_invoke(self, args): gef_print("input: {:#x}".format(args.value)) for i in range(4): shift = (2 ** i) * 8 msb_mask = 1 << (shift - 1) if (1 << shift) > args.value and args.value & msb_mask == 0: value = args.value * -1 else: value = args.value mask = (1 << shift) - 1 unsigned = value & mask if i == 0: signed = struct.unpack("= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("value") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return def pack(self, value): try: value = int(value, 0) self.out.append(titlify("pack")) self.out.append("pack8: {!s}".format(p8(value & 0xff))) self.out.append("pack16: {!s}".format(p16(value & 0xffff))) self.out.append("pack32: {!s}".format(p32(value & 0xffff_ffff))) self.out.append("pack64: {!s}".format(p64(value & 0xffff_ffff_ffff_ffff))) low = value & 0xffff_ffff_ffff_ffff high = (value >> 64) & 0xffff_ffff_ffff_ffff val128 = p64(low) + p64(high) self.out.append("pack128: {!s}".format(val128)) except ValueError: pass return def pack_hex(self, value): try: value = int(value, 0) self.out.append(titlify("pack-hex")) self.out.append("pack8-hex: {!s}".format(p8(value & 0xff).hex())) self.out.append("pack16-hex: {!s}".format(p16(value & 0xffff).hex())) self.out.append("pack32-hex: {!s}".format(p32(value & 0xffff_ffff).hex())) self.out.append("pack64-hex: {!s}".format(p64(value & 0xffff_ffff_ffff_ffff).hex())) low = value & 0xffff_ffff_ffff_ffff high = (value >> 64) & 0xffff_ffff_ffff_ffff val128 = p64(low) + p64(high) self.out.append("pack128-hex: {!s}".format(val128.hex())) except ValueError: pass return def unpack(self, value): try: value = codecs.escape_decode(value)[0] + b"\0" * 16 self.out.append(titlify("unpack")) self.out.append("unpack8: {:#04x}".format(u8(value[:1]))) self.out.append("unpack16: {:#06x}".format(u16(value[:2]))) self.out.append("unpack32: {:#010x}".format(u32(value[:4]))) self.out.append("unpack64: {:#018x}".format(u64(value[:8]))) low, high = value[:8], value[8:16] self.out.append("unpack128: {:#034x}".format((u64(high) << 64) | u64(low))) except binascii.Error: pass return def tohex(self, value): try: value = codecs.escape_decode(value)[0] self.out.append(titlify("tohex")) hexed = binascii.hexlify(value) self.out.append("tohex: {!s}".format(hexed)) hexed_null = b"00".join(slicer(hexed, 2)) + b"00" self.out.append("tohex w/NULL: {!s}".format(hexed_null)) except binascii.Error: pass return def unhex(self, value): try: if value.startswith("0x"): value = binascii.unhexlify(value[2:]) else: value = binascii.unhexlify(value) self.out.append(titlify("unhex")) self.out.append("unhex: {!s}".format(value)) value_null = b"\x00".join(slicer(value, 1)) + b"\x00" self.out.append("unhex w/NULL: {!s}".format(value_null)) except (binascii.Error, ValueError): pass return def byteswap(self, value): try: value = int(value, 0) converted32 = byteswap(value, 4) converted64 = byteswap(value, 8) self.out.append(titlify("byteswap")) self.out.append("byteswap-64: {:#018x}".format(converted64)) self.out.append("byteswap-32: {:#010x}".format(converted32)) except ValueError: pass return def bit_reverse(self, value): def bit_reverse(x, n): mask = (1 << n) - 1 b = "{:0{:d}b}".format(x & mask, n) return int(b[::-1], 2) try: value = int(value, 0) br8 = bit_reverse(value, 8) br16 = bit_reverse(value, 16) br32 = bit_reverse(value, 32) br64 = bit_reverse(value, 64) self.out.append(titlify("bit-reverse")) self.out.append("bit-reverse8: {:#04x}".format(br8)) self.out.append("bit-reverse16: {:#06x}".format(br16)) self.out.append("bit-reverse32: {:#010x}".format(br32)) self.out.append("bit-reverse64: {:#018x}".format(br64)) bl = (value.bit_length() + 3) // 4 * 4 if bl > 64: brN = bit_reverse(value, bl) self.out.append("bit-reverse: {:#0{:d}x}".format(brN, bl // 4 + 2)) except ValueError: pass return def integer(self, value): try: value = int(value, 0) self.out.append(titlify("integer")) self.out.append("hex: {:#x}".format(value)) self.out.append("dec: {:d}".format(value)) self.out.append("oct: {:#o}".format(value)) self.out.append("bin: {:#b}".format(value)) out = "" x = value while x: if x & 1: out = "1" + out else: out = "0" + out if (len(out) + 1) % 5 == 0: out = "_" + out x >>= 1 splitted_value = "0b" + out.lstrip("_") self.out.append("bin w/sep: {:s}".format(splitted_value)) except ValueError: pass return def signed(self, value): pQ = lambda a: struct.pack("> (8 - i)) & 0xff]) for x in value) self.out.append("rol-{:02X}: {!s}".format(i, rored)) except (binascii.Error, ValueError): pass return def unhex_rol_whole(self, value): try: if value.startswith("0x"): value = binascii.unhexlify(value[2:]) else: value = binascii.unhexlify(value) self.out.append(titlify("unhex - ROL (whole)")) bits = [] for v in value: for i in range(8): bits.append(str((v >> (7 - i)) & 1)) for i in range(9): rored = bits[i:] + bits[:i] rored = [int("".join(x), 2) for x in slicer(rored, 8)] rored = bytes(rored) self.out.append("rol-{:02X}: {!s}".format(i, rored)) except ValueError: pass return def unhex_caesar(self, value): try: if value.startswith("0x"): value = binascii.unhexlify(value[2:]) else: value = binascii.unhexlify(value) self.out.append(titlify("unhex - caesar")) for i in range(26): slided = [] for x in value: if ord("A") <= x <= ord("Z"): x += i if x > ord("Z"): x -= ord("Z") x += ord("A") - 1 elif ord("a") <= x <= ord("z"): x += i if x > ord("z"): x -= ord("z") x += ord("a") - 1 slided.append(x) self.out.append("caesar-{:02d}: {!s}".format(i, bytes(slided))) except (binascii.Error, ValueError): pass return def string_xor(self, value): try: value = codecs.escape_decode(value)[0] self.out.append(titlify("str - XOR")) for i in range(0x100): xored = b"".join(bytes([x ^ i]) for x in value) if 0x20 <= i < 0x7f: self.out.append("xor-{:02X}({:s}): {!s}".format(i, chr(i), xored)) else: self.out.append("xor-{:02X}: {!s}".format(i, xored)) except ValueError: pass return def string_add(self, value): try: value = codecs.escape_decode(value)[0] self.out.append(titlify("str - ADD")) for i in range(0x100): added = b"".join(bytes([(x + i) & 0xff]) for x in value) if 0x20 <= i < 0x7f: self.out.append("add-{:02X}({:s}): {!s}".format(i, chr(i), added)) else: self.out.append("add-{:02X}: {!s}".format(i, added)) except ValueError: pass return def string_rol_for_each_byte(self, value): try: value = codecs.escape_decode(value)[0] self.out.append(titlify("str - ROL (for each byte)")) for i in range(9): rored = b"".join(bytes([((x << i) | x >> (8 - i)) & 0xff]) for x in value) self.out.append("rol-{:02X}: {!s}".format(i, rored)) except ValueError: pass return def string_rol_whole(self, value): try: value = codecs.escape_decode(value)[0] self.out.append(titlify("str - ROL (whole)")) bits = [] for v in value: for i in range(8): bits.append(str((v >> (7 - i)) & 1)) for i in range(9): rored = bits[i:] + bits[:i] rored = [int("".join(x), 2) for x in slicer(rored, 8)] rored = bytes(rored) self.out.append("rol-{:02X}: {!s}".format(i, rored)) except ValueError: pass return def string_caesar(self, value): try: value = codecs.escape_decode(value)[0] self.out.append(titlify("str - caesar")) for i in range(26): slided = [] for x in value: if ord("A") <= x <= ord("Z"): x += i if x > ord("Z"): x -= ord("Z") x += ord("A") - 1 elif ord("a") <= x <= ord("z"): x += i if x > ord("z"): x -= ord("z") x += ord("a") - 1 slided.append(x) self.out.append("caesar-{:02d}: {!s}".format(i, bytes(slided))) except ValueError: pass return def convert(self, value, args): self.pack(value) self.pack_hex(value) self.unpack(value) self.tohex(value) self.unhex(value) self.byteswap(value) self.bit_reverse(value) self.integer(value) self.signed(value) self.string(value) self.url_encode(value) self.url_decode(value) if args.verbose: self.unhex_xor(value) self.unhex_add(value) self.unhex_rol_for_each_byte(value) self.unhex_rol_whole(value) self.unhex_caesar(value) self.string_xor(value) self.string_add(value) self.string_rol_for_each_byte(value) self.string_rol_whole(value) self.string_caesar(value) return @parse_args def do_invoke(self, args): self.usage() return @register_command class ConvertMemoryCommand(ConvertCommand): """Convert memory values to various.""" _cmdline_ = "convert memory" _category_ = "07-a. Misc - Conversion" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address for hash calculation.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for hash calculation.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running def do_invoke(self, args): try: value = read_memory(args.location, args.size) except (gdb.MemoryError, MemoryError): err("Memory read error") return value = str(value)[2:-1] self.out = [] self.convert(value, args) self.print_output() return @register_command class ConvertValueCommand(ConvertCommand): """Convert values to various.""" _cmdline_ = "convert value" _category_ = "07-a. Misc - Conversion" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the value or string to convert.") parser.add_argument("--hex", action="store_true", help="interpret VALUE as hex. invalid character is ignored.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0xdeadbeef", '{0:s} "\\\\x41\\\\x42\\\\x43\\\\x44" -v', '{0:s} --hex "41 42 43 44" -v', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): if args.hex: # "41414141" -> "\x41\x41\x41\x41" value = GefUtil.fromhex_ignore_invalid(args.value, to_str=True) if not value: return else: value = args.value self.out = [] self.convert(value, args) self.print_output() return class KernelAddressHeuristicFinderUtil: """A class that has utility for KernelAddressHeuristicFinder.""" @staticmethod def common_addr_gen(res, regexp, skip, skip_msb_check, read_valid): for line in res.splitlines(): m = re.search(regexp, line) if not m: continue v = AddressUtil.align_address(int(m.group(1), 16)) if not skip_msb_check and not AddressUtil.is_msb_on(v): continue if read_valid and not is_valid_addr_addr(v): # not is_valid_addr, but is_valid_addr_addr continue if skip > 0: skip -= 1 continue yield v @staticmethod def x64_x86_any_const(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"(?:# |,)(0x\w{8,})" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_x86_mov_reg_const(res, reg=r"\w+", skip=0, skip_msb_check=False, read_valid=False): regexp = r"mov\s+" + reg + r"\s*,\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_lea_reg_const(res, reg=r"\w+", skip=0, skip_msb_check=False, read_valid=False): regexp = r"lea\s+" + reg + r"\s*,\s*\[.*([+-]0x\w+)\]" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_x86_cmp_const(res, reg=r"\w+", skip=0, skip_msb_check=False, read_valid=False): regexp = r"cmp\s+" + reg + r"\s*,\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_x86_imul_const(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"imul\s+\w+\s*,\s*\w+\s*,\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_x86_dword_ptr_src(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r",\s*DWORD PTR \[.*([+-]0x\w+)\]" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_x86_byte_ptr(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"BYTE PTR \[.*([+-]0x\w+)\]" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_dword_ptr_rip_base(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"DWORD PTR \[rip\+0x\w+\].*#\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_qword_ptr_rip_base(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"QWORD PTR \[rip\+0x\w+\].*#\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_qword_ptr_gs_rip_base(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"QWORD PTR gs:\[rip\+0x\w+\].*#\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_qword_ptr_array_base(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"QWORD PTR \[.*\*8([-+]0x\w+)\]" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x86_dword_ptr_array4_base(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"DWORD PTR \[.*\*4([+-]0x\w+)\]" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x86_dword_ptr_array8_base(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"DWORD PTR \[.*\*8([+-]0x\w+)\]" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_qword_ptr_ds(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"QWORD PTR ds:\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x64_qword_ptr_gs(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"QWORD PTR gs:\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x86_dword_ptr_ds(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"DWORD PTR ds:\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x86_dword_ptr_fs(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"DWORD PTR fs:\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x86_noptr_ds(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"ds:\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def x86_mov_noptr_ds(res, skip=0, skip_msb_check=False, read_valid=False): regexp = r"mov.*ds:\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def aarch64_cmp_const(res, reg=r"\w+", skip=0, skip_msb_check=False, read_valid=False): regexp = r"cmp\s+" + reg + r"\s*,\s*#(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def aarch64_adrp_ldr(res, skip=0, skip_msb_check=False, read_valid=False): bases = {} for line in res.splitlines(): m = re.search(r"adrp\s+(\w+),\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) bases[reg] = v continue m = re.search(r"ldr\s+\w+,\s*\[(\w+),\s*#(\d+)\]", line) if m: srcreg = m.group(1) v = int(m.group(2), 0) if srcreg in bases: w = AddressUtil.align_address(bases[srcreg] + v) if not skip_msb_check and not AddressUtil.is_msb_on(w): continue if read_valid and not is_valid_addr_addr(w): continue if skip > 0: skip -= 1 continue yield w @staticmethod def aarch64_adrp_add(res, skip=0, skip_msb_check=False, read_valid=False): bases = {} for line in res.splitlines(): m = re.search(r"adrp\s+(\w+),\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) bases[reg] = v continue m = re.search(r"add\s+(\w+),\s*(\w+),\s*#(0x\w+)", line) if m: srcreg = m.group(2) v = int(m.group(3), 16) if srcreg in bases: w = AddressUtil.align_address(bases[srcreg] + v) if not skip_msb_check and not AddressUtil.is_msb_on(w): continue if read_valid and not is_valid_addr_addr(w): continue if skip > 0: skip -= 1 continue yield w @staticmethod def aarch64_adrp_add_add(res, skip=0, skip_msb_check=False, read_valid=False): bases = {} add1time = {} for line in res.splitlines(): m = re.search(r"adrp\s+(\w+),\s*(0x\w+)", line) if m: reg = m.group(1) base = int(m.group(2), 16) bases[reg] = base continue m = re.search(r"add\s+(\w+),\s*(\w+),\s*#(0x\w+)", line) if m: dstreg = m.group(1) srcreg = m.group(2) v = int(m.group(3), 16) if srcreg in add1time: w = AddressUtil.align_address(add1time[srcreg] + v) if not skip_msb_check or AddressUtil.is_msb_on(w): if not read_valid or is_valid_addr_addr(w): if skip <= 0: yield w skip -= 1 if srcreg in bases: add1time[dstreg] = bases[srcreg] + v continue @staticmethod def aarch64_adrp_add_ldr(res, skip=0, skip_msb_check=False, read_valid=False): bases = {} add1time = {} for line in res.splitlines(): m = re.search(r"adrp\s+(\w+),\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) bases[reg] = v continue m = re.search(r"add\s+(\w+),\s*(\w+),\s*#(0x\w+)", line) if m: dstreg = m.group(1) srcreg = m.group(2) v = int(m.group(3), 16) if srcreg in bases: add1time[dstreg] = bases[srcreg] + v continue m = re.search(r"ldr\s+\w+,\s*\[(\w+),\s*#(\d+)\]", line) if m: srcreg = m.group(1) v = int(m.group(2), 0) if srcreg in add1time: w = AddressUtil.align_address(add1time[srcreg] + v) if not skip_msb_check and not AddressUtil.is_msb_on(w): continue if read_valid and not is_valid_addr_addr(w): continue if skip > 0: skip -= 1 continue yield w @staticmethod def arm32_movw_movt(res, skip=0, skip_msb_check=False, read_valid=False, allow_cc=False): bases = {} for line in res.splitlines(): if allow_cc: m = re.search(r"movw(?:cc)?\s+(\w+),.+[;@]\s*(0x\w+)", line) else: m = re.search(r"movw\s+(\w+),.+[;@]\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) bases[reg] = v continue if allow_cc: m = re.search(r"movt(?:cc)?\s+(\w+),.+[;@]\s*(0x\w+)", line) else: m = re.search(r"movt\s+(\w+),.+[;@]\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) << 16 if reg in bases: w = AddressUtil.align_address(bases[reg] + v) if not skip_msb_check and not AddressUtil.is_msb_on(w): continue if read_valid and not is_valid_addr_addr(w): continue if skip > 0: skip -= 1 continue yield w @staticmethod def arm32_movw_movt_ldr(res, skip=0, skip_msb_check=False, read_valid=False, allow_cc=False): bases = {} add1time = {} for line in res.splitlines(): if allow_cc: m = re.search(r"movw(?:cc)?\s+(\w+),.+[;@]\s*(0x\w+)", line) else: m = re.search(r"movw\s+(\w+),.+[;@]\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) bases[reg] = v continue if allow_cc: m = re.search(r"movt(?:cc)?\s+(\w+),.+[;@]\s*(0x\w+)", line) else: m = re.search(r"movt\s+(\w+),.+[;@]\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) << 16 if reg in bases: add1time[reg] = bases[reg] + v continue m = re.search(r"ldr\s+\w+,\s*\[(\w+),\s*#(\d+)\]", line) if m: reg = m.group(1) v = int(m.group(2), 0) if reg in add1time: w = AddressUtil.align_address(add1time[reg] + v) if not skip_msb_check and not AddressUtil.is_msb_on(w): continue if read_valid and not is_valid_addr_addr(w): continue if skip > 0: skip -= 1 continue yield w @staticmethod def arm32_movw_movt_add(res, skip=0, skip_msb_check=False, read_valid=False): bases = {} add1time = {} for line in res.splitlines(): m = re.search(r"movw\s+(\w+),.+[;@]\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) bases[reg] = v continue m = re.search(r"movt\s+(\w+),.+[;@]\s*(0x\w+)", line) if m: reg = m.group(1) v = int(m.group(2), 16) << 16 if reg in bases: add1time[reg] = bases[reg] + v continue m = re.search(r"add\s+\w+,\s*(\w+),\s*#(\d+)", line) if m: reg = m.group(1) v = int(m.group(2), 0) if reg in add1time: w = AddressUtil.align_address(add1time[reg] + v) if not skip_msb_check and not AddressUtil.is_msb_on(w): continue if read_valid and not is_valid_addr_addr(w): continue if skip > 0: skip -= 1 continue yield w @staticmethod def arm32_ldr_reg_const(res, reg=r"\w+", skip=0, skip_msb_check=False, read_valid=False): regexp = r"ldr\s+" + reg + r",.*[;@]\s*(0x\w+)" return KernelAddressHeuristicFinderUtil.common_addr_gen(res, regexp, skip, skip_msb_check, read_valid) @staticmethod def arm32_ldr_pc_relative(res, skip=0, read_valid=False): for line in res.splitlines(): m = re.search(r"ldr\s+\w+,\s*\[pc,\s*#(\d+)\]", line) if m: ofs = AddressUtil.align_address(int(m.group(1), 0)) pos = AddressUtil.align_address(int(line.split()[0].replace(":", ""), 16)) v = read_int_from_memory(pos + 4 * 2 + ofs) if is_valid_addr(v): if skip <= 0: yield v skip -= 1 continue m = re.search(r"ldr\s+\w+,\s*\[pc\]", line) if m: pos = AddressUtil.align_address(int(line.split()[0].replace(":", ""), 16)) v = read_int_from_memory(pos + 4 * 2) if is_valid_addr(v): if read_valid and not is_valid_addr_addr(v): continue if skip <= 0: yield v skip -= 1 continue @staticmethod def arm32_ldr_pc_relative_ldr(res, skip=0, read_valid=False): bases = {} for line in res.splitlines(): m = re.search(r"ldr\s+(\w+),\s*\[pc,\s*#(\d+)\]", line) if m: reg = m.group(1) ofs = AddressUtil.align_address(int(m.group(2), 0)) pos = AddressUtil.align_address(int(line.split()[0].replace(":", ""), 16)) v = read_int_from_memory(pos + 4 * 2 + ofs) bases[reg] = v continue m = re.search(r"ldr\s+\w+,\s*\[(\w+),\s*#(\d*)\]", line) if m: reg = m.group(1) ofs = AddressUtil.align_address(int(m.group(2), 0)) if reg in bases: w = AddressUtil.align_address(bases[reg] + ofs) if skip <= 0: yield w skip -= 1 continue m = re.search(r"ldr\s+\w+,\s*\[(\w+)\]", line) if m: reg = m.group(1) if reg in bases: w = AddressUtil.align_address(bases[reg]) if read_valid and not is_valid_addr_addr(w): continue if skip <= 0: yield w skip -= 1 continue class KernelConstsBase: """A class that manages constants by version.""" SZ_64K = 0x0001_0000 SZ_256K = 0x0004_0000 SZ_2M = 0x0020_0000 SZ_4M = 0x0040_0000 SZ_8M = 0x0080_0000 SZ_16M = 0x0100_0000 SZ_32M = 0x0200_0000 SZ_64M = 0x0400_0000 SZ_128M = 0x0800_0000 SZ_256M = 0x1000_0000 SZ_1G = 0x4000_0000 SZ_2G = 0x8000_0000 SZ_1T = 0x0100_0000_0000 def __init__(self, version=None): if version: vs = version.split(".") if len(vs) == 2: vs = [int(vs[0]), int(vs[1]), 0] else: vs = [int(vs[0]), int(vs[1]), int(vs[2])] self.kversion = Kernel.KernelVersion(0, "", *vs) else: self.kversion = Kernel.kernel_version() return def order_base_2(self, n): if n > 1: return GefUtil.log2(n) return 0 def round_up(self, x, y): return ((x - 1) | (y - 1)) + 1 def ALIGN(self, x, a): mask = a - 1 return (x + mask) & ~mask def test(self): # noqa if is_32bit(): target = ["PAGE_OFFSET", "PAGE_OFFSET_END", "VMALLOC_START", "VMALLOC_END"] else: target = ["PAGE_OFFSET", "PAGE_OFFSET_END", "VMALLOC_START", "VMALLOC_END", "VMEMMAP_START", "VMEMMAP_END"] for attr in target: try: v = getattr(self, attr) if v: info("{:16s} = {:#x}".format(attr, v)) else: err("{:16s} = None".format(attr)) except Exception: err("{:16s} = {}".format(attr, Color.colorify("ERROR", "bold red"))) return class KernelConstsX86(KernelConstsBase): """A class that manages x86 constants by version.""" def __init__(self, version=None, kaslr=None, pae=None): super().__init__(version) self.kaslr = kaslr self.pae = pae return PAGE_SHIFT = 12 PAGE_SIZE = 1 << PAGE_SHIFT @property def CONFIG_HIGHMEM(self): addr = Symbol.get_ksymaddr("nr_free_highpages") return bool(addr) @property def CONFIG_X86_PAE(self): if self.pae is not None: return self.pae cr4 = get_register("cr4", use_monitor=True) if not cr4: self.pae = False elif (cr4 >> 5) & 1: # PAE check self.pae = True else: self.pae = False return self.pae @property def CONFIG_INTEL_TXT(self): addr = Symbol.get_ksymaddr("tboot_probe") return bool(addr) @property def CONFIG_PAGE_OFFSET(self): if hasattr(self, "cached_PAGE_OFFSET"): return self.cached_PAGE_OFFSET kern_min = Kernel.get_maps()[0][0] if 0xc000_0000 <= kern_min: self.cached_PAGE_OFFSET = 0xc000_0000 # VMSPLIT_3G elif 0xb000_0000 <= kern_min: self.cached_PAGE_OFFSET = 0xb000_0000 # VMSPLIT_3G_OPT elif 0x8000_0000 <= kern_min: self.cached_PAGE_OFFSET = 0x8000_0000 # VMSPLIT_2G elif 0x7800_0000 <= kern_min: self.cached_PAGE_OFFSET = 0x7800_0000 # VMSPLIT_2G_OPT elif 0x4000_0000 <= kern_min: self.cached_PAGE_OFFSET = 0x4000_0000 # VMSPLIT_1G return self.cached_PAGE_OFFSET @property def __PAGE_OFFSET(self): return self.CONFIG_PAGE_OFFSET @property def PAGE_OFFSET(self): return self.__PAGE_OFFSET @property def PAGE_OFFSET_END(self): return self.high_memory @property def high_memory(self): if hasattr(self, "cached_high_memory"): return self.cached_high_memory max_hm = AddressUtil.align_address(-128 << 20) vmalloc_start = KernelAddressHeuristicFinder._get_VMALLOC_START() if vmalloc_start is None: self.cached_high_memory = max_hm else: real_hm = vmalloc_start - self.VMALLOC_OFFSET self.cached_high_memory = min(real_hm, max_hm) return self.cached_high_memory @property def __FIXADDR_TOP(self): return 0xffff_f000 @property def FIXADDR_TOP(self): return self.__FIXADDR_TOP @property def __end_of_permanent_fixed_addresses(self): end = self.__end_of_fixed_addresses if end is None: return None if "3.0" <= self.kversion < "3.10": # (FIX_TBOOT_BASE), FIX_WP_TEST, FIX_BTMAP_BEGIN ~ FIX_BTMAP_END return end - int(self.CONFIG_INTEL_TXT) - 1 - 256 elif "3.10" <= self.kversion: # (FIX_TBOOT_BASE), FIX_WP_TEST, FIX_BTMAP_BEGIN ~ FIX_BTMAP_END return end - int(self.CONFIG_INTEL_TXT) - 1 - 512 return None @property def __end_of_fixed_addresses(self): return KernelAddressHeuristicFinder.get_end_of_fixed_addresses() @property def FIXADDR_SIZE(self): if self.__end_of_permanent_fixed_addresses is None: return None return self.__end_of_permanent_fixed_addresses << self.PAGE_SHIFT @property def FIXADDR_BOOT_SIZE(self): if "3.0" <= self.kversion < "3.19": return self.__end_of_fixed_addresses << self.PAGE_SHIFT return None @property def FIXADDR_TOT_SIZE(self): if "4.14" <= self.kversion: return self.__end_of_fixed_addresses << self.PAGE_SHIFT return None @property def FIXADDR_START(self): return self.FIXADDR_TOP - self.FIXADDR_SIZE @property def FIXADDR_BOOT_START(self): if "3.0" <= self.kversion < "3.19": return self.FIXADDR_TOP - self.FIXADDR_BOOT_SIZE return None @property def FIXADDR_TOT_START(self): if "4.14" <= self.kversion: return self.FIXADDR_TOP - self.FIXADDR_TOT_SIZE return None @property def PMD_SHIFT(self): if self.CONFIG_X86_PAE: return 21 else: return 22 # == PUD_SHIFT == PGDIR_SHIFT @property def PMD_SIZE(self): return 1 << self.PMD_SHIFT @property def PMD_MASK(self): return ~(self.PMD_SIZE - 1) @property def VMALLOC_OFFSET(self): return 8 * 1024 * 1024 @property def VMALLOC_START(self): return self.high_memory + self.VMALLOC_OFFSET @property def LAST_PKMAP(self): if self.CONFIG_X86_PAE: return 512 else: return 1024 @property def CPU_ENTRY_AREA_SIZE(self): if "4.14" <= self.kversion: if hasattr(self, "cached_CPU_ENTRY_AREA_SIZE"): return self.cached_CPU_ENTRY_AREA_SIZE cpu_entry_area_size = KernelAddressHeuristicFinder.get_sizeof_cpu_entry_area() if cpu_entry_area_size: self.cached_CPU_ENTRY_AREA_SIZE = cpu_entry_area_size return cpu_entry_area_size return None @property def CPU_ENTRY_AREA_PAGES(self): if "4.14" <= self.kversion: return self.CPU_ENTRY_AREA_SIZE // self.PAGE_SIZE return None @property def CPU_ENTRY_AREA_BASE(self): if "4.14" <= self.kversion: return (self.FIXADDR_TOT_START - self.PAGE_SIZE * (self.CPU_ENTRY_AREA_PAGES + 1)) & self.PMD_MASK return None @property def CPU_ENTRY_AREA_END(self): if "4.14" <= self.kversion: return self.CPU_ENTRY_AREA_BASE + self.CPU_ENTRY_AREA_SIZE return None @property def LDT_BASE_ADDR(self): if "4.19" <= self.kversion: return (self.CPU_ENTRY_AREA_BASE - self.PAGE_SIZE) & self.PMD_MASK return None @property def LDT_END_ADDR(self): if "4.19" <= self.kversion: return self.LDT_BASE_ADDR + self.PMD_SIZE return None @property def PKMAP_BASE(self): if "3.0" <= self.kversion < "3.19": return (self.FIXADDR_BOOT_START - self.PAGE_SIZE * (self.LAST_PKMAP + 1)) & self.PMD_MASK elif "3.19" <= self.kversion < "4.14": return (self.FIXADDR_START - self.PAGE_SIZE * (self.LAST_PKMAP + 1)) & self.PMD_MASK elif "4.14" <= self.kversion < "4.19": return (self.CPU_ENTRY_AREA_BASE - self.PAGE_SIZE) & self.PMD_MASK elif "4.19" <= self.kversion: return (self.LDT_BASE_ADDR - self.PAGE_SIZE) & self.PMD_MASK return None @property def VMALLOC_END(self): if "3.0" <= self.kversion < "4.14": if self.CONFIG_HIGHMEM: return self.PKMAP_BASE - 2 * self.PAGE_SIZE else: return self.FIXADDR_START - 2 * self.PAGE_SIZE elif "4.14" <= self.kversion < "4.19": if self.CONFIG_HIGHMEM: return self.PKMAP_BASE - 2 * self.PAGE_SIZE else: return self.CPU_ENTRY_AREA_BASE - 2 * self.PAGE_SIZE elif "4.19" <= self.kversion: if self.CONFIG_HIGHMEM: return self.PKMAP_BASE - 2 * self.PAGE_SIZE else: return self.LDT_BASE_ADDR - 2 * self.PAGE_SIZE return None @property def MODULES_VADDR(self): return self.VMALLOC_START @property def MODULES_END(self): return self.VMALLOC_END @property # noqa def MODULES_LEN(self): return self.MODULES_VADDR - self.MODULES_END @property def mem_map(self): if hasattr(self, "cached_mem_map"): return self.cached_mem_map self.cached_mem_map = KernelAddressHeuristicFinder.get_mem_map() return self.cached_mem_map @property def mem_section(self): if hasattr(self, "cached_mem_section"): return self.cached_mem_section self.cached_mem_section = KernelAddressHeuristicFinder.get_mem_section() return self.cached_mem_section @property def CONFIG_FLATMEM(self): return bool(self.mem_map) @property def CONFIG_SPARSEMEM(self): return bool(self.mem_section) @property def MAX_PHYSMEM_BITS(self): if self.CONFIG_SPARSEMEM: if self.CONFIG_X86_PAE: return 36 else: return 32 return None @property def SECTION_SIZE_BITS(self): if self.CONFIG_SPARSEMEM: if self.CONFIG_X86_PAE: return 29 else: return 26 return None @property def SECTIONS_SHIFT(self): if self.CONFIG_SPARSEMEM: return self.MAX_PHYSMEM_BITS - self.SECTION_SIZE_BITS return 0 @property def SECTIONS_WIDTH(self): if self.CONFIG_SPARSEMEM: return self.SECTIONS_SHIFT return 0 @property def SECTIONS_PGOFF(self): return 4 * 8 - self.SECTIONS_WIDTH @property def SECTIONS_PGSHIFT(self): return self.SECTIONS_PGOFF * int(self.SECTIONS_WIDTH != 0) @property def SECTIONS_MASK(self): return (1 << self.SECTIONS_WIDTH) - 1 @property def SECTION_HAS_MEM_MAP(self): return 1 << 1 @property def SECTION_MAP_LAST_BIT(self): if self.kversion < "4.13": return 1 << 2 elif "4.13" <= self.kversion < "5.3": return 1 << 3 elif "5.3" <= self.kversion < "5.12": return 1 << 4 elif "5.12" <= self.kversion < "6.0": return 1 << 5 elif "6.0" <= self.kversion: return 1 << 4 @property def SECTION_MAP_MASK(self): return ~(self.SECTION_MAP_LAST_BIT - 1) & 0xffff_ffff @property def NR_MEM_SECTIONS(self): if self.CONFIG_SPARSEMEM: return 1 << self.SECTIONS_SHIFT return None @property def PFN_SECTION_SHIFT(self): if self.CONFIG_SPARSEMEM: return self.SECTION_SIZE_BITS - self.PAGE_SHIFT return None @property def CONFIG_PAGE_EXTENSION(self): if "3.19" <= self.kversion: addr = Symbol.get_ksymaddr("page_ext_init") return bool(addr) return None @property def sizeof_mem_section(self): if not self.CONFIG_SPARSEMEM: return None if self.CONFIG_PAGE_EXTENSION: return current_arch.ptrsize * 4 return current_arch.ptrsize * 2 @property def sizeof_struct_page(self): if hasattr(self, "cached_sizeof_struct_page"): return self.cached_sizeof_struct_page if not (self.CONFIG_FLATMEM or self.CONFIG_SPARSEMEM): return None if self.PAGE_OFFSET is None: return None ret = Kernel.get_page_virt_pair() if not ret: return None page, vaddr = ret pfn = (vaddr - self.PAGE_OFFSET) >> self.PAGE_SHIFT if pfn == 0: return None if self.CONFIG_FLATMEM: base = self.mem_map if base is None: return None else: flags = read_int_from_memory(page) section_id = (flags >> self.SECTIONS_PGSHIFT) & self.SECTIONS_MASK ms = self.mem_section + self.sizeof_mem_section * section_id if ms is None: return None section_mem_map = read_int_from_memory(ms) if (section_mem_map & self.SECTION_HAS_MEM_MAP) == 0: return None base = section_mem_map & self.SECTION_MAP_MASK if base == 0: return None delta = page - base if delta <= 0: return None if (delta % pfn) != 0: return None size = delta // pfn if size == 0: return None self.cached_sizeof_struct_page = size return size class KernelConstsX64(KernelConstsBase): """A class that manages x64 constants by version.""" def __init__(self, version=None, kaslr=None, level5pt=None): super().__init__(version) self.kaslr = kaslr self.level5pt = level5pt return PAGE_SHIFT = 12 PAGE_SIZE = 1 << PAGE_SHIFT def check_kaslr(self): if self.kaslr is not None: return self.kaslr kcmdline = Kernel.kernel_cmdline() ksym_ret = gdb.execute("ksymaddr-remote --quiet --no-pager kaslr_", to_string=True) if not ksym_ret: self.kaslr = False elif kcmdline and "nokaslr" in kcmdline.cmdline: self.kaslr = False else: self.kaslr = True return self.kaslr @property def sizeof_struct_page(self): if hasattr(self, "cached_sizeof_struct_page"): return self.cached_sizeof_struct_page ret = Kernel.get_page_virt_pair() if not ret: return None page, vaddr = ret pfn = (vaddr - self.PAGE_OFFSET) >> self.PAGE_SHIFT sizeof_struct_page_value = align_to_ptrsize((page - self.VMEMMAP_START) // pfn) if sizeof_struct_page_value != 0: self.cached_sizeof_struct_page = sizeof_struct_page_value return sizeof_struct_page_value @property def CONFIG_X86_5LEVEL(self): if self.level5pt is not None: return self.level5pt cr4 = get_register("cr4", use_monitor=True) if not cr4: self.level5pt = False elif (cr4 >> 12) & 1: # PML5T check self.level5pt = True else: self.level5pt = False return self.level5pt @property def CONFIG_RANDOMIZE_BASE(self): if "3.14" <= self.kversion: return self.check_kaslr() return None @property def CONFIG_RANDOMIZE_MEMORY(self): if "4.8" <= self.kversion: return self.check_kaslr() # change if needed return None @property def CONFIG_DYNAMIC_MEMORY_LAYOUT(self): if "4.17" <= self.kversion < "6.16": return self.check_kaslr() # change if needed return None @property def CONFIG_RANDOMIZE_BASE_MAX_OFFSET(self): if "3.14" <= self.kversion < "4.7": return 0x4000_0000 # change if needed return None @property def CONFIG_DEBUG_KMAP_LOCAL_FORCE_MAP(self): if "5.11" <= self.kversion: return False # change if needed return None @property def CONFIG_KMSAN(self): if "6.1" <= self.kversion: return False # change if needed return None @property def CONFIG_INTEL_TXT(self): addr = Symbol.get_ksymaddr("tboot_probe") return bool(addr) @property def KERNEL_IMAGE_SIZE_DEFAULT(self): if "3.14" <= self.kversion < "4.7": return 512 * 1024 * 1024 return None @property def KERNEL_IMAGE_SIZE(self): if "3.0" <= self.kversion < "3.14": return 512 * 1024 * 1024 elif "3.14" <= self.kversion < "4.7": if self.CONFIG_RANDOMIZE_BASE and self.CONFIG_RANDOMIZE_BASE_MAX_OFFSET > self.KERNEL_IMAGE_SIZE_DEFAULT: return self.CONFIG_RANDOMIZE_BASE_MAX_OFFSET else: return self.KERNEL_IMAGE_SIZE_DEFAULT elif "4.7" <= self.kversion: if self.CONFIG_RANDOMIZE_BASE: return 1024 * 1024 * 1024 else: return 512 * 1024 * 1024 return None @property def __START_KERNEL_map(self): if "3.0" <= self.kversion: return 0xffff_ffff_8000_0000 return None @property # noqa def START_KERNEL_map(self): return self.__START_KERNEL_map @property def __PAGE_OFFSET_BASE(self): if "4.8" <= self.kversion < "4.12": return 0xffff_8800_0000_0000 elif "4.12" <= self.kversion < "4.17": if self.CONFIG_X86_5LEVEL: return 0xff10_0000_0000_0000 else: return 0xffff_8800_0000_0000 return None @property def __PAGE_OFFSET_BASE_L4(self): if "4.17" <= self.kversion < "4.19": return 0xffff_8800_0000_0000 elif "4.19" <= self.kversion: return 0xffff_8880_0000_0000 return None @property # noqa def __PAGE_OFFSET_BASE_L5(self): if "4.17" <= self.kversion < "4.19": return 0xff10_0000_0000_0000 elif "4.19" <= self.kversion: return 0xff11_0000_0000_0000 return None @property def page_offset_base(self): if "4.8" <= self.kversion: if hasattr(self, "cached_page_offset_base"): return self.cached_page_offset_base page_offset = KernelAddressHeuristicFinder._get_PAGE_OFFSET() if page_offset: self.cached_page_offset_base = page_offset return page_offset return None @property def __PAGE_OFFSET(self): if "3.0" <= self.kversion < "4.8": return 0xffff_8800_0000_0000 elif "4.8" <= self.kversion < "4.17": if self.CONFIG_RANDOMIZE_MEMORY: return self.page_offset_base else: return self.__PAGE_OFFSET_BASE elif "4.17" <= self.kversion < "6.16": if self.CONFIG_DYNAMIC_MEMORY_LAYOUT: return self.page_offset_base else: return self.__PAGE_OFFSET_BASE_L4 elif "6.16" <= self.kversion: return self.page_offset_base return None @property def PAGE_OFFSET(self): if "3.0" <= self.kversion: return self.__PAGE_OFFSET return None @property def PAGE_OFFSET_END(self): if "3.0" <= self.kversion: return self.VMALLOC_START return None @property def LDT_PGD_ENTRY_L4(self): if "4.17" <= self.kversion < "4.19": return -3 return None @property def LDT_PGD_ENTRY_L5(self): if "4.17" <= self.kversion < "4.19": return -112 return None @property def LDT_PGD_ENTRY(self): if "4.14" <= self.kversion < "4.15": return -240 elif "4.15" <= self.kversion < "4.17": if self.CONFIG_X86_5LEVEL: return -112 else: return -3 elif "4.17" <= self.kversion < "4.19": if self.CONFIG_X86_5LEVEL: return self.LDT_PGD_ENTRY_L5 else: return self.LDT_PGD_ENTRY_L4 elif "4.19" <= self.kversion: return -240 return None @property def LDT_BASE_ADDR(self): if "4.14" <= self.kversion: ldt_base_addr = self.LDT_PGD_ENTRY << self.PGDIR_SHIFT return AddressUtil.align_address(ldt_base_addr) return None @property def LDT_END_ADDR(self): if "4.19" <= self.kversion: return self.LDT_BASE_ADDR + self.PGDIR_SIZE return None @property def VMALLOC_SIZE_TB_L4(self): if "4.17" <= self.kversion: return 32 return None @property def VMALLOC_SIZE_TB_L5(self): if "4.17" <= self.kversion: return 12800 return None @property def VMALLOC_SIZE_TB(self): if "4.8" <= self.kversion < "4.12": return 32 elif "4.12" <= self.kversion < "4.14": if self.CONFIG_X86_5LEVEL: return 16384 else: return 32 elif "4.14" <= self.kversion < "4.17": if self.CONFIG_X86_5LEVEL: return 12800 else: return 32 elif "4.17" <= self.kversion < "6.16": if self.CONFIG_DYNAMIC_MEMORY_LAYOUT: if self.CONFIG_X86_5LEVEL: return self.VMALLOC_SIZE_TB_L5 else: return self.VMALLOC_SIZE_TB_L4 else: return self.VMALLOC_SIZE_TB_L4 elif "6.16" <= self.kversion: if self.CONFIG_X86_5LEVEL: return self.VMALLOC_SIZE_TB_L5 else: return self.VMALLOC_SIZE_TB_L4 return None @property def __VMALLOC_BASE(self): if "4.8" <= self.kversion < "4.12": return 0xffff_c900_0000_0000 elif "4.12" <= self.kversion < "4.14": if self.CONFIG_X86_5LEVEL: return 0xff92_0000_0000_0000 else: return 0xffff_c900_0000_0000 elif "4.14" <= self.kversion < "4.17": if self.CONFIG_X86_5LEVEL: return 0xffa0_0000_0000_0000 else: return 0xffff_c900_0000_0000 return None @property def __VMALLOC_BASE_L4(self): if "4.17" <= self.kversion: return 0xffff_c900_0000_0000 return None @property # noqa def __VMALLOC_BASE_L5(self): if "4.17" <= self.kversion: return 0xffa0_0000_0000_0000 return None @property def vmalloc_base(self): if "4.8" <= self.kversion: if hasattr(self, "cached_vmalloc_base"): return self.cached_vmalloc_base vmalloc_start = KernelAddressHeuristicFinder._get_VMALLOC_START() if vmalloc_start: self.cached_vmalloc_base = vmalloc_start return vmalloc_start return None @property def VMALLOC_START(self): if "3.0" <= self.kversion < "4.8": return 0xffff_c900_0000_0000 elif "4.8" <= self.kversion < "4.17": if self.CONFIG_RANDOMIZE_MEMORY: return self.vmalloc_base else: return self.__VMALLOC_BASE elif "4.17" <= self.kversion < "6.16": if self.CONFIG_DYNAMIC_MEMORY_LAYOUT: return self.vmalloc_base else: return self.__VMALLOC_BASE_L4 elif "6.16" <= self.kversion: return self.vmalloc_base return None @property def VMEMORY_END(self): if "6.1" <= self.kversion: return self.VMALLOC_START + (self.VMALLOC_SIZE_TB << 40) return None @property def VMALLOC_QUARTER_SIZE(self): if "6.1" <= self.kversion: if self.CONFIG_KMSAN: return (self.VMALLOC_SIZE_TB << 40) >> 2 return None @property def VMALLOC_END(self): if "3.0" <= self.kversion < "4.8": return 0xffff_e900_0000_0000 elif "4.8" <= self.kversion < "6.1": return self.VMALLOC_START + (self.VMALLOC_SIZE_TB << 40) elif "6.1" <= self.kversion: if not self.CONFIG_KMSAN: return self.VMEMORY_END else: return self.VMALLOC_START + self.VMALLOC_QUARTER_SIZE return None @property def __VMEMMAP_BASE(self): if "4.9" <= self.kversion < "4.12": return 0xffff_ea00_0000_0000 elif "4.12" <= self.kversion < "4.17": if self.CONFIG_X86_5LEVEL: return 0xffd4_0000_0000_0000 else: return 0xffff_ea00_0000_0000 return None @property def __VMEMMAP_BASE_L4(self): if "4.17" <= self.kversion: return 0xffff_ea00_0000_0000 return None @property # noqa def __VMEMMAP_BASE_L5(self): if "4.17" <= self.kversion: return 0xffd4_0000_0000_0000 return None @property def vmemmap_base(self): if "4.9" <= self.kversion: if hasattr(self, "cached_vmemmap_base"): return self.cached_vmemmap_base vmemmap_start = KernelAddressHeuristicFinder._get_VMEMMAP_START() if vmemmap_start: self.cached_vmemmap_base = vmemmap_start return vmemmap_start return None @property def VMEMMAP_START(self): if "3.0" <= self.kversion < "4.9": return 0xffff_ea00_0000_0000 elif "4.9" <= self.kversion < "4.17": if self.CONFIG_RANDOMIZE_MEMORY: return self.vmemmap_base else: return self.__VMEMMAP_BASE elif "4.17" <= self.kversion < "6.16": if self.CONFIG_DYNAMIC_MEMORY_LAYOUT: return self.vmemmap_base else: return self.__VMEMMAP_BASE_L4 elif "6.16" <= self.kversion: return self.vmemmap_base return None @property def VMEMMAP_END(self): if "3.0" <= self.kversion: return self.VMEMMAP_START + self.SZ_1T return None @property def MODULES_VADDR(self): if "3.0" <= self.kversion < "3.14": return 0xffff_ffff_a000_0000 elif "3.14" <= self.kversion: return self.__START_KERNEL_map + self.KERNEL_IMAGE_SIZE return None @property def VSYSCALL_START(self): if "3.0" <= self.kversion < "3.16": vsyscall_start = (-10 << 20) return AddressUtil.align_address(vsyscall_start) return None @property def VSYSCALL_END(self): if "3.0" <= self.kversion < "3.16": vsyscall_end = (-2 << 20) return AddressUtil.align_address(vsyscall_end) return None @property def VSYSCALL_ADDR(self): if "3.0" <= self.kversion < "3.16": return self.VSYSCALL_START elif "3.16" <= self.kversion: vsyscall_addr = (-10 << 20) return AddressUtil.align_address(vsyscall_addr) return None @property def FIXADDR_TOP(self): if "3.0" <= self.kversion < "3.16": return self.VSYSCALL_END - self.PAGE_SIZE elif "3.16" <= self.kversion: return self.round_up(self.VSYSCALL_ADDR + self.PAGE_SIZE, 1 << self.PMD_SHIFT) - self.PAGE_SIZE return None def __fix_to_virt(self, x): return self.FIXADDR_TOP - (x << self.PAGE_SHIFT) @property def __end_of_permanent_fixed_addresses(self): end = self.__end_of_fixed_addresses if end is None: return None if "3.0" <= self.kversion < "3.10": # (FIX_TBOOT_BASE), FIX_BTMAP_BEGIN ~ FIX_BTMAP_END return end - int(self.CONFIG_INTEL_TXT) - 256 elif "3.10" <= self.kversion: # (FIX_TBOOT_BASE), FIX_BTMAP_BEGIN ~ FIX_BTMAP_END return end - int(self.CONFIG_INTEL_TXT) - 512 return None @property def __end_of_fixed_addresses(self): return KernelAddressHeuristicFinder.get_end_of_fixed_addresses() @property def FIXADDR_SIZE(self): if self.__end_of_permanent_fixed_addresses is None: return None return self.__end_of_permanent_fixed_addresses << self.PAGE_SHIFT @property def FIXADDR_START(self): if self.FIXADDR_TOP is None: return None if self.FIXADDR_SIZE is None: return None return self.FIXADDR_TOP - self.FIXADDR_SIZE @property def MODULES_END(self): if "3.0" <= self.kversion < "4.12": return 0xffff_ffff_ff00_0000 elif "4.12" <= self.kversion < "4.14": return self.__fix_to_virt(self.__end_of_fixed_addresses + 1) elif "4.14" <= self.kversion < "5.11": return 0xffff_ffff_ff00_0000 elif "5.11" <= self.kversion: if not self.CONFIG_DEBUG_KMAP_LOCAL_FORCE_MAP: return 0xffff_ffff_ff00_0000 else: return 0xffff_ffff_fe00_0000 return None @property # noqa def MODULES_LEN(self): if "3.0" <= self.kversion: return self.MODULES_END - self.MODULES_VADDR return None @property def P4D_SHIFT(self): if "4.12" <= self.kversion: if self.CONFIG_X86_5LEVEL: return 39 else: return self.PGDIR_SHIFT return None @property def pgdir_shift(self): if "4.17" <= self.kversion: return 48 return None @property def PGDIR_SHIFT(self): if "3.0" <= self.kversion < "4.12": return 39 elif "4.12" <= self.kversion < "4.17": if self.CONFIG_X86_5LEVEL: return 48 else: return 39 elif "4.17" <= self.kversion: if self.CONFIG_X86_5LEVEL: return self.pgdir_shift else: return 39 return None @property def PGDIR_SIZE(self): return 1 << self.PGDIR_SHIFT @property def PMD_SHIFT(self): return 21 @property def ESPFIX_PGD_ENTRY(self): if "3.0" <= self.kversion: return -2 return None @property def ESPFIX_BASE_ADDR(self): if "3.0" <= self.kversion < "4.12": espfix_base_addr = self.ESPFIX_PGD_ENTRY << self.PGDIR_SHIFT return AddressUtil.align_address(espfix_base_addr) elif "4.12" <= self.kversion: espfix_base_addr = self.ESPFIX_PGD_ENTRY << self.P4D_SHIFT return AddressUtil.align_address(espfix_base_addr) return None @property # noqa def ESPFIX_END(self): if "3.0" <= self.kversion: return self.ESPFIX_BASE_ADDR + 0x0000_0080_0000_0000 return None @property def CPU_ENTRY_AREA_PGD(self): if "4.14" <= self.kversion: return -4 return None @property def CPU_ENTRY_AREA_BASE(self): if "4.14" <= self.kversion: cpu_entry_area_base = self.CPU_ENTRY_AREA_PGD << self.P4D_SHIFT return AddressUtil.align_address(cpu_entry_area_base) return None @property def CPU_ENTRY_AREA_END(self): if "4.14" <= self.kversion: return self.CPU_ENTRY_AREA_BASE + 0x0000_0080_0000_0000 return None @property # noqa def EFI_VA_START(self): if "3.19" <= self.kversion: efi_va_start = -4 * (1 << 30) return AddressUtil.align_address(efi_va_start) return None @property # noqa def EFI_VA_END(self): if "3.19" <= self.kversion: efi_va_end = -68 * (1 << 30) return AddressUtil.align_address(efi_va_end) return None class KernelConstsArm32(KernelConstsBase): """A class that manages arm32 constants by version.""" PAGE_SHIFT = 12 PAGE_SIZE = 1 << PAGE_SHIFT def __init__(self, version=None): super().__init__(version) return @property def CONFIG_PAGE_OFFSET(self): if hasattr(self, "cached_PAGE_OFFSET"): return self.cached_PAGE_OFFSET kern_min = Kernel.get_maps()[0][0] if 0xc000_0000 - 0x0100_0000 <= kern_min: # 0xbf000000-0xc0000000 is kernel module area. # Even if it is VMSPLIT_3G, this is used. self.cached_PAGE_OFFSET = 0xc000_0000 # VMSPLIT_3G elif 0xb000_0000 - 0x0100_0000 <= kern_min: self.cached_PAGE_OFFSET = 0xb000_0000 # VMSPLIT_3G_OPT elif 0x8000_0000 - 0x0100_0000 <= kern_min: self.cached_PAGE_OFFSET = 0x8000_0000 # VMSPLIT_2G elif 0x4000_0000 - 0x0100_0000 <= kern_min: self.cached_PAGE_OFFSET = 0x4000_0000 # VMSPLIT_1G return self.cached_PAGE_OFFSET @property def CONFIG_HIGHMEM(self): addr = Symbol.get_ksymaddr("nr_free_highpages") return bool(addr) @property def CONFIG_THUMB2_KERNEL(self): if hasattr(self, "cached_CONFIG_THUMB2_KERNEL"): return self.cached_CONFIG_THUMB2_KERNEL if is_in_kernel(): self.cached_CONFIG_THUMB2_KERNEL = current_arch.is_thumb() return self.cached_CONFIG_THUMB2_KERNEL return None @property def PMD_SHIFT(self): return 21 @property def PMD_SIZE(self): return 1 << self.PMD_SHIFT @property def PAGE_OFFSET(self): return self.CONFIG_PAGE_OFFSET @property def PAGE_OFFSET_END(self): return self.high_memory @property def high_memory(self): if hasattr(self, "cached_high_memory"): return self.cached_high_memory res = PageMap.get_page_maps_by_pagewalk("pagewalk --quiet --no-pager --disable-color") res = sorted(set(res.splitlines())) res = list(filter(lambda line: line.endswith("]"), res)) res = list(filter(lambda line: "[+]" not in line, res)) res = list(filter(lambda line: "*" not in line, res)) maps = [] for line in res: line = line.split() vaddr_start = int(line[0].split("-")[0], 16) if vaddr_start < self.PAGE_OFFSET: continue dic = { "vaddr_start": vaddr_start, "vaddr_end": int(line[0].split("-")[1], 16), "paddr_start": int(line[1].split("-")[0], 16), "paddr_end": int(line[1].split("-")[1], 16), } Maps = collections.namedtuple("Maps", dic.keys()) maps.append(Maps(*dic.values())) physmap = maps[0] for m in maps[1:]: if physmap.vaddr_end != m.vaddr_start: break if physmap.paddr_end != m.paddr_start: break physmap = m self.cached_high_memory = physmap.vaddr_end return self.cached_high_memory @property def MODULES_VADDR(self): if "3.0" <= self.kversion < "3.8": if self.CONFIG_THUMB2_KERNEL is None: return None elif self.CONFIG_THUMB2_KERNEL is False: return self.PAGE_OFFSET - 16 * 1024 * 1024 else: return self.PAGE_OFFSET - 8 * 1024 * 1024 elif "3.8" <= self.kversion: if self.CONFIG_THUMB2_KERNEL is None: return None elif self.CONFIG_THUMB2_KERNEL is False: return self.PAGE_OFFSET - self.SZ_16M else: return self.PAGE_OFFSET - self.SZ_8M return None @property def MODULES_END(self): if self.CONFIG_HIGHMEM: return self.PAGE_OFFSET - self.PMD_SIZE else: return self.PAGE_OFFSET @property def VMALLOC_OFFSET(self): return 8 * 1024 * 1024 @property def VMALLOC_START(self): return (self.high_memory + self.VMALLOC_OFFSET) & ~(self.VMALLOC_OFFSET - 1) @property def VMALLOC_END(self): if "3.0" <= self.kversion < "3.3": return self.FIXADDR_START elif "3.3" <= self.kversion < "4.4": return 0xff00_0000 elif "4.4" <= self.kversion: return 0xff80_0000 return None @property def DTB_START(self): if "5.10" <= self.kversion: return 0xff80_0000 return None @property def DTB_END(self): if "5.10" <= self.kversion: return 0xffc0_0000 return None @property def FIXADDR_START(self): if self.kversion < "3.16": return 0xfff0_0000 elif self.kversion < "5.4": return 0xffc0_0000 else: return 0xffc8_0000 @property def FIXADDR_TOP(self): if self.kversion < "3.16": return 0xfffe_0000 elif self.kversion < "3.19": return 0xffe0_0000 else: return 0xfff0_0000 @property def FIXADDR_SIZE(self): return self.FIXADDR_TOP - self.FIXADDR_START @property def RESERVED_START(self): return 0xffff_1000 @property def RESERVED_END(self): return 0xffff_8000 @property def PHYS_OFFSET(self): if hasattr(self, "cached_PHYS_OFFSET"): return self.cached_PHYS_OFFSET # When p2v and v2p are available, memstart_addr can be resolved without relying on symbols. if self.PAGE_OFFSET is None: return None kinfo = Kernel.get_kernel_layout() if kinfo is None: return None phys_kbase = Kernel.v2p(kinfo.text_base) if phys_kbase is None: return None cands = Kernel.p2v(phys_kbase) linear_cands = [x for x in cands if self.PAGE_OFFSET <= x < self.PAGE_OFFSET_END] if len(linear_cands) != 1: return None linear_kbase = linear_cands[0] self.cached_PHYS_OFFSET = AddressUtil.align_address(phys_kbase - (linear_kbase - self.PAGE_OFFSET)) return self.cached_PHYS_OFFSET @property def mem_map(self): if hasattr(self, "cached_mem_map"): return self.cached_mem_map self.cached_mem_map = KernelAddressHeuristicFinder.get_mem_map() return self.cached_mem_map @property def mem_section(self): if hasattr(self, "cached_mem_section"): return self.cached_mem_section self.cached_mem_section = KernelAddressHeuristicFinder.get_mem_section() return self.cached_mem_section @property def CONFIG_FLATMEM(self): return bool(self.mem_map) @property def CONFIG_SPARSEMEM(self): return bool(self.mem_section) @property def MAX_PHYSMEM_BITS(self): if self.CONFIG_SPARSEMEM: return 36 return None @property def SECTION_SIZE_BITS(self): if self.CONFIG_SPARSEMEM: return 28 return None @property def SECTIONS_SHIFT(self): if self.CONFIG_SPARSEMEM: return self.MAX_PHYSMEM_BITS - self.SECTION_SIZE_BITS return 0 @property def SECTIONS_WIDTH(self): if self.CONFIG_SPARSEMEM: return self.SECTIONS_SHIFT return 0 @property def SECTIONS_PGOFF(self): return 4 * 8 - self.SECTIONS_WIDTH @property def SECTIONS_PGSHIFT(self): return self.SECTIONS_PGOFF * int(self.SECTIONS_WIDTH != 0) @property def SECTIONS_MASK(self): return (1 << self.SECTIONS_WIDTH) - 1 @property def SECTION_HAS_MEM_MAP(self): return 1 << 1 @property def SECTION_MAP_LAST_BIT(self): if self.kversion < "4.13": return 1 << 2 elif "4.13" <= self.kversion < "5.3": return 1 << 3 elif "5.3" <= self.kversion < "5.12": return 1 << 4 elif "5.12" <= self.kversion < "6.0": return 1 << 5 elif "6.0" <= self.kversion: return 1 << 4 @property def SECTION_MAP_MASK(self): return ~(self.SECTION_MAP_LAST_BIT - 1) & 0xffff_ffff @property def NR_MEM_SECTIONS(self): if self.CONFIG_SPARSEMEM: return 1 << self.SECTIONS_SHIFT return None @property def PFN_SECTION_SHIFT(self): if self.CONFIG_SPARSEMEM: return self.SECTION_SIZE_BITS - self.PAGE_SHIFT return None @property def PHYS_PFN_OFFSET(self): return self.PHYS_OFFSET >> self.PAGE_SHIFT @property def CONFIG_PAGE_EXTENSION(self): if "3.19" <= self.kversion: addr = Symbol.get_ksymaddr("page_ext_init") return bool(addr) return None @property def sizeof_mem_section(self): if not self.CONFIG_SPARSEMEM: return None if self.CONFIG_PAGE_EXTENSION: return current_arch.ptrsize * 4 return current_arch.ptrsize * 2 @property def sizeof_struct_page(self): if hasattr(self, "cached_sizeof_struct_page"): return self.cached_sizeof_struct_page if not (self.CONFIG_FLATMEM or self.CONFIG_SPARSEMEM): return None if self.PAGE_OFFSET is None or self.PHYS_PFN_OFFSET is None: return None ret = Kernel.get_page_virt_pair() if not ret: return None page, vaddr = ret pfn = ((vaddr - self.PAGE_OFFSET) >> self.PAGE_SHIFT) + self.PHYS_PFN_OFFSET if self.CONFIG_FLATMEM: base = self.mem_map index = pfn - self.PHYS_PFN_OFFSET else: flags = read_int_from_memory(page) section_id = (flags >> self.SECTIONS_PGSHIFT) & self.SECTIONS_MASK ms = self.mem_section + self.sizeof_mem_section * section_id section_mem_map = read_int_from_memory(ms) if (section_mem_map & self.SECTION_HAS_MEM_MAP) == 0: return None base = section_mem_map & self.SECTION_MAP_MASK if base == 0: return None index = pfn delta = page - base if delta < 0: return None if index <= 0: return None if (delta % index) != 0: return None size = delta // index if size == 0: return None self.cached_sizeof_struct_page = size return size class KernelConstsArm64(KernelConstsBase): """A class that manages arm64 constants by version.""" def __init__(self, version=None, kasan=None): super().__init__(version) self.kasan = kasan assert "3.7" <= self.kversion # arm64 support start version return @Cache.cache_until_next def TCR_EL1(self): return get_register("$TCR_EL1", use_mbed_exec=True) @Cache.cache_until_next def ID_AA64MMFR2_EL1(self): return get_register("$ID_AA64MMFR2_EL1", use_mbed_exec=True) @property def PAGE_SHIFT(self): tcr = self.TCR_EL1() if tcr is not None: tg1 = (tcr >> 30) & 0b11 if tg1 == 0b01: return 14 elif tg1 == 0b10: return 12 elif tg1 == 0b11: return 16 # fallback return 12 @property def PAGE_SIZE(self): return 1 << self.PAGE_SHIFT @property def FEAT_LVA(self): ID_AA64MMFR2_EL1 = self.ID_AA64MMFR2_EL1() if ID_AA64MMFR2_EL1 is not None: FEAT_LVA = ((ID_AA64MMFR2_EL1 >> 16) & 0b1111) == 0b0001 else: FEAT_LVA = False return FEAT_LVA @property def CONFIG_KASAN(self): if self.kasan is not None: return bool(self.kasan) res = gdb.execute("ksymaddr-remote --quiet kasan_", to_string=True) self.kasan = bool(res) return self.kasan @property def CONFIG_KASAN_SW_TAGS(self): if "5.4" <= self.kversion: return False # change if needed return None @property def CONFIG_ARM64_16K_PAGES(self): if "6.9" <= self.kversion: return False # change if needed return None @property def CONFIG_ARM64_64K_PAGES(self): if "3.7" <= self.kversion: return False # change if needed return None @property def CONFIG_ARM64_VA_BITS(self): tcr = self.TCR_EL1() T1SZ = (tcr >> 16) & 0b111111 region_end = 2 ** 64 region_start = region_end - (2 ** (64 - T1SZ)) region_bits = GefUtil.log2(region_end - region_start) if self.FEAT_LVA: return min(52, region_bits) return region_bits @property def PTDESC_ORDER(self): if "6.15" <= self.kversion: return 3 return None @property def PTDESC_TABLE_SHIFT(self): if "6.15" <= self.kversion: return self.PAGE_SHIFT - self.PTDESC_ORDER return None def ARM64_HW_PGTABLE_LEVEL_SHIFT(self, n): if "4.4" <= self.kversion < "6.15": return (self.PAGE_SHIFT - 3) * (4 - n) + 3 elif "6.15" <= self.kversion: return self.PTDESC_TABLE_SHIFT * (4 - n) + self.PTDESC_ORDER return None @property def PMD_SHIFT(self): if "3.17" <= self.kversion < "4.4": return (self.PAGE_SHIFT - 3) * 2 + 3 elif "4.4" <= self.kversion: return self.ARM64_HW_PGTABLE_LEVEL_SHIFT(2) return None @property def PMD_SIZE(self): if "3.17" <= self.kversion: return 1 << self.PMD_SHIFT return None @property def PUD_SHIFT(self): if "3.17" <= self.kversion < "4.4": return (self.PAGE_SHIFT - 3) * 3 + 3 elif "4.4" <= self.kversion: return self.ARM64_HW_PGTABLE_LEVEL_SHIFT(1) return None @property def PUD_SIZE(self): if "3.17" <= self.kversion: return 1 << self.PUD_SHIFT return None def _PAGE_END(self, va): if "5.4" <= self.kversion: _page_end = -(1 << (va - 1)) return AddressUtil.align_address(_page_end) return None def _PAGE_OFFSET(self, va): if "5.4" <= self.kversion: _page_offset = -(1 << va) return AddressUtil.align_address(_page_offset) return None @property def KASAN_SHADOW_SCALE_SHIFT(self): if "4.16" <= self.kversion < "5.0": if self.CONFIG_KASAN: return 3 elif "5.0" <= self.kversion < "5.11": # arch/arm64/Makefile if self.CONFIG_KASAN_SW_TAGS: return 4 else: return 3 elif "5.11" <= self.kversion: # arch/arm64/Makefile if self.CONFIG_KASAN_SW_TAGS: return 4 elif self.CONFIG_KASAN: return 3 return None return None def _KASAN_SHADOW_START(self, va): if "5.4" <= self.kversion: return self.KASAN_SHADOW_END - (1 << (va - self.KASAN_SHADOW_SCALE_SHIFT)) return None @property def KASAN_SHADOW_OFFSET(self): if "5.4" <= self.kversion < "5.11": if not self.CONFIG_KASAN_SW_TAGS: if self.VA_BITS == 48 or self.VA_BITS == 52: return 0xdfff_a000_0000_0000 elif self.VA_BITS == 47: return 0xdfff_d000_0000_0000 elif self.VA_BITS == 42: return 0xdfff_fe80_0000_0000 elif self.VA_BITS == 39: return 0xdfff_ffd0_0000_0000 elif self.VA_BITS == 36: return 0xdfff_fffa_0000_0000 else: if self.VA_BITS == 48 or self.VA_BITS == 52: return 0xefff_9000_0000_0000 elif self.VA_BITS == 47: return 0xefff_c800_0000_0000 elif self.VA_BITS == 42: return 0xefff_fe40_0000_0000 elif self.VA_BITS == 39: return 0xefff_ffc8_0000_0000 elif self.VA_BITS == 36: return 0xefff_fff9_0000_0000 elif "5.11" <= self.kversion < "6.10": if not self.CONFIG_KASAN_SW_TAGS: if self.VA_BITS == 48 or self.VA_BITS == 52: return 0xdfff_8000_0000_0000 elif self.VA_BITS == 47: return 0xdfff_c000_0000_0000 elif self.VA_BITS == 42: return 0xdfff_fe00_0000_0000 elif self.VA_BITS == 39: return 0xdfff_ffc0_0000_0000 elif self.VA_BITS == 36: return 0xdfff_fff8_0000_0000 else: if self.VA_BITS == 48 or self.VA_BITS == 52: return 0xefff_8000_0000_0000 elif self.VA_BITS == 47: return 0xefff_c000_0000_0000 elif self.VA_BITS == 42: return 0xefff_fe00_0000_0000 elif self.VA_BITS == 39: return 0xefff_ffc0_0000_0000 elif self.VA_BITS == 36: return 0xefff_fff8_0000_0000 elif "6.10" <= self.kversion: if not self.CONFIG_KASAN_SW_TAGS: if self.VA_BITS == 48 or (self.VA_BITS == 52 and not self.CONFIG_ARM64_16K_PAGES): return 0xdfff_8000_0000_0000 elif (self.VA_BITS == 47 or self.VA_BITS == 52) and self.CONFIG_ARM64_16K_PAGES: return 0xdfff_c000_0000_0000 elif self.VA_BITS == 42: return 0xdfff_fe00_0000_0000 elif self.VA_BITS == 39: return 0xdfff_ffc0_0000_0000 elif self.VA_BITS == 36: return 0xdfff_fff8_0000_0000 else: if self.VA_BITS == 48 or (self.VA_BITS == 52 and not self.CONFIG_ARM64_16K_PAGES): return 0xefff_8000_0000_0000 elif (self.VA_BITS == 47 or self.VA_BITS == 52) and self.CONFIG_ARM64_16K_PAGES: return 0xefff_c000_0000_0000 elif self.VA_BITS == 42: return 0xefff_fe00_0000_0000 elif self.VA_BITS == 39: return 0xefff_ffc0_0000_0000 elif self.VA_BITS == 36: return 0xefff_fff8_0000_0000 return None @property def vabits_actual(self): if "5.4" <= self.kversion < "6.0": # stored at arch/arm64/kernel/head.S if self.FEAT_LVA: return 52 else: return self.VA_BITS_MIN elif "6.0" <= self.kversion < "6.9": # stored at arch/arm64/kernel/head.S if self.FEAT_LVA: return self.VA_BITS else: return self.VA_BITS_MIN elif "6.9" <= self.kversion: if self.VA_BITS > 48: tcr = self.TCR_EL1() return (64 - ((tcr >> 16) & 63)) else: return self.VA_BITS return None @property def KASAN_SHADOW_START(self): if "4.4" <= self.kversion < "5.4": return self.VA_START elif "5.4" <= self.kversion: return self._KASAN_SHADOW_START(self.vabits_actual) return None @property def KASAN_SHADOW_END(self): if "4.4" <= self.kversion < "4.6": return self.KASAN_SHADOW_START + (1 << (self.VA_BITS - 3)) elif "4.6" <= self.kversion < "5.4": return self.KASAN_SHADOW_START + self.KASAN_SHADOW_SIZE elif "5.4" <= self.kversion < "5.11": if self.CONFIG_KASAN: return (1 << (64 - self.KASAN_SHADOW_SCALE_SHIFT)) + self.KASAN_SHADOW_OFFSET else: return self._PAGE_END(self.VA_BITS_MIN) elif "5.11" <= self.kversion: if self.CONFIG_KASAN or self.CONFIG_KASAN_SW_TAGS: return (1 << (64 - self.KASAN_SHADOW_SCALE_SHIFT)) + self.KASAN_SHADOW_OFFSET return None @property def KASAN_SHADOW_SIZE(self): if "4.6" <= self.kversion < "4.16": if self.CONFIG_KASAN: return 1 << (self.VA_BITS - 3) else: return 0 elif "4.16" <= self.kversion < "5.4": if self.CONFIG_KASAN: return 1 << (self.VA_BITS - self.KASAN_SHADOW_SCALE_SHIFT) else: return 0 return None @property def sizeof_struct_page(self): return 0x40 @property def STRUCT_PAGE_MAX_SHIFT(self): if "4.7" <= self.kversion < "4.20": return 6 elif "4.20" <= self.kversion: return self.order_base_2(self.sizeof_struct_page) return None @property def VMEMMAP_UNUSED_NPAGES(self): if "6.9" <= self.kversion: return (self._PAGE_OFFSET(self.vabits_actual) - self.PAGE_OFFSET) >> self.PAGE_SHIFT return None @property def VMEMMAP_SHIFT(self): if "5.11" <= self.kversion < "6.9": return self.PAGE_SHIFT - self.STRUCT_PAGE_MAX_SHIFT return None @property def VMEMMAP_RANGE(self): if "6.9" <= self.kversion: return self._PAGE_END(self.VA_BITS_MIN) - self.PAGE_OFFSET return None @property def VMEMMAP_SIZE(self): if "3.17" <= self.kversion < "4.7": return self.ALIGN((1 << (self.VA_BITS - self.PAGE_SHIFT)) * self.sizeof_struct_page, self.PUD_SIZE) elif "4.7" <= self.kversion < "5.4": return 1 << (self.VA_BITS - self.PAGE_SHIFT - 1 + self.STRUCT_PAGE_MAX_SHIFT) elif "5.4" <= self.kversion < "5.11": return (self._PAGE_END(self.VA_BITS_MIN) - self.PAGE_OFFSET) >> (self.PAGE_SHIFT - self.STRUCT_PAGE_MAX_SHIFT) elif "5.11" <= self.kversion < "6.9": return (self._PAGE_END(self.VA_BITS_MIN) - self.PAGE_OFFSET) >> self.VMEMMAP_SHIFT elif "6.9" <= self.kversion: return (self.VMEMMAP_RANGE >> self.PAGE_SHIFT) * self.sizeof_struct_page return None @property def VA_BITS(self): if "3.7" <= self.kversion < "3.12": return 39 elif "3.12" <= self.kversion < "3.17": if self.CONFIG_ARM64_64K_PAGES: return 42 else: return 39 elif "3.17" <= self.kversion: return self.CONFIG_ARM64_VA_BITS return None @property def VA_START(self): if "4.4" <= self.kversion < "4.5": return 0xffff_ffff_ffff_ffff - (1 << self.VA_BITS) + 1 elif "4.5" <= self.kversion < "4.9": va_start = 0xffff_ffff_ffff_ffff << self.VA_BITS return AddressUtil.align_address(va_start) elif "4.9" <= self.kversion < "4.10": return 0xffff_ffff_ffff_ffff - (1 << self.VA_BITS) + 1 elif "4.10" <= self.kversion < "4.13": va_start = 0xffff_ffff_ffff_ffff << self.VA_BITS return AddressUtil.align_address(va_start) elif "4.13" <= self.kversion < "5.4": return 0xffff_ffff_ffff_ffff - (1 << self.VA_BITS) + 1 return None @property def PAGE_OFFSET(self): if "3.17" <= self.kversion < "4.4": page_offset = 0xffff_ffff_ffff_ffff << (self.VA_BITS - 1) return AddressUtil.align_address(page_offset) elif "4.4" <= self.kversion < "4.5": return 0xffff_ffff_ffff_ffff - (1 << (self.VA_BITS - 1)) + 1 elif "4.5" <= self.kversion < "4.9": page_offset = 0xffff_ffff_ffff_ffff << (self.VA_BITS - 1) return AddressUtil.align_address(page_offset) elif "4.9" <= self.kversion < "4.10": return 0xffff_ffff_ffff_ffff - (1 << (self.VA_BITS - 1)) + 1 elif "4.10" <= self.kversion < "4.13": page_offset = 0xffff_ffff_ffff_ffff << (self.VA_BITS - 1) return AddressUtil.align_address(page_offset) elif "4.13" <= self.kversion < "5.4": return 0xffff_ffff_ffff_ffff - (1 << (self.VA_BITS - 1)) + 1 elif "5.4" <= self.kversion: return self._PAGE_OFFSET(self.VA_BITS) return None @property def PAGE_OFFSET_END(self): if "3.17" <= self.kversion: return self.PAGE_OFFSET + 2 ** (self.VA_BITS - 1) # no need to align return None @property # noqa def KIMAGE_VADDR(self): if "4.6" <= self.kversion: return self.MODULES_END return None @property def BPF_JIT_REGION_START(self): if "5.0" <= self.kversion < "5.4": return self.VA_START + self.KASAN_SHADOW_SIZE elif "5.4" <= self.kversion < "5.11": return self.KASAN_SHADOW_END elif "5.11" <= self.kversion < "5.15": return self._PAGE_END(self.VA_BITS_MIN) return None @property def BPF_JIT_REGION_SIZE(self): if "5.0" <= self.kversion < "5.15": return self.SZ_128M return None @property def BPF_JIT_REGION_END(self): if "5.0" <= self.kversion < "5.15": return self.BPF_JIT_REGION_START + self.BPF_JIT_REGION_SIZE return None @property def MODULES_END(self): if "3.7" <= self.kversion < "4.6": return self.PAGE_OFFSET elif "4.6" <= self.kversion: return self.MODULES_VADDR + self.MODULES_VSIZE return None @property def MODULES_VADDR(self): if "3.7" <= self.kversion < "4.6": return self.MODULES_END - self.SZ_64M elif "4.6" <= self.kversion < "5.0": return self.VA_START + self.KASAN_SHADOW_SIZE elif "5.0" <= self.kversion < "5.15": return self.BPF_JIT_REGION_END elif "5.15" <= self.kversion: return self._PAGE_END(self.VA_BITS_MIN) return None @property def MODULES_VSIZE(self): if "4.6" <= self.kversion < "6.5": return self.SZ_128M elif "6.5" <= self.kversion: return self.SZ_2G return None @property def VMEMMAP_START(self): if "3.17" <= self.kversion < "4.7": return self.VMALLOC_END + self.SZ_64K elif "4.7" <= self.kversion < "5.4": return self.PAGE_OFFSET - self.VMEMMAP_SIZE elif "5.4" <= self.kversion < "5.11": vmemmap_start = -self.VMEMMAP_SIZE - self.SZ_2M return AddressUtil.align_address(vmemmap_start) elif "5.11" <= self.kversion < "6.9": vmemmap_start = -(1 << (self.VA_BITS - self.VMEMMAP_SHIFT)) return AddressUtil.align_address(vmemmap_start) elif "6.9" <= self.kversion: return self.VMEMMAP_END - self.VMEMMAP_SIZE return None @property def VMEMMAP_END(self): if "3.17" <= self.kversion < "6.9": return self.VMEMMAP_START + self.VMEMMAP_SIZE elif "6.9" <= self.kversion: vmemmap_end = -self.SZ_1G return AddressUtil.align_address(vmemmap_end) return None @property def PCI_IO_SIZE(self): if "4.0" <= self.kversion: return self.SZ_16M return None @property def PCI_IO_START(self): if "4.0" <= self.kversion < "6.9": return self.PCI_IO_END - self.PCI_IO_SIZE elif "6.9" <= self.kversion: return self.VMEMMAP_END + self.SZ_8M return None @property def PCI_IO_END(self): if "4.0" <= self.kversion < "4.6": return self.MODULES_VADDR - self.SZ_2M elif "4.6" <= self.kversion < "4.7": return self.PAGE_OFFSET - self.SZ_2M elif "4.7" <= self.kversion < "5.10": return self.VMEMMAP_START - self.SZ_2M elif "5.10" <= self.kversion < "6.9": return self.VMEMMAP_START - self.SZ_8M elif "6.9" <= self.kversion: return self.PCI_IO_START + self.PCI_IO_SIZE return None @property # noqa def FIXADDR_TOP(self): if "3.15" <= self.kversion < "4.0": return self.MODULES_VADDR - self.SZ_2M - self.PAGE_SIZE elif "4.0" <= self.kversion < "5.10": return self.PCI_IO_START - self.SZ_2M elif "5.10" <= self.kversion < "6.9": return self.VMEMMAP_START - self.SZ_32M elif "6.9" <= self.kversion: fixaddr_top = -self.SZ_8M return AddressUtil.align_address(fixaddr_top) return None @property def NR_FIX_BTMAPS(self): if "3.7" <= self.kversion < "4.4": if self.CONFIG_ARM64_64K_PAGES: return 4 else: return 64 elif "4.4" <= self.kversion: return self.SZ_256K // self.PAGE_SIZE return None @property def FIX_BTMAPS_SLOTS(self): return 7 @property def TOTAL_FIX_BTMAPS(self): return self.NR_FIX_BTMAPS * self.FIX_BTMAPS_SLOTS @property def __end_of_permanent_fixed_addresses(self): end = self.__end_of_fixed_addresses if end is None: return None if "3.14" <= self.kversion < "4.0": # FIX_BTMAP_BEGIN ~ FIX_BTMAP_END return end - self.TOTAL_FIX_BTMAPS elif "4.0" <= self.kversion < "4.1": # FIX_BTMAP_BEGIN ~ FIX_BTMAP_END, FIX_TEXT_POKE0 return end - self.TOTAL_FIX_BTMAPS - 1 elif "4.1" <= self.kversion < "4.6": # FIX_BTMAP_BEGIN ~ FIX_BTMAP_END return end - self.TOTAL_FIX_BTMAPS elif "4.6" <= self.kversion < "6.9": # FIX_BTMAP_BEGIN ~ FIX_BTMAP_END, FIX_PTE ~ FIXPGD return end - self.TOTAL_FIX_BTMAPS - 4 elif "6.9" <= self.kversion: # FIX_BTMAP_BEGIN ~ FIX_BTMAP_END, FIX_PTE ~ FIXPGD return end - self.TOTAL_FIX_BTMAPS - 5 return None @property def __end_of_fixed_addresses(self): return KernelAddressHeuristicFinder.get_end_of_fixed_addresses() @property def FIXADDR_SIZE(self): if self.__end_of_permanent_fixed_addresses is None: return None return self.__end_of_permanent_fixed_addresses << self.PAGE_SHIFT @property def FIXADDR_START(self): if self.FIXADDR_TOP is None: return None if self.FIXADDR_SIZE is None: return None return self.FIXADDR_TOP - self.FIXADDR_SIZE @property # noqa def EARLYCON_IOBASE(self): if "3.7" <= self.kversion < "3.15": return self.MODULES_VADDR - self.SZ_4M return None @property def VA_BITS_MIN(self): if "5.4" <= self.kversion < "6.9": if self.VA_BITS > 48: return 48 else: return self.VA_BITS elif "6.9" <= self.kversion: if self.VA_BITS > 48: if self.CONFIG_ARM64_16K_PAGES: return 47 else: return 48 else: return self.VA_BITS return None @property def VMALLOC_START(self): if "3.17" <= self.kversion < "4.4": vmalloc_start = 0xffff_ffff_ffff_ffff << self.VA_BITS return AddressUtil.align_address(vmalloc_start) elif "4.4" <= self.kversion < "4.6": if not self.CONFIG_KASAN: return self.VA_START else: return self.KASAN_SHADOW_END + self.SZ_64K elif "4.6" <= self.kversion: return self.MODULES_END return None @property def VMALLOC_END(self): if "3.17" <= self.kversion < "5.4": return self.PAGE_OFFSET - self.PUD_SIZE - self.VMEMMAP_SIZE - self.SZ_64K elif "5.4" <= self.kversion < "5.11": vmalloc_end = -self.PUD_SIZE - self.VMEMMAP_SIZE - self.SZ_64K return AddressUtil.align_address(vmalloc_end) elif "5.11" <= self.kversion < "6.9": return self.VMEMMAP_START - self.SZ_256M elif "6.9" <= self.kversion: if self.VA_BITS == self.VA_BITS_MIN: return self.VMEMMAP_START - self.SZ_8M else: return self.VMEMMAP_START + self.VMEMMAP_UNUSED_NPAGES * self.sizeof_struct_page - self.SZ_8M return None @property def PHYS_MASK_SHIFT(self): if "6.12" <= self.kversion: return self.VA_BITS return None @property def PHYS_MASK(self): if "6.12" <= self.kversion: return (1 << self.PHYS_MASK_SHIFT) - 1 return None @property def physmap_base(self): if hasattr(self, "cached_physmap_base"): return self.cached_physmap_base if self.PAGE_OFFSET is None: self.cached_physmap_base = None return None # physmap_base is used in KGDB mode when pseudo reading physical addresses without page walking. # physmap_base is calculated as PAGE_OFFSET - PHYS_OFFSET, where PHYS_OFFSET is stored in memstart_addr. # However, at this stage, p2v and v2p are not yet available, so they cannot be used to resolve memstart_addr. # Therefore, the address of memstart_addr is obtained directly from the vmlinux symbols. # Prevent recursion: # read_physmem -> kgdb_use_physmap -> get_ksymaddr -> pagewalk -> read_physmem -> ... if not __gef_command_instances__["ksymaddr-remote"].kallsyms: # None does not cache, because kallsyms may be resolved later return None memstart_addr = Symbol.get_ksymaddr("memstart_addr") if memstart_addr is None: self.cached_physmap_base = None return None PHYS_OFFSET = read_int_from_memory(memstart_addr) if "6.12" <= self.kversion: PHYS_OFFSET &= self.PHYS_MASK self.cached_physmap_base = AddressUtil.align_address(self.PAGE_OFFSET - PHYS_OFFSET) return self.cached_physmap_base @property def memstart_addr(self): if hasattr(self, "cached_memstart_addr"): return self.cached_memstart_addr # When p2v and v2p are available, memstart_addr can be resolved without relying on symbols. if self.PAGE_OFFSET is None: return None kinfo = Kernel.get_kernel_layout() if kinfo is None: return None phys_kbase = Kernel.v2p(kinfo.text_base) if phys_kbase is None: return None cands = Kernel.p2v(phys_kbase) linear_cands = [x for x in cands if self.PAGE_OFFSET <= x < self.PAGE_OFFSET_END] if len(linear_cands) != 1: return None linear_kbase = linear_cands[0] self.cached_memstart_addr = AddressUtil.align_address(phys_kbase - (linear_kbase - self.PAGE_OFFSET)) return self.cached_memstart_addr @property def PHYS_OFFSET(self): return self.memstart_addr class KernelAddressHeuristicFinder: """A class that heuristically finds a specific symbol in the kernel.""" USE_DIRECTLY = True # for debug USE_KSYSCTL = True # for debug CONSTS = None @staticmethod def consts(): if KernelAddressHeuristicFinder.CONSTS: return KernelAddressHeuristicFinder.CONSTS if is_x86_64(): KernelAddressHeuristicFinder.CONSTS = KernelConstsX64() elif is_x86_32(): KernelAddressHeuristicFinder.CONSTS = KernelConstsX86() elif is_arm64(): KernelAddressHeuristicFinder.CONSTS = KernelConstsArm64() elif is_arm32(): KernelAddressHeuristicFinder.CONSTS = KernelConstsArm32() return KernelAddressHeuristicFinder.CONSTS @staticmethod @switch_to_intel_syntax def get_saved_command_line(): # Do not use Symbol.get_ksymaddr since this function is used to discover KPTI, # because Symbol.get_ksymaddr uses a cache. kversion = Kernel.kernel_version() # plan 1 (available v2.6.28 or later) if kversion and "2.6.28" <= kversion: # This is OK since we are not looking for `saved_command_line` directly. addr = Symbol.get_ksymaddr("cmdline_proc_show") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), ) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_current_task(): if not is_x86(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("current_task") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.1 or later) if kversion and "4.1" <= kversion: addr = Symbol.get_ksymaddr("common_cpu_up") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, skip_msb_check=True) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, skip_msb_check=True) for x in g: if x < 0x100: continue if is_x86_64(): if x & 0x7: continue if not AddressUtil.is_msb_on(x) and x > 0x10_0000: continue elif is_x86_32(): if x & 0x3: continue return x # plan 3 (available v2.5.33 or later) if kversion and "2.5.33" <= kversion: addr = Symbol.get_ksymaddr("setup_arg_pages") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = itertools.chain( KernelAddressHeuristicFinderUtil.x64_qword_ptr_ds(res), KernelAddressHeuristicFinderUtil.x64_qword_ptr_gs(res, skip_msb_check=True), KernelAddressHeuristicFinderUtil.x64_qword_ptr_gs_rip_base(res, skip_msb_check=True), ) elif is_x86_32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.x86_dword_ptr_ds(res), KernelAddressHeuristicFinderUtil.x86_dword_ptr_fs(res, skip_msb_check=True), ) for x in g: if x < 0x100: continue if is_x86_64(): if x & 0x7: continue elif is_x86_32(): if x & 0x3: continue return x return None @staticmethod def get_current_task_for_current_thread(): if is_arm32(): # plan 1 (from special register) r = get_register("$TPIDRURO") if r and is_valid_addr(r): return r r = get_register("$TPIDRURO_S") if r and is_valid_addr(r): return r # plan 2 (from stack top) # We need to consider the case where Linux and RTOS are running on different CPUs at the same time. # If the stack is not the address the kernel expects to use, it should not be interpreted as a task. # check if valid kernel address or not current_thread_info = current_arch.sp & ~0x1fff if current_thread_info < KernelAddressHeuristicFinder.get_PAGE_OFFSET(): return None kversion = Kernel.kernel_version() try: """ struct thread_info { unsigned long flags; int preempt_count; mm_segment_t addr_limit; // ~v5.14 struct task_struct *task; // ~v5.17 ... } """ if kversion < "5.15": v = read_int_from_memory(current_thread_info + current_arch.ptrsize * 3) if v and is_valid_addr(v): return v elif kversion < "5.18": v = read_int_from_memory(current_thread_info + current_arch.ptrsize * 2) if v and is_valid_addr(v): return v except gdb.MemoryError: # In some threads, $sp points to an invalid address. return None elif is_arm64(): # plan 1 (from special register) return get_register("$SP_EL0") return None @staticmethod @switch_to_intel_syntax def get_init_task(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("init_task") if x: return x kversion = Kernel.kernel_version() # Detecting `init_task` is very difficult. # This is because `init_task` itself is rarely used, while `init_task.tasks` is used in most cases. # On x86/x64, only one case has been found where detection is stable. # However, there appear to be cases where it cannot be detected. # plan 2 (available v3.4 or later) if kversion and "3.4" <= kversion: if is_x86_64() or is_x86_32(): addr = Symbol.get_ksymaddr("do_exit") if addr: res = gdb.execute("x/600i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_cmp_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_cmp_const(res) for x in g: # There are cases where init_pid_ns is falsely detected as init_task. # The initial value of kref is 2, so exclude this. if read_int_from_memory(x) == 2: continue return x # On arm32/arm64, that pattern could not be found. # However, there is a method to locate `current_task` with 100% stability on arm32/arm64 # using a special register. # In addition, `init_task` is always located in the kernel .data section. # Therefore, the following method is implemented: # 1. Traverse the linked list `current_task.tasks` starting from `current_task` # and collect all task addresses. # 2. Select the task with the smallest distance from the kernel .data section. # This method can also be applied to x86/x64 as long as `current_task` can be obtained. def get_offset_tasks(current_task): # search for init_task->tasks # On CPU1, the task list is doubly linked, but on others it is not. # For example: # CPU1: cpu1_current_task <-> task1 <-> task2 <-> ... <-> cpu1_current_task # CPU2: cpu2_current_task -> task1 <-> task2 <-> ... <-> cpu1_current_task # Therefore, we should read one element at a time and verify the linkage. for i in range(0x200): offset_tasks = current_arch.ptrsize * i current_task_tasks = current_task + offset_tasks if not is_valid_addr(current_task_tasks): return None task1 = read_int_from_memory(current_task_tasks) if is_double_link_list(task1, min_len=5): return offset_tasks return None def get_task_list(task, offset_tasks): pos = task + offset_tasks task_list = [pos] # validating candidate offset while True: pos = read_int_from_memory(pos) if pos in task_list: break task_list.append(pos) return [x - offset_tasks for x in task_list] # plan 3 (from current) current = None if is_arm64() or is_arm32(): current = KernelAddressHeuristicFinder.get_current_task_for_current_thread() elif is_x86_64() or is_x86_32(): current_task = KernelAddressHeuristicFinder.get_current_task() if current_task: if AddressUtil.is_msb_on(current_task) and is_valid_addr(current_task): # no __per_cpu_offset current = read_int_from_memory(current_task) else: # use __per_cpu_offset p = KernelAddressHeuristicFinder.get_per_cpu_offset() if p and is_valid_addr(p): cpu_base = read_int_from_memory(p) current_ptr = AddressUtil.align_address(cpu_base + current_task) current = read_int_from_memory(current_ptr) if current: offset_tasks = get_offset_tasks(current) if offset_tasks: task_list = get_task_list(current, offset_tasks) kinfo = Kernel.get_kernel_layout() min_distance_task = (None, 0xffff_ffff_ffff_ffff) for task in task_list: distance = abs((kinfo.rw_base or kinfo.text_base) - task) if min_distance_task[1] > distance: min_distance_task = (task, distance) if min_distance_task[0] is not None: return min_distance_task[0] return None @staticmethod @switch_to_intel_syntax def get_init_cred(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("init_cred") if x: return x # plan2 (from ktask) res = gdb.execute("ktask --filter swapper/0 --no-pager", to_string=True) m = re.search(r"offsetof\(task_struct, cred\): (0x\w+)", res) if m: cred_offset = int(m.group(1), 16) line = res.strip().splitlines()[-1] addr, _, _, name, *_ = line.split() if name == "swapper/0": task = int(addr, 16) return read_int_from_memory(task + cred_offset) # plan3 (from ktask, not swapper/0, just swapper) res = gdb.execute("ktask --filter swapper --no-pager", to_string=True) m = re.search(r"offsetof\(task_struct, cred\): (0x\w+)", res) if m: cred_offset = int(m.group(1), 16) line = res.strip().splitlines()[-1] addr, _, _, name, *_ = line.split() if name == "swapper": task = int(addr, 16) return read_int_from_memory(task + cred_offset) return None @staticmethod @switch_to_intel_syntax def get_init_net(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("init_net") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.6.35 or later) if kversion and "2.6.35" <= kversion: addr = Symbol.get_ksymaddr("net_initial_ns") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x # plan 3 (available v2.6.24 or later) if kversion and "2.6.24" <= kversion: addr = Symbol.get_ksymaddr("netdev_boot_base") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: if not is_valid_addr(x): continue if read_cstring_from_memory(x) == "%s%d": continue return x return None @staticmethod @switch_to_intel_syntax def get_init_user_ns(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("init_user_ns") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.6.39 or later) if kversion and "2.6.39" <= kversion: addr = Symbol.get_ksymaddr("has_capability") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_modules(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("modules") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v3.7.5 or later) if kversion and "3.7.5" <= kversion: addr = Symbol.get_ksymaddr("find_module_all") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_ldr(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative_ldr(res), ) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_chrdevs(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("chrdevs") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.6.16.12 or later) if kversion and "2.6.17" <= kversion: addr = Symbol.get_ksymaddr("chrdev_show") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_array4_base(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), ) for x in g: if not is_valid_addr(x): continue for i in range(255): v = read_int_from_memory(x + current_arch.ptrsize * i) if not is_single_link_list(v): break else: # Case where all 255 entries meet the conditions return x return None @staticmethod @switch_to_intel_syntax def get_cdev_map(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("cdev_map") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.5.70 or later) if kversion and "2.5.70" <= kversion: addr = Symbol.get_ksymaddr("cdev_del") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative_ldr(res), ) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_sys_call_table_x64(): if not is_x86_64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("sys_call_table") if x: return x kversion = Kernel.kernel_version() if kversion and "6.6.26" <= kversion: # On x64, each entry is embedded in `x64_sys_call` as call instruction. # So sys_call_table is no longer in use, but it still remains. # We won't return yet because we may be able to detect this in plan 5. pass # plan 2 (available v4.6 ~ v6.6.26) if kversion and "4.6" <= kversion < "6.6.26": addr = Symbol.get_ksymaddr("do_syscall_64") if addr: res = gdb.execute("x/40i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res) for x in g: return x # plan 3 (available v4.2 ~ v4.13) if kversion and "4.2" <= kversion < "4.14": addr = Symbol.get_ksymaddr("entry_SYSCALL_64_fastpath") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res) for x in g: return x # plan 4 (available v2.6.27 ~ v4.1) if kversion and "2.6.27" <= kversion < "4.2": addr = Symbol.get_ksymaddr("system_call_fastpath") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res) for x in g: return x # plan 5 (search for the memory) sys_read = Symbol.get_ksymaddr("__x64_sys_read") sys_write = Symbol.get_ksymaddr("__x64_sys_write") sys_open = Symbol.get_ksymaddr("__x64_sys_open") sys_close = Symbol.get_ksymaddr("__x64_sys_close") seq_to_find = p64(sys_read) + p64(sys_write) + p64(sys_open) + p64(sys_close) kinfo = Kernel.get_kernel_layout() if kinfo and kinfo.ro_base: ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) sys_call_table_offset = ro_data.find(seq_to_find) if sys_call_table_offset >= 0: return kinfo.ro_base + sys_call_table_offset return None @staticmethod @switch_to_intel_syntax def get_sys_call_table_x32(): if not is_x86_64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("x32_sys_call_table") if x: return x kversion = Kernel.kernel_version() if kversion and kversion < "5.4": # Not introduced return None if kversion and "6.6.26" <= kversion: # On x64, each entry is embedded in `x32_sys_call` as call instruction. # So x32_sys_call_table is no longer in use, and removed from 6.6.26. return None # plan 2 (available v5.4 or later) if kversion and "5.4" <= kversion: addr = Symbol.get_ksymaddr("do_syscall_64") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res, skip=1) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_sys_call_table_x86(): if not is_x86(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: if is_x86_64(): x = Symbol.get_ksymaddr("ia32_sys_call_table") elif is_x86_32(): x = Symbol.get_ksymaddr("sys_call_table") if x: return x kversion = Kernel.kernel_version() if kversion and "6.6.26" <= kversion: if is_x86_64(): # On x64, ia32_sys_call_table is removed from 6.6.26. return None else: # On i386, each entry is embedded in `ia32_sys_call` as call instruction. # So sys_call_table is no longer in use, but it still remains. # We won't return yet because we may be able to detect this in plan 3. pass # plan 2 (available v2.6.24 ~ v6.6.26) if kversion and "6.6.7" <= kversion < "6.6.26": if is_x86_64(): # ia32_sys_call_table is still used, but no detection logic. addr = None else: addr = Symbol.get_ksymaddr("do_int80_syscall_32") elif kversion and "4.6" <= kversion < "6.6.7": addr = Symbol.get_ksymaddr("do_int80_syscall_32") elif kversion and "4.4" <= kversion < "4.6": if is_x86_64(): addr = Symbol.get_ksymaddr("do_syscall_32_irqs_off") else: addr = Symbol.get_ksymaddr("do_syscall_32_irqs_on") elif kversion and "2.6.24" <= kversion < "4.4": addr = Symbol.get_ksymaddr("syscall_call") else: addr = None if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_array4_base(res) for x in g: return x # plan 3 (search for the memory) sys_restart_syscall = Symbol.get_ksymaddr("sys_restart_syscall") sys_exit = Symbol.get_ksymaddr("sys_exit") sys_fork = Symbol.get_ksymaddr("sys_fork") sys_read = Symbol.get_ksymaddr("sys_read") if None not in [sys_restart_syscall, sys_exit, sys_fork, sys_read]: if is_x86_64(): seq_to_find = p64(sys_restart_syscall) + p64(sys_exit) + p64(sys_fork) + p64(sys_read) else: seq_to_find = p32(sys_restart_syscall) + p32(sys_exit) + p32(sys_fork) + p32(sys_read) kinfo = Kernel.get_kernel_layout() if kinfo and kinfo.ro_base: ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) sys_call_table_offset = ro_data.find(seq_to_find) if sys_call_table_offset >= 0: return kinfo.ro_base + sys_call_table_offset return None @staticmethod def get_sys_call_table_arm32(): if not is_arm32(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("sys_call_table") if x: return x # plan 2 (search for the memory) sys_restart_syscall = Symbol.get_ksymaddr("sys_restart_syscall") sys_exit = Symbol.get_ksymaddr("sys_exit") sys_fork = Symbol.get_ksymaddr("sys_fork") sys_read = Symbol.get_ksymaddr("sys_read") if None not in [sys_restart_syscall, sys_exit, sys_fork, sys_read]: seq_to_find = p32(sys_restart_syscall) + p32(sys_exit) + p32(sys_fork) + p32(sys_read) kinfo = Kernel.get_kernel_layout() # `sys_call_table` is embedded in the .text area even if `CONFIG_KALLSYMS_ALL=n` if kinfo and kinfo.text_base: text_data = read_memory(kinfo.text_base, kinfo.text_size) sys_call_table_offset = text_data.find(seq_to_find) if sys_call_table_offset >= 0: return kinfo.text_base + sys_call_table_offset return None @staticmethod def get_sys_call_table_arm64(): if not is_arm64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("sys_call_table") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v3.7 or later) if kversion and "5.6" <= kversion: addr = Symbol.get_ksymaddr("do_el0_svc") elif kversion and "4.18" <= kversion < "5.6": addr = Symbol.get_ksymaddr("el0_svc_handler") elif kversion and "3.7" <= kversion < "4.18": addr = Symbol.get_ksymaddr("el0_svc") else: addr = None if addr: res = gdb.execute("x/100i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res, read_valid=True) for x in g: return x # plan 3 (search for the memory) sys_io_setup = Symbol.get_ksymaddr("__arm64_sys_io_setup") sys_io_destroy = Symbol.get_ksymaddr("__arm64_sys_io_destroy") sys_io_submit = Symbol.get_ksymaddr("__arm64_sys_io_submit") sys_io_cancel = Symbol.get_ksymaddr("__arm64_sys_io_cancel") if None not in [sys_io_setup, sys_io_destroy, sys_io_submit, sys_io_cancel]: seq_to_find = p64(sys_io_setup) + p64(sys_io_destroy) + p64(sys_io_submit) + p64(sys_io_cancel) kinfo = Kernel.get_kernel_layout() if kinfo and kinfo.ro_base: ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) sys_call_table_offset = ro_data.find(seq_to_find) if sys_call_table_offset >= 0: return kinfo.ro_base + sys_call_table_offset return None @staticmethod def get_sys_call_table_arm64_compat(): if not is_arm64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("compat_sys_call_table") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v3.7 or later) if kversion and "5.6" <= kversion: addr = Symbol.get_ksymaddr("do_el0_svc_compat") elif kversion and "4.18" <= kversion < "5.6": addr = Symbol.get_ksymaddr("el0_svc_compat_handler") elif kversion and "3.7" <= kversion < "4.18": addr = Symbol.get_ksymaddr("el0_svc_compat") else: addr = None if addr: res = gdb.execute("x/100i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res, read_valid=True) for x in g: return x # plan 3 (search for the memory) sys_restart_syscall = Symbol.get_ksymaddr("__arm64_sys_restart_syscall") sys_exit = Symbol.get_ksymaddr("__arm64_sys_exit") sys_fork = Symbol.get_ksymaddr("__arm64_sys_fork") sys_read = Symbol.get_ksymaddr("__arm64_sys_read") sys_write = Symbol.get_ksymaddr("__arm64_sys_write") sys_open = Symbol.get_ksymaddr("__arm64_compat_sys_open") if None not in [sys_restart_syscall, sys_exit, sys_fork, sys_read, sys_write, sys_open]: seq_to_find = p64(sys_restart_syscall) + p64(sys_exit) + p64(sys_fork) + p64(sys_read) + p64(sys_write) + p64(sys_open) kinfo = Kernel.get_kernel_layout() if kinfo and kinfo.ro_base: ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) sys_call_table_offset = ro_data.find(seq_to_find) if sys_call_table_offset >= 0: return kinfo.ro_base + sys_call_table_offset return None @staticmethod @switch_to_intel_syntax def get_per_cpu_offset(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("__per_cpu_offset") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v3.3 or later) if kversion and "3.3" <= kversion: addr = Symbol.get_ksymaddr("nr_iowait_cpu") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = itertools.chain( KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res), KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res), ) elif is_x86_32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.x86_dword_ptr_array4_base(res), KernelAddressHeuristicFinderUtil.x64_x86_dword_ptr_src(res), ) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: cpu0 = read_int_from_memory(x) if cpu0 and (cpu0 & 0xfff) == 0: return x return None @staticmethod @switch_to_intel_syntax def get_slab_caches(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("slab_caches") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.9 or later) if kversion and "4.9" <= kversion: addr = Symbol.get_ksymaddr("slub_cpu_dead") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_any_const(res, skip=1) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_any_const(res, skip=1) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res, skip=1) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x # plan 3 (available v5.9 or later) if kversion and "5.9" <= kversion: addr = Symbol.get_ksymaddr("find_mergeable") if addr: res = gdb.execute("x/50i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_cmp_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_cmp_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt_add(res) for x in g: return x # plan 4 (available v4.10 or before and CONFIG_MEMCG=y) if kversion and kversion < "4.11": addr = Symbol.get_ksymaddr("memcg_update_all_caches") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_any_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x # plan 5 (available v3.11 ~ v4.10 and CONFIG_SLABINFO=y) if kversion and "3.11" <= kversion < "4.11": addr = Symbol.get_ksymaddr("slab_next") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_any_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_any_const(res) elif is_arm64(): # TODO g = [] elif is_arm32(): # TODO g = [] for x in g: return x # plan 6 (available if CONFIG_SLAB=y) addr = Symbol.get_ksymaddr("cache_reap") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): # TODO g = [] elif is_arm64(): # TODO g = [] elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res, skip=1) for x in g: return x # plan 7 (available v2.6.24 ~ v3.10 and CONFIG_SLABINFO=y) if kversion and "2.6.24" <= kversion < "3.11": addrs = Symbol.get_ksymaddr_multiple("s_next") if addrs: for s_next in addrs: res = gdb.execute("x/20i {:#x}".format(s_next), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, read_valid=True) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, read_valid=True) elif is_arm64(): # TODO g = [] elif is_arm32(): # TODO g = [] for x in g: v1 = read_int_from_memory(x) v2 = read_int_from_memory(x + current_arch.ptrsize) if is_valid_addr(v1) and is_valid_addr(v2): return x # plan 8 (available v4.11 ~ v6.11) if kversion and "4.11" <= kversion < "6.12": addr = Symbol.get_ksymaddr("slab_caches_to_rcu_destroy_workfn") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for slab_mutex in g: for i in range(16): x = slab_mutex + current_arch.ptrsize * i if is_double_link_list(x, min_len=10): return x return None @staticmethod @switch_to_intel_syntax def get_slab_kset(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("slab_kset") if x: return x # plan 2 addr = Symbol.get_ksymaddr("sysfs_slab_add") if addr: res = gdb.execute("x/100i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_ds(res) elif is_arm64(): g = itertools.chain( KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res), KernelAddressHeuristicFinderUtil.aarch64_adrp_add_ldr(res), ) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), ) for x in g: if is_valid_addr(x): y = read_int_from_memory(x) if is_double_link_list(y): return x return None @staticmethod @switch_to_intel_syntax def get_modprobe_path(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("modprobe_path") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.modprobe") if x: return x return None @staticmethod @switch_to_intel_syntax def get_poweroff_cmd(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("poweroff_cmd") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.poweroff_cmd") if x: return x return None @staticmethod @switch_to_intel_syntax def get_core_pattern(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("core_pattern") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.core_pattern") if x: return x return None @staticmethod @switch_to_intel_syntax def get_phys_base(): if not is_x86_64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("phys_base") if x: return read_int_from_memory(x) kversion = Kernel.kernel_version() # plan 2 (available v2.6.24 or later) if kversion and "2.6.24" <= kversion: addr = Symbol.get_ksymaddr("secondary_startup_64") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) for x in g: return read_int_from_memory(x) # plan 3 (available v2.6.25 ~ v5.5) if kversion and "2.6.25" <= kversion < "5.5": addr = Symbol.get_ksymaddr("arch_crash_save_vmcoreinfo") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) for x in g: s = read_cstring_from_memory(x) if not s: return read_int_from_memory(x) # plan 4 (available v3.9 or later) if kversion and "3.9" <= kversion: addr = Symbol.get_ksymaddr("__virt_addr_valid") if addr: res = gdb.execute("x/50i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) for x in g: return read_int_from_memory(x) return None @staticmethod @switch_to_intel_syntax def get_PAGE_OFFSET_base(): if not is_x86_64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("page_offset_base") if x: return x # plan 2 (from pagewalk) kinfo = Kernel.get_kernel_layout() page_offset_base_raw = kinfo.maps[0][0] ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) ro_data = slice_unpack(ro_data, current_arch.ptrsize) try: index = ro_data.index(page_offset_base_raw) return kinfo.ro_base + index * current_arch.ptrsize except ValueError: pass return None @staticmethod @switch_to_intel_syntax def get_PAGE_OFFSET(): return KernelAddressHeuristicFinder.consts().PAGE_OFFSET @staticmethod @switch_to_intel_syntax def _get_PAGE_OFFSET(): if is_x86_64(): # plan 1 (fixed address) kversion = Kernel.kernel_version() if kversion and kversion < "4.8": # kASLR and Level5 pagetable is unsupported, so fixed address return 0xffff_8800_0000_0000 # plan 2 (from get_PAGE_OFFSET_base) page_offset_base = KernelAddressHeuristicFinder.get_PAGE_OFFSET_base() if page_offset_base: return read_int_from_memory(page_offset_base) # plan 3 (from pagewalk) kinfo = Kernel.get_kernel_layout() if kinfo.maps and len(kinfo.maps) > 0: page_offset_base_raw = kinfo.maps[0][0] return page_offset_base_raw return None @staticmethod @switch_to_intel_syntax def get_PAGE_OFFSET_END(): return KernelAddressHeuristicFinder.consts().PAGE_OFFSET_END @staticmethod @switch_to_intel_syntax def get_VMALLOC_START(): return KernelAddressHeuristicFinder.consts().VMALLOC_START @staticmethod @switch_to_intel_syntax def _get_VMALLOC_START(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: vmalloc_base = Symbol.get_ksymaddr("vmalloc_base") if vmalloc_base: return read_int_from_memory(vmalloc_base) kversion = Kernel.kernel_version() if is_x86_64(): # plan 2 (fixed address) if kversion and kversion < "4.8": # kASLR and Level5 pagetable is unsupported, so fixed address return 0xffff_c900_0000_0000 # plan 3 (from get_PAGE_OFFSET_base) page_offset_base = KernelAddressHeuristicFinder.get_PAGE_OFFSET_base() if page_offset_base: """ [v6.13.9] 0xffffffff8d1f0198|+0x0018|+003: 0x0000000100000027 // ? 0xffffffff8d1f01a0|+0x0020|+004: 0xffff9d9080000000 // page_offset_base 0xffffffff8d1f01a8|+0x0028|+005: 0xffffaeed80000000 // vmalloc_base 0xffffffff8d1f01b0|+0x0030|+006: 0xffffec7a80000000 // vmemmap_base [v6.2.8] 0xffffffffa5549a68|+0x0000|+000: 0xffffea0000000000 // vmemmap_base 0xffffffffa5549a70|+0x0008|+001: 0xffffc90000000000 // vmalloc_base 0xffffffffa5549a78|+0x0010|+002: 0xffff888000000000 // page_offset_base 0xffffffffa5549a80|+0x0018|+003: 0x0000002700000001 // ? """ page_offset_base_b = read_int_from_memory(page_offset_base - current_arch.ptrsize) page_offset_base_0 = read_int_from_memory(page_offset_base) page_offset_base_a = read_int_from_memory(page_offset_base + current_arch.ptrsize) if page_offset_base_0 < page_offset_base_b: return page_offset_base_b if page_offset_base_0 < page_offset_base_a: return page_offset_base_a # plan 4 (from vmalloc-dump) if kversion and "5.2" <= kversion: res = gdb.execute("vmalloc-dump --quiet --no-pager --only-freed", to_string=True) """ # state virtual address size flags 0 freed 0x0000000000000001-0xffffc90000000000 0xffffc8ffffffffff """ if res: res = Color.remove_color(res) lines = res.splitlines() if len(lines) >= 2: _, _, vrange, _, *_ = lines[1].split() s, e = vrange.split("-") s = int(s, 16) e = int(e, 16) if s == 1: return e # plan 5 (from vmalloc-dump and pagewalk) if kversion and kversion < "6.9": res = gdb.execute("vmalloc-dump --quiet --no-pager --only-used", to_string=True) """ [vmalloc-dump; x64] # state virtual address size flags 0 in-use 0xffffa1c040000000-0xffffa1c040002000 0x2000 VM_IOREMAP [pagewalk; x64] 0xffff9927bfe00000-0xffff9927bffe0000 0x1e0000 0x1000 480 [RW- KERN ACCESSED DIRTY] 0xffffa1c040000000-0xffffa1c040001000 0x1000 0x1000 1 [RW- KERN ACCESSED DIRTY] [vmalloc-dump; x86] # state virtual address size flags 0 in-use 0x00000000e07e0000-0x00000000e07e2000 0x2000 VM_IOREMAP [pagewalk; x86] 0x00000000c1b83000-0x00000000dffe0000 - 0x1e45d000 - - [RWX KERN] 0x00000000e07e0000-0x00000000e07e1000 - 0x1000 - - [RWX KERN] """ if res: res = Color.remove_color(res) lines = res.splitlines() if len(lines) >= 2: _, _, vrange, _, *_ = lines[1].split() s, _ = vrange.split("-") s = int(s, 16) # incontinuity check kinfo = Kernel.get_kernel_layout() prev = None for vstart, _, _ in kinfo.maps: if vstart == s: break prev = vstart if prev is not None: if is_64bit(): mask = 0xffff_ff00_0000_0000 if (prev & mask) != (s & mask): if (s & 0x0fff_ffff) == 0: return s else: mask = 0xff00_0000 if (prev & mask) != (s & mask): if (s & 0x0fff) == 0: return s else: return s return None @staticmethod @switch_to_intel_syntax def get_VMALLOC_END(): return KernelAddressHeuristicFinder.consts().VMALLOC_END @staticmethod @switch_to_intel_syntax def get_VMEMMAP_START(): if is_x86_64() or is_arm64(): return KernelAddressHeuristicFinder.consts().VMEMMAP_START return None @staticmethod @switch_to_intel_syntax def _get_VMEMMAP_START(): if is_x86_64(): # plan 1 (fixed address) kversion = Kernel.kernel_version() if kversion and kversion < "4.8": # kASLR and Level5 pagetable is unsupported, so fixed address return 0xffff_ea00_0000_0000 # plan 2 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: vmemmap_base = Symbol.get_ksymaddr("vmemmap_base") if vmemmap_base: return read_int_from_memory(vmemmap_base) def get_min_page(r): if r is None: return None min_page = None for x in r: x = int(x, 16) if not is_valid_addr(x): continue if min_page is None or x < min_page: min_page = x return min_page # plan 3 (from slub-dump / slub-tiny-dump) allocator = Kernel.get_slab_type() if allocator in ["SLUB", "SLUB_TINY"]: command = {"SLUB": "slub-dump --node --skip-sheaf", "SLUB_TINY": "slub-tiny-dump"}[allocator] for n in [8, 16, 32, 64, 128, 192, 256, 512]: ret = gdb.execute( "{:s} --simple --no-pager --quiet kmalloc-{:d}".format(command, n), to_string=True, ) r = re.findall(r"(?:active|partial|node) page: (0x\S\S+)", Color.remove_color(ret)) min_page = get_min_page(r) if min_page is not None: return min_page & 0xffff_ffff_c000_0000 # ~((1 << PUD_SHIFT) - 1) # plan 4 (from slab-dump) if allocator == "SLAB": ret = gdb.execute("slab-dump --simple --no-pager --quiet kmalloc-256", to_string=True) r = re.findall(r"node\[\d+\]\.slabs_(?:partial|full): (0x\S+)", Color.remove_color(ret)) min_page = get_min_page(r) if min_page is not None: return min_page & 0xffff_ffff_c000_0000 # ~((1 << PUD_SHIFT) - 1) # plan 5 (from slob-dump) if allocator == "SLOB": ret = gdb.execute("slob-dump --simple --large --no-pager --quiet", to_string=True) r = re.findall(r"page: (0x\S+)", Color.remove_color(ret)) min_page = get_min_page(r) if min_page is not None: return min_page & 0xffff_ffff_c000_0000 # ~((1 << PUD_SHIFT) - 1) return None @staticmethod @switch_to_intel_syntax def get_VMEMMAP_END(): if is_x86_64() or is_arm64(): return KernelAddressHeuristicFinder.consts().VMEMMAP_END return None @staticmethod @switch_to_intel_syntax def get_end_of_fixed_addresses(): if not is_x86() and not is_arm64(): return kversion = Kernel.kernel_version() # plan 1 (available v2.6.27 ~) if is_x86(): if kversion and "2.6.27" <= kversion: addr = Symbol.get_ksymaddr("__native_set_fixmap") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_x86_cmp_const(res, skip_msb_check=True) for x in g: return x # plan 2 (available v3.19 ~) if is_arm64(): if kversion and "3.19" <= kversion: addr = Symbol.get_ksymaddr("__set_fixmap") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.aarch64_cmp_const(res, skip_msb_check=True) for x in g: if x <= 1: continue return x return None @staticmethod @switch_to_intel_syntax def get_sizeof_cpu_entry_area(): if not is_x86_32(): return None kversion = Kernel.kernel_version() if kversion and "4.14" <= kversion: addr = Symbol.get_ksymaddr("get_cpu_entry_area") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_x86_imul_const(res, skip_msb_check=True) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_mem_section(): if not is_x86_32() and not is_arm32(): return None # Since mem_map and mem_section are mutually exclusive, make sure that mem_map is not being used if KernelAddressHeuristicFinder.get_mem_map() is not None: return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: addr = Symbol.get_ksymaddr("mem_section") if addr: return addr kversion = Kernel.kernel_version() # plan 2 (available v2.4.0 or later) if kversion and "2.4" <= kversion: addr = Symbol.get_ksymaddr("free_pages") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_32(): # 0xc12f491b : mov eax,DWORD PTR [eax*8-0x3d19e3a0] # gef> x/w -0x3d19e3a0 # 0xc2e61c60 : 0xf708000f g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_array8_base(res) elif is_arm32(): # 0xc0501dd4 : movw r3, #45144 @ 0xb058 # 0xc0501dd8 : movt r3, #49664 @ 0xc200 # 0xc0501df0 : movwcc r2, #17344 @ 0x43c0 # 0xc0501df4 : movtcc r2, #49707 @ 0xc22b # gef> x/w 0xc200b058 # 0xc200b058 <__pv_phys_pfn_offset>: 0x00060000 # gef> x/w 0xc22b43c0 # 0xc22b43c0 : 0x00000000 g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res, allow_cc=True) for x in g: v = read_int_from_memory(x) if v and not is_valid_addr(v): continue return x return None @staticmethod @switch_to_intel_syntax def get_mem_map(): if not is_x86_32() and not is_arm32(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: addr = Symbol.get_ksymaddr("mem_map") if addr: v = read_int_from_memory(addr) if v != 0: return v return None kversion = Kernel.kernel_version() # plan 2 (available v2.4.0 or later) if kversion and "2.4" <= kversion: addr = Symbol.get_ksymaddr("free_pages") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_32(): g = itertools.chain( # 0xd3bf91d9 : mov ecx,DWORD PTR ds:0xd4f337c4 # gef> x/w 0xd4f337c4 # 0xd4f337c4 : 0xf67fe000 KernelAddressHeuristicFinderUtil.x86_dword_ptr_ds(res), KernelAddressHeuristicFinderUtil.x86_noptr_ds(res), ) elif is_arm32(): g = itertools.chain( # 0xc049f880 : movw r3, #46272 @ 0xb4c0 # 0xc049f884 : movt r3, #49625 @ 0xc1d9 # gef> x/w 0xc1d9b4c0 # 0xc1d9b4c0 : 0xcbdd9000 KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), ) for x in g: v = read_int_from_memory(x) if v != 0: return v return None @staticmethod @switch_to_intel_syntax def get_page_address_htable(): if not is_x86_32() and not is_arm32(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("page_address_htable") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.5.41 or later) if kversion and "2.5.41" <= kversion: addr = Symbol.get_ksymaddr("set_page_address") if addr: res = gdb.execute("x/40i {:#x}".format(addr), to_string=True) if is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_lea_reg_const(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: if is_double_link_list(x): return x return None @staticmethod @switch_to_intel_syntax def get_clocksource_tsc(): if not is_x86(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("clocksource_tsc") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.16.8 or later) if kversion and "4.16.8" <= kversion: addr = Symbol.get_ksymaddr("mark_tsc_unstable.part.0") or Symbol.get_ksymaddr("mark_tsc_unstable.cold") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "rdi", skip=2) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "eax", skip=1) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_clocksource_list(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("clocksource_list") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.6.21 or later / v2.6.32 or later) if kversion and "2.6.21" <= kversion: addr = Symbol.get_ksymaddr("clocksource_enqueue") or Symbol.get_ksymaddr("clocksource_resume") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), ) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_capability_hooks(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("capability_hooks") if x: return x # plan 2 nothing return None @staticmethod @switch_to_intel_syntax def get_n_tty_ops(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("n_tty_ops") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.6 or later) if kversion and "4.6" <= kversion: addr = Symbol.get_ksymaddr("n_tty_inherit_ops") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.x64_x86_dword_ptr_src(res), KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res), ) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_tty_ldiscs(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("tty_ldiscs") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.6.37 or later) if kversion and "2.6.37" <= kversion: addr = Symbol.get_ksymaddr("tty_register_ldisc") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_array4_base(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), ) for x in g: for i in range(2): v = read_int_from_memory(x + current_arch.ptrsize * i) if not is_valid_addr(v): continue if kversion < "5.13": w = read_int32_from_memory(v) if w == 0x00005403: # magic return x else: w = read_int_from_memory(v) if not is_valid_addr(w): continue s = read_cstring_from_memory(w) # name if s and len(s) > 2: return x return None @staticmethod @switch_to_intel_syntax def get_sysctl_table_root(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("sysctl_table_root") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v3.4 or later) if kversion and "3.4" <= kversion: addr = Symbol.get_ksymaddr("register_sysctl") or Symbol.get_ksymaddr("register_sysctl_sz") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "rdi") elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "e[a-d]x") elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), ) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_selinux_state(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("selinux_state") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v5.0 ~ v6.3) if kversion and "5.0" <= kversion < "6.4": addr = Symbol.get_ksymaddr("show_sid") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "rdi") elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "e[a-d]x") elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_ldr_reg_const(res, "r0") for x in g: if not is_valid_addr(x): continue if is_arm32(): v = read_int32_from_memory(x) if is_valid_addr(v): return v else: return x return None @staticmethod @switch_to_intel_syntax def get_apparmor_enabled(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("apparmor_enabled") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.12 or later) if kversion and "4.12" <= kversion: addr = Symbol.get_ksymaddr("param_get_aauint") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_apparmor_initialized(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("apparmor_initialized") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.12 or later) if kversion and "4.12" <= kversion: addr = Symbol.get_ksymaddr("param_get_aauint") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res, skip=1) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res, skip=1) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res, skip=1) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res, skip=1) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_kernel_locked_down(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("kernel_locked_down") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v5.4 or later) if kversion and "5.4" <= kversion: addr = Symbol.get_ksymaddr("lock_kernel_down") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_tomoyo_enabled(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("tomoyo_enabled") if x: return x # plan 2 nothing return None @staticmethod @switch_to_intel_syntax def get_mmap_min_addr(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("mmap_min_addr") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("vm.mmap_min_addr") if x: return x kversion = Kernel.kernel_version() # plan 3 (available v4.19.27 or later) if kversion and "4.19.27" <= kversion: addr = Symbol.get_ksymaddr("expand_downwards") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_sysctl_unprivileged_userfaultfd(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("sysctl_unprivileged_userfaultfd") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("vm.unprivileged_userfaultfd") if x: return x return None @staticmethod @switch_to_intel_syntax def get_sysctl_unprivileged_bpf_disabled(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("sysctl_unprivileged_bpf_disabled") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.unprivileged_bpf_disabled") if x: return x kversion = Kernel.kernel_version() # plan 3 (available v4.9.91 ~ v5.18.19) if kversion and "4.9.91" <= kversion < "5.19": addr = Symbol.get_ksymaddr("__do_sys_bpf") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_kptr_restrict(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("kptr_restrict") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.kptr_restrict") if x: return x kversion = Kernel.kernel_version() # plan 3 (available v4.15 or later) if kversion and "4.15" <= kversion: addr = Symbol.get_ksymaddr("kallsyms_show_value") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_sysctl_perf_event_paranoid(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("sysctl_perf_event_paranoid") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.perf_event_paranoid") if x: return x kversion = Kernel.kernel_version() # plan 3 (available v4.15 or later) if kversion and "4.15" <= kversion: addr = Symbol.get_ksymaddr("kallsyms_show_value") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res, skip=1) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res, skip=1) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res, skip=1) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res, skip=1) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_dmesg_restrict(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("dmesg_restrict") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.dmesg_restrict") if x: return x kversion = Kernel.kernel_version() # plan 3 (available v3.11 or later) if kversion and "3.11" <= kversion: addr = Symbol.get_ksymaddr("check_syslog_permissions") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_kexec_load_disabled(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("kexec_load_disabled") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.kexec_load_disabled") if x: return x return None @staticmethod @switch_to_intel_syntax def get_loadpin_enabled(): # plan 1 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.loadpin.enabled") if x: return x return None @staticmethod @switch_to_intel_syntax def get_loadpin_enforce(): # plan 1 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.loadpin.enforce") if x: return x return None @staticmethod @switch_to_intel_syntax def get_ptrace_scope(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("ptrace_scope") if x: return x # plan 2 (from ksysctl) if KernelAddressHeuristicFinder.USE_KSYSCTL: x = Kernel.get_ksysctl("kernel.yama.ptrace_scope") if x: return x return None @staticmethod @switch_to_intel_syntax def get_vdso_image_64(): if not is_x86_64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("vdso_image_64") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.2 or later) if kversion and "4.2" <= kversion: addr = Symbol.get_ksymaddr("arch_setup_additional_pages") if addr: res = gdb.execute("x/40i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "rdi", read_valid=True) for x in g: v = read_int_from_memory(x) if read_memory(v, 4) == b"\x7fELF": return x # another pattern # gef> |x/40i arch_setup_additional_pages | grep mov # 0xffffffff82401030 : mov eax,DWORD PTR [rip+0x43738a] # 0xffffffff828383c0 # 0xffffffff8240103e : mov edx,DWORD PTR [rip+0x1ffb24] # 0xffffffff82600b68 g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res) for x in g: if not is_valid_addr(x): continue for i in range(10): v = read_int_from_memory(x - current_arch.ptrsize * i) if not is_valid_addr(v): continue if read_memory(v, 4) == b"\x7fELF": return x - current_arch.ptrsize * i return None @staticmethod @switch_to_intel_syntax def get_vdso_image_x32(): if not is_x86_64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("vdso_image_x32") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.2 or later) if kversion and "4.2" <= kversion: addr = Symbol.get_ksymaddr("compat_arch_setup_additional_pages") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "rdi", read_valid=True) for x in g: v = read_int_from_memory(x) if read_memory(v, 4) == b"\x7fELF" and read_memory(v + 0x12, 1) == b"\x3e": # Elf.Machine return x return None @staticmethod @switch_to_intel_syntax def get_vdso_image_32(): if not is_x86(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("vdso_image_32") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.2 or later) if kversion and "4.2" <= kversion: if is_x86_64(): addr = Symbol.get_ksymaddr("compat_arch_setup_additional_pages") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "rdi", read_valid=True) for x in g: v = read_int_from_memory(x) if read_memory(v, 4) == b"\x7fELF" and read_memory(v + 0x12, 1) == b"\x03": # Elf.Machine return x elif is_x86_32(): addr = Symbol.get_ksymaddr("arch_setup_additional_pages") if addr: # pattern 1 res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "eax", read_valid=True) for x in g: v = read_int_from_memory(x) if read_memory(v, 4) == b"\x7fELF": return x # pattern 2 res = gdb.execute("x/40i {:#x}".format(addr), to_string=True) g2 = KernelAddressHeuristicFinderUtil.x86_dword_ptr_ds(res) for x in g2: if read_int_from_memory(x) == 0x1000: v = read_int_from_memory(x - 4) if read_memory(v, 4) == b"\x7fELF": return x - 4 return None @staticmethod def get_vdso_info(): if not is_arm64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("vdso_info") if x: return x kversion = Kernel.kernel_version() # vdso_info is introduced from v5.8 if kversion < "5.8": return None # plan 2 (available v5.8 or later) if kversion and "5.8" <= kversion: addr = Symbol.get_ksymaddr("__vdso_init") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) for x in g: return x # plan 3 (from .rodata) """ static struct vdso_abi_info vdso_info[] __ro_after_init = { [VDSO_ABI_AA64] = { .name = "vdso", .vdso_code_start = vdso_start, .vdso_code_end = vdso_end, }, """ kinfo = Kernel.get_kernel_layout() if kinfo.ro_base and kinfo.ro_size: ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) pos = -1 while True: # search for aligned ELF header from .rodata pos = ro_data.find(b"\x7fELF", pos + 1) if pos == -1: break if pos % get_pagesize() != 0: continue # calc address of ELF header if is_32bit(): vdso_addr_byteseq = p32(kinfo.ro_base + pos) else: vdso_addr_byteseq = p64(kinfo.ro_base + pos) # search for it from .rodata again pos2 = -1 while True: pos2 = ro_data.find(vdso_addr_byteseq, pos2 + 1) if pos2 == -1: break if pos2 % current_arch.ptrsize != 0: continue maybe_vdso_info = kinfo.ro_base + pos2 maybe_vdso_info -= current_arch.ptrsize name = read_int_from_memory(maybe_vdso_info) if not is_valid_addr(name): continue if read_cstring_from_memory(name) == "vdso": return maybe_vdso_info return None @staticmethod def get_vdso_lookup(): if not is_arm64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("vdso_lookup") if x: return x kversion = Kernel.kernel_version() # vdso_info is introduced until v5.8 if kversion and (kversion < "5.3" or "5.8" <= kversion): return None # plan 2 (available v5.3 or later) if kversion and "5.3" <= kversion: addr = Symbol.get_ksymaddr("__vdso_init") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) for x in g: return x # plan 3 (from .rodata) """ static struct __vdso_abi vdso_lookup[VDSO_TYPES] __ro_after_init = { { .name = "vdso", .vdso_code_start = vdso_start, .vdso_code_end = vdso_end, }, """ kinfo = Kernel.get_kernel_layout() if kinfo.ro_base and kinfo.ro_size: ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) pos = -1 while True: # search for aligned ELF header from .rodata pos = ro_data.find(b"\x7fELF", pos + 1) if pos == -1: break if pos % get_pagesize() != 0: continue # calc address of ELF header if is_32bit(): vdso_addr_byteseq = p32(kinfo.ro_base + pos) else: vdso_addr_byteseq = p64(kinfo.ro_base + pos) # search for it from .rodata again pos2 = -1 while True: pos2 = ro_data.find(vdso_addr_byteseq, pos2 + 1) if pos2 == -1: break if pos2 % current_arch.ptrsize != 0: continue maybe_vdso_lookup = kinfo.ro_base + pos2 maybe_vdso_lookup -= current_arch.ptrsize name = read_int_from_memory(maybe_vdso_lookup) if not is_valid_addr(name): continue if read_cstring_from_memory(name) == "vdso": return maybe_vdso_lookup return None @staticmethod def get_vdso_start(): if not is_arm32() and not is_arm64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("vdso_start") if x: return x kversion = Kernel.kernel_version() # plan 2 (from vdso_info or vdso_lookup) if kversion and is_arm64(): if "5.8" <= kversion: vdso_info = KernelAddressHeuristicFinder.get_vdso_info() else: vdso_info = KernelAddressHeuristicFinder.get_vdso_lookup() if vdso_info and is_valid_addr(vdso_info): vdso_name = read_int_from_memory(vdso_info) if is_valid_addr(vdso_name): if "vdso" == read_cstring_from_memory(vdso_name): x = read_int_from_memory(vdso_info + current_arch.ptrsize) return x # plan 3 (from .rodata) kinfo = Kernel.get_kernel_layout() if kinfo.ro_base and kinfo.ro_size: ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) pos = -1 while True: # search for aligned ELF header from .rodata pos = ro_data.find(b"\x7fELF", pos + 1) if pos == -1: break if pos % get_pagesize() == 0: return kinfo.ro_base + pos return None @staticmethod def get_vdso32_start(): if not is_arm64(): return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("vdso32_start") if x: return x kversion = Kernel.kernel_version() # plan 2 (from vdso_info or vdso_lookup) if kversion: if "5.8" <= kversion: vdso_info = KernelAddressHeuristicFinder.get_vdso_info() else: vdso_info = KernelAddressHeuristicFinder.get_vdso_lookup() if vdso_info: vdso_info_2 = vdso_info + current_arch.ptrsize * 6 if vdso_info_2 and is_valid_addr(vdso_info_2): vdso_name = read_int_from_memory(vdso_info_2) if is_valid_addr(vdso_name): if "vdso32" == read_cstring_from_memory(vdso_name): x = read_int_from_memory(vdso_info_2 + current_arch.ptrsize) return x return None @staticmethod @switch_to_intel_syntax def get_file_systems(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("file_systems") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.5.7 or later) if kversion and "2.5.7" <= kversion: addr = Symbol.get_ksymaddr("unregister_filesystem") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res, read_valid=True) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res, read_valid=True) elif is_arm64(): g = itertools.chain( KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res, read_valid=True), KernelAddressHeuristicFinderUtil.aarch64_adrp_add_ldr(res, read_valid=True), ) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res, read_valid=True), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res, read_valid=True), ) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_printk_rb_static(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("printk_rb_static") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v5.13 or later) if kversion and "5.13" <= kversion: addr = Symbol.get_ksymaddr("kmsg_dump_rewind") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res, read_valid=True) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res, read_valid=True) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res, read_valid=True) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res, read_valid=True) for x in g: return read_int_from_memory(x) # plan 3 (available v5.10 or later) if kversion and "5.10" <= kversion: addr = Symbol.get_ksymaddr("kmsg_dump_rewind_nolock") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res, read_valid=True) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res, read_valid=True) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res, read_valid=True) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res, read_valid=True) for x in g: return read_int_from_memory(x) return None @staticmethod @switch_to_intel_syntax def get_log_first_idx(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("log_first_idx") if x: return x kversion = Kernel.kernel_version() # this is old dmesg structure if kversion and "5.10" <= kversion: return False # plan 2 (available v3.5 or later) if kversion and "3.5" <= kversion: addr = Symbol.get_ksymaddr("devkmsg_open") if addr: res = gdb.execute("x/50i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_mov_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_ldr(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative_ldr(res), ) for x in g: v = read_int32_from_memory(x) if is_valid_addr(v): continue return x return None @staticmethod @switch_to_intel_syntax def get_log_next_idx(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("log_next_idx") if x: return x kversion = Kernel.kernel_version() # this is old dmesg structure if kversion and "5.10" <= kversion: return False # plan 2 (available v3.5 or later) if kversion and "3.5" <= kversion: addr = Symbol.get_ksymaddr("kmsg_dump_rewind_nolock") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res, skip=1) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_ds(res) g = list(g)[::-1] elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_ldr(res, skip=1) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt_ldr(res, skip=1), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative_ldr(res, skip=1), ) for x in g: v = read_int32_from_memory(x) if v == 0: continue return x return None @staticmethod @switch_to_intel_syntax def get___log_buf(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("__log_buf") if x: return x kversion = Kernel.kernel_version() # this is old dmesg structure if kversion and "5.10" <= kversion: return False # plan 2 (available v3.5 or later) if kversion and "3.5" <= kversion: log_buf_len = KernelAddressHeuristicFinder.get_log_buf_len() if log_buf_len: # static char *log_buf = __log_buf; # static u32 log_buf_len = __LOG_BUF_LEN; if is_32bit(): # pattern1 (x86): log_buf_len -> log_buf # 0xc1ba30b8|+0x0000|+000: 0x00040000 # 0xc1ba30bc|+0x0004|+001: 0xc1cf7720 -> 0x00000000 # pattern2 (ARM): log_buf -> log_buf_len # 0xc03d26dc|+0x0000|+000: 0xc03ec9f8 -> 0x00000000 # 0xc03d26e0|+0x0004|+001: 0x00004000 pattern = [4, -4] elif is_64bit(): # pattern1 (x64): log_buf_len -> log_buf # 0xffffffffaa240720|+0x0000|+000: 0x0000000000020000 # 0xffffffffaa240728|+0x0008|+001: 0xffffffffaa2f87dc -> 0x0000000000000000 # pattern1 (ARM64): log_buf_len -> log_buf # 0xffff000011256c68|+0x0000|+000: 0x0000000000020000 # 0xffff000011256c70|+0x0008|+001: 0xffff0000113f5350 <__log_buf> -> 0x0000000000000000 # pattern2 (x64): log_buf_len -> log_buf (no-padding) # 0xffffffffa5c476cc|+0x0000|+000: 0xa62e9cf400040000 # 0xffffffffa5c476d4|+0x0008|+001: 0x00000000ffffffff (=0xffffffffa62e9cf4) # pattern3 (x64): log_buf -> log_buf_len # 0xffffffff81c1df48|+0x0008|+001: 0xffffffff81d98e20 -> 0x0000000000000000 # 0xffffffff81c1df50|+0x0010|+002: 0xffffffff00040000 (=0x00040000) pattern = [8, 4, -8] for diff in pattern: log_buf = log_buf_len + diff __log_buf = read_int_from_memory(log_buf) if is_valid_addr(__log_buf): return __log_buf return None @staticmethod @switch_to_intel_syntax def get_log_buf_len(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("log_buf_len") if x: return x kversion = Kernel.kernel_version() # this is old dmesg structure if kversion and "5.10" <= kversion: return False # plan 2 (available v3.17 or later) if kversion and "3.17" <= kversion: addr = Symbol.get_ksymaddr("log_buf_len_get") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative_ldr(res), ) for x in g: v = read_int32_from_memory(x) if v and (v & 0xfff) == 0: return x # plan 3 (available v3.5 or later) if kversion and "3.5" <= kversion: addr = Symbol.get_ksymaddr("do_syslog") if addr: res = gdb.execute("x/300i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_dword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): # TODO g = [] elif is_arm32(): # TODO g = [] for x in g: v = read_int32_from_memory(x) if v and (v & 0xfff) == 0: return x return None @staticmethod @switch_to_intel_syntax def get_idt_base(): if is_x86(): if is_qemu_system(): res = gdb.execute("monitor info registers", to_string=True) idtr = re.search(r"IDT\s*=\s*(\w+) (\w+)", res) base, _limit = [int(idtr.group(i), 16) for i in range(1, 3)] return base elif is_vmware(): res = gdb.execute("monitor r idtr", to_string=True) r = re.search(r"idtr base=(\w+) limit=(\w+)", res) base = int(r.group(1), 16) return base return None @staticmethod @switch_to_intel_syntax def get_gdt_base(): if is_x86(): if is_qemu_system(): res = gdb.execute("monitor info registers", to_string=True) gdtr = re.search(r"GDT\s*=\s*(\w+) (\w+)", res) base, _limit = [int(gdtr.group(i), 16) for i in range(1, 3)] return base elif is_vmware(): res = gdb.execute("monitor r gdtr", to_string=True) r = re.search(r"gdtr base=(\w+) limit=(\w+)", res) base = int(r.group(1), 16) return base return None @staticmethod @switch_to_intel_syntax def get_tss_base(): if is_x86(): if is_qemu_system(): res = gdb.execute("monitor info registers", to_string=True) tr = re.search(r"TR\s*=\s*(\w+) (\w+) (\w+) (\w+)", res) _trseg, base, _limit, _attr = [int(tr.group(i), 16) for i in range(1, 5)] return base return None @staticmethod @switch_to_intel_syntax def get_ldt_base(): if is_x86(): if is_qemu_system(): res = gdb.execute("monitor info registers", to_string=True) ldtr = re.search(r"LDT\s*=\s*(\w+) (\w+) (\w+) (\w+)", res) _seg, base, _limit, _attr = [int(ldtr.group(i), 16) for i in range(1, 5)] return base elif is_vmware(): # `monitor r ldtr` is buggy return None return None @staticmethod @switch_to_intel_syntax def get_node_data(): # when CONFIG_NUMA=y (maybe) # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("node_data") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.6.17 or later) if kversion and "2.6.17" <= kversion: addr = Symbol.get_ksymaddr("first_online_pgdat") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = itertools.chain( KernelAddressHeuristicFinderUtil.x64_qword_ptr_array_base(res), KernelAddressHeuristicFinderUtil.x64_lea_reg_const(res), ) elif is_x86_32(): # TODO g = [] elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): # TODO g = [] for x in g: """ node_data[]: 0xffffdfab1c1be0b8 gef> telescope 0xffffdfab1c1be0b8 0xffffdfab1c1be0b8|+0x0000|+000: 0xffff00007fbf09c0 -> 0x0000000000001600 0xffffdfab1c1be0c0|+0x0008|+001: 0x0000000000000000 0xffffdfab1c1be0c8|+0x0010|+002: 0x0000000000000000 gef> telescope 0xffff00007fbf09c0 0xffff00007fbf09c0|+0x0000|+000: 0x0000000000001600 0xffff00007fbf09c8|+0x0008|+001: 0x0000000000001b80 0xffff00007fbf09d0|+0x0010|+002: 0x0000000000002100 0xffff00007fbf09d8|+0x0018|+003: 0x0000000000002680 0xffff00007fbf09e0|+0x0020|+004: 0x0000000000000000 0xffff00007fbf09e8|+0x0028|+005: 0x0000000000000000 0xffff00007fbf09f0|+0x0030|+006: 0x0000000000000000 0xffff00007fbf09f8|+0x0038|+007: 0x0000000000000000 0xffff00007fbf0a00|+0x0040|+008: 0x0000000000000000 0xffff00007fbf0a08|+0x0048|+009: 0x0000000000000000 0xffff00007fbf0a10|+0x0050|+010: 0x0000000000000000 0xffff00007fbf0a18|+0x0058|+011: 0xffff00007fbf09c0 -> 0x0000000000001600 0xffff00007fbf0a20|+0x0060|+012: 0xffffdfab1ba8a540 0xffff00007fbf0a28|+0x0068|+013: 0xffffdfab1ba8a4e0 0xffff00007fbf0a30|+0x0070|+014: 0x0000003f000006e0 0xffff00007fbf0a38|+0x0078|+015: 0x0000000000040000 0xffff00007fbf0a40|+0x0080|+016: 0x0000000000077153 0xffff00007fbf0a48|+0x0088|+017: 0x0000000000080000 0xffff00007fbf0a50|+0x0090|+018: 0x0000000000080000 0xffff00007fbf0a58|+0x0098|+019: 0x0000000000080000 0xffff00007fbf0a60|+0x00a0|+020: 0x0000000000002000 0xffff00007fbf0a68|+0x00a8|+021: 0xffffdfab1b792690 -> 0x0000000000414d44 ('DMA'?) """ v = read_int_from_memory(x) if not v or not is_valid_addr(v): continue # avoid false positive if is_double_link_list(x): continue # avoid false positive water_mark0 = read_int_from_memory(v) if water_mark0 > 0x0000_1000_0000_0000: continue # ok return x return None @staticmethod @switch_to_intel_syntax def get_node_data0(): # when CONFIG_NUMA=n # This method can only be called when `get_node_data()` fails. kversion = Kernel.kernel_version() # plan 1 (available v2.6.17 or later) if kversion and "2.6.17" <= kversion: addr = Symbol.get_ksymaddr("first_online_pgdat") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "rax") elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "eax") elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res) ) for x in g: v = read_int_from_memory(x) if v and is_valid_addr(v): continue return x return None @staticmethod @switch_to_intel_syntax def get_prog_idr(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("prog_idr") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.13 or later) # In certain cases it may return `prog_idr_lock` instead of `prog_idr`. # It was not possible to distinguish them because their structures are very similar. # However, `prog_idr` and `prog_idr_lock` are placed consecutively. # Even if there is a slight deviation, there is no problem because the member # identification logic of the caller (`kbpf` command) works. if kversion and "4.13" <= kversion: addr = Symbol.get_ksymaddr("bpf_prog_free_id.part.0") or Symbol.get_ksymaddr("bpf_prog_free_id") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_map_idr(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("map_idr") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.13 or later) # In certain cases it may return `map_idr_lock` instead of `map_idr`. # It was not possible to distinguish them because their structures are very similar. # However, `map_idr` and `map_idr_lock` are placed consecutively. # Even if there is a slight deviation, there is no problem because the member # identification logic of the caller (`kbpf` command) works. if kversion and "4.13" <= kversion: addr = Symbol.get_ksymaddr("bpf_map_free_id") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_vmap_area_list(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("vmap_area_list") if x: return x kversion = Kernel.kernel_version() if "6.9" <= kversion: return None # plan 2 (available v4.7~) if kversion and "4.7" <= kversion: addr = Symbol.get_ksymaddr("register_vmap_purge_notifier") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) """ x86 or x64 [~v5.1] mm/vmalloc.c vmap_notify_list # rarely leads to long lists vmap_area_list <- here [v5.2~v6.8] mm/vmalloc.c vmap_notify_list # rarely leads to long lists free_vmap_area_list <- here (false positive) vmap_area_list <- here """ elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) """ arm32 or arm64 [~v6.8] mm/vmalloc.c vmap_notify_list # rarely leads to long lists vmap_area_list <- here free_vmap_area_list """ elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: if is_x86(): if kversion < "5.2": count = 1 else: count = 2 else: count = 1 for i in range(16): a = x + current_arch.ptrsize * i if is_double_link_list(a, min_len=5): count -= 1 if count == 0: return a # plan 3 (available v3.10 ~ v6.3: vread, v6.4~: vread_iter) if kversion and "3.17" <= kversion: addr = Symbol.get_ksymaddr("vread") or Symbol.get_ksymaddr("vread_iter") if addr: res = gdb.execute("x/100i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_cmp_const(res, read_valid=True) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_cmp_const(res, read_valid=True) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_ldr(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), ) for x in g: if is_double_link_list(x): return x # plan 4 (available v4.10~) if kversion and "4.10" <= kversion: addrs = Symbol.get_ksymaddr_multiple("s_next") if addrs: for s_next in addrs: res = gdb.execute("x/20i {:#x}".format(s_next), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "rsi") elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, "e.x") elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_add(res, read_valid=True) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res) for x in g: if is_double_link_list(x): return x # plan 5 (available v2.6.28 ~ v5.1) if kversion and "2.6.28" <= kversion < "5.2": addr = Symbol.get_ksymaddr("__insert_vmap_area") if addr: res = gdb.execute("x/100i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res, read_valid=True) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_dword_ptr_ds(res, read_valid=True) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_ldr(res) elif is_arm32(): g = itertools.chain( KernelAddressHeuristicFinderUtil.arm32_movw_movt(res), KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res), ) for x in g: if is_double_link_list(x): return x return None @staticmethod @switch_to_intel_syntax def get_free_vmap_area_list(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("free_vmap_area_list") if x: return x kversion = Kernel.kernel_version() if kversion < "5.2": return None # plan 2 (available v4.7~; here, always True) addr = Symbol.get_ksymaddr("register_vmap_purge_notifier") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) """ x86 or x64 [v5.2~v6.8] mm/vmalloc.c vmap_notify_list # rarely leads to long lists free_vmap_area_list <- here vmap_area_list [v6.9~] mm/vmalloc.c vmap_notify_list # rarely leads to long lists free_vmap_area_list <- here """ elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) """ arm32 or arm64 [v5.2~v6.8] mm/vmalloc.c vmap_notify_list # rarely leads to long lists vmap_area_list <- here (false positive) free_vmap_area_list <- here [v6.9~] mm/vmalloc.c vmap_notify_list # rarely leads to long lists free_vmap_area_list <- here """ elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: if is_arm32() or is_arm64(): if kversion < "6.9": count = 2 else: count = 1 else: count = 1 for i in range(16): a = x + current_arch.ptrsize * i if is_double_link_list(a, min_len=3): count -= 1 if count == 0: return a return None @staticmethod @switch_to_intel_syntax def get_timer_bases(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("timer_bases") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.8 or later) if kversion and "4.8" <= kversion: addr = Symbol.get_ksymaddr("run_timer_softirq") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = itertools.chain( KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, skip_msb_check=True), KernelAddressHeuristicFinderUtil.x64_lea_reg_const(res, skip_msb_check=True), ) g2 = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, skip_msb_check=True) g2 = KernelAddressHeuristicFinderUtil.x64_x86_dword_ptr_src(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res, skip_msb_check=True) g2 = [] elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res, skip_msb_check=True) g2 = [] __per_cpu_offset = KernelAddressHeuristicFinder.get_per_cpu_offset() if __per_cpu_offset: # pattern1: per_cpu # pattern1-a: # 0xffffffff8cf25b05 : mov rdi,0x24b40 <-- timer_bases # pattern1-b: # 0xffffffffa440831e : lea rbx,[rax+0x22400] for x in g: if not is_valid_addr(x) and (x & 0x7) == 0: return x else: # pattern2: not per_cpu # 0xffffffff8aa6e450 : mov rax,QWORD PTR [rip+0x7cfba9] # 0xffffffff8b23e000 # 0xffffffff8aa6e457 : cmp rax,QWORD PTR [rip+0x7ce92a] # 0xffffffff8b23cd88 # 0xffffffff8aa6e474 : mov rdx,QWORD PTR [rip+0x7cfb85] # 0xffffffff8b23e000 # 0xffffffff8aa6e47b : mov rax,QWORD PTR [rip+0x7ce906] # 0xffffffff8b23cd88 jiffies = KernelAddressHeuristicFinder.get_jiffies() addrs = [x for x in g2 if (is_valid_addr(x) and (not jiffies or jiffies != x))] if addrs: return min(addrs) return None @staticmethod @switch_to_intel_syntax def get_hrtimer_bases(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("hrtimer_bases") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v4.8 or later) if kversion and "4.8" <= kversion: addr = Symbol.get_ksymaddr("hrtimer_run_queues") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = itertools.chain( KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, skip_msb_check=True), KernelAddressHeuristicFinderUtil.x64_x86_byte_ptr(res, skip_msb_check=True), KernelAddressHeuristicFinderUtil.x64_lea_reg_const(res, skip_msb_check=True), ) g2 = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, skip_msb_check=True) g2 = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res, skip_msb_check=True) g2 = [] elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res, skip_msb_check=True) g2 = [] __per_cpu_offset = KernelAddressHeuristicFinder.get_per_cpu_offset() if __per_cpu_offset: # pattern1: per_cpu # pattern1-a: # 0xffffffff9b127acb: mov rbx,0x27040 <-- hrtimer_bases # pattern1-b: # 0xffffffffa440b87d : test BYTE PTR [rax+0x25b90],0x1 # The exact value is 0x25b80, but don't worry about a slight deviation. # pattern1-c: # 0xffffffff818f57b5 : lea rbx,[rax+0x1df00] for x in g: if not is_valid_addr(x) and (x & 0x7) == 0: return x else: # pattern2: not per_cpu # 0xffffffffbb8668bc : mov rdx,0xffffffffbc046138 # 0xffffffffbb8668c3 : mov rcx,0xffffffffbc046178 # 0xffffffffbb8668ca : mov rsi,0xffffffffbc0460f8 # 0xffffffffbb8668d1 : mov rdi,0xffffffffbc046048 <-- hrtimer_bases+8 addrs = [x for x in g2 if is_valid_addr(x)] if addrs: return min(addrs) return None @staticmethod @switch_to_intel_syntax def get_jiffies(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("jiffies") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.6.18 or later) if kversion and "2.6.18" <= kversion: addr = Symbol.get_ksymaddr("jiffies_read") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_ldr(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_pci_root_buses(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("pci_root_buses") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v2.5.71 or later) if kversion and "2.5.71" <= kversion: addr = Symbol.get_ksymaddr("pci_find_next_bus") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x86_noptr_ds(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res, read_valid=True) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res, read_valid=True) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_ioport_resource(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("ioport_resource") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v3.3 or later) if kversion and "3.3" <= kversion: addr = Symbol.get_ksymaddr("pci_scan_bus") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, r"r\w+") elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res, r"e\w+") elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: name_ptr = read_int_from_memory(x + 0x8 * 2) # sizeof(resource_size_t) == 8 if name_ptr and is_valid_addr(name_ptr): name = read_cstring_from_memory(name_ptr) if name == "PCI IO": return x name_ptr = read_int_from_memory(x + 0x4 * 2) # sizeof(resource_size_t) == 4 if name_ptr and is_valid_addr(name_ptr): name = read_cstring_from_memory(name_ptr) if name == "PCI IO": return x # plan 3 (from .rodata) kinfo = Kernel.get_kernel_layout() if kinfo.ro_base and kinfo.ro_size and is_valid_addr(kinfo.ro_base): ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) if kinfo.rw_base and kinfo.rw_size: rw_data = read_memory(kinfo.rw_base, min(kinfo.rw_size, 0x1000000)) else: rw_data = ro_data pos = -1 while True: # search for aligned string from .rodata pos = ro_data.find(b"PCI IO\x00", pos + 1) if pos == -1: break # calc address of ELF header if is_32bit(): addr_byteseq = p32(kinfo.ro_base + pos) else: addr_byteseq = p64(kinfo.ro_base + pos) # search for it from .data pos2 = -1 while True: pos2 = rw_data.find(addr_byteseq, pos2 + 1) if pos2 == -1: break if pos2 % current_arch.ptrsize != 0: continue # TODO: How to find the exact value of sizeof(resource_size_t) if kinfo.rw_base and kinfo.rw_size: maybe_ioport_resource = kinfo.rw_base + pos2 - current_arch.ptrsize * 2 else: maybe_ioport_resource = kinfo.ro_base + pos2 - current_arch.ptrsize * 2 return maybe_ioport_resource return None @staticmethod @switch_to_intel_syntax def get_iomem_resource(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("iomem_resource") if x: return x # plan 2 (from ioport_resource) x = KernelAddressHeuristicFinder.get_ioport_resource() if x: # offsetof(resource, name) offset_name = None name_ptr = read_int_from_memory(x + 0x8 * 2) # sizeof(resource_size_t) == 8 if name_ptr and is_valid_addr(name_ptr): name = read_cstring_from_memory(name_ptr) if name == "PCI IO": offset_name = 0x8 * 2 if offset_name is None: name_ptr = read_int_from_memory(x + 0x4 * 2) # sizeof(resource_size_t) == 4 if name_ptr and is_valid_addr(name_ptr): name = read_cstring_from_memory(name_ptr) if name == "PCI IO": offset_name = 0x4 * 2 # find "PCI mem" if offset_name is not None: for i in range(-30, 30): diff = (current_arch.ptrsize * i) name_ptr = read_int_from_memory(x + diff) if name_ptr and is_valid_addr(name_ptr): name = read_cstring_from_memory(name_ptr) if name == "PCI mem": return x + diff - offset_name return None @staticmethod @switch_to_intel_syntax def get_db_list(): # need DMA_SHARED_BUFFER=y # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("db_list") if x: return x kversion = Kernel.kernel_version() # plan 2 (available v5.10 or later) if kversion and "5.10" <= kversion: addr = Symbol.get_ksymaddr("dma_buf_file_release") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res) for x in g: # here, x points &db_list.lock v = x - current_arch.ptrsize * 2 if is_double_link_list(v): return v # plan 3 (available v3.17 ~ v5.9) if kversion and "3.17" <= kversion < "5.10": addr = Symbol.get_ksymaddr("dma_buf_release") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_ldr_pc_relative(res) for x in g: # here, x points &db_list.lock v = x - current_arch.ptrsize * 2 if is_double_link_list(v): return v return None @staticmethod @switch_to_intel_syntax def get_irq_desc_tree(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("irq_desc_tree") if x: return x kversion = Kernel.kernel_version() if kversion and "6.5" <= kversion: return False # plan 2 (available v2.6.37 ~ v6.4) if kversion and "2.6.37" <= kversion < "6.5": addr = Symbol.get_ksymaddr("irq_to_desc") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_sparse_irqs(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("sparse_irqs") if x: return x kversion = Kernel.kernel_version() if kversion and kversion < "6.5": return False # plan 2 (available v6.5 or later) if kversion and "6.5" <= kversion: addr = Symbol.get_ksymaddr("irq_to_desc") if addr: res = gdb.execute("x/20i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_x86_32(): g = KernelAddressHeuristicFinderUtil.x64_x86_mov_reg_const(res) elif is_arm64(): g = KernelAddressHeuristicFinderUtil.aarch64_adrp_add(res) elif is_arm32(): g = KernelAddressHeuristicFinderUtil.arm32_movw_movt(res) for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_slub_tlbflush_queue(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("slub_tlbflush_queue") if x: return x # plan 2 (from slub_tlbflush_worker) addr = Symbol.get_ksymaddr("slub_tlbflush_worker") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): # TODO g = [] elif is_arm64(): # TODO g = [] elif is_arm32(): # TODO g = [] for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_slub_addr_base(): # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: x = Symbol.get_ksymaddr("slub_addr_base") if x: return x kversion = Kernel.kernel_version() if not kversion: return None if kversion < "6.6": # plan 2 (available in 6.1-based from `alloc_slab_pv_page`) # bruteforces all accesses into global variables in `alloc_slab_pv_page` # In source, `slub_addr_base` is referenced only by `alloc_slab_meta`, but # compiler seems to inline this function (because calls once, maybe) addr = Symbol.get_ksymaddr("alloc_slab_pv_page") if addr: res = gdb.execute("x/200i {:#x}".format(addr), to_string=True) r""" gef> |x/200i alloc_slab_pv_page| grep -i 'qword ptr \[rip\+.*\]' 0xffffffff8901e65a : mov rsi,QWORD PTR [rip+0x162c627] 0xffffffff8901e672 : sub rax,QWORD PTR [rip+0x12ec317] 0xffffffff8901e6a8 : and rcx,QWORD PTR [rip+0x162c5e1] 0xffffffff8901e758 : mov rax,QWORD PTR [rip+0x15432c1] <- this gef> """ if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): # TODO g = [] elif is_arm64(): # TODO g = [] elif is_arm32(): # TODO g = [] for x in g: v = read_int_from_memory(x) if v in [0xffff_ffff_ffff_ffff, 0xffff_ffff_ffff_feff, 1]: continue # the value of `slub_addr_base` is unmapped address. if not is_valid_addr(v): return x else: # plan 3 (available in 6.6-based from vaddr_ranges_debug_start) addr = Symbol.get_ksymaddr("vaddr_ranges_debug_start") if addr: res = gdb.execute("x/8i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): # TODO g = [] elif is_arm64(): # TODO g = [] elif is_arm32(): # TODO g = [] for x in g: return x return None @staticmethod @switch_to_intel_syntax def get_slub_addr_current(): if Kernel.kernel_version() < "6.6": return None # plan 1 (directly) if KernelAddressHeuristicFinder.USE_DIRECTLY: addr = Symbol.get_ksymaddr("slub_addr_current") if addr: return # plan 2 (from vaddr_ranges_first_valid_slab) addr = Symbol.get_ksymaddr("vaddr_ranges_first_valid_slab") if addr: res = gdb.execute("x/10i {:#x}".format(addr), to_string=True) if is_x86_64(): g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res) elif is_x86_32(): # TODO g = [] elif is_arm64(): # TODO g = [] elif is_arm32(): # TODO g = [] for x in g: return x return None KF = KernelAddressHeuristicFinder # for convenience using from python-interactive # noqa: F841 KFU = KernelAddressHeuristicFinderUtil # for convenience using from python-interactive # noqa: F841 class Kernel: """A collection of utility functions that are related to kernel specific features.""" @staticmethod @Cache.cache_until_next def get_maps(): maps = [] res = PageMap.get_page_maps_by_pagewalk("pagewalk --quiet --no-pager --simple --disable-color") res = sorted(set(res.splitlines())) res = list(filter(lambda line: line.endswith("]"), res)) res = list(filter(lambda line: "[+]" not in line, res)) res = list(filter(lambda line: "*" not in line, res)) if is_x86(): for line in res: line = line.split() if line[6] != "KERN]": continue vaddr = int(line[0].split("-")[0], 16) size = int(line[2], 16) perm = line[5][1:] # [xxx maps.append([vaddr, size, perm]) elif is_arm32(): for line in res: line = line.split() vaddr = int(line[0].split("-")[0], 16) if line[5] != "[PL0/---" and vaddr != 0xffff_0000: continue size = int(line[2], 16) perm = line[6][4:7] # PL1/xxx maps.append([vaddr, size, perm]) elif is_arm64(): for line in res: line = line.split() if line[5] != "[EL0/---": continue vaddr = int(line[0].split("-")[0], 16) size = int(line[2], 16) perm = line[6][4:7] # EL1/xxx maps.append([vaddr, size, perm]) elif is_riscv64() or is_riscv32(): for line in res: line = line.split() if line[6] != "KERN]": continue vaddr = int(line[0].split("-")[0], 16) size = int(line[2], 16) perm = line[5][1:] # [xxx maps.append([vaddr, size, perm]) if len(maps) <= 1: if is_x86(): warn("Make sure you are in ring0 (=kernel mode); See pagewalk") elif is_arm32(): warn("Make sure you are in supervisor mode (=kernel mode); See pagewalk") warn("Make sure qemu 3.x or higher") elif is_arm64(): warn("Make sure you are in EL1 (=kernel mode); See pagewalk") warn("Make sure qemu 3.x or higher") elif is_riscv64() or is_riscv32(): warn("Make sure you are in S-mode (=kernel mode); See pagewalk") return None else: return maps # No caching intentionally @staticmethod def get_kernel_base_hint(): # This function is designed for the case where the symbol is not available. # Do not use Symbol.get_ksymaddr. if is_x86(): if is_qemu_system(): # [5.8~] # 0 #DE: Divide-by-zero ... 0x0010:0xffffffff82c01030 # [~5.7] # 0 #DE: Divide-by-zero ... 0x0010:0xffffffff8178cfc0 res = gdb.execute("idtinfo -n", to_string=True) r = re.search(r"Divide-by-zero.+\S+:(\S+)\s+<", res) if r: div0_handler = int(r.group(1), 16) if is_valid_addr(div0_handler): return div0_handler elif is_vmware(): res = gdb.execute("monitor r idtr", to_string=True) r = re.search(r"idtr base=(\S+) limit=(\S+)", res) if r: base = int(r.group(1), 16) limit = int(r.group(2), 16) idt_data = read_memory(base, min(limit + 1, current_arch.ptrsize * 2 * 256)) entries = slice_unpack(idt_data, current_arch.ptrsize * 2) idt0 = IdtInfoCommand.idt_unpack(entries[0]) div0_handler = idt0.offset if is_valid_addr(div0_handler): return div0_handler elif is_kdb(): # not kgdb r = Symbol.get_symbol_by_monitor("asm_exc_divide_error") if r: return r r = Symbol.get_symbol_by_monitor("divide_error") if r: return r elif is_arm64(): # `VBAR` register has interrupt vector address vbar = get_register("$VBAR") or get_register("$VBAR_EL1") if is_valid_addr(vbar): return vbar elif is_riscv32() or is_riscv64(): # `stvec` register has interrupt vector address stvec = get_register("stvec") if is_valid_addr(stvec): return stvec return None @staticmethod @Cache.cache_this_session def get_kernel_layout(): dic = { "maps": None, "text_base": None, "text_size": None, "text_end": None, "ro_base": None, "ro_size": None, "ro_end": None, "rw_base": None, "rw_size": None, "rw_end": None, "rwx": False, "has_none": False, } Kinfo = collections.namedtuple("Kinfo", dic.keys()) if is_kdb(): # no-symbol, but monitor may be used dic["text_base"] = Symbol.get_symbol_by_monitor("_stext") dic["text_end"] = Symbol.get_symbol_by_monitor("_etext") if dic["text_base"] and dic["text_end"]: dic["text_size"] = dic["text_end"] - dic["text_base"] dic["rw_base"] = Symbol.get_symbol_by_monitor("_stext") dic["rw_end"] = Symbol.get_symbol_by_monitor("_etext") if dic["rw_base"] and dic["rw_end"]: dic["rw_size"] = dic["rw_end"] - dic["rw_base"] dic["ro_base"] = Symbol.get_symbol_by_monitor("__start_rodata") dic["ro_end"] = Symbol.get_symbol_by_monitor("__end_rodata_aligned") or \ Symbol.get_symbol_by_monitor("__end_rodata") if dic["ro_base"] and dic["ro_end"]: dic["ro_size"] = dic["ro_end"] - dic["ro_base"] dic["has_none"] = None in dic.values() return Kinfo(*dic.values()) if is_kgdb(): # use symbol dic["text_base"] = Symbol.get_ksymaddr("_stext") dic["text_end"] = Symbol.get_ksymaddr("_etext") if dic["text_base"] and dic["text_end"]: dic["text_size"] = dic["text_end"] - dic["text_base"] dic["rw_base"] = Symbol.get_ksymaddr("_stext") dic["rw_end"] = Symbol.get_ksymaddr("_etext") if dic["rw_base"] and dic["rw_end"]: dic["rw_size"] = dic["rw_end"] - dic["rw_base"] dic["ro_base"] = Symbol.get_ksymaddr("__start_rodata") dic["ro_end"] = Symbol.get_ksymaddr("__end_rodata_aligned") or \ Symbol.get_ksymaddr("__end_rodata") if dic["ro_base"] and dic["ro_end"]: dic["ro_size"] = dic["ro_end"] - dic["ro_base"] dic["has_none"] = None in dic.values() return Kinfo(*dic.values()) # Could not find the maps, so fast return dic["maps"] = Kernel.get_maps() if dic["maps"] is None: dic["has_none"] = None in dic.values() return Kinfo(*dic.values()) # 1a. search for the kernel base exact way if is_x86(): div0_handler = Kernel.get_kernel_base_hint() if div0_handler is not None: for i, (vaddr, size, _perm) in enumerate(dic["maps"]): if vaddr <= div0_handler < vaddr + size: dic["text_base"] = vaddr dic["text_size"] = size dic["text_end"] = vaddr + size text_base_map_index = i break elif is_arm64(): vbar = Kernel.get_kernel_base_hint() if vbar is not None: for i, (vaddr, size, _perm) in enumerate(dic["maps"]): if vaddr <= vbar < vaddr + size: dic["text_base"] = vaddr dic["text_size"] = size dic["text_end"] = vaddr + size text_base_map_index = i break elif is_riscv64() or is_riscv32(): stvec = Kernel.get_kernel_base_hint() if stvec is not None: for i, (vaddr, size, _perm) in enumerate(dic["maps"]): if vaddr <= stvec < vaddr + size: dic["text_base"] = vaddr dic["text_size"] = size dic["text_end"] = vaddr + size text_base_map_index = i break # 1b. search for the kernel base heuristic way if dic["text_base"] is None: # .text is usually noticeably larger than other areas. # It just determines this size heuristically and detects it, but it works well in most cases. TEXT_REGION_MIN_SIZE = 0x100000 for i, (vaddr, size, perm) in enumerate(dic["maps"]): if perm == "R-X" and size >= TEXT_REGION_MIN_SIZE: dic["text_base"] = vaddr dic["text_size"] = size dic["text_end"] = vaddr + size text_base_map_index = i break else: # not found, maybe old kernel for i, (vaddr, size, perm) in enumerate(dic["maps"]): if perm == "RWX" and size >= TEXT_REGION_MIN_SIZE: dic["text_base"] = vaddr dic["text_size"] = size dic["text_end"] = vaddr + size text_base_map_index = i break else: # Not found, so fast return dic["has_none"] = None in dic.values() return Kinfo(*dic.values()) # 2a. search for the kernel RO base # If the -enable-kvm option for qemu-system is not enabled, # there might be multiple 'r-- but non-.rodata' regions between .text and .rodata. # [ .text ] # [ .text ] # [ ??? (r--) ] # [ ??? (r--) ] # [ ??? (r--) ] # [ .rodata ] <- near the top of this area has "Linux version" # [ .rodata ] # In other words, .rodata may not be located immediately after .text. # This has been observed on qemu with Debian 11 on x86_64. # Therefore, detection based solely on location may produce incorrect results. # As a result, detection also checks for the presence of the string "Linux version" # near the beginning of the .rodata page. for i, (vaddr, size, perm) in enumerate(dic["maps"][text_base_map_index + 1:]): if perm == "R--": if dic["ro_base"] is None: if not is_valid_addr(vaddr): continue data = read_memory(vaddr, get_pagesize()) if b"Linux version" in data: dic["ro_base"] = vaddr dic["ro_size"] = size dic["ro_end"] = vaddr + size ro_base_map_index = text_base_map_index + i elif dic["ro_end"] == vaddr: # merge contiguous region. # This is important because .rodata may be split into GLOBAL and non-GLOBAL areas. dic["ro_size"] += size dic["ro_end"] += size ro_base_map_index = text_base_map_index + i else: break # 2b. search for the kernel RO base by region size # Some kernels do not have the string "Linux version" at the beginning of the .rodata. # [ .text ] # [ .text ] # [ ??? (r--) ] # [ ??? (r--) ] # [ ??? (r--) ] # [ .rodata ] <- There is no "Linux Version" near the top of this area. # [ .rodata ] <- but these .rodata total is large enough to determine .rodata. # [ .rodata ] if dic["ro_base"] is None: RO_REGION_MIN_SIZE = 0x100000 for i, (vaddr, size, perm) in enumerate(dic["maps"][text_base_map_index + 1:]): if perm == "R--": if dic["ro_base"] is None: if size >= RO_REGION_MIN_SIZE: dic["ro_base"] = vaddr dic["ro_size"] = size dic["ro_end"] = vaddr + size ro_base_map_index = text_base_map_index + i elif dic["ro_end"] == vaddr: # merge contiguous region. # This is important because .rodata may be split into GLOBAL and non-GLOBAL areas. dic["ro_size"] += size dic["ro_end"] += size ro_base_map_index = text_base_map_index + i else: break # 2c. search for the kernel RO base for old kernel # If it can not detect .rodata, maybe it is an old kernel (32-bit?). # Old kernel is no-NX, so .rodata is RWX. # Detected .text range includes .rodata, so use heuristic search and split. # [ .text ] <- maybe .text is larger than 0x8000 (it fails in certain cases if 0x7000) # [ .text ] # [ .text ] # [ .text ] <- end of this area has [0x00, 0x00, 0x00, ...] # [ .rodata ] <- near the top of this area has "Linux version" # [ .rodata ] # [ .rodata ] if dic["ro_base"] is None: dic["rwx"] = True start = dic["text_base"] + get_pagesize() * 8 end = dic["text_base"] + dic["text_size"] block_size = 0x20 zero_data = b"\0" * block_size for addr in range(start, end, get_pagesize()): data_prev = read_memory(addr - block_size, block_size) if data_prev == zero_data: data = read_memory(addr, get_pagesize()) if b"Linux version" in data: dic["ro_base"] = addr dic["ro_size"] = end - addr dic["ro_end"] = end dic["text_size"] -= dic["ro_size"] # In this case, rw_base is not detected. # This is because ksymaddr-remote appears to provide better results. dic["rw_base"] = 0 dic["rw_size"] = 0 dic["rw_end"] = 0 break else: # Not found, so fast return dic["has_none"] = None in dic.values() return Kinfo(*dic.values()) else: # 3. Search for the kernel RW base. # If ro_base can be detected, the RW area is searched starting after ro_base. # TODO: A fixed size is currently used, but better algorithms may exist. RW_REGION_MIN_SIZE = 0x20000 if dic["ro_base"] is not None: for vaddr, size, perm in dic["maps"][ro_base_map_index + 1:]: if perm == "RW-": if dic["rw_base"] is None: if size >= RW_REGION_MIN_SIZE: dic["rw_base"] = vaddr dic["rw_size"] = size dic["rw_end"] = vaddr + size break dic["has_none"] = None in dic.values() return Kinfo(*dic.values()) @staticmethod @Cache.cache_this_session def get_kernel_base(): def resolve_syms_safely(syms): for sym in syms: try: return to_unsigned_long(gdb.parse_and_eval(sym)) except gdb.error: pass return None # kdb specific if is_kdb(): return Symbol.get_symbol_by_monitor("_stext") # fast path hint = Kernel.get_kernel_base_hint() if hint: # invalid if arm32 stext = resolve_syms_safely(["_stext"]) if stext: handler = None if is_x86(): handler = resolve_syms_safely(["asm_exc_divide_error", "divide_error"]) elif is_arm64(): handler = resolve_syms_safely(["vectors"]) elif is_riscv32() or is_riscv64(): handler = resolve_syms_safely(["handle_exception"]) if handler: diff = hint - handler if diff & get_pagesize_mask_low() == 0: return stext - diff if is_kgdb(): # Kernel.get_kernel_layout is too slow, so return if not found with fast path return None # slow path kinfo = Kernel.get_kernel_layout() return kinfo.text_base class KernelVersion: def __init__(self, address, version_string, major, minor, patch): self.address = address self.version_string = version_string self.major = major self.minor = minor self.patch = patch self.version_tuple = (major, minor, patch) return def to_version_tuple(self, _v): v = _v.split(".") if len(v) == 2: return (int(v[0]), int(v[1]), 0) elif len(v) == 3: return (int(v[0]), int(v[1]), int(v[2])) raise def __ge__(self, v): return self.to_version_tuple(v) <= self.version_tuple def __gt__(self, v): return self.to_version_tuple(v) < self.version_tuple def __le__(self, v): return self.to_version_tuple(v) >= self.version_tuple def __lt__(self, v): return self.to_version_tuple(v) > self.version_tuple def __eq__(self, v): return self.to_version_tuple(v) == self.version_tuple def __ne__(self, v): return self.to_version_tuple(v) != self.version_tuple def __str__(self): return "{:d}.{:d}.{:d}".format(*self.version_tuple) @staticmethod @Cache.cache_this_session_skip_None_cache def kernel_version(): # fast path linux_banner = None if is_kdb(): linux_banner = Symbol.get_symbol_by_monitor("linux_banner") if linux_banner is None: linux_banner = Symbol.get_ksymaddr("linux_banner") if linux_banner and is_valid_addr(linux_banner): version_string = read_cstring_from_memory(linux_banner, 0x200).rstrip() r = re.search(r"Linux version (\d)\.(\d+)\.(\d+)", version_string) if r: major, minor, patch = int(r.group(1)), int(r.group(2)), int(r.group(3)) return Kernel.KernelVersion(linux_banner, version_string, major, minor, patch) # slow path kinfo = Kernel.get_kernel_layout() if kinfo.has_none: return None area = [] for addr in kinfo.maps: # resolve search range if addr[0] < kinfo.text_base: continue if kinfo.rw_base and addr[0] >= kinfo.rw_base: continue area.append([addr[0], addr[0] + addr[1]]) if area == []: return None for start, end in area: # find version string try: data = read_memory(start, end - start) except gdb.MemoryError: continue data = "".join([chr(x) for x in data]) r = re.findall(r"(Linux version (?:\d+\.[\d.]*\d)[ -~]+)", data) if not r: continue version_string = r[0] address = start + data.find(version_string) r = re.search(r"Linux version (\d)\.(\d+)\.(\d+)", version_string) major, minor, patch = int(r.group(1)), int(r.group(2)), int(r.group(3)) return Kernel.KernelVersion(address, version_string, major, minor, patch) return None @staticmethod @Cache.cache_this_session_skip_None_cache def kernel_cmdline(): saved_command_line = None if is_kdb(): saved_command_line = Symbol.get_symbol_by_monitor("saved_command_line") if saved_command_line is None: saved_command_line = KernelAddressHeuristicFinder.get_saved_command_line() if saved_command_line is None: return None try: ptr = read_int_from_memory(saved_command_line) cmdline = read_cstring_from_memory(ptr, max_length=0x1000) Kcmdline = collections.namedtuple("Kcmdline", ["address", "cmdline"]) return Kcmdline(ptr, cmdline) except Exception: return None @staticmethod @Cache.cache_this_session def get_ksysctl(sym): try: res = gdb.execute("ksysctl --quiet --no-pager --exact --filter {:s}".format(sym), to_string=True) return int(res.split()[1], 16) except (gdb.error, IndexError, ValueError): return None @staticmethod @Cache.cache_this_session def get_slab_type(): # Cases where ksymaddr-remote is not working properly if not Symbol.get_ksymaddr("commit_creds"): return "Unknown" if gdb.execute("ksymaddr-remote --quiet --no-pager slub_", to_string=True): kversion = Kernel.kernel_version() if kversion < "6.2": return "SLUB" else: # care for both deactivate_slab and deactivate_slab.cold if gdb.execute("ksymaddr-remote --quiet --no-pager deactivate_slab", to_string=True): return "SLUB" else: return "SLUB_TINY" # care for both cache_reap and cache_reap.cold if gdb.execute("ksymaddr-remote --quiet --no-pager cache_reap", to_string=True): return "SLAB" if gdb.execute("ksymaddr-remote --quiet --no-pager slob_", to_string=True): return "SLOB" return "Unknown" @staticmethod @Cache.cache_this_session def slab_page_str(): kversion = Kernel.kernel_version() if kversion < "5.17": return "page" else: return "slab" @staticmethod @Cache.cache_this_session def get_page_virt_pair(): allocator = Kernel.get_slab_type() if allocator in ["SLUB", "SLUB_TINY"]: # get valid page and vaddr pair command = {"SLUB": "slub-dump --node --skip-sheaf", "SLUB_TINY": "slub-tiny-dump"}[allocator] for n in [8, 16, 32, 64, 128, 192, 256, 512]: # this function calls slub-dump, but it called from slub-dump itself. # * get_page_virt_pair # -> slub-dump <-- first # -> page2virt # -> get_VMEMMAP_START # -> get_page_virt_pair # -> slub-dump <-- recursively # To avoid infinite recursion, must add the `--skip-page2virt` option # when calling slub-dump from page2virt. ret = gdb.execute( "{:s} --simple --no-pager --quiet --skip-page2virt kmalloc-{:d}".format(command, n), to_string=True, ) r1 = re.findall(r"(?:active|partial|node) page: (0x\S\S+)", Color.remove_color(ret)) r2 = re.findall(r"virtual address: (0x\S+|\?\?\?)", Color.remove_color(ret)) valid_pairs = [(p, v) for p, v in zip(r1, r2) if v != "???"] if valid_pairs: page = int(valid_pairs[0][0], 16) vaddr = int(valid_pairs[0][1], 16) break else: return False elif allocator == "SLAB": # get valid page and vaddr pair ret = gdb.execute("slab-dump --simple --cpu 0 --no-pager --quiet kmalloc-256", to_string=True) r1 = re.search(r"node\[\d+\]\.slabs_(?:partial|full): (0x\S+)", Color.remove_color(ret)) r2 = re.search(r"virtual address \(s_mem & ~0xfff\): (0x\S+)", Color.remove_color(ret)) if not r1 or not r2: return False page = int(r1.group(1), 16) vaddr = int(r2.group(1), 16) elif allocator == "SLOB": # get valid page and vaddr pair ret = gdb.execute("slob-dump --simple --large --no-pager --quiet", to_string=True) r1 = re.search(r"page: (0x\S+)", Color.remove_color(ret)) r2 = re.search(r"virtual address: (0x\S+)", Color.remove_color(ret)) if not r1 or not r2: return False page = int(r1.group(1), 16) vaddr = int(r2.group(1), 16) else: return False return page, vaddr @staticmethod @Cache.cache_until_next def get_slab_contains(addr, allow_unaligned=False, keep_color=False): if not is_valid_addr(addr): return None ret = gdb.execute("slab-contains --quiet {:#x}".format(addr), to_string=True).strip() if not ret: return None if not allow_unaligned and "(unaligned?)" in ret: return None if keep_color: return ret return Color.remove_color(ret) @staticmethod @Cache.cache_until_next def p2v(paddr): # return list ret = gdb.execute("p2v {:#x}".format(paddr), to_string=True) return [int(x, 16) for x in re.findall(r"Phys: \S+ -> Virt: (\S+)", ret)] @staticmethod @Cache.cache_until_next def v2p(vaddr): # v2p is slow since it needs maps parsing for each time. # more faster using gva2gpa if available. try: ret = gdb.execute("monitor gva2gpa {:#x}".format(vaddr), to_string=True) r = re.search(r"gpa: (0x\S+)", ret) if r: return int(r.group(1), 16) except gdb.error: pass ret = gdb.execute("v2p {:#x}".format(vaddr), to_string=True) r = re.search(r"Virt: 0x\S+ -> Phys: (0x\S+)", ret) if r: return int(r.group(1), 16) return None @staticmethod def page2virt(page): ret = gdb.execute("page2virt {:#x}".format(page), to_string=True) r = re.search(r"Virt: (\S+)", ret) if r: return int(r.group(1), 16) return None @staticmethod def virt2page(virt): ret = gdb.execute("virt2page {:#x}".format(virt), to_string=True) r = re.search(r"Page: (\S+)", ret) if r: return int(r.group(1), 16) return None @register_command class KernelbaseCommand(GenericCommand): """Display kernel base address.""" _cmdline_ = "kbase" _category_ = "06-c. Qemu-system/KGDB Cooperation - Linux Basic" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64", "RISCV32", "RISCV64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): if args.rescan: Cache.reset_gef_caches(all=True) # resolve text_base, ro_base self.quiet_info("Wait for memory scan") kinfo = Kernel.get_kernel_layout() self.out = [] if kinfo.text_base: self.out.append("kernel text: {:#x}-{:#x} ({:#x} bytes)".format(kinfo.text_base, kinfo.text_end, kinfo.text_size)) gdb.execute(f"set $kbase = {kinfo.text_base:#x}") else: err("Failed to resolve kernel text") if kinfo.ro_base: self.out.append("kernel rodata: {:#x}-{:#x} ({:#x} bytes)".format(kinfo.ro_base, kinfo.ro_end, kinfo.ro_size)) gdb.execute(f"set $kro_base = {kinfo.ro_base:#x}") else: err("Failed to resolve kernel rodata") if kinfo.rw_base: self.out.append("kernel data: {:#x}-{:#x} ({:#x} bytes)".format(kinfo.rw_base, kinfo.rw_end, kinfo.rw_size)) gdb.execute(f"set $kdata_base = {kinfo.rw_base:#x}") else: err("Failed to resolve kernel data") if self.out: gef_print("\n".join(self.out)) return @register_command class KernelVersionCommand(GenericCommand): """Display kernel version string.""" _cmdline_ = "kversion" _category_ = "06-c. Qemu-system/KGDB Cooperation - Linux Basic" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64", "RISCV32", "RISCV64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): if args.rescan: Cache.reset_gef_caches(all=True) self.quiet_info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion is None: self.quiet_err("Failed to resolve") return self.out = [] self.out.append("{:#x}: {:s}".format(kversion.address, kversion.version_string)) if self.out: gef_print("\n".join(self.out)) return @register_command class KernelCmdlineCommand(GenericCommand): """Display kernel command-line string.""" _cmdline_ = "kcmdline" _category_ = "06-c. Qemu-system/KGDB Cooperation - Linux Basic" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): if args.rescan: Cache.reset_gef_caches(all=True) self.quiet_info("Wait for memory scan") kcmdline = Kernel.kernel_cmdline() if kcmdline is None: self.quiet_err("Failed to resolve") return self.out = [] self.out.append("{:#x}: '{:s}'".format(kcmdline.address, kcmdline.cmdline)) if self.out: gef_print("\n".join(self.out)) return @register_command class KernelCurrentCommand(GenericCommand): """Display current task.""" _cmdline_ = "kcurrent" _category_ = "06-c. Qemu-system/KGDB Cooperation - Linux Basic" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.__per_cpu_offset = None self.cpu_offset = None self.offset_comm = None return @staticmethod def get_each_cpu_offset(__per_cpu_offset): """ Note that the number of CPUs and the number of threads may not match. x64 example: len(gdb.selected_inferior().threads()) == 2; but __per_cpu_offset entry is 1 0xffffffff93980680|+0x0000|+000: 0xffff9724c7800000 -> 0x0000000000000000 0xffffffff93980688|+0x0008|+001: 0xffffffff93d0d000 0xffffffff93980690|+0x0010|+002: 0xffffffff93d0d000 Therefore, when the same address is repeated, it is considered to be the end. """ cpu_offset = [] i = 0 while True: off = read_int_from_memory(__per_cpu_offset + i * current_arch.ptrsize) """ off itself may refer to inaccessible memory. x86 example: __per_cpu_offset: 0xc6a27440 0xc6a27440|+0x0000|+000: 0x2d849000 -> inaccessible 0xc6a27444|+0x0004|+001: 0x00000000 """ if (off <= 0x10) or (off & 0xf): break if len(cpu_offset) >= 1 and off == cpu_offset[-1]: cpu_offset.pop() # remove last one break cpu_offset.append(off) i += 1 return cpu_offset def get_cpu_offset(self): # use cache if self.cpu_offset: self.quiet_info("__per_cpu_offset: {:#x}".format(self.__per_cpu_offset)) self.quiet_info("Num of cpu: {:d} (guessed)".format(len(self.cpu_offset))) return self.cpu_offset # resolve __per_cpu_offset __per_cpu_offset = KernelAddressHeuristicFinder.get_per_cpu_offset() # not found if __per_cpu_offset is None: self.quiet_err("Failed to resolve `__per_cpu_offset`") return None # found self.quiet_info("__per_cpu_offset: {:#x}".format(__per_cpu_offset)) self.__per_cpu_offset = __per_cpu_offset self.cpu_offset = KernelCurrentCommand.get_each_cpu_offset(__per_cpu_offset) self.quiet_info("Num of cpu: {:d} (guessed)".format(len(self.cpu_offset))) return self.cpu_offset def get_comm_str(self, task_addr): if self.offset_comm is None: ret = gdb.execute("ktask --no-pager --meta", to_string=True) r = re.search(r"offsetof\(task_struct, comm\): (0x\S+)", ret) if r is not None: self.offset_comm = int(r.group(1), 16) else: self.quiet_err("ktask is failed") self.offset_comm = False if self.offset_comm is False: return "???" comm = read_cstring_from_memory(task_addr + self.offset_comm) return comm or "???" def dump_current_arm(self): orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() threads = gdb.selected_inferior().threads() threads = sorted(threads, key=lambda th: th.num) for thread in threads: thread.switch() # change thread task = KernelAddressHeuristicFinder.get_current_task_for_current_thread() if task is None: continue if is_valid_addr(task): cpu_num = thread.num - 1 # ? gef_print("current (cpu{:d}): {:#x} {:s}".format(cpu_num, task, self.get_comm_str(task))) orig_thread.switch() # revert thread orig_frame.select() return def dump_current_x86(self): current_task = KernelAddressHeuristicFinder.get_current_task() if not current_task: self.quiet_err("Failed to resolve `current_task`") return cpu_bases = self.get_cpu_offset() if cpu_bases: # pattern 1. Offset from __per_cpu_offset. self.quiet_info("current_task: {:#x}".format(current_task)) task_offset = current_task for i, cpu_base in enumerate(cpu_bases): task = read_int_from_memory(AddressUtil.align_address(cpu_base + task_offset)) if not is_valid_addr(task): break gef_print("current (cpu{:d}): {:#x} {:s}".format(i, task, self.get_comm_str(task))) else: # pattern 2: current_task is the address that stores a pointer to the current task (not per_cpu). self.quiet_info("__per_cpu_offset is unused") task = read_int_from_memory(current_task) if is_valid_addr(task): gef_print("current: {:#x} {:s}".format(task, self.get_comm_str(task))) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") if is_arm32() or is_arm64(): self.dump_current_arm() elif is_x86(): self.dump_current_x86() return @register_command class KernelTaskCommand(GenericCommand, BufferingOutput): """Display process list.""" _cmdline_ = "ktask" _category_ = "06-f. Qemu-system/KGDB Cooperation - Linux Task" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="comm string REGEXP filter.") parser.add_argument("-T", "--task-filter", action="append", type=AddressUtil.parse_address, default=[], help="task address filter.") parser.add_argument("-m", "--print-maps", action="store_true", help="print memory map for each user-land process.") parser.add_argument("-r", "--print-regs", action="store_true", help="print general registers saved on kstack for each user-land process.") parser.add_argument("-i", "--print-all-id", action="store_true", help="print suid, sgid, euid, egid, fsuid and fsgid.") parser.add_argument("-t", "--print-thread", action="store_true", help="display by thread (LWP), not by process.") parser.add_argument("-F", "--print-fd", action="store_true", help="print file descriptors for each user process.") parser.add_argument("-s", "--print-sighand", action="store_true", help="print signal handlers for each user process.") parser.add_argument("-S", "--print-seccomp", action="store_true", help="dump the seccomp filter. If the tool is available, it dumps orig_prog; otherwise, it disassembles bpf_func.") parser.add_argument("-N", "--print-namespace", action="store_true", help="print namespaces for each user process.") parser.add_argument("-u", "--user-process-only", action="store_true", help="display user-land process (+ thread) only.") parser.add_argument("--init-task", type=AddressUtil.parse_address, help="specifies the address of init_task.") parser.add_argument("--meta", action="store_true", help="display offset information.") parser.add_argument("--all", action="store_true", help="enable all option.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -q", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "", "Simplified task_struct structure:", "", " +-init_task-+", " | list_head |---+ +-->+-kstack----------+ +--->+-vm_area_struct--+", " +-----------+ | | | (thread_info) | | | vm_start |", " | | | STACK_END_MAGIC | | | vm_end |", "+-------------------+ | | ... | | | vm_next (~6.1) |", "| | | ... | | | ... |", "| +-task_struct---+ | | ... | | | vm_flags |", "| | (thread_info) | | | ... | | | vm_file |-----+", "| | ... | | | pt_regs | | | ... | |", "| | stack |----+ +-----------------+ | +-----------------+ |", "| | ... | | |", "+-->| tasks |-->... +---------+<------------------------+ |", " | ... | | | |", " | mm |-->+-mm_struct----+ | +-------->+-maple_node(6.1~)--+ | |", " | ... | | mmap (~6.1) |--+ | | ... | | |", " | pid | | ... | | | mr64|ma64|alloc | | |", " | tid | | mm_mt (6.1~) | | | ... | | |", " | ... | | ma_root |-----+ | slot[] |--+ |", " | stack_canary | | ... | +-------------------+ |", " | ... | +--------------+ |", " | group_leader | +------------------+ +-mount----------+", " | ... | +-->+-cred--------------+ | | ... |", " | thread_group |-->... | | ... | | | mnt_parent |-->mount", " | ... | | | uid, gid | | | mnt_mountpoint |-->dentry", " | cred |---------+ | suid, sgid | | +->| mnt (vfsmount) |", " | ... | | euid, egid | | | | mnt_root |-->dentry", " | comm[16] | | fsuid, fsgid | | | | ... |", " | ... | | ..., user_ns, ... | | | | ... |", " | files |--+ +-------------------+ | | +----------------+", " | ... | | | |", " | nsproxy |------->+-nsproxy----------------+ | | +--->+-dentry-----+", " | ... | | | count | | | | | ... |", " | sighand |-----+ | uts_ns, ipc_ns, mnt_ns | | | | | d_parent |-->dentry", " | ... | | | | pid_ns_for_children | | | | | ... |", " | seccomp | | | | net_ns, time_ns, ... | | | | | d_inode |--+", " | ... | | | +------------------------+ | | | | d_iname | |", " +---------------+ | | | | | | ... | |", " | +->+-sighand_struct----+ | | | +------------+ |", " | | ... | v | | |", " | | action[64] | +-->+-file-----------+ | | +------------------+", "+----------------------+ +-------------------+ | | ... | | | |", "| | | f_path | | | v", "+-->+-files_struct-+ +-->+-fdtable---+ +-->+-file*[]-----+ | | mnt |--+ | +->+-inode------+", " | ... | | | max_fds | | | [0] |--+ | dentry |----+ | | ... |", " | fdt |--+ | fd |--+ | ... | | f_inode (3.9~) |------+ | i_ino |", " | ... | | ... | | [max_fds-1] | | ... | | ... |", " +--------------+ +-----------+ +-------------+ +----------------+ +------------+", "", "This command will only track tasks that can be tracked from `init_task` or the result of `kcurrent` command.", "Other tasks (such as `swapper/1` if thread 1 is running some task) will not be detected.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__() # task_struct self.offset_tasks = None self.offset_mm = None self.offset_stack = None self.offset_pid = None self.offset_kcanary = None self.offset_group_leader = None self.offset_thread_group = None self.offset_comm = None self.offset_cred = None self.offset_files = None self.offset_sighand = None self.offset_nsproxy = None self.offset_signal = None self.offset_seccomp = None # files_struct self.offset_fdt = None # kstack self.offset_ptregs = None # cred self.offset_uid = None self.offset_user_ns = None # vm_area_struct self.offset_vm_mm = None self.offset_vm_flags = None self.offset_vm_file = None # file self.offset_mnt = None self.offset_dentry = None # dentry self.offset_d_iname = None self.offset_d_parent = None self.offset_d_inode = None # inode self.offset_i_ino = None # signal self.offset_thread_head = None # sighand_struct self.offset_action = None self.sizeof_action = None # seccomp_filter self.offset_prev = None self.offset_prog = None # bpf_prog self.offset_bpf_func = None self.offset_orig_prog = None return def get_offset_tasks(self, init_task): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).tasks") ) except gdb.error: pass # slow path # search for init_task->tasks for i in range(0x200): offset_tasks = current_arch.ptrsize * i if is_double_link_list(init_task + offset_tasks, min_len=5): return offset_tasks return None def get_task_list(self, init_task, offset_tasks): pos = init_task + offset_tasks task_list = [pos] # validating candidate offset while True: try: pos = read_int_from_memory(pos) except gdb.MemoryError: return None if pos in task_list: break task_list.append(pos) return [x - offset_tasks for x in task_list] def get_offset_mm(self, task_addr, offset_tasks): """ struct task_struct { ... struct list_head tasks; #ifdef CONFIG_SMP struct plist_node { int prio; struct list_head prio_list; struct list_head node_list; } pushable_tasks; struct rb_node { unsigned long __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; } pushable_dl_tasks; // v3.14~ #endif struct mm_struct *mm; struct mm_struct *active_mm; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).mm") ) except gdb.error: pass # slow path kversion = Kernel.kernel_version() if kversion is None: return None if kversion < "6.17": offset_mm = offset_tasks + 2 * current_arch.ptrsize r = read_int_from_memory(task_addr + offset_mm) if 0 < r <= 0xffff: # maybe prio, so CONFIG_SMP is y if "3.14" <= kversion: offset_mm = offset_tasks + 10 * current_arch.ptrsize else: offset_mm = offset_tasks + 7 * current_arch.ptrsize else: offset_mm = offset_tasks + 10 * current_arch.ptrsize return offset_mm def get_offset_comm(self, task_addrs): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).comm") ) except gdb.error: pass # slow path for i in range(0x300): offset_comm = i * current_arch.ptrsize valid = True for task in task_addrs: if not is_ascii_string(task + offset_comm): valid = False break s = read_cstring_from_memory(task + offset_comm) # very common name, so for speeding up, we assume that offset is found if s == "swapper/0": break if len(s) < 2: valid = False break if valid: return offset_comm return None def get_offset_cred(self, task_addrs, offset_comm): """ struct task_struct { ... const struct cred __rcu *real_cred; // These may point to the same address const struct cred __rcu *cred; // These may point to the same address #ifdef CONFIG_KEYS struct key *cached_requested_key; #endif char comm[TASK_COMM_LEN]; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).cred") ) except gdb.error: pass # slow path # backward search from `comm` for i in range(0x2): offset_cred = offset_comm - ((i + 1) * current_arch.ptrsize) for task in task_addrs: val1 = read_int_from_memory(task + offset_cred) val2 = read_int_from_memory(task + offset_cred - current_arch.ptrsize) if val1 == val2 and val1 != 0: return offset_cred return None def get_offset_stack(self, task_addrs): """ struct task_struct { ... #ifdef CONFIG_THREAD_INFO_IN_TASK struct thread_info thread_info; #endif volatile long state; void *stack; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).stack") ) except gdb.error: pass # slow path for i in range(0x100): found = False zcount = 0 for task in task_addrs: v = read_int_from_memory(task + current_arch.ptrsize * i) if v == 0: zcount += 1 continue if (v & 0x1fff) != 0: break if not is_valid_addr(v): break else: found = True # For unknown reasons, processes without a stack are sometimes observed. # 0xffffa122c17db000 U 106 chal.sh ... 0xffffa43100218000 0xd16ef01535a35500 # 0xffffa122c17da000 U 108 socat ... 0xffffa43100278000 0x3d378b9e6f59bf00 # 0xffffa122c17dc000 U 109 chal ... 0xffffa431002d4000 0x277c67d5e5854500 # 0xffffa122c17dd000 K 110 3 ... 0x0000000000000000 0x22a999743f180500 <-- here # Therefore, a small number of NULL pointers are allowed. if zcount > len(task_addrs) // 10: found = False if found is False: continue offset_stack = current_arch.ptrsize * i return offset_stack return None def get_thread_info(self, task_addr, offset_stack): # fast path try: return task_addr + to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).thread_info") ) except gdb.error: try: # task_struct exists but has no thread_info member gdb.parse_and_eval("(struct task_struct*)0") return read_int_from_memory(task_addr + offset_stack) except gdb.error: pass # slow path kstack = read_int_from_memory(task_addr + offset_stack) if not is_valid_addr(kstack): return None stack_top_val = read_int32_from_memory(kstack) if stack_top_val == 0x57ac6e9d: # STACK_END_MAGIC """ struct task_struct { #ifdef CONFIG_THREAD_INFO_IN_TASK struct thread_info thread_info; #endif ... """ return task_addr # CONFIG_THREAD_INFO_IN_TASK=y else: return kstack # CONFIG_THREAD_INFO_IN_TASK=n def has_seccomp(self, task_addr): thread_info = self.get_thread_info(task_addr, self.offset_stack) if thread_info is None: return None kversion = Kernel.kernel_version() if kversion is None: return None if is_x86(): if "5.11" <= kversion: syscall_work = read_int_from_memory(thread_info + current_arch.ptrsize) return bool(syscall_work & (1 << 0)) # SYSCALL_WORK_SECCOMP elif "4.9" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 8)) # TIF_SECCOMP elif "4.1" <= kversion: flags = read_int32_from_memory(thread_info + current_arch.ptrsize) return bool(flags & (1 << 8)) # TIF_SECCOMP else: flags = read_int32_from_memory(thread_info + current_arch.ptrsize * 2) return bool(flags & (1 << 8)) # TIF_SECCOMP elif is_arm32(): if "6.0" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 23)) # TIF_SECCOMP elif "5.16" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 7)) # TIF_SECCOMP elif "5.15" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 23)) # TIF_SECCOMP elif "5.11" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 7)) # TIF_SECCOMP elif "5.10" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 23)) # TIF_SECCOMP elif "4.3" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 7)) # TIF_SECCOMP elif "3.8" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 11)) # TIF_SECCOMP else: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 21)) # TIF_SECCOMP elif is_arm64(): if "3.16" <= kversion: flags = read_int_from_memory(thread_info) return bool(flags & (1 << 11)) # TIF_SECCOMP else: # unimplemented return None return None def get_offset_ptregs(self, task_addrs, offset_stack): # calc kstack address pattern kstacks_raw = [] for task in task_addrs: kstack = read_int_from_memory(task + offset_stack) if kstack == 0: continue kstacks_raw.append(kstack) # calc kstack size kstacks = sorted({x & 0xffff for x in kstacks_raw}) # uniq and sort diffs = [] for i in range(len(kstacks) - 1): diff = kstacks[i + 1] - kstacks[i] diffs.append(diff) if len(diffs) == 0: kstack_size = get_pagesize() * 2 else: kstack_size = min(diffs) # check while kstack_size >= 0x2000: for kstack in kstacks_raw: if not is_valid_addr(kstack + kstack_size - current_arch.ptrsize): kstack_size //= 2 break # for, then retry while else: break # while if is_x86_64(): """ struct pt_regs { unsigned long r15; unsigned long r14; unsigned long r13; unsigned long r12; unsigned long rbp; unsigned long rbx; unsigned long r11; unsigned long r10; unsigned long r9; unsigned long r8; unsigned long rax; unsigned long rcx; unsigned long rdx; unsigned long rsi; unsigned long rdi; unsigned long orig_rax; unsigned long rip; unsigned long cs; unsigned long eflags; unsigned long rsp; unsigned long ss; }; """ ptregs_size = current_arch.ptrsize * 21 # Sometimes register values are stored a short distance away from the bottom of the kstack. # It is unclear whether this depends on the kernel version. # In 6.10.11 it was at offset 0, and in 6.10.0-rc2 it was at offset 16. # For this reason, dynamic detection is used. # TODO: Dynamic detection may also be necessary for x86, ARM, and ARM64. for i in range(8): init_process_kstack = read_int_from_memory(task_addrs[1] + offset_stack) init_process_kstack_end = init_process_kstack + kstack_size v = read_int_from_memory(init_process_kstack_end - current_arch.ptrsize * (i + 1)) if v == 0x2b: # ss segment default value bottom_offset = current_arch.ptrsize * i break else: bottom_offset = 0 elif is_x86_32(): """ struct pt_regs { long ebx; long ecx; long edx; long esi; long edi; long ebp; long eax; int xds; int xes; int xfs; int xgs; long orig_eax; long eip; int xcs; long eflags; long esp; int xss; }; """ ptregs_size = current_arch.ptrsize * 17 bottom_offset = current_arch.ptrsize * 2 # ? elif is_arm64(): """ struct pt_regs { u64 regs[31]; u64 sp; u64 pc; u64 pstate; u64 orig_x0; u64 syscallno; u64 orig_addr_limit; u64 pmr_save; u64 stackframe[2]; u64 lockdep_hardirqs; u64 exit_rcu; }; """ ptregs_size = current_arch.ptrsize * 35 bottom_offset = current_arch.ptrsize * 7 elif is_arm32(): """ struct pt_regs { unsigned long uregs[18]; }; """ ptregs_size = current_arch.ptrsize * 18 bottom_offset = current_arch.ptrsize * 2 # ? else: return None offset_ptregs = kstack_size - ptregs_size - bottom_offset return kstack_size, offset_ptregs def get_regs(self, kstack, offset_ptregs): if is_x86_64(): regs_name = [ "r15", "r14", "r13", "r12", "rbp", "rbx", "r11", "r10", "r9", "r8", "rax", "rcx", "rdx", "rsi", "rdi", "orig_rax", "rip", "cs", "eflags", "rsp", "ss", ] elif is_x86_32(): regs_name = [ "ebx", "ecx", "edx", "esi", "edi", "ebp", "eax", "ds", "es", "fs", "gs", "orig_eax", "eip", "cs", "eflags", "esp", "ss", ] elif is_arm64(): regs_name = [ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp", "pc", "pstate", "orig_x0", ] elif is_arm32(): regs_name = [ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", "cpsr", "orig_r0", ] ptregs_addr = kstack + offset_ptregs ptregs_size = len(regs_name) * current_arch.ptrsize regs_data = read_memory(ptregs_addr, ptregs_size) # maybe kernel thread if is_x86(): if regs_data == b"\0" * len(regs_data): return None elif is_arm32(): kernel_thread_regs = p32(0) * (len(regs_name) - 2) + p32(0x13) + p32(0) if regs_data == kernel_thread_regs: return None elif is_arm64(): kernel_thread_regs = p64(0) * (len(regs_name) - 2) + p64(0x5) + p64(0) if regs_data == kernel_thread_regs: return None # get regs value regs_data = slice_unpack(regs_data, current_arch.ptrsize) regs = {} for name, value in zip(regs_name, regs_data): regs[name] = value return regs def get_offset_pid(self, task_addrs): """ struct task_struct { ... pid_t pid; // int pid_t tgid; // int ... }; 0xc6d1e888: 0xc6d1f048 0xc6d1c1c8 0x0000008c 0xc6d1e894 0xc6d1e898: 0xc6d1e894 0xc6d1e89c 0xc6d1e89c 0xc6d1e8a4 0xc6d1e8a8: 0x00000000 0x00000000 0xc7830660 0xc7830660 0xc6d1e8b8: 0x00000008 0x00000000 0xc6d51300 0x00000000 0xc6d1e8c8: 0x00000000 0xc6d514e0 0x00000017 0x000000a9 0xc6d1e8d8: 0x00000005 0x00000000 0x00000000 0x00000000 0xc6d1e8e8: 0x00000000 0x00000011 0x00000000 0x00000000 0xc6d1e8f8: 0x00000000 0x00000000 0x00000000 0x00000000 0xc6d1e908: 0xc245a8d0 0x00000000 0x00000000 0x00000000 0xc6d1e918: 0x00000000 0x00000000 0x00000000 0x00000000 0xc6d1e928: 0x00000031* 0x00000031 0xc7834000 0xc7834000 """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).pid") ) except gdb.error: pass # slow path pid_max = 0x400000 if is_64bit() else 0x8000 for i in range(0x400): found = False seen_pid = [] # swapper/0 has pid 0. Don't use it as it will cause false positives. for j, task in enumerate(task_addrs[1:]): v1 = read_int32_from_memory(task + (i + 0) * 4) v2 = read_int32_from_memory(task + (i + 1) * 4) if j == 0 and v1 != 1: # init process has always 1 break if v1 == 0 or pid_max < v1: # pid is 1 ~ pid_max break if v2 == 0 or pid_max < v2: # tgid is 1 ~ pid_max break if v1 in seen_pid: break seen_pid.append(v1) else: found = True if found is False: continue offset_pid = i * 4 return offset_pid return None def get_offset_canary(self, task_addrs, offset_pid): """ struct task_struct { ... pid_t pid; pid_t tgid; #ifdef CONFIG_STACKPROTECTOR unsigned long stack_canary; #endif struct task_struct __rcu *real_parent; struct task_struct __rcu *parent; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).stack_canary") ) except gdb.error: try: # task_struct exists but has no stack_canary member gdb.parse_and_eval("(struct task_struct*)0") return None except gdb.error: pass # slow path kversion = Kernel.kernel_version() if kversion is None: return None offset_stack_canary = align_to_ptrsize(offset_pid + 4 + 4) found = True for task in task_addrs: v1 = read_int_from_memory(task + offset_stack_canary) v2 = read_int_from_memory(task + offset_stack_canary + current_arch.ptrsize) if v1 == v2: # stack canary != real_parent found = False break if kversion and "4.13" <= kversion: if is_64bit() and (v1 & 0xff) != 0: # 32-bit canary does not have 0xXXXXXX00 found = False break if found: return offset_stack_canary return None def get_offset_group_leader(self, offset_pid, offset_kcanary): """ struct task_struct { ... pid_t pid; pid_t tgid; #ifdef CONFIG_STACKPROTECTOR unsigned long stack_canary; #endif struct task_struct __rcu *real_parent; struct task_struct __rcu *parent; struct list_head children; struct list_head sibling; struct task_struct *group_leader; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).group_leader") ) except gdb.error: pass # slow path if offset_kcanary is None: offset_real_parent = align_to_ptrsize(offset_pid + 4 + 4) else: offset_real_parent = offset_kcanary + current_arch.ptrsize offset_group_leader = offset_real_parent + current_arch.ptrsize * (1 + 1 + 2 + 2) return offset_group_leader def get_offset_thread_group(self, offset_group_leader): """ struct task_struct { ... struct task_struct *group_leader; struct list_head ptraced; struct list_head ptrace_entry; struct pid *thread_pid; // v4.19~ struct hlist_node pid_links[4]; // v4.19~ struct pid_link pids[3]; // ~v4.18 struct list_head thread_group; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).thread_group") ) except gdb.error: pass # slow path kversion = Kernel.kernel_version() if kversion is None: return None if "4.19" <= kversion: offset_thread_group = offset_group_leader + current_arch.ptrsize * (1 + 2 + 2 + 1 + (2 * 4)) else: offset_thread_group = offset_group_leader + current_arch.ptrsize * (1 + 2 + 2 + (3 * 3)) return offset_thread_group def get_offset_signal(self, offset_nsproxy): """ struct task_struct { ... struct nsproxy *nsproxy; struct signal_struct *signal; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).signal") ) except gdb.error: pass # slow path return offset_nsproxy + current_arch.ptrsize def get_offset_seccomp(self, task_addrs, offset_signal): """ struct task_struct { ... struct signal_struct *signal; struct sighand_struct __rcu *sighand; sigset_t blocked; sigset_t real_blocked; sigset_t saved_sigmask; struct sigpending { struct list_head list; sigset_t signal; } pending; unsigned long sas_ss_sp; size_t sas_ss_size; unsigned int sas_ss_flags; struct callback_head *task_works; #ifdef CONFIG_AUDIT #ifdef CONFIG_AUDITSYSCALL struct audit_context *audit_context; #endif kuid_t loginuid; unsigned int sessionid; #endif struct seccomp { int mode; atomic_t filter_count; struct seccomp_filter *filter; } seccomp; ... } """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).seccomp") ) except gdb.error: pass # slow path # search for seccomped process for task in task_addrs: if self.has_seccomp(task): seccomped_task = task break else: # Not found return None """ 0xffff99353fe721d8|+0x0000|+000: 0xffff99353fe6e600 <- &task_struct.signal 0xffff99353fe721e0|+0x0008|+001: 0x0000000000004002 0xffff99353fe721e8|+0x0010|+002: 0x0000000000000000 0xffff99353fe721f0|+0x0018|+003: 0x0000000000000000 0xffff99353fe721f8|+0x0020|+004: 0xffff99353fe721f8 <- &task_struct.pending.list 0xffff99353fe72200|+0x0028|+005: 0xffff99353fe721f8 0xffff99353fe72208|+0x0030|+006: 0x0000000000000000 0xffff99353fe72210|+0x0038|+007: 0x0000000000000000 0xffff99353fe72218|+0x0040|+008: 0x0000000000000000 0xffff99353fe72220|+0x0048|+009: 0x0000000000000002 0xffff99353fe72228|+0x0050|+010: 0x0000000000000000 0xffff99353fe72230|+0x0058|+011: 0x0000000000000000 0xffff99353fe72238|+0x0060|+012: 0xffffffffffffffff 0xffff99353fe72240|+0x0068|+013: 0x0000002300000002 <- &task_struct.seccomp 0xffff99353fe72248|+0x0070|+014: 0xffff9934c39e2300 <- &task_struct.seccomp.filter 0xffff99353fe72250|+0x0078|+015: 0x0000000000000003 0xffff99353fe72258|+0x0080|+016: 0x0000000000000004 """ # search for sigpending base = offset_signal + current_arch.ptrsize for i in range(0x100): if is_double_link_list(seccomped_task + base + current_arch.ptrsize * i): base += current_arch.ptrsize * i * 2 break else: # Could not find sigpending return None # search for seccomp for i in range(0x100): offset_filter = base + current_arch.ptrsize * i filt = read_int_from_memory(seccomped_task + offset_filter) if not is_valid_addr(filt): continue mode = read_int32_from_memory(seccomped_task + offset_filter - 4 * 2) filtcnt = read_int32_from_memory(seccomped_task + offset_filter - 4) """ #define SECCOMP_MODE_DISABLED 0 #define SECCOMP_MODE_STRICT 1 #define SECCOMP_MODE_FILTER 2 """ if mode == 0 or filtcnt == 0: continue offset_seccomp = offset_filter - 4 * 2 return offset_seccomp return None def get_offset_prev(self, task_addrs, offset_seccomp): """ struct seccomp_filter { refcount_t refs; // v5.9~ refcount_t users; // v5.9~ refcount_t usage; // ~v5.8 bool log; // v4.14~ bool wait_killable_recv; // v5.19~ struct action_cache cache; // v5.11~ struct seccomp_filter *prev; struct bpf_prog *prog; struct notification *notif; // v5.0~ struct mutex notify_lock; // v5.0~ wait_queue_head_t wqh; // v5.9~ }; [Example x64; v6.12.3] 0xffff976901e9a300|+0x0000|+000: 0x0000000100000001 // refs, users 0xffff976901e9a308|+0x0008|+001: 0x0000000000000000 // log, wait_killable_recv 0xffff976901e9a310|+0x0010|+002: 0x1000000000000007 // cache 0xffff976901e9a318|+0x0018|+003: 0x0000000000000000 // ... 0xffff976901e9a320|+0x0020|+004: 0x0000000000000000 0xffff976901e9a328|+0x0028|+005: 0x0000008000000000 0xffff976901e9a330|+0x0030|+006: 0x0000000000000000 0xffff976901e9a338|+0x0038|+007: 0x0000000000000000 0xffff976901e9a340|+0x0040|+008: 0x0000000000000000 0xffff976901e9a348|+0x0048|+009: 0xffffffffffff8000 0xffff976901e9a350|+0x0050|+010: 0x0000000000000000 0xffff976901e9a358|+0x0058|+011: 0x0000000000000000 0xffff976901e9a360|+0x0060|+012: 0x0000000000000000 0xffff976901e9a368|+0x0068|+013: 0x0000000000000000 0xffff976901e9a370|+0x0070|+014: 0x0000000000000000 0xffff976901e9a378|+0x0078|+015: 0x0000000000000000 0xffff976901e9a380|+0x0080|+016: 0x0000000000000000 // ... 0xffff976901e9a388|+0x0088|+017: 0xffffffffffff8000 // cache 0xffff976901e9a390|+0x0090|+018: 0x0000000000000000 // prev 0xffff976901e9a398|+0x0098|+019: 0xffffaf7c0008d000 -> 0x0000000000030001 // bpf_prog 0xffff976901e9a3a0|+0x00a0|+020: 0x0000000000000000 0xffff976901e9a3a8|+0x00a8|+021: 0x0000000000000000 0xffff976901e9a3b0|+0x00b0|+022: 0x0000000000000000 0xffff976901e9a3b8|+0x00b8|+023: 0xffff976901e9a3b8 -> [loop detected] 0xffff976901e9a3c0|+0x00c0|+024: 0xffff976901e9a3b8 -> [loop detected] [Example x64; v5.10.0] 0xffff8c1f827f39c0|+0x0000|+000: 0x0000000100000001 // refs, users 0xffff8c1f827f39c8|+0x0008|+001: 0x0000000000000000 // log 0xffff8c1f827f39d0|+0x0010|+002: 0xffff8c1f827f3780 -> 0x0000000100000001 // prev 0xffff8c1f827f39d8|+0x0018|+003: 0xffffa1b3c032b000 -> 0x0000000000030001 // bpf_prog 0xffff8c1f827f39e0|+0x0020|+004: 0x0000000000000000 0xffff8c1f827f39e8|+0x0028|+005: 0x0000000000000000 0xffff8c1f827f39f0|+0x0030|+006: 0x0000000000000000 0xffff8c1f827f39f8|+0x0038|+007: 0xffff8c1f827f39f8 -> [loop detected] 0xffff8c1f827f3a00|+0x0040|+008: 0xffff8c1f827f39f8 -> [loop detected] """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct seccomp_filter*)0).prev") ) except gdb.error: pass # slow path if offset_seccomp is None: return None for task in task_addrs: if not self.has_seccomp(task): continue mode = read_int32_from_memory(task + self.offset_seccomp) if mode != 2: # SECCOMP_MODE_FILTER continue filter_count = read_int32_from_memory(task + self.offset_seccomp + 4) if filter_count == 0: continue # something is wrong filter_ = read_int_from_memory(task + self.offset_seccomp + 4 + 4) for i in range(0x100): # prev x = read_int_from_memory(filter_ + current_arch.ptrsize * i) if (x & 0x7) or (x != 0 and not is_valid_addr(x)): # must be aligned or NULL continue # prog y = read_int_from_memory(filter_ + current_arch.ptrsize * (i + 1)) if (y & 0xfff) or not is_valid_addr(y): # must be page aligned continue bpf_prog = read_int_from_memory(y) if is_valid_addr(bpf_prog): # not address continue return current_arch.ptrsize * i return None def get_offset_prog(self, offset_prev): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct seccomp_filter*)0).prog") ) except gdb.error: pass # slow path if offset_prev is None: return None kversion = Kernel.kernel_version() if kversion is None: return None if kversion < "3.16": return None return offset_prev + current_arch.ptrsize def get_offset_bpf_func(self, task_addrs, offset_seccomp, offset_prog): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct bpf_prog*)0).bpf_func") ) except gdb.error: pass # slow path if offset_seccomp is None: return None if offset_prog is None: return None def is_executable(x): maps = Kernel.get_maps() for start, size, perm in maps: if start <= x and x < start + size: return perm.endswith("X") return False for task in task_addrs: if not self.has_seccomp(task): continue filter_ = read_int_from_memory(task + offset_seccomp + 4 + 4) bpf_prog = read_int_from_memory(filter_ + offset_prog) for i in range(0x100): x = read_int_from_memory(bpf_prog + current_arch.ptrsize * i) if is_valid_addr(x) and is_executable(x): if read_int_from_memory(x) == 0: # something is wrong continue return current_arch.ptrsize * i return None def get_offset_orig_prog(self, offset_bpf_func): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct bpf_prog*)0).orig_prog") ) except gdb.error: pass # slow path if offset_bpf_func is None: return None kversion = Kernel.kernel_version() if kversion is None: return None if "5.12" <= kversion: return offset_bpf_func + current_arch.ptrsize * 2 elif "4.1" <= kversion: return offset_bpf_func - current_arch.ptrsize elif "3.18" <= kversion: return offset_bpf_func - current_arch.ptrsize * 2 elif "3.16" <= kversion: return offset_bpf_func - current_arch.ptrsize return None def get_offset_thread_head(self, task_addr, offset_signal): """ struct signal_struct { refcount_t sigcnt; atomic_t live; int nr_threads; int quick_threads; struct list_head thread_head; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct signal_struct*)0).thread_head") ) except gdb.error: pass # slow path signal = read_int_from_memory(task_addr + offset_signal) for i in range(10): x = read_int_from_memory(signal + current_arch.ptrsize * i) y = read_int_from_memory(signal + current_arch.ptrsize * (i + 1)) if is_valid_addr(x) and is_valid_addr(y): offset_thread_head = current_arch.ptrsize * i return offset_thread_head return None def get_offset_files(self, task_addrs, offset_comm): """ struct task_struct { ... char comm[TASK_COMM_LEN]; struct nameidata *nameidata; #ifdef CONFIG_SYSVIPC struct sysv_sem { struct sem_undo_list *undo_list; } sysvsem; struct sysv_shm { struct list_head shm_clist; } sysvshm; #endif #ifdef CONFIG_DETECT_HUNG_TASK unsigned long last_switch_count; unsigned long last_switch_time; #endif struct thread_struct thread; // ~v4.1 struct fs_struct *fs; struct files_struct *files; <-- here ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).files") ) except gdb.error: pass # slow path base = offset_comm + 16 # comm base += current_arch.ptrsize # nameidata kversion = Kernel.kernel_version() if kversion is None: return None if "4.2" <= kversion: repeat_times = 6 else: # sizeof(struct thread_struct) is very large, need more exproring repeat_times = 100 for i in range(repeat_times): # check fs v1 = read_int_from_memory(task_addrs[0] + base + current_arch.ptrsize * i) if not is_valid_addr(v1): continue if is_valid_addr(read_int_from_memory(v1)): continue # check files v2 = read_int_from_memory(task_addrs[0] + base + current_arch.ptrsize * (i + 1)) if not is_valid_addr(v2): continue if is_valid_addr(read_int_from_memory(v2)): continue # found offset_files = base + current_arch.ptrsize * (i + 1) return offset_files return None def get_offset_fdt(self, task_addrs, offset_files): """ struct files_struct { atomic_t count; // int bool resize_in_progress; // v4.2~ wait_queue_head_t { // v4.2~ spinlock_t lock; // v4.2~ struct list_head head; // v4.2~ } resize_wait; // v4.2~ struct fdtable __rcu *fdt; <-- here struct fdtable { unsigned int max_fds; struct file __rcu **fd; unsigned long *close_on_exec; unsigned long *open_fds; unsigned long *full_fds_bits; struct rcu_head rcu; } fdtab; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct files_struct*)0).fdt") ) except gdb.error: pass # slow path MAX_FDS_DEFAULT = AddressUtil.get_memory_alignment(in_bits=True) files = read_int_from_memory(task_addrs[0] + offset_files) for i in range(1, 0x100): v = read_int_from_memory(files + current_arch.ptrsize * i) if v != MAX_FDS_DEFAULT: continue offset_fdt = current_arch.ptrsize * (i - 1) return offset_fdt return None def get_offset_uid(self, init_task_cred_ptr): """ struct cred { atomic_t usage; // ~v6.1.69, v6.2~v6.6.7 atomic_long_t usage; // v6.1.69~v6.1.143, v6.6.8~ #ifdef CONFIG_DEBUG_CREDENTIALS // ~v6.6.7 atomic_t subscribers; // ~v6.6.7 void *put_addr; // ~v6.6.7 unsigned magic; // ~v6.6.7 #endif // ~v6.6.7 kuid_t uid; kgid_t gid; kuid_t suid; kgid_t sgid; kuid_t euid; kgid_t egid; kuid_t fsuid; kgid_t fsgid; unsigned securebits; kernel_cap_t cap_inheritable; kernel_cap_t cap_permitted; kernel_cap_t cap_effective; kernel_cap_t cap_bset; kernel_cap_t cap_ambient; ... }; [Example x64] 0xffffffff820460c0: 0x0000000000000004 0x0000000000000000 0xffffffff820460d0: 0x0000000000000000 0x0000000000000000 0xffffffff820460e0: 0x0000000000000000 0x0000000000000000 0xffffffff820460f0: 0x0000003fffffffff 0x0000003fffffffff 0xffffffff82046100: 0x0000003fffffffff 0x0000000000000000 0xffffffff82046110: 0x0000000000000000 0x0000000000000000 """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct cred*)0).uid") ) except gdb.error: pass # slow path kversion = Kernel.kernel_version() if kversion is None: return None if kversion < "6.1.69": offset_uid = 4 elif kversion < "6.2": offset_uid = current_arch.ptrsize elif kversion < "6.6.8": offset_uid = 4 else: offset_uid = current_arch.ptrsize if kversion < "6.6.8": init_task_cred = read_int_from_memory(init_task_cred_ptr) uid_gid_size = 4 * 8 # uid_t:4byte. len([uid,gid,suid,sgid,euid,egid,fsuid,fsgid]) == 8 ret = read_memory(init_task_cred + offset_uid, uid_gid_size) if ret == b"\0" * uid_gid_size: pass else: offset_uid += 4 + current_arch.ptrsize + 4 return offset_uid def get_offset_user_ns(self, init_task_cred_ptr, offset_uid): """ struct cred { ... kernel_cap_t cap_bset; kernel_cap_t cap_ambient; // v4.3~ #ifdef CONFIG_KEYS unsigned char jit_keyring; struct key *session_keyring; struct key *process_keyring; struct key *thread_keyring; struct key *request_key_auth; #endif #ifdef CONFIG_SECURITY void *security; #endif struct user_struct *user; struct user_namespace *user_ns; struct ucounts *ucounts; // v5.12.17~ struct group_info *group_info; union { int non_rcu; struct rcu_head rcu; }; } __randomize_layout; [Example x64; CONFIG_KEYS=y, CONFIG_SECURITY=y] 0xffffffffbb454580|+0x0000|+000: 0x0000000000000004 0xffffffffbb454588|+0x0008|+001: 0x0000000000000000 0xffffffffbb454590|+0x0010|+002: 0x0000000000000000 0xffffffffbb454598|+0x0018|+003: 0x0000000000000000 0xffffffffbb4545a0|+0x0020|+004: 0x0000000000000000 0xffffffffbb4545a8|+0x0028|+005: 0x0000000000000000 0xffffffffbb4545b0|+0x0030|+006: 0x000001ffffffffff 0xffffffffbb4545b8|+0x0038|+007: 0x000001ffffffffff 0xffffffffbb4545c0|+0x0040|+008: 0x000001ffffffffff // cap_bset 0xffffffffbb4545c8|+0x0048|+009: 0x0000000000000000 // cap_ambilent 0xffffffffbb4545d0|+0x0050|+010: 0x0000000000000000 // jit_keyring 0xffffffffbb4545d8|+0x0058|+011: 0x0000000000000000 // session_keyring 0xffffffffbb4545e0|+0x0060|+012: 0x0000000000000000 // process_keyring 0xffffffffbb4545e8|+0x0068|+013: 0x0000000000000000 // thread_keyring 0xffffffffbb4545f0|+0x0070|+014: 0x0000000000000000 // request_key_auth 0xffffffffbb4545f8|+0x0078|+015: 0xffff998d8106cb68 -> 0xffff998d81052eb0 // security 0xffffffffbb454600|+0x0080|+016: 0xffffffffbb44c6c0 -> 0x0000004e00000075 // user 0xffffffffbb454608|+0x0088|+017: 0xffffffffbb44c740 -> 0x0000000000000001 // user_ns [Example x64; CONFIG_KEYS=y, CONFIG_SECURITY=y] 0xffff9ec6c88379c0|+0x0000|+000: 0x000000000000000a 0xffff9ec6c88379c8|+0x0008|+001: 0x0000000000000000 0xffff9ec6c88379d0|+0x0010|+002: 0x0000000000000000 0xffff9ec6c88379d8|+0x0018|+003: 0x0000000000000000 0xffff9ec6c88379e0|+0x0020|+004: 0x0000000000000000 0xffff9ec6c88379e8|+0x0028|+005: 0x0000000000000000 0xffff9ec6c88379f0|+0x0030|+006: 0x000001ffffffffff 0xffff9ec6c88379f8|+0x0038|+007: 0x000001ffffffffff 0xffff9ec6c8837a00|+0x0040|+008: 0x000001ffffffffff // cap_bset 0xffff9ec6c8837a08|+0x0048|+009: 0x0000000000000000 // cap_ambient 0xffff9ec6c8837a10|+0x0050|+010: 0x0000000000000000 // jit_keyring 0xffff9ec6c8837a18|+0x0058|+011: 0xffff9ec6c4643700 -> 0x182031ce00000006 // session_keyring 0xffff9ec6c8837a20|+0x0060|+012: 0x0000000000000000 // process_keyring 0xffff9ec6c8837a28|+0x0068|+013: 0x0000000000000000 // thread_keyring 0xffff9ec6c8837a30|+0x0070|+014: 0x0000000000000000 // request_key_auth 0xffff9ec6c8837a38|+0x0078|+015: 0xffff9ec6c8873fe0 -> 0xffff9ec6c1052eb0 // security 0xffff9ec6c8837a40|+0x0080|+016: 0xffffffffbb64c5c0 -> 0x0000004f00000084 // user 0xffff9ec6c8837a48|+0x0088|+017: 0xffff9ec6c820eaa0 -> 0x0000000000000001 // user_ns [Example ARM64; CONFIG_KEYS=n, CONFIG_SECURITY=y] 0xffffd9e53efef538|+0x0000|+000: 0x0000000000000004 0xffffd9e53efef540|+0x0008|+001: 0x0000000000000000 0xffffd9e53efef548|+0x0010|+002: 0x0000000000000000 0xffffd9e53efef550|+0x0018|+003: 0x0000000000000000 0xffffd9e53efef558|+0x0020|+004: 0x0000000000000000 0xffffd9e53efef560|+0x0028|+005: 0x0000000000000000 0xffffd9e53efef568|+0x0030|+006: 0x000001ffffffffff 0xffffd9e53efef570|+0x0038|+007: 0x000001ffffffffff 0xffffd9e53efef578|+0x0040|+008: 0x000001ffffffffff // cap_bset 0xffffd9e53efef580|+0x0048|+009: 0x0000000000000000 // cap_ambient 0xffffd9e53efef588|+0x0050|+010: 0x0000000000000000 // security 0xffffd9e53efef590|+0x0058|+011: 0xffffd9e53efeeb10 -> 0x000000000000002a // user 0xffffd9e53efef598|+0x0060|+012: 0xffffd9e53efeeb98 -> 0x0000000000000001 // user_ns [Example x86; CONFIG_KEYS=y, CONFIG_SECURITY=y] 0xc1aabbe0|+0x0000|+000: 0x00000004 0xc1aabbe4|+0x0004|+001: 0x00000000 0xc1aabbe8|+0x0008|+002: 0x00000000 0xc1aabbec|+0x000c|+003: 0x00000000 0xc1aabbf0|+0x0010|+004: 0x00000000 0xc1aabbf4|+0x0014|+005: 0x00000000 0xc1aabbf8|+0x0018|+006: 0x00000000 0xc1aabbfc|+0x001c|+007: 0x00000000 0xc1aabc00|+0x0020|+008: 0x00000000 0xc1aabc04|+0x0024|+009: 0x00000000 0xc1aabc08|+0x0028|+010: 0x00000000 0xc1aabc0c|+0x002c|+011: 0x00000000 0xc1aabc10|+0x0030|+012: 0xffffffff 0xc1aabc14|+0x0034|+013: 0x000001ff 0xc1aabc18|+0x0038|+014: 0xffffffff 0xc1aabc1c|+0x003c|+015: 0x000001ff 0xc1aabc20|+0x0040|+016: 0xffffffff // cap_bset 0xc1aabc24|+0x0044|+017: 0x000001ff 0xc1aabc28|+0x0048|+018: 0x00000000 // cap_abmient 0xc1aabc2c|+0x004c|+019: 0x00000000 0xc1aabc30|+0x0050|+020: 0x00000000 // jit_keyring 0xc1aabc34|+0x0054|+021: 0x00000000 // session_keyring 0xc1aabc38|+0x0058|+022: 0x00000000 // process_keyring 0xc1aabc3c|+0x005c|+023: 0x00000000 // thread_keyring 0xc1aabc40|+0x0060|+024: 0x00000000 // request_key_auth 0xc1aabc44|+0x0064|+025: 0xc201e8b0 -> 0xc20ecd94 // security 0xc1aabc48|+0x0068|+026: 0xc1aa6b80 -> 0x00000068 // user 0xc1aabc4c|+0x006c|+027: 0xc1aa6be0 -> 0x00000001 // user_ns """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct cred*)0).user_ns") ) except gdb.error: pass # slow path kversion = Kernel.kernel_version() if kversion is None: return None # uid_t:4byte. len([uid,gid,suid,sgid,euid,egid,fsuid,fsgid]) == 8 uid_gid_size = 4 * 8 sizeof_securebits = 4 if "4.3" <= kversion: # cap_t:8byte. len([cap_inheritable,cap_permitted,cap_effective,cap_bset,cap_ambient]) == 5 cap_size = 8 * 5 else: # cap_t:8byte. len([cap_inheritable,cap_permitted,cap_effective,cap_bset]) == 4 cap_size = 8 * 4 """ struct user_namespace { struct uid_gid_map uid_map; ... }; struct uid_gid_map { /* 64 bytes -- 1 cache line */ u32 nr_extents; // ~v6.11 union { struct { struct uid_gid_extent extent[UID_GID_MAP_MAX_BASE_EXTENTS]; u32 nr_extents; v6.12~ }; struct { struct uid_gid_extent *forward; struct uid_gid_extent *reverse; }; }; }; """ if kversion < "6.12": offset_nr_extents = 0 else: offset_nr_extents = 60 for i in range(10): offset_user_ns = offset_uid + uid_gid_size + sizeof_securebits offset_user_ns = align_to_ptrsize(offset_user_ns) offset_user_ns += cap_size + current_arch.ptrsize * i v = read_int_from_memory(init_task_cred_ptr + offset_user_ns) if not is_valid_addr(v): continue w = read_int_from_memory(v + offset_nr_extents) if w == 1: return offset_user_ns return None class MapleTree: """Linux v6.1 introduces maple_tree. This is a simple parser.""" MT_FLAGS_HEIGHT_MASK = 0x7c MT_FLAGS_HEIGHT_OFFSET = 0x02 MAPLE_NODE_TYPE_SHIFT = 0x03 MAPLE_NODE_TYPE_MASK = 0x0f MAPLE_NODE_POINTER_MASK = 0xff MAPLE_DENSE = 0 MAPLE_LEAF_64 = 1 MAPLE_RANGE_64 = 2 MAPLE_ARANGE_64 = 3 def __init__(self, mm, quiet): self.quiet = quiet kversion = Kernel.kernel_version() """ struct mm_struct { struct { struct { atomic_t mm_count; } ____cacheline_aligned_in_smp; // v6.4~ struct maple_tree { union { spinlock_t ma_lock; lockdep_map_p ma_external_lock; }; unsigned int ma_flags; // v6.6~ void __rcu *ma_root; // this points root maple_node. (lower 8-bits are some flags) unsigned int ma_flags; // ~v6.5 } mm_mt; ... } __randomize_layout; ... }; """ # ____cacheline_aligned_in_smp attribute, spinlock_t and lockdep_map_p can be different size # in each environment or situation, so search for it heuristically. for i in range(0x20): x = read_int_from_memory(mm + current_arch.ptrsize * i) """ [x64 v6.4.2] 0xffff8bedc104db00|+0x0000|+000: 0x0000000000000000 // union <-- mm_mt 0xffff8bedc104db08|+0x0008|+001: 0xffff8bedc1a6601e // ma_root 0xffff8bedc104db10|+0x0010|+002: 0x000000000000030b // ma_flags [x64 v6.6.1] 0xffff972801b78a38|+0x0040|+008: 0x0000000000000000 // (the end of cacheline?) 0xffff972801b78a40|+0x0040|+008: 0x0000030b00000000 // ma_flags || union <-- mm_mt 0xffff972801b78a48|+0x0048|+009: 0xffff972801b0cc1e // ma_root """ if is_valid_addr(x) and (x & 0xff) in [0x1e, 0x0e]: offset_ma_root = current_arch.ptrsize * i if kversion < "6.6": offset_ma_flags = offset_ma_root + current_arch.ptrsize else: offset_ma_flags = offset_ma_root - 4 if is_64bit() and read_int32_from_memory(mm + offset_ma_flags) == 0: offset_ma_flags = offset_ma_root - 8 break else: raise self.ma_root_raw = read_int_from_memory(mm + offset_ma_root) self.ma_flags = read_int32_from_memory(mm + offset_ma_flags) self.max_depth = (self.ma_flags & self.MT_FLAGS_HEIGHT_MASK) >> self.MT_FLAGS_HEIGHT_OFFSET if is_64bit(): self.MAPLE_NODE_SLOTS = 31 self.MAPLE_RANGE64_SLOTS = 16 self.MAPLE_ARANGE64_SLOTS = 10 self.MAPLE_ALLOC_SLOTS = self.MAPLE_NODE_SLOTS - 1 self.maple_range_64_offset_slot = current_arch.ptrsize * self.MAPLE_RANGE64_SLOTS self.maple_arange_64_offset_slot = current_arch.ptrsize * self.MAPLE_ARANGE64_SLOTS self.maple_alloc_offset_slot = current_arch.ptrsize * 2 else: self.MAPLE_NODE_SLOTS = 63 self.MAPLE_RANGE64_SLOTS = 32 self.MAPLE_ARANGE64_SLOTS = 21 self.MAPLE_ALLOC_SLOTS = self.MAPLE_NODE_SLOTS - 2 self.maple_range_64_offset_slot = current_arch.ptrsize * self.MAPLE_RANGE64_SLOTS self.maple_arange_64_offset_slot = current_arch.ptrsize * self.MAPLE_ARANGE64_SLOTS self.maple_alloc_offset_slot = current_arch.ptrsize * 3 self.seen = set() self.iters = self.parse_node(self.ma_root_raw, 1) return def get_next(self, _=None): # iterate all `vm_area_struct` pointers for addr in self.iters: return addr return None def parse_node(self, entry, depth): if entry in self.seen: return self.seen.add(entry) if self.max_depth < depth: return pointer = entry & ~(self.MAPLE_NODE_POINTER_MASK) node_type = (entry >> self.MAPLE_NODE_TYPE_SHIFT) & self.MAPLE_NODE_TYPE_MASK if node_type == self.MAPLE_DENSE: slot_top = pointer + self.maple_alloc_offset_slot for i in range(self.MAPLE_ALLOC_SLOTS): slot = read_int_from_memory(slot_top + current_arch.ptrsize * i) if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: if is_valid_addr(slot): yield slot elif node_type == self.MAPLE_LEAF_64: slot_top = pointer + self.maple_range_64_offset_slot for i in range(self.MAPLE_RANGE64_SLOTS): slot = read_int_from_memory(slot_top + current_arch.ptrsize * i) if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: if is_valid_addr(slot): yield slot elif node_type == self.MAPLE_RANGE_64: slot_top = pointer + self.maple_range_64_offset_slot for i in range(self.MAPLE_RANGE64_SLOTS): slot = read_int_from_memory(slot_top + current_arch.ptrsize * i) if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self.parse_node(slot, depth + 1) elif node_type == self.MAPLE_ARANGE_64: slot_top = pointer + self.maple_arange_64_offset_slot for i in range(self.MAPLE_ARANGE64_SLOTS): slot = read_int_from_memory(slot_top + current_arch.ptrsize * i) if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self.parse_node(slot, depth + 1) return def get_vm_area_struct(self, mm): kversion = Kernel.kernel_version() if kversion is None: return None, None if kversion < "6.1": """ struct mm_struct { struct { struct vm_area_struct *mmap; ... } __randomize_layout; }; """ offset_mmap = 0 vm_area_struct = read_int_from_memory(mm + offset_mmap) """ struct vm_area_struct { unsigned long vm_start; unsigned long vm_end; struct vm_area_struct *vm_next, *vm_prev; struct rb_node vm_rb; unsigned long rb_subtree_gap; struct mm_struct *vm_mm; pgprot_t vm_page_prot; unsigned long vm_flags; struct { struct rb_node rb; unsigned long rb_subtree_last; } shared; struct list_head anon_vma_chain; struct anon_vma *anon_vma; const struct vm_operations_struct *vm_ops; unsigned long vm_pgoff; struct file *vm_file; ... }; """ def get_next_vma_area_struct(current): return read_int_from_memory(current + current_arch.ptrsize * 2) else: # "6.1" <= kversion """ struct mm_struct { struct { struct { atomic_t mm_count; } ____cacheline_aligned_in_smp; // v6.4~ struct maple_tree { union { spinlock_t ma_lock; lockdep_map_p ma_external_lock; }; unsigned int ma_flags; // v6.6~ void __rcu *ma_root; // this points root maple_node. (lower 8-bits are some flags) unsigned int ma_flags; // ~v6.5 } mm_mt; ... } __randomize_layout; ... }; struct maple_node { union { struct { struct maple_pnode *parent; void __rcu *slot[MAPLE_NODE_SLOTS]; // 64-bit: 31; 32-bit: 63 }; struct { void *pad; struct rcu_head rcu; struct maple_enode *piv_parent; unsigned char parent_slot; enum maple_type type; unsigned char slot_len; unsigned int ma_flags; }; struct maple_range_64 { struct maple_pnode *parent; unsigned long pivot[MAPLE_RANGE64_SLOTS - 1]; // 64-bit: 15; 32-bit: 31 union { void __rcu *slot[MAPLE_RANGE64_SLOTS]; // 64-bit: 16; 32-bit: 32 struct { void __rcu *pad[MAPLE_RANGE64_SLOTS - 1]; // 64-bit: 15; 32-bit: 31 struct maple_metadata meta; }; }; } mr64; struct maple_arange_64 { struct maple_pnode *parent; unsigned long pivot[MAPLE_ARANGE64_SLOTS - 1]; // 64-bit: 9; 32-bit: 20 void __rcu *slot[MAPLE_ARANGE64_SLOTS]; // 64-bit: 10; 32-bit: 21 unsigned long gap[MAPLE_ARANGE64_SLOTS]; // 64-bit: 10; 32-bit: 21 struct maple_metadata meta; } ma64; struct maple_alloc { unsigned long total; unsigned char node_count; unsigned int request_count; struct maple_alloc *slot[MAPLE_ALLOC_SLOTS]; // 64-bit: 30; 32-bit: 31 } alloc; }; }; """ get_next_vma_area_struct = self.MapleTree(mm, self.args.quiet).get_next vm_area_struct = get_next_vma_area_struct() """ struct vm_area_struct { unsigned long vm_start; unsigned long vm_end; struct mm_struct *vm_mm; pgprot_t vm_page_prot; unsigned long vm_flags; #ifdef CONFIG_PER_VMA_LOCK // v6.4~ int vm_lock_seq; // v6.4~ struct vma_lock *vm_lock; // v6.4~ bool detached; // v6.4~ #endif // v6.4~ struct { // v6.2~ struct rb_node rb; // v6.2~ unsigned long rb_subtree_last; // v6.2~ } shared; // v6.2~ union { // ~v6.1 struct { // ~v6.1 struct rb_node rb; // ~v6.1 unsigned long rb_subtree_last; // ~v6.1 } shared; // ~v6.1 struct anon_vma_name *anon_name; // ~v6.1 }; // ~v6.1 struct list_head anon_vma_chain; struct anon_vma *anon_vma; const struct vm_operations_struct *vm_ops; unsigned long vm_pgoff; struct file *vm_file; ... }; """ return vm_area_struct, get_next_vma_area_struct def get_offset_vm_mm(self, task_addrs, offset_mm): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct vm_area_struct*)0).vm_mm") ) except gdb.error: pass # slow path for task in task_addrs: mm = read_int_from_memory(task + offset_mm) if mm == 0: continue vm_area_struct, _ = self.get_vm_area_struct(mm) if vm_area_struct is None: return None current = vm_area_struct while True: x = read_int_from_memory(current) if x == mm: break current += current_arch.ptrsize offset_vm_mm = current - vm_area_struct return offset_vm_mm return None def get_offset_vm_flags(self, offset_vm_mm): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct vm_area_struct*)0).vm_flags") ) except gdb.error: pass # slow path if is_64bit(): offset_vm_flags = offset_vm_mm + 8 * 2 elif is_x86_32(): cr4 = get_register("cr4", use_monitor=True) if (cr4 >> 5) & 1: # PAE check offset_vm_flags = offset_vm_mm + 8 * 2 else: offset_vm_flags = offset_vm_mm + 4 * 2 elif is_arm32(): ret = gdb.execute("pagewalk --no-pager --disable-color", to_string=True) if "using long description" in ret: offset_vm_flags = offset_vm_mm + 8 * 2 else: offset_vm_flags = offset_vm_mm + 4 * 2 return offset_vm_flags def get_offset_vm_file(self, task_addrs, offset_mm, offset_vm_flags): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct vm_area_struct*)0).vm_file") ) except gdb.error: pass # slow path for i in range(50): found = True for task in task_addrs: # skip kernel thread mm = read_int_from_memory(task + offset_mm) if mm == 0: continue """ normal case: [x64 5.10.127; corjail; sh] 0xffff9df049f75cc0|+0x0000|+000: 0x0000564e44351000 // vm_start 0xffff9df049f75cc8|+0x0008|+001: 0x0000564e4437f000 // vm_end 0xffff9df049f75cd0|+0x0010|+002: 0xffff9df049f75000 // vm_next 0xffff9df049f75cd8|+0x0018|+003: 0x0000000000000000 // vm_prev 0xffff9df049f75ce0|+0x0020|+004: 0xffff9df049f75021 // vm_rb.__rb_parent_color 0xffff9df049f75ce8|+0x0028|+005: 0x0000000000000000 // vm_rb.rb_right 0xffff9df049f75cf0|+0x0030|+006: 0x0000000000000000 // vm_rb.rb_left 0xffff9df049f75cf8|+0x0038|+007: 0x0000564e44351000 // rb_subtree_gap 0xffff9df049f75d00|+0x0040|+008: 0xffff9df0426c8800 // vm_mm 0xffff9df049f75d08|+0x0048|+009: 0x8000000000000025 // vm_page_prot 0xffff9df049f75d10|+0x0050|+010: 0x0000000008000871 // vm_flags 0xffff9df049f75d18|+0x0058|+011: 0xffff9df049f75059 // shared.rb.__rb_parent_color 0xffff9df049f75d20|+0x0060|+012: 0x0000000000000000 // shared.rb.rb_right 0xffff9df049f75d28|+0x0068|+013: 0x0000000000000000 // shared.rb.rb_left 0xffff9df049f75d30|+0x0070|+014: 0x000000000000002d // shared.rb_subtree_last 0xffff9df049f75d38|+0x0078|+015: 0xffff9df049f75d38 // anon_vma_chain.next 0xffff9df049f75d40|+0x0080|+016: 0xffff9df049f75d38 // anon_vma_chain.prev 0xffff9df049f75d48|+0x0088|+017: 0x0000000000000000 // anon_vma 0xffff9df049f75d50|+0x0090|+018: 0xffffffff9b034380 // vm_ops 0xffff9df049f75d58|+0x0098|+019: 0x0000000000000000 // vm_pgoff 0xffff9df049f75d60|+0x00a0|+020: 0xffff9df0427a5800 // vm_file rare case: both vm_ops and vm_file are NULL [x64; 5.10.127; corjail; dockerd] 0xffff9df04678aa80|+0x0000|+000: 0x000000c000000000 // vm_start 0xffff9df04678aa88|+0x0008|+001: 0x000000c000400000 // vm_end 0xffff9df04678aa90|+0x0010|+002: 0xffff9df04670d9c0 // vm_next 0xffff9df04678aa98|+0x0018|+003: 0x0000000000000000 // vm_prev 0xffff9df04678aaa0|+0x0020|+004: 0xffff9df04670d9e1 // vm_rb.__rb_parent_color 0xffff9df04678aaa8|+0x0028|+005: 0x0000000000000000 // vm_rb.rb_right 0xffff9df04678aab0|+0x0030|+006: 0x0000000000000000 // vm_rb.rb_left 0xffff9df04678aab8|+0x0038|+007: 0x000000c000000000 // rb_subtree_gap 0xffff9df04678aac0|+0x0040|+008: 0xffff9df0426ca800 // vm_mm 0xffff9df04678aac8|+0x0048|+009: 0x8000000000000025 // vm_page_prot 0xffff9df04678aad0|+0x0050|+010: 0x0000000008100073 // vm_flags 0xffff9df04678aad8|+0x0058|+011: 0x0000000000000000 // shared.rb.__rb_parent_color 0xffff9df04678aae0|+0x0060|+012: 0x0000000000000000 // shared.rb.rb_right 0xffff9df04678aae8|+0x0068|+013: 0x0000000000000000 // shared.rb.rb_left 0xffff9df04678aaf0|+0x0070|+014: 0x0000000000000000 // shared.rb_subtree_last 0xffff9df04678aaf8|+0x0078|+015: 0xffff9df04676ea90 // anon_vma_chain.next 0xffff9df04678ab00|+0x0080|+016: 0xffff9df04676ea90 // anon_vma_chain.prev 0xffff9df04678ab08|+0x0088|+017: 0xffff9df04279e318 // anon_vma 0xffff9df04678ab10|+0x0090|+018: 0x0000000000000000 // vm_ops 0xffff9df04678ab18|+0x0098|+019: 0x000000000c000000 // vm_pgoff 0xffff9df04678ab20|+0x00a0|+020: 0x0000000000000000 // vm_file normal case: [x64 6.6.0; trust_storage; init] 0xffff000001ee6630|+0x0000|+000: 0x0000aaaac690d000 // vm_start 0xffff000001ee6638|+0x0008|+001: 0x0000aaaac69d4000 // vm_end 0xffff000001ee6640|+0x0010|+002: 0xffff0000010a84c0 // vm_mm 0xffff000001ee6648|+0x0018|+003: 0x0020000000000fc3 // vm_page_prot 0xffff000001ee6650|+0x0020|+004: 0x0000000000000075 // vm_flags 0xffff000001ee6658|+0x0028|+005: 0x0000000000000003 // vm_lock_seq 0xffff000001ee6660|+0x0030|+006: 0xffff000001ee7168 // vm_lock 0xffff000001ee6668|+0x0038|+007: 0x0000000000000000 // detached 0xffff000001ee6670|+0x0040|+008: 0xffff000005f831a1 // shared.rb.__rb_parent_color 0xffff000001ee6678|+0x0048|+009: 0x0000000000000000 // shared.rb.rb_right 0xffff000001ee6680|+0x0050|+010: 0x0000000000000000 // shared.rb.rb_left 0xffff000001ee6688|+0x0058|+011: 0x00000000000000c6 // shared.rb_subtree_last 0xffff000001ee6690|+0x0060|+012: 0xffff000001ee6690 // anon_vma_chain.next 0xffff000001ee6698|+0x0068|+013: 0xffff000001ee6690 // anon_vma_chain.prev 0xffff000001ee66a0|+0x0070|+014: 0x0000000000000000 // anon_vma 0xffff000001ee66a8|+0x0078|+015: 0xffffa4277c4d80c8 // vm_ops 0xffff000001ee66b0|+0x0080|+016: 0x0000000000000000 // vm_pgoff 0xffff000001ee66b8|+0x0088|+017: 0xffff00000025d400 // vm_file """ vm_area_struct, _ = self.get_vm_area_struct(mm) ptr_anon_vma_chain = vm_area_struct + offset_vm_flags + current_arch.ptrsize * i if not is_double_link_list(ptr_anon_vma_chain): found = False break ptr_anon_vma = vm_area_struct + offset_vm_flags + current_arch.ptrsize * (i + 2) anon_vma = read_int_from_memory(ptr_anon_vma) if anon_vma != 0 and not is_valid_addr(anon_vma): # allow NULL found = False break ptr_vm_ops = vm_area_struct + offset_vm_flags + current_arch.ptrsize * (i + 3) vm_ops = read_int_from_memory(ptr_vm_ops) if vm_ops != 0 and not is_valid_addr(vm_ops): # allow NULL found = False break ptr_vm_file = vm_area_struct + offset_vm_flags + current_arch.ptrsize * (i + 5) vm_file = read_int_from_memory(ptr_vm_file) if vm_file != 0 and not is_valid_addr(vm_file): # allow NULL found = False break if found: return offset_vm_flags + current_arch.ptrsize * (i + 5) return None def get_mm(self, task, offset_mm): mm = read_int_from_memory(task + offset_mm) if mm == 0: return [] vm_areas = [] VmArea = collections.namedtuple("VmArea", "start end flags file") current, get_next_vma_area_struct = self.get_vm_area_struct(mm) while current: vm_start = read_int_from_memory(current) vm_end = read_int_from_memory(current + current_arch.ptrsize) vm_flags = read_int_from_memory(current + self.offset_vm_flags) vm_file = read_int_from_memory(current + self.offset_vm_file) filepath = self.get_filepath(vm_file) perm = Permission(value=vm_flags) vm_areas.append(VmArea(vm_start, vm_end, str(perm), filepath)) current = get_next_vma_area_struct(current) return vm_areas def get_offset_mnt(self, file): """ [~v6.4] struct file { union { // ~v5.19 struct llist_node fu_llist; // ~v5.19 struct rcu_head fu_rcuhead; // ~v5.19 } f_u; // ~v5.19 union { // v6.0~ struct llist_node f_llist; // v6.0~ struct rcu_head f_rcuhead; // v6.0~ unsigned int f_iocb_flags; // v6.0~ }; // v6.0~ struct path { struct vfsmount *mnt; struct dentry *dentry; } f_path; struct inode *f_inode; // v3.9~ ... }; [v6.5~v6.11] struct file { union { struct callback_head { struct callback_head *next; void (*func)(struct callback_head *head); } f_task_work; // v6.8~; struct llist_node f_llist; struct rcu_head f_rcuhead; // ~v6.7 (=callback_head) unsigned int f_iocb_flags; }; spinlock_t f_lock; fmode_t f_mode; atomic_long_t f_count; struct mutex f_pos_lock; loff_t f_pos; unsigned int f_flags; struct fown_struct { rwlock_t lock; struct pid *pid; enum pid_type pid_type; kuid_t uid, euid; int signum; } f_owner; const struct cred *f_cred; struct file_ra_state { pgoff_t start; unsigned int size; unsigned int async_size; unsigned int ra_pages; unsigned int mmap_miss; loff_t prev_pos; } f_ra; struct path { struct vfsmount *mnt; struct dentry *dentry; } f_path; struct inode *f_inode; ... }; [v6.12~] struct file { atomic_long_t f_count; // v6.12 file_ref_t f_ref; // v6.13~v6.14 spinlock_t f_lock; fmode_t f_mode; const struct file_operations *f_op; struct address_space *f_mapping; void *private_data; struct inode *f_inode; unsigned int f_flags; unsigned int f_iocb_flags; const struct cred *f_cred; struct fown_struct *f_owner; // v6.15~ /* --- cacheline 1 boundary (64 bytes) --- */ struct path { struct vfsmount *mnt; struct dentry *dentry; } f_path; union { struct mutex f_pos_lock; u64 f_pipe; }; loff_t f_pos; ... """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct file*)0).f_path") ) + to_unsigned_long( gdb.parse_and_eval("&((struct path*)0).mnt") ) except gdb.error: pass # slow path if not is_valid_addr(file): return None kversion = Kernel.kernel_version() if kversion is None: return None if kversion < "6.5": offset_mnt = current_arch.ptrsize * 2 elif "6.5" <= kversion < "6.12": # plan 1 """ gef> slab-contains 0xffff9f49811d33e0 slab: 0xfffff93f800474c0 kmem_cache: 0xffff9f4981048c00 base: 0xffff9f49811d3000 name: mnt_cache size: 0x140 num_pages: 0x1 (unaligned?) """ for i in range(0x40): cand_offset_mnt = current_arch.ptrsize * i mnt = read_int_from_memory(file + cand_offset_mnt) # f_path.mnt points in the middle of the chunk, so the "unaligned?" warning is not a problem ret = Kernel.get_slab_contains(mnt, allow_unaligned=True) if not ret: continue if "mnt_cache" in ret: offset_mnt = cand_offset_mnt break else: # plan 2 """ It has also been observed when mnt_cache is not used. In this case, the 2 previous elements from ext4_inode_cache or shmem_inode_cache seem to be the relevant pointer. 0xffff8b864013a298|+0x0098|+019: 0xffff8b86436e4da0 (task_group) <-- here is mnt but various slab names 0xffff8b864013a2a0|+0x00a0|+020: 0xffff8b86404079c0 (kmalloc-rcl-192) 0xffff8b864013a2a8|+0x00a8|+021: 0xffff8b864041e0a8 (ext4_inode_cache) <- unique (`*_inode_cache`) 0xffff8b864013a698|+0x0098|+019: 0xffff8b8640171020 (task_group) <-- here is mnt but various slab names 0xffff8b864013a6a0|+0x00a0|+020: 0xffff8b86436159c0 (kmalloc-rcl-192) 0xffff8b864013a6a8|+0x00a8|+021: 0xffff8b8643730640 (shmem_inode_cache) <- unique (`*_inode_cache`) """ for i in range(0x40): cand_offset_mnt = current_arch.ptrsize * i mnt = read_int_from_memory(file + cand_offset_mnt) ret = Kernel.get_slab_contains(mnt, allow_unaligned=True) if not ret: continue if "inode_cache" in ret: offset_mnt = cand_offset_mnt - current_arch.ptrsize * 2 break else: raise elif "6.12" <= kversion: if is_64bit(): offset_mnt = 64 else: """ 0x811f3180|+0x0000|+000: f_count : 0x00000004 0x811f3184|+0x0004|+001: f_lock : 0x00000000 0x811f3188|+0x0008|+002: f_mode : 0x004a801d 0x811f318c|+0x000c|+003: f_op : 0x80a0e040 -> 0x00000000 0x811f3190|+0x0010|+004: f_mapping : 0x813a2140 -> 0x813a2050 -> 0x000589ed 0x811f3194|+0x0014|+005: private_data : 0x00000000 0x811f3198|+0x0018|+006: f_inode : 0x813a2050 -> 0x000589ed 0x811f319c|+0x001c|+007: f_flags : 0x00020020 0x811f31a0|+0x0020|+008: f_iocb_flags : 0x00000000 0x811f31a4|+0x0024|+009: f_cred : 0x81378280 -> 0x00000005 0x811f31a8|+0x0028|+010: f_path.mnt : 0x810043d0 -> 0x81402088 -> 0x00210000 0x811f31ac|+0x002c|+011: f_path.dentry : 0x814fd990 -> 0x00400008 0x811f31b0|+0x0030|+012: mutex.owner : 0x00000000 0x811f31b4|+0x0034|+013: mutex.wait_lock: 0x00000000 0x811f31b8|+0x0038|+014: : 0x00000000 pattern of sizeof(lock) == 0: 0xc33c7100|+0x0000|+000: f_lock,f_mode : 0x0c4a801d 0xc33c7104|+0x0004|+001: f_op : 0xc1f9bee0 -> 0x00000000 0xc33c7108|+0x0008|+002: f_mapping : 0xc3491150 -> 0xc3491068 -> 0x000d89ed 0xc33c710c|+0x000c|+003: private_data : 0x00000000 0xc33c7110|+0x0010|+004: f_inode : 0xc3491068 -> 0x000d89ed 0xc33c7114|+0x0014|+005: f_flags : 0x00008020 0xc33c7118|+0x0018|+006: f_iocb_flags : 0x00000000 0xc33c711c|+0x001c|+007: f_cred : 0xc30ba080 -> 0x00000004 0xc33c7120|+0x0020|+008: f_owner : 0x00000000 0xc33c7124|+0x0024|+009: f_path.mnt : 0xc38b4f10 -> 0xc3459300 -> 0x00100000 0xc33c7128|+0x0028|+010: f_path.dentry : 0xc3459580 -> 0x00200000 0xc33c712c|+0x002c|+011: mutex.owner : 0x00000000 0xc33c7130|+0x0030|+012: mutex.wait_{lock,list}: 0xc33c7130 -> [loop detected] 0xc33c7134|+0x0034|+013: : 0xc33c7130 -> [loop detected] """ for i in range(16): cand_offset_mnt = current_arch.ptrsize * (i + 9) # f_path.mnt if not is_valid_addr_addr(file + cand_offset_mnt): continue # f_path.mnt.mnt_root x = read_int_from_memory(read_int_from_memory(file + cand_offset_mnt)) if not is_valid_addr(x): continue # f_path.dentry if not is_valid_addr_addr(file + cand_offset_mnt + current_arch.ptrsize): continue offset_mnt = cand_offset_mnt break else: raise return offset_mnt def get_offset_dentry(self, offset_mnt): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct file*)0).f_path") ) + to_unsigned_long( gdb.parse_and_eval("&((struct path*)0).dentry") ) except gdb.error: pass # slow path return offset_mnt + current_arch.ptrsize def get_offset_d_iname(self, dentry): """ struct dentry { unsigned int d_flags; seqcount_spinlock_t d_seq; struct hlist_bl_node d_hash; struct dentry *d_parent; // Padding can be added here struct qstr { union { struct { HASH_LEN_DECLARE; }; u64 hash_len; }; const unsigned char *name; // this points d_iname } d_name; struct inode *d_inode; unsigned char d_iname[DNAME_INLINE_LEN]; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct dentry*)0).d_iname") ) except gdb.error: pass # slow path current = dentry while True: name = read_int_from_memory(current) if 0 < name - current <= 0x20: offset_d_iname = name - dentry break current += current_arch.ptrsize return offset_d_iname def get_offset_d_inode(self, offset_d_iname): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct dentry*)0).d_inode") ) except gdb.error: pass # slow path return offset_d_iname - current_arch.ptrsize def get_offset_d_parent(self, dentry, offset_d_iname): # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct dentry*)0).d_parent") ) except gdb.error: pass # slow path offset_dname_name = offset_d_iname - current_arch.ptrsize * 2 # skip if padding while read_int_from_memory(dentry + offset_dname_name) != dentry + offset_d_iname: offset_dname_name -= current_arch.ptrsize offset_d_parent = offset_dname_name - 8 - current_arch.ptrsize # skip if padding while True: if is_valid_addr_addr(dentry + offset_d_parent): # roughly check parent = read_int_from_memory(dentry + offset_d_parent) if (parent & 0b11) == 0: # align check parent_parent = read_int_from_memory(parent + offset_d_parent) if is_valid_addr(parent_parent): break offset_d_parent -= current_arch.ptrsize return offset_d_parent def get_offset_i_ino(self, inode): """ struct inode { umode_t i_mode; unsigned short i_opflags; kuid_t i_uid; kgid_t i_gid; unsigned int i_flags; #ifdef CONFIG_FS_POSIX_ACL struct posix_acl *i_acl; struct posix_acl *i_default_acl; #endif const struct inode_operations *i_op; struct super_block *i_sb; struct address_space *i_mapping; #ifdef CONFIG_SECURITY void *i_security; #endif unsigned long i_ino; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct inode*)0).i_no") ) except gdb.error: pass # slow path current = inode + 2 + 2 + 4 + 4 + 4 # now, `current` points i_acl or i_op while True: v = read_int_from_memory(current) if v == 0: current += current_arch.ptrsize continue if is_64bit() and v == 0xffff_ffff_ffff_ffff: current += current_arch.ptrsize continue elif is_32bit() and v == 0xffff_ffff: current += current_arch.ptrsize continue elif is_valid_addr(v): current += current_arch.ptrsize continue offset_i_ino = current - inode break return offset_i_ino def get_ino(self, file): dentry = read_int_from_memory(file + self.offset_dentry) inode = read_int_from_memory(dentry + self.offset_d_inode) i_ino = read_int_from_memory(inode + self.offset_i_ino) return i_ino def get_filepath(self, file): if not is_valid_addr(file): return "" if file in self.filepath_cache: return self.filepath_cache[file] """ struct path { struct vfsmount *mnt; struct dentry *dentry; } f_path; struct mount { struct hlist_node mnt_hash; struct mount *mnt_parent; struct dentry *mnt_mountpoint; struct vfsmount { struct dentry *mnt_root; struct super_block *mnt_sb; int mnt_flags; struct mnt_idmap *mnt_idmap; // v6.2~ struct user_namespace *mnt_userns; // v5.12~v6.1 } mnt; <-- f_path.mnt points here ... }; """ def is_root(vfsmnt, dentry): mnt_root = read_int_from_memory(vfsmnt + offset_vfsmount_mnt_root) parent = read_int_from_memory(dentry + self.offset_d_parent) return dentry == mnt_root or parent == dentry def is_global_root(mnt): parent = read_int_from_memory(mnt + offset_mount_mnt_parent) return parent == mnt def read_dentry_str(dentry): # Try d_shortname (inline name) directly name = read_cstring_from_memory(dentry + self.offset_d_iname) if name: return name # Try d_name.name pointer (no padding case) # Validate pointer before dereferencing for back in [2, 3]: name_ptr = read_int_from_memory( dentry + self.offset_d_iname - current_arch.ptrsize * back ) if not is_valid_addr(name_ptr) or (name_ptr & 0b11) != 0: continue name = read_cstring_from_memory(name_ptr) if name: return name return "" offset_vfsmount_mnt_root = 0 offset_mount_mnt_parent = current_arch.ptrsize * 2 offset_mount_mnt_mountpoint = current_arch.ptrsize * 3 offset_mount_mnt = current_arch.ptrsize * 4 filepath = [] dentry = read_int_from_memory(file + self.offset_dentry) vfsmnt = read_int_from_memory(file + self.offset_mnt) mnt = vfsmnt - offset_mount_mnt while True: if is_root(vfsmnt, dentry): if is_global_root(mnt): name = read_dentry_str(dentry) filepath.append(name) break else: dentry = read_int_from_memory(mnt + offset_mount_mnt_mountpoint) mnt = read_int_from_memory(mnt + offset_mount_mnt_parent) vfsmnt = mnt + offset_mount_mnt continue else: name = read_dentry_str(dentry) filepath.append(name) dentry = read_int_from_memory(dentry + self.offset_d_parent) filepath = os.path.join(*filepath[::-1]) if filepath in ["UNIX", "NETLINK", "TCP", "TCPv6", "UDP", "UDPv6", "PACKET"]: filepath = "socket:[{:d}]".format(self.get_ino(file)) elif filepath and not filepath.startswith("/"): filepath = "anon_inode:{:s}".format(filepath) elif filepath == "": filepath = "pipe:[{:d}]".format(self.get_ino(file)) self.filepath_cache[file] = filepath return filepath def add_lwp_task(self, task_addrs): lwp_task_addrs = [] kversion = Kernel.kernel_version() for task in task_addrs: seen = [] if kversion < "6.7": lwp = task while lwp not in seen: seen.append(lwp) try: lwp = read_int_from_memory(lwp + self.offset_thread_group) - self.offset_thread_group except gdb.MemoryError: break lwp_task_addrs.extend(seen) else: signal = read_int_from_memory(task + self.offset_signal) head = signal + self.offset_thread_head seen = [head] curr = read_int_from_memory(head) while curr not in seen: seen.append(curr) lwp = curr - self.offset_thread_group lwp_task_addrs.append(lwp) try: curr = read_int_from_memory(curr) except gdb.MemoryError: break return lwp_task_addrs def get_offset_nsproxy(self, task_addr, offset_files): """ struct task_struct { ... struct files_struct *files; #ifdef CONFIG_IO_URING struct io_uring_task *io_uring; #endif struct nsproxy *nsproxy; struct signal_struct *signal; struct sighand_struct __rcu *sighand; sigset_t blocked; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).nsproxy") ) except gdb.error: pass # slow path offset_nsproxy = offset_files + current_arch.ptrsize v = read_int_from_memory(task_addr + offset_nsproxy + current_arch.ptrsize * 3) # blocked if not is_valid_addr(v): # CONFIG_IO_URING=n return offset_nsproxy # CONFIG_IO_URING=y return offset_nsproxy + current_arch.ptrsize def get_offset_sighand(self, task_addr, offset_files): """ struct task_struct { ... struct files_struct *files; #ifdef CONFIG_IO_URING struct io_uring_task *io_uring; #endif struct nsproxy *nsproxy; struct signal_struct *signal; struct sighand_struct __rcu *sighand; sigset_t blocked; ... }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct task_struct*)0).sighand") ) except gdb.error: pass # slow path offset_sighand = offset_files + current_arch.ptrsize * 3 if not is_valid_addr(read_int_from_memory(task_addr + offset_sighand + current_arch.ptrsize)): # blocked # CONFIG_IO_URING=n return offset_sighand # CONFIG_IO_URING=y return offset_sighand + current_arch.ptrsize def get_offset_action(self, sighand): """ [v5.3~] struct sighand_struct { spinlock_t siglock; refcount_t count; struct wait_queue_head { spinlock_t lock; struct list_head head; } signalfd_wqh; struct k_sigaction { struct sigaction { __sighandler_t sa_handler; unsigned long sa_flags; #ifdef __ARCH_HAS_SA_RESTORER __sigrestore_t sa_restorer; #endif sigset_t sa_mask; } sa; #ifdef __ARCH_HAS_KA_RESTORER __sigrestore_t ka_restorer; #endif } action[_NSIG]; // 64 }; [~v5.2] struct sighand_struct { refcount_t count; struct k_sigaction { struct sigaction sa; #ifdef __ARCH_HAS_KA_RESTORER __sigrestore_t ka_restorer; #endif } action[_NSIG]; // 64 spinlock_t siglock; wait_queue_head_t signalfd_wqh; }; """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("&((struct sighand_struct*)0).action") ) except gdb.error: pass # slow path kversion = Kernel.kernel_version() if kversion is None: return None if "5.3" <= kversion: # search for signalfd_wqh.list_head found = False for i in range(1, 30): offset_list_head = current_arch.ptrsize * i head = sighand + offset_list_head if not is_valid_addr(head): continue current = read_int_from_memory(head) seen = [] while True: if current == head: found = True break if not is_valid_addr(current): break if current in seen: break seen.append(current) current = read_int_from_memory(current) if found: break if not found: return None offset_action = offset_list_head + current_arch.ptrsize * 2 else: # < 5.3 offset_action = current_arch.ptrsize return offset_action def get_sizeof_action(self, task_addrs, offset_sighand, offset_action, offset_mm): """ case 1 (x64) 0xffff8f63011e4400|+0x0000|+000: 0x0000000100000000 0xffff8f63011e4408|+0x0008|+001: 0x0000000000000000 0xffff8f63011e4410|+0x0010|+002: 0xffff8f63593e11e0 -> [loop detected] 0xffff8f63011e4418|+0x0018|+003: 0xffff8f63593e11e0 -> 0xffff8f63011e4410 -> [loop detected] 0xffff8f63011e4420|+0x0020|+004: 0x0000000000000000 <- action[0] 0xffff8f63011e4428|+0x0028|+005: 0x0000000014000000 0xffff8f63011e4430|+0x0030|+006: 0x00007f3ec71d0d60 0xffff8f63011e4438|+0x0038|+007: 0x0000000000000000 0xffff8f63011e4440|+0x0040|+008: 0x0000000000000000 <- action[1] 0xffff8f63011e4448|+0x0048|+009: 0x0000000014000000 0xffff8f63011e4450|+0x0050|+010: 0x00007f3ec71d0d60 0xffff8f63011e4458|+0x0058|+011: 0x0000000000000000 0xffff8f63011e4460|+0x0060|+012: 0x0000562e3b34bdf0 <- action[2] 0xffff8f63011e4468|+0x0068|+013: 0x0000000044000000 0xffff8f63011e4470|+0x0070|+014: 0x00007f3ec71d0d60 0xffff8f63011e4478|+0x0078|+015: 0x0000000000000000 0xffff8f63011e4480|+0x0080|+016: 0x0000562e3b34bdf0 <- action[3] 0xffff8f63011e4488|+0x0088|+017: 0x0000000044000000 0xffff8f63011e4490|+0x0090|+018: 0x00007f3ec71d0d60 0xffff8f63011e4498|+0x0098|+019: 0x0000000000000000 ... case 2 (arm64) 0xffff000003080000|+0x0000|+000: 0x0000000100000000 0xffff000003080008|+0x0008|+001: 0x0000000000000000 0xffff000003080010|+0x0010|+002: 0xffff000003080010 -> [loop detected] 0xffff000003080018|+0x0018|+003: 0xffff000003080010 -> [loop detected] 0xffff000003080020|+0x0020|+004: 0x0000000000000000 <- action[0] 0xffff000003080028|+0x0028|+005: 0x0000000000000000 0xffff000003080030|+0x0030|+006: 0x0000000000000000 0xffff000003080038|+0x0038|+007: 0x0000000000000000 0xffff000003080040|+0x0040|+008: 0x000000000051dc20 <- action[1] 0xffff000003080048|+0x0048|+009: 0x0000000000000000 0xffff000003080050|+0x0050|+010: 0x0000000000000002 0xffff000003080058|+0x0058|+011: 0xfffffffe7ffbfeff 0xffff000003080060|+0x0060|+012: 0x0000000000000001 <- action[2] 0xffff000003080068|+0x0068|+013: 0x0000000000000000 0xffff000003080070|+0x0070|+014: 0x0000000000000002 0xffff000003080078|+0x0078|+015: 0xfffffffe7ffbfeff 0xffff000003080080|+0x0080|+016: 0x0000000000000000 <- action[3] 0xffff000003080088|+0x0088|+017: 0x0000000000000000 0xffff000003080090|+0x0090|+018: 0x0000000000000000 0xffff000003080098|+0x0098|+019: 0x0000000000000000 ... """ # fast path try: return to_unsigned_long( gdb.parse_and_eval("sizeof(struct k_sigaction)") ) except gdb.error: pass # slow path if is_32bit(): possible_sizes = [0x10, 0x14, 0x18] else: possible_sizes = [0x18, 0x20, 0x28] # calc sizeof(action[0]) sizeof_action = 0xffff_ffff_ffff_ffff for task in task_addrs: mm = read_int_from_memory(task + offset_mm) if mm == 0: # for speed up; ignore if kernel thread continue sighand = read_int_from_memory(task + offset_sighand) current = sighand + offset_action found_offset_case1 = [] found_offset_case2 = [] for i in range(64 * 4): offset = current_arch.ptrsize * i # check case 1 (sa_flags) v = read_int_from_memory(current + offset) # SA_RESTORER, SA_RESTART, SA_NODEFER, SA_RESTART|SA_RESTORER, SA_NODEFER|SA_RESTORER if v in [0x0400_0000, 0x1000_0000, 0x4000_0000, 0x1400_0000, 0x4400_0000]: found_offset_case1.append(offset) # check case 2 (sa_mask) v = read_int64_from_memory(current + offset) if bin(v)[2:].count("1") > 56: # heuristic threshold found_offset_case2.append(offset) if len(found_offset_case1) >= 2: sizeof_action_tmp = min(y - x for x, y in zip(found_offset_case1[:-1], found_offset_case1[1:])) # it is minimum size, so fast return if sizeof_action_tmp in possible_sizes: return sizeof_action_tmp # not minimum size, so check next task sizeof_action = min(sizeof_action, sizeof_action_tmp) if len(found_offset_case2) >= 2: sizeof_action_tmp = min(y - x for x, y in zip(found_offset_case2[:-1], found_offset_case2[1:])) # it is minimum size, so fast return if sizeof_action_tmp in possible_sizes: return sizeof_action_tmp # not minimum size, so check next task sizeof_action = min(sizeof_action, sizeof_action_tmp) if sizeof_action != 0xffff_ffff_ffff_ffff: for ps in possible_sizes: if sizeof_action % ps == 0: return sizeof_action return None def initialize(self): kversion = Kernel.kernel_version() if kversion is None: self.quiet_err("Could not find Linux kernel") return False # init_task if self.args.init_task is not None: init_task = self.args.init_task else: init_task = KernelAddressHeuristicFinder.get_init_task() if init_task is None: self.quiet_err("Could not find init_task") return False self.quiet_info("init_task: {:#x}".format(init_task)) # task_struct->tasks if self.offset_tasks is None: self.offset_tasks = self.get_offset_tasks(init_task) if self.offset_tasks is None: self.quiet_err("Could not find task_struct->tasks") return False self.quiet_info("offsetof(task_struct, tasks): {:#x}".format(self.offset_tasks)) # task addresses task_addrs = self.get_task_list(init_task, self.offset_tasks) if task_addrs is None: self.quiet_err("Failed to list each tasks") return False self.quiet_info("Number of tasks: {:d}".format(len(task_addrs))) # task_struct->mm if self.offset_mm is None: self.offset_mm = self.get_offset_mm(task_addrs[0], self.offset_tasks) if self.offset_mm is None: self.quiet_err("Could not find task_struct->mm") return False self.quiet_info("offsetof(task_struct, mm): {:#x}".format(self.offset_mm)) # task_struct->stack if self.offset_stack is None: self.offset_stack = self.get_offset_stack(task_addrs) if self.offset_stack is None: self.quiet_err("Could not find task_struct->stack") return False self.quiet_info("offsetof(task_struct, stack): {:#x}".format(self.offset_stack)) # task_struct->pid if self.offset_pid is None: self.offset_pid = self.get_offset_pid(task_addrs) if self.offset_pid is None: self.quiet_err("Could not find task_struct->pid") return False self.quiet_info("offsetof(task_struct, pid): {:#x}".format(self.offset_pid)) # task_struct->stack_canary if self.offset_kcanary is None: self.offset_kcanary = self.get_offset_canary(task_addrs, self.offset_pid) if self.offset_kcanary is None: self.quiet_info("offsetof(task_struct, stack_canary): None") else: self.quiet_info("offsetof(task_struct, stack_canary): {:#x}".format(self.offset_kcanary)) # task_struct->comm if self.offset_comm is None: self.offset_comm = self.get_offset_comm(task_addrs) if self.offset_comm is None: self.quiet_err("Could not find task_struct->comm[TASK_CMM_LEN]") return False self.quiet_info("offsetof(task_struct, comm): {:#x}".format(self.offset_comm)) # task_struct->cred if self.offset_cred is None: self.offset_cred = self.get_offset_cred(task_addrs, self.offset_comm) if self.offset_cred is None: self.quiet_err("Could not find task_struct->cred") return False self.quiet_info("offsetof(task_struct, cred): {:#x}".format(self.offset_cred)) # cred.uid if self.offset_uid is None: self.offset_uid = self.get_offset_uid(task_addrs[0] + self.offset_cred) if self.offset_uid is None: self.quiet_err("Could not find cred->uid") return False self.quiet_info("offsetof(cred, uid): {:#x}".format(self.offset_uid)) # kstack_top->saved_ptregs if self.args.print_regs: if self.offset_ptregs is None: self.kstack_size, self.offset_ptregs = self.get_offset_ptregs(task_addrs, self.offset_stack) if self.offset_ptregs is None: self.quiet_err("Could not find saved ptregs") return False self.quiet_info("kstack size: {:#x}".format(self.kstack_size)) self.quiet_info("offsetof(kstack_top, saved ptregs): {:#x}".format(self.offset_ptregs)) # vm_area_struct->vm_mm # vm_area_struct->vm_flags # vm_area_struct->vm_file # file->f_path.mnt # file->f_path.dentry # dentry->d_iname # dentry->d_parent # dentry->d_inode # inode->i_ino if self.args.print_maps or self.args.print_fd: if self.offset_vm_mm is None: self.offset_vm_mm = self.get_offset_vm_mm(task_addrs, self.offset_mm) if self.offset_vm_mm is None: self.quiet_err("Could not find vm_area_struct->vm_mm") return False self.quiet_info("offsetof(vm_area_struct, vm_mm): {:#x}".format(self.offset_vm_mm)) if self.offset_vm_flags is None: self.offset_vm_flags = self.get_offset_vm_flags(self.offset_vm_mm) if self.offset_vm_flags is None: self.quiet_err("Could not find vm_area_struct->vm_flags") return False self.quiet_info("offsetof(vm_area_struct, vm_flags): {:#x}".format(self.offset_vm_flags)) if self.offset_vm_file is None: self.offset_vm_file = self.get_offset_vm_file(task_addrs, self.offset_mm, self.offset_vm_flags) if self.offset_vm_file is None: self.quiet_err("Could not find vm_area_struct->vm_file") return False self.quiet_info("offsetof(vm_area_struct, vm_file): {:#x}".format(self.offset_vm_file)) if self.offset_mnt is None: mm = read_int_from_memory(task_addrs[1] + self.offset_mm) current, _ = self.get_vm_area_struct(mm) self.quiet_info("vm_area_struct (init process): {:#x}".format(current)) vm_file = read_int_from_memory(current + self.offset_vm_file) self.quiet_info("vm_file (init process): {:#x}".format(vm_file)) self.offset_mnt = self.get_offset_mnt(vm_file) if self.offset_mnt is None: self.quiet_err("Could not find file->f_path.mnt") return False self.quiet_info("offsetof(file, f_path.mnt): {:#x}".format(self.offset_mnt)) if self.offset_dentry is None: self.offset_dentry = self.get_offset_dentry(self.offset_mnt) self.quiet_info("offsetof(file, f_path.dentry): {:#x}".format(self.offset_dentry)) if self.offset_d_iname is None: mm = read_int_from_memory(task_addrs[1] + self.offset_mm) current, _ = self.get_vm_area_struct(mm) vm_file = read_int_from_memory(current + self.offset_vm_file) dentry = read_int_from_memory(vm_file + self.offset_dentry) self.offset_d_iname = self.get_offset_d_iname(dentry) self.quiet_info("offsetof(dentry, d_iname): {:#x}".format(self.offset_d_iname)) if self.offset_d_inode is None: self.offset_d_inode = self.get_offset_d_inode(self.offset_d_iname) self.quiet_info("offsetof(dentry, d_inode): {:#x}".format(self.offset_d_inode)) if self.offset_d_parent is None: dentry = read_int_from_memory(vm_file + self.offset_dentry) self.offset_d_parent = self.get_offset_d_parent(dentry, self.offset_d_iname) self.quiet_info("offsetof(dentry, d_parent): {:#x}".format(self.offset_d_parent)) if self.offset_i_ino is None: dentry = read_int_from_memory(vm_file + self.offset_dentry) inode = read_int_from_memory(dentry + self.offset_d_inode) self.offset_i_ino = self.get_offset_i_ino(inode) self.quiet_info("offsetof(inode, i_ino): {:#x}".format(self.offset_i_ino)) # task_struct->files if self.args.print_fd or self.args.print_sighand or self.args.print_namespace or \ ("6.7" <= kversion and self.args.print_thread) or self.args.print_seccomp: if self.offset_files is None: self.offset_files = self.get_offset_files(task_addrs, self.offset_comm) if self.offset_files is None: self.quiet_err("Could not find task_struct->files") return False self.quiet_info("offsetof(task_struct, files): {:#x}".format(self.offset_files)) # files_struct->fdt if self.args.print_fd: if self.offset_fdt is None: self.offset_fdt = self.get_offset_fdt(task_addrs, self.offset_files) if self.offset_fdt is None: self.quiet_err("Could not find files_struct->fdt") return False self.quiet_info("offsetof(files_struct, fdt): {:#x}".format(self.offset_fdt)) # cred->user_ns # task_struct->nsproxy if self.args.print_namespace or ("6.7" <= kversion and self.args.print_thread) or self.args.print_seccomp: if self.offset_user_ns is None: init_cred = read_int_from_memory(task_addrs[0] + self.offset_cred) self.offset_user_ns = self.get_offset_user_ns(init_cred, self.offset_uid) if self.offset_user_ns is None: self.quiet_err("Could not find cred->user_ns") return False self.quiet_info("offsetof(cred, user_ns): {:#x}".format(self.offset_user_ns)) if self.offset_nsproxy is None: self.offset_nsproxy = self.get_offset_nsproxy(task_addrs[0], self.offset_files) if self.offset_nsproxy is None: self.quiet_err("Could not find task_struct->nsproxy") return False self.quiet_info("offsetof(task_struct, nsproxy): {:#x}".format(self.offset_nsproxy)) # task_struct->group_leader # task_struct->thread_group # task_struct->signal (6.7~) # signal->thread_head (6.7~) if self.args.print_thread: if self.offset_group_leader is None: self.offset_group_leader = self.get_offset_group_leader(self.offset_pid, self.offset_kcanary) self.quiet_info("offsetof(task_struct, group_leader): {:#x}".format(self.offset_group_leader)) if self.offset_thread_group is None: self.offset_thread_group = self.get_offset_thread_group(self.offset_group_leader) if self.offset_thread_group is None: self.quiet_err("Could not find task_struct->thread_group") return False if "6.7" <= kversion: self.quiet_info("offsetof(task_struct, thread_node): {:#x}".format(self.offset_thread_group)) else: self.quiet_info("offsetof(task_struct, thread_group): {:#x}".format(self.offset_thread_group)) if "6.7" <= kversion: if self.offset_signal is None: self.offset_signal = self.get_offset_signal(self.offset_nsproxy) self.quiet_info("offsetof(task_struct, signal): {:#x}".format(self.offset_signal)) if self.offset_thread_head is None: self.offset_thread_head = self.get_offset_thread_head(task_addrs[0], self.offset_signal) self.quiet_info("offsetof(signal, thread_head): {:#x}".format(self.offset_thread_head)) # task_struct->sighand if self.args.print_sighand: if self.offset_sighand is None: self.offset_sighand = self.get_offset_sighand(task_addrs[0], self.offset_files) if self.offset_sighand is None: self.quiet_err("Could not find task_struct->sighand") return False self.quiet_info("offsetof(task_struct, sighand): {:#x}".format(self.offset_sighand)) if self.offset_action is None: sighand = read_int_from_memory(task_addrs[1] + self.offset_sighand) self.offset_action = self.get_offset_action(sighand) if self.offset_action is None: self.quiet_err("Could not find sighand_struct->action") return False self.quiet_info("offsetof(sighand_struct, action): {:#x}".format(self.offset_action)) if self.sizeof_action is None: self.sizeof_action = self.get_sizeof_action( task_addrs, self.offset_sighand, self.offset_action, self.offset_mm, ) if self.sizeof_action is None: self.quiet_err("Could not find sizeof(action[0])") return False self.quiet_info("sizeof(action[0]): {:#x}".format(self.sizeof_action)) self.signame_list = { 1: "SIGHUP", 2: "SIGINT", 3: "SIGQUIT", 4: "SIGILL", 5: "SIGTRAP", 6: "SIGABRT", 7: "SIGBUS", 8: "SIGFPE", 9: "SIGKILL", 10: "SIGUSR1", 11: "SIGSEGV", 12: "SIGUSR2", 13: "SIGPIPE", 14: "SIGALRM", 15: "SIGTERM", 16: "SIGSTKFLT", 17: "SIGCHLD", 18: "SIGCONT", 19: "SIGSTOP", 20: "SIGTSTP", 21: "SIGTTIN", 22: "SIGTTOU", 23: "SIGURG", 24: "SIGXCPU", 25: "SIGXFSZ", 26: "SIGVTALRM", 27: "SIGPROF", 28: "SIGWINCH", 29: "SIGIO", 30: "SIGPWR", 31: "SIGSYS", 32: "SIGCANCEL", # from glibc source code 33: "SIGSETXID", # from glibc source code 34: "SIGRTMIN", # 35 ... 49: SIGRTMIN+i # 50 ... 63: SIGRTMAX-i 64: "SIGRTMAX", } for i in range(35, 50): self.signame_list[i] = "SIGRTMIN+{:d}".format(i - 34) for i in range(63, 49, -1): self.signame_list[i] = "SIGRTMAX-{:d}".format(64 - i) # task_struct->seccomp if self.args.print_seccomp: if self.offset_signal is None: self.offset_signal = self.get_offset_signal(self.offset_nsproxy) self.quiet_info("offsetof(task_struct, signal): {:#x}".format(self.offset_signal)) if self.offset_seccomp is None: self.offset_seccomp = self.get_offset_seccomp(task_addrs, self.offset_signal) if self.offset_seccomp is None: self.quiet_err("Could not find task_struct->seccomp") return False self.quiet_info("offsetof(task_struct, seccomp): {:#x}".format(self.offset_seccomp)) if self.offset_prev is None: self.offset_prev = self.get_offset_prev(task_addrs, self.offset_seccomp) if self.offset_prev is None: self.quiet_err("Could not find seccomp_filter->prev") return False self.quiet_info("offsetof(seccomp_filter, prev): {:#x}".format(self.offset_prev)) if self.offset_prog is None: self.offset_prog = self.get_offset_prog(self.offset_prev) if self.offset_prog is None: self.quiet_err("Could not find seccomp_filter->prog") return False self.quiet_info("offsetof(seccomp_filter, prog): {:#x}".format(self.offset_prog)) if self.offset_bpf_func is None: self.offset_bpf_func = self.get_offset_bpf_func(task_addrs, self.offset_seccomp, self.offset_prog) if self.offset_bpf_func is None: self.quiet_err("Could not find bpf_prog->bpf_func") return False self.quiet_info("offsetof(bpf_prog, bpf_func): {:#x}".format(self.offset_bpf_func)) if self.offset_orig_prog is None: self.offset_orig_prog = self.get_offset_orig_prog(self.offset_bpf_func) if self.offset_orig_prog is None: self.quiet_err("Could not find bpf_prog->orig_prog") return False self.quiet_info("offsetof(bpf_prog, orig_prog): {:#x}".format(self.offset_orig_prog)) self.offset_jited_len = 16 try: self.seccomp_tools_command = [GefUtil.which("ceccomp"), "disasm", "-c", "always"] self.quiet_info("ceccomp is found") except FileNotFoundError: try: self.seccomp_tools_command = [GefUtil.which("seccomp-tools"), "disasm"] self.quiet_info("seccomp-tools is found") if is_arm32(): self.quiet_warn("`seccomp-tools` is not supported on ARM32. " "Consider using `ceccomp` instead, as it supports ARM32.") self.quiet_info("GEF uses `capstone-disassemble bpf_func`") self.seccomp_tools_command = None except FileNotFoundError: self.quiet_info("Could not find ceccomp or seccomp-tools, GEF uses `capstone-disassemble bpf_func`") self.seccomp_tools_command = None return task_addrs def get_current_task_list(self): args = self.args # backup try: res = gdb.execute("kcurrent --quiet", to_string=True) except gdb.error: return {} # kcurrent calls ktask itself, so self.args will be overwritten. this is workaround. self.args = args # revert tmp_current_tasks = {} for line in res.splitlines(): r = re.search(r"current \(cpu(\d+)\): (0x\S+) .+", line.strip()) if r: cpu = int(r.group(1)) task = int(r.group(2), 16) new_list = tmp_current_tasks.get(task, []) + [cpu] tmp_current_tasks[task] = new_list continue r = re.search(r"current: (0x\S+) .+", line.strip()) if r: cpu = 0 task = int(r.group(1), 16) new_list = tmp_current_tasks.get(task, []) + [cpu] tmp_current_tasks[task] = new_list continue current_tasks = {} for k, v in tmp_current_tasks.items(): if len(v) > 1: # It is unclear whether this case can occur. current_tasks[k] = "cpu{:d},..".format(min(v)) else: current_tasks[k] = "cpu{:d}".format(v[0]) return current_tasks def dump(self, task_addrs): # add current tasks (cpuN > 0) current_tasks = self.get_current_task_list() to_add_tasks = [task for task in current_tasks.keys() if task not in task_addrs] task_addrs = task_addrs[:1] + to_add_tasks + task_addrs[1:] # LWP if self.args.print_thread: task_addrs = self.add_lwp_task(task_addrs) # print legend if not self.args.quiet: fmt = "{:<18s} {:7s} {:3s} {:<7s} {:<16s} {:<18s} [{:s}] {:<8s} {:<18s} {:<18s}" if self.args.print_all_id: ids_str = ["uid", "gid", "suid", "sgid", "euid", "egid", "fsuid", "fsgid"] uids_fmt = "{:>5s} {:>5s} {:>5s} {:>5s} {:>5s} {:>5s} {:>5s} {:>5s}" else: ids_str = ["uid", "gid"] uids_fmt = "{:>5s} {:>5s}" uids_str = uids_fmt.format(*ids_str) legend = [ "task", "current", "K/U", "lwpid", "task->comm", "task->cred", uids_str, "seccomp", "kstack", "kcanary", ] self.out.append(GefUtil.make_legend(fmt.format(*legend))) if self.args.print_namespace: kversion = Kernel.kernel_version() nsproxy_members = ["count", "uts_ns", "ipc_ns", "mnt_ns", "pid_ns_for_children", "net_ns"] if "5.6" <= kversion: nsproxy_members += ["time_ns", "time_ns_for_children"] if "4.6" <= kversion: nsproxy_members += ["cgroup_ns"] if task_addrs: init_cred = read_int_from_memory(task_addrs[0] + self.offset_cred) init_user_ns = read_int_from_memory(init_cred + self.offset_user_ns) init_nsproxy = read_int_from_memory(task_addrs[0] + self.offset_nsproxy) # task parse tqdm = GefUtil.get_tqdm(not self.args.quiet) for task in tqdm(task_addrs, leave=False, desc="task"): comm_string = read_cstring_from_memory(task + self.offset_comm) if self.args.filter: if not any(re_pattern.search(comm_string) for re_pattern in self.args.filter): continue if self.args.task_filter: if task not in self.args.task_filter: continue kstack = read_int_from_memory(task + self.offset_stack) pid = read_int32_from_memory(task + self.offset_pid) cred = read_int_from_memory(task + self.offset_cred) # current currentN = current_tasks.get(task, "-") # get process type (kernel or user-land) mm = read_int_from_memory(task + self.offset_mm) if mm == 0 or pid == 0: proctype = "K" else: proctype = "U" if self.args.user_process_only: if proctype == "K": continue # get process type (main process or not) if self.args.print_thread: leader = read_int_from_memory(task + self.offset_group_leader) if leader != task: proctype += "T" # uid if self.args.print_all_id: uids = [read_int32_from_memory(cred + self.offset_uid + j * 4) for j in range(8)] uids_fmt = "{:>5d},{:>5d},{:>5d},{:>5d},{:>5d},{:>5d},{:>5d},{:>5d}" else: uids = [read_int32_from_memory(cred + self.offset_uid + j * 4) for j in range(2)] uids_fmt = "{:>5d},{:>5d}" uids_str = uids_fmt.format(*uids) # kcanary if self.offset_kcanary: kcanary = read_int_from_memory(task + self.offset_kcanary) kcanary = "{:#018x}".format(kcanary) else: kcanary = "None" # seccomp if self.has_seccomp(task): seccomp = "Enabled" else: seccomp = "Disabled" # make output self.out.append("{:#018x} {:<7s} {:<3s} {:<7d} {:<16s} {:#018x} [{:s}] {:<8s} {:#018x} {:<18s}".format( task, currentN, proctype, pid, comm_string, cred, uids_str, seccomp, kstack, kcanary, ).rstrip()) # skip additional information when swapper/N if pid == 0: continue additional = False # additional information (maps) if self.args.print_maps: additional = True mms = self.get_mm(task, self.offset_mm) if mms: self.out.append(titlify("memory map of `{:s}`".format(comm_string))) for mm in mms: self.out.append("{:#018x}-{:#018x} {:s} {:s}".format( mm.start, mm.end, mm.flags, mm.file, ).rstrip()) # additional information (regs) if proctype == "U" and self.args.print_regs: additional = True regs = self.get_regs(kstack, self.offset_ptregs) nr_table = get_syscall_table().nr_table syscall_nr_regs = ["orig_rax", "orig_eax", "r7", "x8"] if regs: self.out.append(titlify("registers of `{:s}`".format(comm_string))) for k, v in regs.items(): if k in syscall_nr_regs and v in nr_table: syscall_name = nr_table[v].name self.out.append("{:16s}: {:s} ({:s})".format( k, AddressUtil.format_address(v, long_fmt=True), syscall_name, )) else: self.out.append("{:16s}: {:s}".format( k, AddressUtil.format_address(v, long_fmt=True, ))) # additional information (files) if proctype == "U" and self.args.print_fd: additional = True self.out.append(titlify("file descriptors of `{:s}`".format(comm_string))) fmt = "{:3s} {:18s} {:18s} {:18s} {:s}" legend = ["fd", "struct file", "struct dentry", "struct inode", "path"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) files = read_int_from_memory(task + self.offset_files) fdt = read_int_from_memory(files + self.offset_fdt) if is_valid_addr(fdt): max_fds = read_int32_from_memory(fdt) array = read_int_from_memory(fdt + current_arch.ptrsize) for i in range(max_fds): file = read_int_from_memory(array + current_arch.ptrsize * i) if file == 0: continue dentry = read_int_from_memory(file + self.offset_dentry) inode = read_int_from_memory(dentry + self.offset_d_inode) filepath = self.get_filepath(file) self.out.append("{:<3d} {:#018x} {:#018x} {:#018x} {:s}".format( i, file, dentry, inode, filepath, )) # additional information (sighands) if proctype == "U" and self.args.print_sighand: additional = True self.out.append(titlify("sighandlers of `{:s}`".format(comm_string))) fmt = "{:14s} {:18s} {:18s} {:18s}" legend = ["sig", "sigaction", "handler", "flags"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) sighand = read_int_from_memory(task + self.offset_sighand) for i in range(64): sigaction = sighand + self.offset_action + self.sizeof_action * i signame = self.signame_list.get(i + 1, "???") handler = read_int_from_memory(sigaction + current_arch.ptrsize * 0) if handler == 0: handler = "SIG_DFL" elif handler == 1: handler = "SIG_IGN" elif handler == -1: handler = "SIG_ERR" else: handler = "{:#018x}".format(handler) flags = read_int_from_memory(sigaction + current_arch.ptrsize * 1) self.out.append("{:<2d} {:11s} {:#018x} {:18s} {:#018x}".format( i + 1, signame, sigaction, handler, flags, )) # additional information (namespace) if proctype == "U" and self.args.print_namespace: additional = True self.out.append(titlify("namespace of `{:s}`".format(comm_string))) fmt = "{:30s} {:18s} {:8s}" legend = ["name", "value", "init_ns?"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # user_ns (via real_cred) real_cred = read_int_from_memory(task + self.offset_cred - current_arch.ptrsize) user_ns = read_int_from_memory(real_cred + self.offset_user_ns) is_init_ns = str(user_ns == init_user_ns) self.out.append("{:30s} {:#018x} {:8s}".format("real_cred->user_ns", user_ns, is_init_ns).rstrip()) # other ns (via nsproxy) nsproxy = read_int_from_memory(task + self.offset_nsproxy) for i, name in enumerate(nsproxy_members): value = read_int_from_memory(nsproxy + current_arch.ptrsize * i) if i == 0: is_init_ns = "-" else: init_value = read_int_from_memory(init_nsproxy + current_arch.ptrsize * i) is_init_ns = str(value == init_value) self.out.append("{:30s} {:#018x} {:8s}".format("nsproxy->" + name, value, is_init_ns).rstrip()) # additional information (seccomp) if proctype == "U" and self.args.print_seccomp: if self.has_seccomp(task): additional = True self.out.append(titlify("seccomp of `{:s}`".format(comm_string))) fmt = "{:18s} {:25s} {:12s} {:18s}" legend = ["&task.seccomp", "mode", "filter_count", "filter"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) seccomp = task + self.offset_seccomp mode = read_int32_from_memory(seccomp) mode_define = { 0: "SECCOMP_MODE_DISABLED", 1: "SECCOMP_MODE_STRICT", 2: "SECCOMP_MODE_FILTER", }.get(mode, "UNKNOWN") mode_str = "{:d} ({:s})".format(mode, mode_define) filter_count = read_int32_from_memory(seccomp + 4) filter_current = read_int_from_memory(seccomp + 4 * 2) self.out.append("{:#018x} {:25s} {:<12d} {:#018x}".format( seccomp, mode_str, filter_count, filter_current, )) for i in tqdm(range(filter_count), leave=False, desc="filter"): if not filter_current: break prog = read_int_from_memory(filter_current + self.offset_prog) filter_prev = read_int_from_memory(filter_current + self.offset_prev) bpf_func = read_int_from_memory(prog + self.offset_bpf_func) orig_prog = read_int_from_memory(prog + self.offset_orig_prog) jited_len = read_int32_from_memory(prog + self.offset_jited_len) self.out.append("") self.out.append( "[{:d}/{:d}] filter:{:#x} prev:{:#x} prog:{:#x} bpf_func:{:#x} jited_len:{:#x} orig_prog:{:#x}".format( i + 1, filter_count, filter_current, filter_prev, prog, bpf_func, jited_len, orig_prog, ) ) if self.seccomp_tools_command and is_valid_addr(orig_prog): # use seccomp-tools or ceccomp cnt = read_int16_from_memory(orig_prog) prog = read_int_from_memory(orig_prog + current_arch.ptrsize) data = read_memory(prog, cnt * 8) tmp_fd, tmp_path = GefUtil.mkstemp(prefix="ktask") os.fdopen(tmp_fd, "wb").write(data) ret = GefUtil.gef_execute_external( self.seccomp_tools_command + [tmp_path], as_list=True, ) self.out.extend(ret) os.unlink(tmp_path) elif is_valid_addr(bpf_func): try: __import__("capstone") # use capstone data = read_memory(bpf_func, jited_len) dump_count = 0 for insn in Disasm.capstone_disassemble(bpf_func, jited_len, code=data.hex()): msg = insn.colored_text(10) self.out.append(msg) dump_count += insn.size if dump_count >= jited_len: break except ImportError: ret = gdb.execute("x/40i {:#x}".format(bpf_func), to_string=True).rstrip() self.out.append(ret) self.out.append("...") else: self.err_add_out("Memory read error") filter_current = filter_prev # print separator if additional: self.out.append(titlify("")) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") if self.args.all: self.args.print_maps = True self.args.print_regs = True self.args.print_all_id = True self.args.print_thread = True self.args.print_fd = True self.args.print_sighand = True self.args.print_seccomp = True self.args.print_namespace = True # initialize self.filepath_cache = {} ret = self.initialize() if ret is False: return task_addrs = ret # skip real parse if specified --meta option if args.meta: return # parse self.out = [] self.dump(task_addrs) self.print_output(check_terminal_size=True) return @register_command class KernelFilesCommand(GenericCommand): """Display open files for each process (shortcut for `ktask -quF`).""" _cmdline_ = "kfiles" _category_ = "06-f. Qemu-system/KGDB Cooperation - Linux Task" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): info("Redirect to `ktask -quF`") no_pager = "" if args.no_pager: no_pager = "--no-pager" gdb.execute("ktask --user-process-only --print-fd --quiet {:s}".format(no_pager)) return @register_command class KernelSavedRegsCommand(GenericCommand): """Display saved registers for each process (shortcut for `ktask -qur`).""" _cmdline_ = "kregs" _category_ = "06-f. Qemu-system/KGDB Cooperation - Linux Task" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): info("Redirect to `ktask -qur`") no_pager = "" if args.no_pager: no_pager = "--no-pager" gdb.execute("ktask --user-process-only --print-regs --quiet {:s}".format(no_pager)) return @register_command class KernelSignalsCommand(GenericCommand): """Display signal handlers for each process (shortcut for `ktask -qus`).""" _cmdline_ = "ksighands" _category_ = "06-f. Qemu-system/KGDB Cooperation - Linux Task" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): info("Redirect to `ktask -qus`") no_pager = "" if args.no_pager: no_pager = "--no-pager" gdb.execute("ktask --user-process-only --print-sighand --quiet {:s}".format(no_pager)) return @register_command class KernelNamespacesCommand(GenericCommand): """Display namespaces for each process (shortcut for `ktask -quN`).""" _cmdline_ = "knamespaces" _category_ = "06-f. Qemu-system/KGDB Cooperation - Linux Task" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): info("Redirect to `ktask -quN`") no_pager = "" if args.no_pager: no_pager = "--no-pager" gdb.execute("ktask --user-process-only --print-namespace --quiet {:s}".format(no_pager)) return @register_command class KernelLoadCommand(GenericCommand): """Load the vmlinux without a load address.""" _cmdline_ = "kload" _category_ = "06-e. Qemu-system/KGDB Cooperation - Linux Symbol/Type" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("path", metavar="VMLINUX_PATH", type=str, help="path of the vmlinux.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_FILENAME) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) def do_invoke(self, args): if not os.path.exists(args.path): err("Invalid path") return info("Wait for memory scan") text_base = Kernel.get_kernel_base() if text_base is None: err("The kernel base is unknown") return gdb.execute("add-symbol-file {!r} {:#x}".format(args.path, text_base)) return @register_command class KernelModuleCommand(GenericCommand, BufferingOutput): """Display kernel module list.""" _cmdline_ = "kmod" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") group = parser.add_mutually_exclusive_group(required=False) group.add_argument("-s", "--resolve-symbol", action="store_true", help="try to resolve symbols.") group.add_argument("-a", "--apply-symbol", action="store_true", help="try to apply symbol in the form 'module_name.symbol'.") parser.add_argument("--symbol-unsort", action="store_true", help="print resolved symbols without sorting by address.") parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="REGEXP filter.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -q", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "", "Simplified module structure:", "", " +-module------------------+", "+-modules-----+ | ... |", "| list_head |--->| list |--->...", "+-------------+ | name[] |", " | ... |", " | mem[] (v6.4~) |", " | base |", " | size |", " | ... |", " | init_layout (v4.5~v6.4) |", " | base |", " | size |", " | text_size |", " | ro_size |", " | ro_after_init_size |", " | ... |", " | module_core (~v4.4) |", " | init_size (~v4.4) |", " | core_size (~v4.4) |", " | init_text_size (~v4.4) | +-->+-mod_kallsyms---+", " | core_text_size (~v4.4) | | | symtab |", " | ... | | | num_symtab |", " | kallsyms |--+ | strtab |", " | ... | | typetab (v5.2~)|", " +-------------------------+ +----------------+", "", "Notes for -a option:", "- You can check the added symbols with the `symbols` command.", "- Added symbols are in the format `module_name.symbol` to avoid collisions.", " When used from the command line, they must be enclosed in single quotes.", " e.g., `p 'virtio_net.__this_module'`", ] _note_ = "\n".join(_note_) def get_modules_list(self, modules): # use cache if self.module_addrs: return self.module_addrs # slow path if modules is None: return None module_addrs = [] current = modules while True: try: addr = read_int_from_memory(current) except gdb.MemoryError: return None if addr == modules: break module_addrs.append(addr - current_arch.ptrsize) current = addr return module_addrs def get_offset_name(self, module_addrs): # fast path try: return to_unsigned_long(gdb.parse_and_eval("&((struct module*)0).name")) except gdb.error: pass # slow_path for i in range(0x100): offset_name = i * current_arch.ptrsize valid = True for module in module_addrs: if not is_ascii_string(module + offset_name): valid = False break s = read_cstring_from_memory(module + offset_name) if len(s) < 2: valid = False break if valid: return offset_name return None def get_offset_mem(self, module_addrs): # v6.4~ """ ac3b43283923440900b4f36ca5f9f0b1ca43b70e changed the module layout information structure MOD_TEXT = 0, MOD_DATA, MOD_RODATA, MOD_RO_AFTER_INIT, MOD_INIT_TEXT, MOD_INIT_DATA, MOD_INIT_RODATA, struct module { enum module_state state; struct list_head list; char name[MODULE_NAME_LEN]; // 64 - sizeof(unsigned long) bytes #ifdef CONFIG_STACKTRACE_BUILD_ID unsigned char build_id[BUILD_ID_SIZE_MAX]; // 20 bytes #endif struct module_kobject mkobj; struct module_attribute *modinfo_attrs; const char *version; const char *srcversion; struct kobject *holders_dir; const struct kernel_symbol *syms; const s32 *crcs; unsigned int num_syms; #ifdef CONFIG_ARCH_USES_CFI_TRAPS s32 *kcfi_traps; s32 *kcfi_traps_end; #endif #ifdef CONFIG_SYSFS struct mutex param_lock; #endif struct kernel_param *kp; unsigned int num_kp; unsigned int num_gpl_syms; const struct kernel_symbol *gpl_syms; const s32 *gpl_crcs; bool using_gplonly_symbols; #ifdef CONFIG_MODULE_SIG bool sig_ok; #endif bool async_probe_requested; unsigned int num_exentries; struct exception_table_entry *extable; int (*init)(void); struct module_memory mem[MOD_MEM_NUM_TYPES] __module_memory_align; <-- here struct mod_arch_specific arch; unsigned long taints; #ifdef CONFIG_GENERIC_BUG unsigned num_bugs; struct list_head bug_list; struct bug_entry *bug_table; #endif #ifdef CONFIG_KALLSYMS struct mod_kallsyms __rcu *kallsyms; struct mod_kallsyms core_kallsyms; struct module_sect_attrs *sect_attrs; struct module_notes_attrs *notes_attrs; #endif }; struct module_memory { void *base; void *rw_copy; // v6.13~ bool is_rox; // v6.13~ unsigned int size; #ifdef CONFIG_MODULES_TREE_LOOKUP struct mod_tree_node mtn; (0x38) #endif }; """ # fast path try: offset_mem = to_unsigned_long(gdb.parse_and_eval("&((struct module*)0).mem")) offset_size = to_unsigned_long(gdb.parse_and_eval("&((struct module_memory*)0).size")) return offset_mem, offset_mem + offset_size except gdb.error: pass # slow_path MOD_TEXT = 0 MOD_DATA = 1 MOD_RODATA = 2 MOD_RO_AFTER_INIT = 3 # noqa: F841 MOD_INIT_TEXT = 4 # noqa: F841 MOD_INIT_DATA = 5 # noqa: F841 MOD_INIT_RODATA = 6 # noqa: F841 MOD_MEM_NUM_TYPES = 7 # noqa: F841 # TODO: only handles non init module type for i in range(300): offset_mem = i * current_arch.ptrsize for sizeof_module_memory in (8, 0x48, 0x50): valid = True for module in module_addrs: for mem_type in (MOD_TEXT, MOD_DATA, MOD_RODATA): mem_ptr = module + offset_mem + mem_type * sizeof_module_memory # memory access check if not is_valid_addr(mem_ptr): valid = False break # base align check cand_base = read_int_from_memory(mem_ptr) if cand_base == 0 or cand_base & 0xfff: valid = False break # size check if sizeof_module_memory == 0x50: offset_size = current_arch.ptrsize * 2 + 4 # void*, void*, bool else: offset_size = current_arch.ptrsize # void* cand_size = read_int32_from_memory(mem_ptr + offset_size) if cand_size == 0 or cand_size > 0x10_0000: valid = False break if valid: return offset_mem, offset_mem + offset_size return None def get_offset_init_layout(self, module_addrs): # v4.5 ~ v6.4 """ struct module { // kernel v4.5~ enum module_state state; struct list_head list; char name[MODULE_NAME_LEN]; // 64 - sizeof(unsigned long) bytes #ifdef CONFIG_STACKTRACE_BUILD_ID unsigned char build_id[BUILD_ID_SIZE_MAX]; // 20 bytes #endif struct module_kobject mkobj; struct module_attribute *modinfo_attrs; const char *version; const char *srcversion; struct kobject *holders_dir; const struct kernel_symbol *syms; const s32 *crcs; unsigned int num_syms; #ifdef CONFIG_CFI_CLANG cfi_check_fn cfi_check; #endif #ifdef CONFIG_SYSFS struct mutex param_lock; #endif struct kernel_param *kp; unsigned int num_kp; unsigned int num_gpl_syms; const struct kernel_symbol *gpl_syms; const s32 *gpl_crcs; bool using_gplonly_symbols; #ifdef CONFIG_MODULE_SIG bool sig_ok; #endif bool async_probe_requested; unsigned int num_exentries; struct exception_table_entry *extable; int (*init)(void); struct module_layout core_layout __module_layout_align; struct module_layout init_layout; <-- here #ifdef CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC struct module_layout data_layout; #endif struct mod_arch_specific arch; unsigned long taints; #ifdef CONFIG_GENERIC_BUG unsigned num_bugs; struct list_head bug_list; struct bug_entry *bug_table; #endif #ifdef CONFIG_KALLSYMS struct mod_kallsyms __rcu *kallsyms; struct mod_kallsyms core_kallsyms; struct module_sect_attrs *sect_attrs; struct module_notes_attrs *notes_attrs; #endif ... }; struct module_layout { /* The actual code + data. */ void *base; /* Total size. */ unsigned int size; /* The size of the executable code. */ unsigned int text_size; /* Size of RO section of the module (text+rodata) */ unsigned int ro_size; /* Size of RO after init section */ unsigned int ro_after_init_size; #ifdef CONFIG_MODULES_TREE_LOOKUP struct mod_tree_node mtn; #endif }; [Example arm32] gef> x/128xw 0x00000000bf22b084 0xbf22b084: 0xbf1bb044 0xc1696530 0x00006773 0x00000000 0xbf22b094: 0x00000000 0x00000000 0x00000000 0x00000000 0xbf22b0a4: 0x00000000 0x00000000 0x00000000 0x00000000 0xbf22b0b4: 0x00000000 0x00000000 0x00000000 0x00000000 0xbf22b0c4: 0x00000000 0xc1ec2d00 0xc1a11d80 0xbf1bb08c 0xbf22b0d4: 0xc1a11d8c 0xc1a11d80 0xc1628e38 0xc8e0b2c0 0xbf22b0e4: 0x00000003 0x00000007 0xbf22b080 0x00000000 0xbf22b0f4: 0xc8d4f380 0x00000000 0xc1e47400 0xc8f6d900 0xbf22b104: 0xc8f6d080 0xc8004300 0x00000000 0x00000000 0xbf22b114: 0x00000000 0x00000000 0x00000000 0x00000000 0xbf22b124: 0xbf22b124 0xbf22b124 0xbf22a990 0x00000003 0xbf22b134: 0x00000000 0x00000000 0x00000000 0x00000001 0xbf22b144: 0x00000000 0x00000000 0x00000000 0x00000000 0xbf22b154: 0x00000000 0xbf17e000 0x00000000 0x00000000 0xbf22b164: 0x00000000 0x00000000 0x00000000 0x00000000 0xbf22b174: 0x00000000 0x00000000 0x00000000 0xbf225000 <- init_layout.base 0xbf22b184: 0x00008000 0x00005000 0x00006000 0x00006000 """ # fast path try: return to_unsigned_long(gdb.parse_and_eval("&((struct module*)0).init_layout")) except gdb.error: pass # slow_path for i in range(300): offset_init_layout = i * current_arch.ptrsize valid = True for module in module_addrs: # memory access check init_layout_ptr = module + offset_init_layout if not is_valid_addr(init_layout_ptr): valid = False break # base align check cand_base = read_int_from_memory(init_layout_ptr) if cand_base == 0 or cand_base & 0xfff: valid = False break # size check cand_size = read_int32_from_memory(init_layout_ptr + current_arch.ptrsize) if cand_size == 0 or cand_size > 0x20_0000: valid = False break # text_size check cand_text_size = read_int32_from_memory(init_layout_ptr + current_arch.ptrsize + 4 * 1) if cand_text_size == 0 or cand_text_size > 0x20_0000: valid = False break # ro_size check cand_ro_size = read_int32_from_memory(init_layout_ptr + current_arch.ptrsize + 4 * 2) if cand_ro_size == 0 or cand_ro_size > 0x20_0000: valid = False break # ro_after_init_size check cand_ro_after_init_size = read_int32_from_memory(init_layout_ptr + current_arch.ptrsize + 4 * 3) if cand_ro_after_init_size == 0 or cand_ro_after_init_size > 0x20_0000: valid = False break if valid: return offset_init_layout return None def get_offset_module_core(self, module_addrs): # ~v4.4 """ struct module { // ~v4.4 enum module_state state; struct list_head list; char name[MODULE_NAME_LEN]; struct module_kobject mkobj; struct module_attribute *modinfo_attrs; const char *version; const char *srcversion; struct kobject *holders_dir; const struct kernel_symbol *syms; const unsigned long *crcs; unsigned int num_syms; #ifdef CONFIG_SYSFS struct mutex param_lock; #endif struct kernel_param *kp; unsigned int num_kp; unsigned int num_gpl_syms; const struct kernel_symbol *gpl_syms; const unsigned long *gpl_crcs; #ifdef CONFIG_UNUSED_SYMBOLS const struct kernel_symbol *unused_syms; const unsigned long *unused_crcs; unsigned int num_unused_syms; unsigned int num_unused_gpl_syms; const struct kernel_symbol *unused_gpl_syms; const unsigned long *unused_gpl_crcs; #endif #ifdef CONFIG_MODULE_SIG bool sig_ok; #endif bool async_probe_requested; const struct kernel_symbol *gpl_future_syms; const unsigned long *gpl_future_crcs; unsigned int num_gpl_future_syms; unsigned int num_exentries; struct exception_table_entry *extable; int (*init)(void); void *module_init ____cacheline_aligned; /* Here is the actual code + data, vfree'd on unload. */ void *module_core; <-- here /* Here are the sizes of the init and core sections */ unsigned int init_size, core_size; /* The size of the executable code in each section. */ unsigned int init_text_size, core_text_size; #ifdef CONFIG_MODULES_TREE_LOOKUP struct mod_tree_node mtn_core; struct mod_tree_node mtn_init; #endif unsigned int init_ro_size, core_ro_size; struct mod_arch_specific arch; unsigned int taints; #ifdef CONFIG_GENERIC_BUG unsigned num_bugs; struct list_head bug_list; struct bug_entry *bug_table; #endif #ifdef CONFIG_KALLSYMS struct mod_kallsyms *kallsyms; <-- here struct mod_kallsyms core_kallsyms; struct module_sect_attrs *sect_attrs; struct module_notes_attrs *notes_attrs; #endif ... }; """ # fast path try: return to_unsigned_long(gdb.parse_and_eval("&((struct module*)0).module_core")) except gdb.error: pass # slow_path for i in range(300): offset_module_core = i * current_arch.ptrsize valid = True for module in module_addrs: module_core_ptr = module + offset_module_core # memory access check if not is_valid_addr(module_core_ptr): valid = False break # module_core align check cand_module_core = read_int_from_memory(module_core_ptr) if cand_module_core == 0 or cand_module_core & 0xfff: valid = False break # init_size check cand_init_size = read_int32_from_memory(module_core_ptr + current_arch.ptrsize) if cand_init_size > 0x10_0000: valid = False break # core_size check cand_core_size = read_int32_from_memory(module_core_ptr + current_arch.ptrsize + 4 * 1) if cand_core_size == 0 or cand_core_size > 0x10_0000: valid = False break # init_text_size check cand_init_text_size = read_int32_from_memory(module_core_ptr + current_arch.ptrsize + 4 * 2) if cand_init_text_size > 0x10_0000: valid = False break # core_text_size check cand_core_text_size = read_int32_from_memory(module_core_ptr + current_arch.ptrsize + 4 * 3) if cand_core_text_size == 0 or cand_core_text_size > 0x10_0000: valid = False break if valid: return offset_module_core return None def get_offset_kallsyms(self, module_addrs): """ struct mod_kallsyms { Elf_Sym *symtab; unsigned int num_symtab; char *strtab; char *typetab; // v5.2~ }; """ # fast path try: return to_unsigned_long(gdb.parse_and_eval("&((struct module*)0).kallsyms")) except gdb.error: pass # slow_path kversion = Kernel.kernel_version() for i in range(300): offset_kallsyms = i * current_arch.ptrsize valid = True for module in module_addrs: kallsyms_ptr = module + offset_kallsyms # access check if not is_valid_addr(kallsyms_ptr): valid = False break # kallsyms access check cand_kallsyms = read_int_from_memory(kallsyms_ptr) if not is_valid_addr(cand_kallsyms): valid = False break # struct mod_kallsyms member access check cand_symtab = read_int_from_memory(cand_kallsyms) if not is_valid_addr(cand_symtab): valid = False break cand_num_symtab = read_int_from_memory(cand_kallsyms + current_arch.ptrsize * 1) if is_valid_addr(cand_num_symtab) or cand_num_symtab == 0: valid = False break cand_strtab = read_int_from_memory(cand_kallsyms + current_arch.ptrsize * 2) if not is_valid_addr(cand_strtab): valid = False break if "5.2" <= kversion: cand_typetab = read_int_from_memory(cand_kallsyms + current_arch.ptrsize * 3) if not is_valid_addr(cand_typetab): valid = False break if valid: return offset_kallsyms return None def initialize(self): if hasattr(self, "initialized"): return True kversion = Kernel.kernel_version() if kversion is None: self.quiet_err("Failed to resolve kernel version") return False # modules self.modules = KernelAddressHeuristicFinder.get_modules() if self.modules is None: self.quiet_err("Could not find modules (CONFIG_MODULES may not be set)") return False self.quiet_info("modules: {:#x}".format(self.modules)) # modules list self.module_addrs = self.get_modules_list(self.modules) if self.module_addrs is None: return False if self.module_addrs == []: self.quiet_err("Could not find any modules") return False # module->name self.offset_name = self.get_offset_name(self.module_addrs) if self.offset_name is None: self.quiet_err("Could not find module->name[MODULE_NAME_LEN]") return False self.quiet_info("offsetof(module, name): {:#x}".format(self.offset_name)) # modules->{mem,mem_size,module_core} if "6.4" <= kversion: ret = self.get_offset_mem(self.module_addrs) if ret is None: self.quiet_err("Could not find module->mem") return False self.offset_mem, self.offset_mem_size = ret self.quiet_info("offsetof(module, mem): {:#x}".format(self.offset_mem)) self.quiet_info("offsetof(module, mem.size): {:#x}".format(self.offset_mem_size)) elif "4.5" <= kversion: self.offset_init_layout = self.get_offset_init_layout(self.module_addrs) if self.offset_init_layout is None: self.quiet_err("Could not find module->init_layout") return False self.quiet_info("offsetof(module, init_layout): {:#x}".format(self.offset_init_layout)) else: # kversion < v4.5 self.offset_module_core = self.get_offset_module_core(self.module_addrs) if self.offset_module_core is None: self.quiet_err("Could not find module->module_core") return False self.quiet_info("offsetof(module, module_core): {:#x}".format(self.offset_module_core)) # module->kallsyms self.offset_kallsyms = self.get_offset_kallsyms(self.module_addrs) if self.offset_kallsyms is None: self.quiet_err("Could not find module->kallsyms") return False self.quiet_info("offsetof(module, kallsyms): {:#x}".format(self.offset_kallsyms)) self.initialized = True return True def parse_kallsyms(self, kallsyms): kversion = Kernel.kernel_version() symtab = read_int_from_memory(kallsyms + current_arch.ptrsize * 0) sizeof_symtab_entry = 24 if is_64bit() else 16 num_symtab = read_int_from_memory(kallsyms + current_arch.ptrsize * 1) strtab = read_int_from_memory(kallsyms + current_arch.ptrsize * 2) strtab_pos = 0 if "5.2" <= kversion: typetab = read_int_from_memory(kallsyms + current_arch.ptrsize * 3) #gef_print("symtab: {:#x}".format(symtab)) #gef_print("sizeof_symtab_entry: {:#x}".format(sizeof_symtab_entry)) #gef_print("num_symab: {:#x}".format(num_symtab)) #gef_print("strtab: {:#x}".format(strtab)) #if "5.2" <= kversion: # gef_print("typetab: {:#x}".format(typetab)) entries = [] tqdm = GefUtil.get_tqdm(not self.args.quiet and not self.args.apply_symbol) for i in tqdm(range(num_symtab), leave=False): sym_addr = read_int_from_memory(symtab + sizeof_symtab_entry * i + current_arch.ptrsize) sym_name = read_cstring_from_memory(strtab + strtab_pos) strtab_pos += len(sym_name) + 1 if "5.2" <= kversion: sym_type = chr(read_int8_from_memory(typetab + i)) elif "5.0" <= kversion: # st_size if is_64bit(): sym_type = chr(read_int8_from_memory(symtab + sizeof_symtab_entry * i + 16)) else: sym_type = chr(read_int8_from_memory(symtab + sizeof_symtab_entry * i + 8)) else: # st_info if is_64bit(): sym_type = chr(read_int8_from_memory(symtab + sizeof_symtab_entry * i + 4)) else: sym_type = chr(read_int8_from_memory(symtab + sizeof_symtab_entry * i + 12)) entries.append([sym_addr, sym_type, sym_name]) return entries def print_symbol(self, entries, symbol_unsort): self.out.append(titlify("module symbols")) # symbol_unsort is used for debugging. if not symbol_unsort: entries = sorted(entries) # add output for sym_addr, sym_type, sym_name in entries: self.out.append("{:#018x} {:s} {:s}".format(sym_addr, sym_type, sym_name)) self.out.append(titlify("")) return def apply_symbol(self, module_name, text_base, entries): # remove old file sym_elf_path = os.path.join(GEF_TEMP_DIR, "kmod-{:s}.elf".format(module_name)) if os.path.exists(sym_elf_path): os.unlink(sym_elf_path) # make blank ELF text_base &= get_pagesize_mask_high() text_end = entries[-1][0] blank_elf = AddSymbolTemporaryCommand.create_blank_elf(text_base, text_end) if blank_elf is None: self.quiet_err("Failed to create blank ELF") return # create command cmd_string_arr = [] for sym_addr, sym_type, sym_name in entries: if sym_addr < text_base: continue if sym_type in ["T", "t", "W", None]: type_flag = "function" else: type_flag = "object" if sym_type and sym_type in "abcdefghijklmnopqrstuvwxyz": global_flag = "local" else: global_flag = "global" # higher address needs relative relative_addr = sym_addr - text_base cmd_string_arr.append("--add-symbol") cmd_string_arr.append("{:s}.{:s}=.text:{:#x},{:s},{:s}".format( # modules often contain the same symbols, such as "__this_module". # to avoid collisions, register them in the form "module_name.symbol". module_name, sym_name, relative_addr, global_flag, type_flag, )) # embedding symbols objcopy = GefUtil.which(Config.get_gef_setting("gef.objcopy_command")) processed_count = 0 for cmd_string_arr_sliced in slicer(cmd_string_arr, 10000 * 2): subprocess.check_output([objcopy] + cmd_string_arr_sliced + [blank_elf]) processed_count += len(cmd_string_arr_sliced) // 2 self.quiet_info("{:s}: {:d} entries were processed".format(module_name, processed_count)) os.rename(blank_elf, sym_elf_path) # apply cmd = "add-symbol-file {!r} {:#x}".format(sym_elf_path, text_base) self.quiet_info("Execute `{:s}`".format(cmd)) gdb.execute(cmd, to_string=True) return def parse_module(self): if not self.args.apply_symbol: if not self.args.quiet: fmt = "{:<18s} {:<24s} {:<18s} {:<18s}" legend = ["module", "module->name", "base", "size"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) kversion = Kernel.kernel_version() tqdm = GefUtil.get_tqdm(not self.args.quiet and not self.args.apply_symbol) for module in tqdm(self.module_addrs, leave=False): name_string = read_cstring_from_memory(module + self.offset_name) if self.args.filter: if not any(re_pattern.search(name_string) for re_pattern in self.args.filter): continue if "6.4" <= kversion: base = read_int_from_memory(module + self.offset_mem) size = read_int32_from_memory(module + self.offset_mem_size) elif "4.5" <= kversion: base = read_int_from_memory(module + self.offset_init_layout) size = read_int32_from_memory(module + self.offset_init_layout + current_arch.ptrsize) else: # kversion < "4.5" base = read_int_from_memory(module + self.offset_module_core) size = read_int32_from_memory(module + self.offset_module_core + current_arch.ptrsize + 4) if not self.args.apply_symbol: self.out.append("{:#018x} {:<24s} {:#018x} {:#018x}".format(module, name_string, base, size)) if self.args.resolve_symbol: kallsyms = read_int_from_memory(module + self.offset_kallsyms) entries = self.parse_kallsyms(kallsyms) self.print_symbol(entries, self.args.symbol_unsort) elif self.args.apply_symbol: kallsyms = read_int_from_memory(module + self.offset_kallsyms) entries = self.parse_kallsyms(kallsyms) self.apply_symbol(name_string, base, entries) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") # initialize self.module_addrs = None ret = self.initialize() if not ret: self.quiet_err("Failed to initialize") return # get module addrs if not self.module_addrs: self.module_addrs = self.get_modules_list(self.modules) if self.module_addrs is None: return if self.module_addrs == []: self.quiet_err("Could not find any modules") return self.quiet_info("Num of modules: {:#x}".format(len(self.module_addrs))) # parse modules self.out = [] self.parse_module() self.print_output(check_terminal_size=True) return @register_command class KernelModuleLoadCommand(GenericCommand): """Load the kernel module without a load address.""" _cmdline_ = "kmod-load" _category_ = "06-e. Qemu-system/KGDB Cooperation - Linux Symbol/Type" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("name", type=str, help="name of the loaded module to search for by `kmod`.") parser.add_argument("path", type=str, help="path to compiled kernel module.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} sample /path/to/sample.ko", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "It is useful if you have a kernel module with debuginfo at hand.", ] _note_ = "\n".join(_note_) def get_modules_list(self, modules): # use cache if self.module_addrs: return self.module_addrs # slow path if modules is None: return None module_addrs = [] current = modules while True: try: addr = read_int_from_memory(current) except gdb.MemoryError: return None if addr == modules: break module_addrs.append(addr - current_arch.ptrsize) current = addr return module_addrs def get_offset_name(self, module_addrs): # fast path try: return to_unsigned_long(gdb.parse_and_eval("&((struct module*)0).name")) except gdb.error: pass # slow_path for i in range(0x100): offset_name = i * current_arch.ptrsize valid = True for module in module_addrs: if not is_ascii_string(module + offset_name): valid = False break s = read_cstring_from_memory(module + offset_name) if len(s) < 2: valid = False break if valid: return offset_name return None def get_offset_sect_attrs(self, module_addrs): """ struct attribute { const char *name; umode_t mode; #ifdef CONFIG_DEBUG_LOCK_ALLOC bool ignore_lockdep:1; struct lock_class_key *key; struct lock_class_key skey; #endif }; struct module_sect_attrs { struct attribute_group { const char *name; umode_t (*is_visible)(struct kobject *, struct attribute *, int); umode_t (*is_bin_visible)(struct kobject *, struct bin_attribute *, int); // v4.4~ size_t (*bin_size)(struct kobject *, const struct bin_attribute *, int); // v6.13~ struct attribute **attrs; struct bin_attribute **bin_attrs; // v3.11~v6.12 union { struct bin_attribute **bin_attrs; const struct bin_attribute *const *bin_attrs_new; }; // v6.13~ } grp; unsigned int nsections; struct module_sect_attr { struct bin_attribute { // v5.7~ struct attribute attr; size_t size; void *private; struct address_space *(*f_mapping)(void); // v5.15~ struct address_space *mapping; // v5.12~5.14 ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); ssize_t (*read_new)(struct file *, struct kobject *, const struct bin_attribute *, char *, loff_t, size_t); // v6.13~ ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); ssize_t (*write_new)(struct file *, struct kobject *, const struct bin_attribute *, char *, loff_t, size_t); // v6.13~ loff_t (*llseek)(struct file *, struct kobject *, struct bin_attribute *, loff_t, int); // v6.7~ int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma); } battr; struct module_attribute { // ~v5.6 struct attribute attr; ssize_t (*show)(struct module_attribute *, struct module_kobject *, char *); ssize_t (*store)(struct module_attribute *, struct module_kobject *, const char *, size_t count); void (*setup)(struct module *, const char *); int (*test)(struct module *); void (*free)(struct module *); } mattr; char *name; // ~v5.6 unsigned long address; } attrs[]; }; """ # fast path try: return to_unsigned_long(gdb.parse_and_eval("&((struct module*)0).sect_attrs")) except gdb.error: pass # slow_path for i in range(300): offset_sect_attrs = current_arch.ptrsize * i valid = True for module in module_addrs: # access check if not is_valid_addr(module + offset_sect_attrs): valid = False break sect_attrs = read_int_from_memory(module + offset_sect_attrs) if not is_valid_addr(sect_attrs): valid = False break firstname = read_int_from_memory(sect_attrs + self.offset_firstname) if not is_valid_addr(firstname): valid = False break sectname = read_cstring_from_memory(firstname) # well formed kernel module sections *should* only start with these prefixes # might be better to do a length based heuristic? if sectname is None or not sectname.startswith((".", "__")): if sectname and len(sectname) >= 4: self.quiet_info( "possible section name (rejected for not starting with . or __): {:s}".format(sectname), ) valid = False break if valid: return offset_sect_attrs return None def initialize(self): if hasattr(self, "initialized"): return True kversion = Kernel.kernel_version() if kversion is None: self.quiet_err("Failed to resolve kernel version") return False if kversion < "3.0": self.quiet_err("Unsupported before v3.0") return False # modules self.modules = KernelAddressHeuristicFinder.get_modules() if self.modules is None: self.quiet_err("Could not find modules (maybe, CONFIG_MODULES is not set)") return False self.quiet_info("modules: {:#x}".format(self.modules)) # modules list self.module_addrs = self.get_modules_list(self.modules) if self.module_addrs is None: return False if self.module_addrs == []: self.quiet_err("Could not find any modules") return False # module->name self.offset_name = self.get_offset_name(self.module_addrs) if self.offset_name is None: self.quiet_err("Could not find module->name[MODULE_NAME_LEN]") return False self.quiet_info("offsetof(module, name): {:#x}".format(self.offset_name)) # module->{nsections,firstname} if kversion < "3.11": self.offset_nsections = current_arch.ptrsize * 3 self.offset_firstname = current_arch.ptrsize * 4 elif kversion < "4.4": self.offset_nsections = current_arch.ptrsize * 4 self.offset_firstname = current_arch.ptrsize * 5 elif kversion < "6.13": self.offset_nsections = current_arch.ptrsize * 5 self.offset_firstname = current_arch.ptrsize * 6 else: self.offset_nsections = current_arch.ptrsize * 6 self.offset_firstname = current_arch.ptrsize * 7 # module->{address,module_sect_attr} if kversion < "5.7": self.offset_address = self.offset_firstname + current_arch.ptrsize * 8 self.sizeof_module_sect_attr = current_arch.ptrsize * 9 elif kversion < "5.12": self.offset_address = self.offset_firstname + current_arch.ptrsize * 7 self.sizeof_module_sect_attr = current_arch.ptrsize * 8 elif kversion < "6.7": self.offset_address = self.offset_firstname + current_arch.ptrsize * 8 self.sizeof_module_sect_attr = current_arch.ptrsize * 9 elif kversion < "6.13": self.offset_address = self.offset_firstname + current_arch.ptrsize * 9 self.sizeof_module_sect_attr = current_arch.ptrsize * 10 else: self.offset_address = self.offset_firstname + current_arch.ptrsize * 11 self.sizeof_module_sect_attr = current_arch.ptrsize * 12 # module->sect_attrs self.offset_sect_attrs = self.get_offset_sect_attrs(self.module_addrs) if self.offset_sect_attrs is None: self.quiet_err("Could not find module->sect_attrs") return False self.quiet_info("offsetof(module, sect_attrs): {:#x}".format(self.offset_sect_attrs)) self.initialized = True return True def kmod_load(self): for module in self.module_addrs: name_string = read_cstring_from_memory(module + self.offset_name) if name_string != self.args.name: continue # get nsections sect_attrs = read_int_from_memory(module + self.offset_sect_attrs) nsections = read_int_from_memory(sect_attrs + self.offset_nsections) & 0xffff_ffff self.quiet_info("nsections = {}".format(nsections)) # get each section name and address sections = [] for i in range(nsections): name_ptr = read_int_from_memory(sect_attrs + self.offset_firstname + self.sizeof_module_sect_attr * i) name = read_cstring_from_memory(name_ptr) addr = read_int_from_memory(sect_attrs + self.offset_address + self.sizeof_module_sect_attr * i) self.quiet_info("name={:s}, addr={:#x}".format(name, addr)) sections.append((name, addr)) # unneeded, but for convenience gdb.execute("set ${:s} = {:#x}".format(name.replace(".", "").replace("-", ""), addr)) # load command = " ".join(['-s {:s} {:#x}'.format(name, addr) for (name, addr) in sections]) gdb.execute("add-symbol-file {!r} {:s}".format(self.args.path, command)) break else: self.quiet_err("Could not find {:s}".format(self.args.name)) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): if not os.path.exists(args.path): self.quiet_err("Could not find {:s}".format(args.path)) return # initialize self.module_addrs = None ret = self.initialize() if not ret: self.quiet_err("Failed to initialize") return # get module addrs if not self.module_addrs: self.module_addrs = self.get_modules_list(self.modules) if self.module_addrs is None: return if self.module_addrs == []: self.quiet_err("Could not find any modules") return self.quiet_info("Num of modules: {:#x}".format(len(self.module_addrs))) # doit self.kmod_load() return @register_command class KernelBlockDevicesCommand(GenericCommand, BufferingOutput): """Display block device list.""" _cmdline_ = "kbdev" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -q", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "If there are too many block devices, detection may fail.", "This is because block devices are not managed in a single location,", "so the list of bdev_cache obtained from the slub-dump results is used.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) self.offset_bd_dev = None return def get_bdev_list(self): allocator = Kernel.get_slab_type() if allocator == "SLUB": ret = gdb.execute("slub-dump --quiet --no-pager -vv bdev_cache", to_string=True) elif allocator == "SLUB_TINY": ret = gdb.execute("slub-tiny-dump --quiet --no-pager bdev_cache", to_string=True) else: self.quiet_err("Unsupported: SLAB, SLOB, Unknown allocator") return None bdevs = [] for line in ret.splitlines(): line = Color.remove_color(line) r = re.search(r"(0x\S+) \(in-use\)", line) if not r: continue bdev = int(r.group(1), 16) bdevs.append(bdev) return bdevs @staticmethod def get_bdev_name(major, minor): # https://www.kernel.org/doc/Documentation/admin-guide/devices.txt # https://github.com/lrs-lang/lib/blob/master/src/dev/lib.rs dev_name_list = { 0: "???", 4: "/dev/root", 7: "/dev/loop{:d}", 9: "/dev/md{:d}", 11: "/dev/scd{0:d} (sr{0:d})", 12: "/dev/dos_cd{:d}", 15: "/dev/sonycd", 16: "/dev/gscd", 17: "/dev/optcd", 18: "/dev/sjcd", 20: "/dev/hitcd", 23: "/dev/mcd", 24: "/dev/cdu535", 29: "/dev/aztcd", 30: "/dev/cm205cd", 32: "/dev/cm206cd", 35: "/dev/slram", 37: "/dev/z2ram", 41: "/dev/bpcd", 43: "/dev/nb{:d}", 46: "/dev/pcd{:d}", 47: "/dev/pf{:d}", 59: "/dev/pda{:d}", 92: "/dev/ppdd{:d}", 97: "/dev/pktcdvd", 99: "/dev/jsfd", 103: "/dev/audit", 115: "/dev/nwfs/v{:d}", 147: "/dev/drbd{:d}", 152: "/dev/etherd/{:d}", 199: "/dev/vx/dsk/*/*", 201: "/dev/vx/dmp/*/*", 258: "/dev/blockrom{:d}", } if major in dev_name_list: return dev_name_list[major].format(minor) def common_pattern(kind_sep, kind_max, devstr, sym_start="a"): kind = minor // kind_sep num = minor % kind_sep if kind < kind_max: dev = "/dev/{:s}{:s}".format(devstr, chr(ord(sym_start) + kind)) if num: dev = "{:s}{:d}".format(dev, num) return dev return "???" def common_pattern_num_p(kind_sep, kind_max, devstr, kind_offset=0): kind = minor // kind_sep num = minor % kind_sep if kind_max is None or kind < kind_max: dev = "/dev/{:s}{:d}".format(devstr, kind + kind_offset) if num: dev = "{:s}p{:d}".format(dev, num) return dev return "???" def iterate_pattern(kind_sep, kind_max, dev_str, sym_start): def gen(): cs = "abcdefghijklmnopqrstuvwxyz" for c in cs: yield c for c1 in cs: for c2 in cs: yield c1 + c2 return name_list = list(gen()) kind = minor // kind_sep num = minor % kind_sep if kind < (kind_max or len(name_list)): offset = name_list.index(sym_start) dev = "/dev/{:s}{:s}".format(dev_str, name_list[offset + kind]) if num: dev = "{:s}{:d}".format(dev, num) return dev return "???" if major == 1: if 0 <= minor < 250: return "/dev/ram{:d}".format(minor) elif minor == 250: return "/dev/initrd" elif major == 2: if 0 <= minor < 128: num = minor % 4 elif 128 <= minor < 256: num = 4 + minor % 4 else: return "???" fd_type = (minor % 128) & ~0b11 type_dic = { 0: "", 4: "d360", 8: "h1200", 12: "u360", 16: "u720", 20: "h360", 24: "h720", 28: "u1440", 32: "u2880", 36: "CompaQ", 40: "h1440", 44: "u1680", 48: "h410", 52: "u820", 56: "h1476", 60: "u1722", 64: "h420", 68: "u830", 72: "h1494", 76: "u1743", 80: "h880", 84: "u1040", 88: "u1120", 92: "h1600", 96: "u1760", 100: "u1920", 104: "u3200", 108: "u3520", 112: "u3840", 116: "u1840", 120: "u800", 124: "u1600", } if fd_type in type_dic: return "/dev/fd{:d}{:s}".format(num, type_dic[fd_type]) elif major == 3: return common_pattern(64, 2, "hd", sym_start="a") elif major == 8: return iterate_pattern(16, 16, "sd", sym_start="a") elif major == 13: return common_pattern(64, 4, "xd") elif major == 14: return common_pattern(64, 4, "dos_hd") elif major == 19: if 0 <= minor < 8: return "/dev/double{:d}".format(minor) elif 128 <= minor < 136: return "/dev/cdouble{:d}".format(minor - 128) elif major == 21: return common_pattern(64, 2, "mfm") elif major == 22: return common_pattern(64, 2, "hd", sym_start="c") elif 25 <= major < 28: if 0 <= minor < 4: return "/dev/sbpcd{:d}".format(minor + (major - 25) * 4) elif major == 28: # 28 are duplicates if is_m68k(): return common_pattern(16, 16, "ad") else: if 0 <= minor < 4: return "/dev/sbpcd{:d}".format(minor + (major - 25) * 4) elif major == 31: if 0 <= minor < 8: return "/dev/rom{:d}".format(minor) elif 8 <= minor < 16: return "/dev/rrom{:d}".format(minor - 8) elif 16 <= minor < 24: return "/dev/flash{:d}".format(minor - 16) elif 24 <= minor < 32: return "/dev/rflash{:d}".format(minor - 24) elif 33 <= major < 35: return common_pattern(64, 2, "hd", sym_start=["e", "g"][major - 33]) elif major == 36: return common_pattern(64, 4, "ed") elif major == 40: if minor == 0: return "/dev/eza" elif 1 <= minor < 64: return "/dev/eza{:d}".format(minor) elif major == 44: return common_pattern(16, 16, "ftl") elif major == 45: return common_pattern(16, 4, "pd") elif 48 <= major < 56: return common_pattern_num_p(8, 32, "rd/c{:d}d".format(major - 48)) elif 56 <= major < 58: return common_pattern(64, 2, "hd", sym_start=["i", "k"][major - 56]) elif major == 64: if minor == 0: return "/dev/scramdisk/master" elif 1 <= minor: return "/dev/scramdisk/{:d}".format(minor) elif 65 <= major < 72: return iterate_pattern(16, 16, "sd", sym_start=["q", "ag", "aw", "bm", "cc", "cs", "di"][major - 65]) elif 72 <= major < 80: return common_pattern_num_p(16, 16, "ida/c{:d}d".format(major - 72)) elif 80 <= major < 88: return iterate_pattern(16, 16, "i2o/hd", sym_start=["a", "q", "ag", "aw", "bm", "cc", "cs", "di"][major - 80]) elif 88 <= major < 92: return common_pattern(64, 2, "hd", sym_start=["m", "o", "q", "s"][major - 88]) elif major == 93: return common_pattern(16, 16, "nftl") elif major == 94: return common_pattern(4, 26, "dasd") elif major == 96: return common_pattern(16, 16, "inftl") elif major == 98: return common_pattern(16, 26, "ubd") elif major == 101: return common_pattern_num_p(16, 16, "amiraid/ar") elif major == 102: return common_pattern(16, 16, "cbd/") elif 104 <= major < 112: return common_pattern_num_p(16, 16, "cciss/c{:d}d".format(major - 104)) elif major == 112: return iterate_pattern(8, 64, "iseries/vd", sym_start="a") elif major == 113: return iterate_pattern(1, None, "iseries/vcd", sym_start="a") elif major == 114: return common_pattern_num_p(16, 16, "ataraid/d") elif major == 116: return common_pattern_num_p(16, 16, "umem/d") elif major == 117: if minor == 0: return "/dev/evms/block_device" elif 1 <= minor < 128: return "/dev/evms/legacyname{:d}".format(minor) elif 128 <= minor < 256: return "/dev/evms/EVMSname{:d}".format(256 - minor) elif 128 <= major < 136: return iterate_pattern(16, 16, "sd", sym_start=["dy", "eo", "fe", "fu", "gk", "ha", "hq", "ig"][major - 128]) elif 136 <= major < 144: return common_pattern_num_p(16, 16, "rd/c{:d}d".format(major - 136 + 8)) elif major == 153: return common_pattern_num_p(16, 16, "emd/") elif 160 <= major < 162: return common_pattern_num_p(32, 8, "carmel/", kind_offset=(major - 160) * 8) # codespell:ignore elif major == 179: return common_pattern_num_p(8, None, "mmcblk") elif major == 180: return common_pattern(8, 26, "ub") elif major == 202: return common_pattern(16, 16, "xvd") elif 240 <= major < 255: # LOCAL/EXPERIMENTAL USE, but maybe this is virtio when qemu-system return common_pattern(16, 16, "vd") elif major == 256: return common_pattern(16, 16, "rfd") elif major == 257: return common_pattern(8, 8, "ssfdc") return "???" def get_dev_num(self, bdev): """ [~v5.10] struct block_device { dev_t bd_dev; ... }; [v5.11~] struct block_device { sector_t bd_start_sect; // sector_t: u64 sector_t bd_nr_sectors; // sector_t: u64, v5.17~ struct gendisk *bd_disk; // v6.4~ struct request_queue *bd_queue; // v6.4~ struct disk_stats __percpu *bd_stats; unsigned long bd_stamp; bool bd_read_only; // 1byte + 3byte padding dev_t bd_dev; ... }; """ if self.offset_bd_dev is None: kversion = Kernel.kernel_version() if kversion < "5.11": self.offset_bd_dev = 0 elif kversion < "5.16": self.offset_bd_dev = 8 * 1 + current_arch.ptrsize * 2 + 4 elif kversion < "6.4": self.offset_bd_dev = 8 * 2 + current_arch.ptrsize * 2 + 4 else: self.offset_bd_dev = 8 * 2 + current_arch.ptrsize * 4 + 4 dev = read_int32_from_memory(bdev + self.offset_bd_dev) major = dev >> 20 minor = dev & ((1 << 20) - 1) name = KernelBlockDevicesCommand.get_bdev_name(major, minor) return major, minor, name @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") bdevs = self.get_bdev_list() if not bdevs: self.quiet_err("Could not find any bdev") return self.out = [] if not args.quiet: fmt = "{:<18s} {:<18s} {:<6s} {:<6s}" legend = ["bdev", "name (guessed)", "major", "minor"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # ignore bdev if major is 0 bdevs = [bdev for bdev in bdevs if self.get_dev_num(bdev)[0] != 0] if not bdevs: self.quiet_err("Could not find any bdev (after filtering major == 0)") return # parse major, minor and name bdevs_with_info = [] for bdev in bdevs: major, minor, name = self.get_dev_num(bdev) bdevs_with_info.append([major, minor, name, bdev]) # print for major, minor, name, bdev in sorted(bdevs_with_info): self.out.append("{:#018x} {:<18s} {:<6d} {:<6d}".format(bdev, name, major, minor).rstrip()) self.print_output(check_terminal_size=True) return @register_command class KernelCharacterDevicesCommand(GenericCommand, BufferingOutput): """Display character device list.""" _cmdline_ = "kcdev" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose mode.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "", "Simplified cdev structure:", "", "+-chrdevs[255]-+ +-char_device_struct-+", "| [0] |--->| next |--->...", "| ... | | major |", "| [254] | | baseminor | +--->+-cdev--+ +-->+-kobject-+", "+--------------+ | minorct | | | kobj |--+ | name |", " | name[64] | | | ... | | ... |", " | cdev |-----------+ | ops | | parent |", " +--------------------+ | | ... | | ... |", " | | dev | +---------+", "+----------+ +-kobj_map----+ +-probe-+ | | ... |", "| cdev_map |--->| probes[0] |--->| next |--->... | +-------+", "+----------+ | ... | | dev | |", " | probes[254] | | ... | |", " | lock | | data |---------+", " +-------------+ +-------+", "", "The character devices are managed at chrdevs[] and cdev_map.", "This command use each of them for getting structure information.", ] _note_ = "\n".join(_note_) @staticmethod def get_cdev_name(major, minor): # https://www.kernel.org/doc/Documentation/admin-guide/devices.txt # https://github.com/lrs-lang/lib/blob/master/src/dev/lib.rs dev_name_list = { (1, 0): "/dev/mem", # Undocumented, but it seems to be used (1, 1): "/dev/mem", (1, 2): "/dev/kmem", (1, 3): "/dev/null", (1, 4): "/dev/port", (1, 5): "/dev/zero", (1, 6): "/dev/core", (1, 7): "/dev/full", (1, 8): "/dev/random", (1, 9): "/dev/urandom", (1, 10): "/dev/aio", (1, 11): "/dev/kmsg", (1, 12): "/dev/oldmem", (5, 0): "/dev/tty", (5, 1): "/dev/console", (5, 2): "/dev/ptmx", (5, 3): "/dev/ttyprintk", (10, 0): "/dev/logibm", (10, 1): "/dev/psaux", (10, 2): "/dev/inportbm", (10, 3): "/dev/atibm", #(10, 4): "/dev/jbm", # duplicates (10, 4): "/dev/amigamouse", (10, 5): "/dev/atarimouse", (10, 6): "/dev/sunmouse", (10, 7): "/dev/amigamouse1", (10, 8): "/dev/smouse", (10, 9): "/dev/pc110pad", (10, 10): "/dev/adbmouse", (10, 11): "/dev/vrtpanel", (10, 13): "/dev/vpcmouse", (10, 14): "/dev/touchscreen/ucb1x00", (10, 15): "/dev/touchscreen/mk712", (10, 128): "/dev/beep", (10, 130): "/dev/watchdog", (10, 131): "/dev/temperature", (10, 132): "/dev/hwtrap", (10, 133): "/dev/exttrp", (10, 134): "/dev/apm_bios", (10, 135): "/dev/rtc", (10, 137): "/dev/vhci", (10, 139): "/dev/openprom", (10, 140): "/dev/relay8", (10, 141): "/dev/relay16", (10, 143): "/dev/pciconf", (10, 144): "/dev/nvram", (10, 145): "/dev/hfmodem", (10, 146): "/dev/graphics", (10, 147): "/dev/opengl", (10, 148): "/dev/gfx", (10, 149): "/dev/input/mouse", (10, 150): "/dev/input/keyboard", (10, 151): "/dev/led", (10, 152): "/dev/kpoll", (10, 153): "/dev/mergemem", (10, 154): "/dev/pmu", (10, 156): "/dev/lcd", (10, 157): "/dev/ac", (10, 158): "/dev/nwbutton", (10, 159): "/dev/nwdebug", (10, 160): "/dev/nwflash", (10, 161): "/dev/userdma", (10, 162): "/dev/smbus", (10, 163): "/dev/lik", # codespell:ignore (10, 164): "/dev/ipmo", (10, 165): "/dev/vmmon", (10, 166): "/dev/i2o/ctl", (10, 167): "/dev/specialix_sxctl", (10, 168): "/dev/tcldrv", (10, 169): "/dev/specialix_rioctl", (10, 170): "/dev/thinkpad/thinkpad", (10, 171): "/dev/srripc", (10, 172): "/dev/usemaclone", (10, 173): "/dev/ipmikcs", (10, 174): "/dev/uctrl", (10, 175): "/dev/agpgart", (10, 176): "/dev/gtrsc", (10, 177): "/dev/cbm", (10, 178): "/dev/jsflash", (10, 179): "/dev/xsvc", (10, 180): "/dev/vrbuttons", (10, 181): "/dev/toshiba", (10, 182): "/dev/perfctr", (10, 183): "/dev/hwrng", (10, 184): "/dev/cpu/microcode", (10, 186): "/dev/atomicps", (10, 187): "/dev/irnet", (10, 188): "/dev/smbusbios", (10, 189): "/dev/ussp_ctl", (10, 190): "/dev/crash", (10, 191): "/dev/pcl181", (10, 192): "/dev/nas_xbus", (10, 193): "/dev/d7s", (10, 194): "/dev/zkshim", (10, 195): "/dev/elographics/e2201", (10, 196): "/dev/vfio/vfio", (10, 197): "/dev/pxa3xx-gcu", (10, 198): "/dev/sexec", (10, 199): "/dev/scanners/cuecat", (10, 200): "/dev/net/tun", (10, 201): "/dev/button/gulpb", (10, 202): "/dev/emd/ctl", (10, 203): "/dev/cuse", (10, 204): "/dev/video/em8300", (10, 205): "/dev/video/em8300_mv", (10, 206): "/dev/video/em8300_ma", (10, 207): "/dev/video/em8300_sp", (10, 208): "/dev/compaq/cpqphpc", (10, 209): "/dev/compaq/cpqrid", (10, 210): "/dev/impi/bt", (10, 211): "/dev/impi/smic", (10, 212): "/dev/watchdogs/0", (10, 213): "/dev/watchdogs/1", (10, 214): "/dev/watchdogs/2", (10, 215): "/dev/watchdogs/3", (10, 216): "/dev/fujitsu/apanel", (10, 217): "/dev/ni/natmotn", (10, 218): "/dev/kchuid", (10, 219): "/dev/modems/mwave", (10, 220): "/dev/mptctl", (10, 221): "/dev/mvista/hssdsi", (10, 222): "/dev/mvista/hasi", (10, 223): "/dev/input/uinput", (10, 224): "/dev/tpm", (10, 225): "/dev/pps", (10, 226): "/dev/systrace", (10, 227): "/dev/mcelog", (10, 228): "/dev/hpet", (10, 229): "/dev/fuse", (10, 230): "/dev/midishare", (10, 231): "/dev/snapshot", (10, 232): "/dev/kvm", (10, 233): "/dev/kmview", (10, 234): "/dev/btrfs-control", (10, 235): "/dev/autofs", (10, 236): "/dev/mapper/control", (10, 237): "/dev/loop-control", (10, 238): "/dev/vhost-net", (10, 239): "/dev/uhid", (10, 240): "/dev/userio", (10, 241): "/dev/vhost-vsock", (10, 242): "/dev/rfkill", (12, 2): "/dev/ntpqic11", (12, 3): "/dev/tpqic11", (12, 4): "/dev/ntpqic24", (12, 5): "/dev/tpqic24", (12, 6): "/dev/ntpqic120", (12, 7): "/dev/tpqic120", (12, 8): "/dev/ntpqic150", (12, 9): "/dev/tpqic150", (14, 0): "/dev/mixer", (14, 1): "/dev/sequencer", (14, 2): "/dev/midi00", (14, 3): "/dev/dsp", (14, 4): "/dev/audio", (14, 7): "/dev/audioctl", (14, 8): "/dev/sequencer2", (14, 16): "/dev/mixer1", (14, 17): "/dev/patmgr0", (14, 18): "/dev/midi01", (14, 19): "/dev/dsp1", (14, 20): "/dev/audio1", (14, 33): "/dev/patmgr1", (14, 34): "/dev/midi02", (14, 50): "/dev/midi03", (30, 0): "/dev/socksys", (30, 1): "/dev/spx", (30, 32): "/dev/inet/ip", (30, 33): "/dev/inet/icmp", (30, 34): "/dev/inet/ggp", (30, 35): "/dev/inet/ipip", (30, 36): "/dev/inet/tcp", (30, 37): "/dev/inet/egp", (30, 38): "/dev/inet/pup", (30, 39): "/dev/inet/udp", (30, 40): "/dev/inet/idp", (30, 41): "/dev/inet/rawip", (31, 0): "/dev/mpu401data", (31, 1): "/dev/mpu401stat", (36, 0): "/dev/route", (36, 1): "/dev/skip", (36, 3): "/dev/fwmonitor", (70, 0): "/dev/apscfg", (70, 1): "/dev/apsauth", (70, 2): "/dev/apslog", (70, 3): "/dev/apsdbg", (70, 64): "/dev/apsisdn", (70, 65): "/dev/apsasync", (70, 128): "/dev/apsmon", (73, 0): "/dev/ip2ipl0", (73, 1): "/dev/ip2stat0", (73, 4): "/dev/ip2ipl1", (73, 5): "/dev/ip2stat1", (73, 8): "/dev/ip2ipl2", (73, 9): "/dev/ip2stat2", (73, 12): "/dev/ip2ipl3", (73, 13): "/dev/ip2stat", (95, 0): "/dev/ipl", (95, 1): "/dev/ipnat", (95, 2): "/dev/ipstate", (95, 3): "/dev/ipauth", (152, 0): "/dev/etherd/ctl", (152, 1): "/dev/etherd/err", (152, 2): "/dev/etherd/raw", (200, 0): "/dev/vx/config", (200, 1): "/dev/vx/trace", (200, 2): "/dev/vx/iod", (200, 3): "/dev/vx/info", (200, 4): "/dev/vx/task", (200, 5): "/dev/vx/taskmon", (207, 0): "/dev/cpqhealth/cpqw", (207, 1): "/dev/cpqhealth/crom", (207, 2): "/dev/cpqhealth/cdt", (207, 3): "/dev/cpqhealth/cevt", (207, 4): "/dev/cpqhealth/casr", (207, 5): "/dev/cpqhealth/cecc", (207, 6): "/dev/cpqhealth/cmca", (207, 7): "/dev/cpqhealth/ccsm", (207, 8): "/dev/cpqhealth/cnmi", (207, 9): "/dev/cpqhealth/css", (207, 10): "/dev/cpqhealth/cram", (207, 11): "/dev/cpqhealth/cpci", } if (major, minor) in dev_name_list: return dev_name_list[major, minor] dev_name_list2 = { 0: "???", 6: "/dev/lp{:d}", 16: "/dev/gs4500", 17: "/dev/ttyH{:d}", 18: "/dev/cuh{:d}", 19: "/dev/ttyC{:d}", 20: "/dev/cub{:d}", 21: "/dev/sg{:d}", 22: "/dev/ttyD{:d}", 23: "/dev/cud{:d}", 24: "/dev/ttyE{:d}", 25: "/dev/cue{:d}", 26: "/dev/wvisfgrab", 29: "/dev/fb{:d}", 32: "/dev/ttyX{:d}", 33: "/dev/cux{:d}", 34: "/dev/scc{:d}", 38: "/dev/mlanai{:d}", 40: "/dev/mmetfgrab", 41: "/dev/yamm", 43: "/dev/ttyI{:d}", 44: "/dev/cui{:d}", 46: "/dev/ttyR{:d}", 47: "/dev/cur{:d}", 48: "/dev/ttyL{:d}", 49: "/dev/cul{:d}", 51: "/dev/bc{:d}", 52: "/dev/dcbri{:d}", 54: "/dev/holter{:d}", 55: "/dev/dsp56k", 56: "/dev/adb", 57: "/dev/ttyP{:d}", 58: "/dev/cup{:d}", 59: "/dev/firewall", 64: "/dev/enskip", 66: "/dev/yppcpci{:d}", 67: "/dev/cfs0", 69: "/dev/ma16", 71: "/dev/ttyF{:d}", 72: "/dev/cuf{:d}", 74: "/dev/SCI/{:d}", 75: "/dev/ttyW{:d}", 76: "/dev/cuw{:d}", 77: "/dev/qng", 78: "/dev/ttyM{:d}", 79: "/dev/cum{:d}", 80: "/dev/at200", 82: "/dev/winradio{:d}", 83: "/dev/mga_vid{:d}", 84: "/dev/ihcp{:d}", 86: "/dev/sch{:d}", 87: "/dev/controla{:d}", 88: "/dev/comx{:d}", 89: "/dev/i2c-{:d}", 91: "/dev/can{:d}", 94: "/dev/dcxx{:d}", 97: "/dev/pg{:d}", 98: "/dev/comedi{:d}", 99: "/dev/parport{:d}", 100: "/dev/phone{:d}", 102: "/dev/tlk{:d}", 103: "/dev/nnpfs{:d}", 105: "/dev/ttyV{:d}", 106: "/dev/cuv{:d}", 107: "/dev/3dfx", 108: "/dev/ppp", 110: "/dev/srnd{:d}", 111: "/dev/av{:d}", 112: "/dev/ttyM{:d}", # same 78 113: "/dev/cum{:d}", # same 79 119: "/dev/vnet{:d}", 136: "/dev/pts/{:d}", 137: "/dev/pts/{:d}", 138: "/dev/pts/{:d}", 139: "/dev/pts/{:d}", 140: "/dev/pts/{:d}", 141: "/dev/pts/{:d}", 142: "/dev/pts/{:d}", 143: "/dev/pts/{:d}", 144: "/dev/pppox{:d}", 146: "/dev/scramnet{:d}", 147: "/dev/aureal{:d}", 148: "/dev/ttyT{:d}", 149: "/dev/cut{:d}", 150: "/dev/rtf{:d}", 151: "/dev/dpti{:d}", 153: "/dev/spi{:d}", 154: "/dev/ttySR{:d}", 155: "/dev/cusr{:d}", 158: "/dev/gfax{:d}", 160: "/dev/gpib{:d}", 166: "/dev/ttyACM{:d}", 167: "/dev/cuacm{:d}", 168: "/dev/ecsa{:d}", 169: "/dev/ecsa8-{:d}", 170: "/dev/megarac{:d}", 174: "/dev/ttySI{:d}", 175: "/dev/cusi{:d}", 176: "/dev/nfastpci{:d}", 178: "/dev/clanvi{:d}", 179: "/dev/dvxirq{:d}", 181: "/dev/pcfclock{:d}", 182: "/dev/pethr{:d}", 183: "/dev/ss5136dn{:d}", 184: "/dev/pevss{:d}", 185: "/dev/intermezzo{:d}", 186: "/dev/obd{:d}", 187: "/dev/deskey{:d}", 188: "/dev/ttyUSB{:d}", 189: "/dev/cuusb{:d}", 190: "/dev/kctt{:d}", 196: "/dev/tor/{:d}", 198: "/dev/tpmp2/{:d}", 199: "/dev/vx/rdsk/*/*", 201: "/dev/vx/rdmp/*", 202: "/dev/cpu/{:d}/msr", 203: "/dev/cpu/{:d}/cpuid", 208: "/dev/ttyU{:d}", 209: "/dev/cuu{:d}", 211: "/dev/addinum/cpci1500/{:d}", 216: "/dev/rfcomm{:d}", 217: "/dev/curf{:d}", 218: "/dev/logicalco/bci/{:d}", 219: "/dev/logicalco/dci1300/{:d}", 224: "/dev/ttyY{:d}", 225: "/dev/cuy{:d}", 226: "/dev/dri/card{:d}", 229: "/dev/hvc{:d}", 256: "/dev/ttyEQ{:d}", 257: "/dev/ptlsec", 259: "/dev/icap{:d}", 260: "/dev/osd{:d}", 261: "/dev/accel/accel{:d}", } if major in dev_name_list2: return dev_name_list2[major].format(minor) def pty_tty_pattern(devstr): cs1 = "pqrstuvwxyzabcde" cs2 = "0123456789abcdef" return "/dev/{:s}{:s}{:s}".format(devstr, cs1[minor // 16], cs2[minor % 16]) if major == 2: return pty_tty_pattern("pty") elif major == 3: return pty_tty_pattern("tty") elif major == 4: if 0 <= minor < 64: return "/dev/tty{:d}".format(minor) elif 64 <= minor < 256: return "/dev/ttyS{:d}".format(minor - 64) elif major == 5: if 64 <= minor < 256: return "/dev/cua{:d}".format(minor - 64) elif major == 7: if minor == 0: return "/dev/vcs" elif 1 <= minor < 64: return "/dev/vcs{:d}".format(minor) elif minor == 64: return "/dev/vcsu" elif 65 <= minor < 128: return "/dev/vcsu{:d}".format(minor - 64) elif minor == 128: return "/dev/vcsa" elif 129 <= minor < 192: return "/dev/vcsa{:d}".format(minor - 128) elif major == 9: if 0 <= minor < 32: return "/dev/st{:d}".format(minor) elif 32 <= minor < 64: return "/dev/st{:d}l".format(minor - 32) elif 64 <= minor < 96: return "/dev/st{:d}m".format(minor - 64) elif 96 <= minor < 128: return "/dev/st{:d}a".format(minor - 96) elif 128 <= minor < 160: return "/dev/nst{:d}".format(minor - 128) elif 160 <= minor < 192: return "/dev/nst{:d}l".format(minor - 160) elif 192 <= minor < 224: return "/dev/nst{:d}m".format(minor - 192) elif 224 <= minor < 256: return "/dev/nst{:d}a".format(minor - 224) elif major == 11: # 11 are duplicates if is_sparc32() or is_sparc32plus() or is_sparc64(): return "/dev/kbd" elif is_hppa32() or is_hppa64(): return "/dev/ttyB{:d}".format(minor) elif major == 13: if 0 <= minor < 32: return "/dev/input/js{:d}".format(minor) elif 32 <= minor < 63: return "/dev/input/mouse{:d}".format(minor - 32) elif minor == 63: return "/dev/input/mice" elif 64 <= minor < 96: return "/dev/input/event{:d}".format(minor - 64) elif major == 15: if 0 <= minor < 128: return "/dev/js{:d}".format(minor) elif 128 <= minor < 256: return "/dev/djs{:d}".format(minor - 128) elif major == 27: if 0 <= minor < 4: return "/dev/qft{:d}".format(minor) elif 4 <= minor < 8: return "/dev/nqft{:d}".format(minor - 4) elif 16 <= minor < 20: return "/dev/zqft{:d}".format(minor - 16) elif 20 <= minor < 24: return "/dev/nzqft{:d}".format(minor - 20) elif 32 <= minor < 36: return "/dev/rawqft{:d}".format(minor - 32) elif 36 <= minor < 40: return "/dev/nrawqft{:d}".format(minor - 36) elif major == 28: # 28 are duplicates if is_m68k(): return "/dev/slm{:d}".format(minor) else: return "/dev/staliomem{:d}".format(minor) elif major == 35: if 0 <= minor < 4: return "/dev/midi{:d}".format(minor) elif 64 <= minor < 67: return "/dev/rmidi{:d}".format(minor - 64) elif 128 <= minor < 132: return "/dev/smpte{:d}".format(minor - 128) elif major == 36: if 16 <= minor < 32: return "/dev/tap{:d}".format(minor - 16) elif major == 37: if 0 <= minor < 128: return "/dev/ht{:d}".format(minor) elif 128 <= minor < 256: return "/dev/nht{:d}".format(minor - 128) elif major == 39: if 0 <= minor % 32 < 16: return "/dev/ml16p{:s}-a{:d}".format(chr(ord("a") + minor // 32), minor) elif minor % 32 == 16: return "/dev/ml16p{:s}-d".format(chr(ord("a") + minor // 32)) elif 17 <= minor % 32 < 20: return "/dev/ml16p{:s}-c{:d}".format(chr(ord("a") + minor // 32), minor - 17) elif major == 45: if 0 <= minor < 64: return "/dev/isdn{:d}".format(minor) elif 64 <= minor < 128: return "/dev/isdncntl{:d}".format(minor - 64) elif 128 <= minor < 192: return "/dev/ippp{:d}".format(minor - 128) elif minor == 255: return "/dev/isdninfo" elif major == 53: if 0 <= minor < 3: return "/dev/pd_bdm{:d}".format(minor) elif 4 <= minor < 7: return "/dev/icd_bdm{:d}".format(minor) elif major == 65: if 0 <= minor < 4: return "/dev/plink{:d}".format(minor) elif 64 <= minor < 67: return "/dev/rplink{:d}".format(minor - 64) elif 128 <= minor < 132: return "/dev/plink{:d}d".format(minor - 128) elif 192 <= minor < 196: return "/dev/rplink{:d}d".format(minor - 192) elif major == 68: if minor == 0: return "/dev/capi20" elif 1 <= minor < 20: return "/dev/capi20.{:02d}".format(minor - 1) elif major == 81: if 0 <= minor < 64: return "/dev/video{:d}".format(minor) elif 64 <= minor < 128: return "/dev/radio{:d}".format(minor - 64) elif 128 <= minor < 192: return "/dev/swradio{:d}".format(minor - 128) elif 192 <= minor < 256: return "/dev/vbi{:d}".format(minor - 192) elif major == 85: if minor == 0: return "/dev/shmiq" elif 1 <= minor: return "/dev/qcntl{:d}".format(minor) elif major == 90: if 0 <= minor < 32: if minor % 2 == 0: return "/dev/mtd{:d}".format(minor // 2) elif minor % 2 == 1: return "/dev/mtdr{:d}".format(minor // 2) elif major == 93: if 0 <= minor < 128: return "/dev/iscc{:d}".format(minor) elif 128 <= minor < 256: return "/dev/isccctl{:d}".format(minor - 128) elif major == 96: if 0 <= minor < 128: return "/dev/pt{:d}".format(minor) elif 128 <= minor < 256: return "/dev/npt{:d}".format(minor - 128) elif major == 101: if minor == 0: return "/dev/mdspstat" elif 1 <= minor < 17: return "/dev/mdsp{:d}".format(minor) elif major == 114: if 0 <= minor < 128: return "/dev/ise{:d}".format(minor) elif 128 <= minor < 256: return "/dev/isex{:d}".format(minor - 128) elif major == 115: if 0 <= minor < 8: return "/dev/tipar{:d}".format(minor) elif 8 <= minor < 16: return "/dev/tiser{:d}".format(minor - 8) elif 16 <= minor < 48: return "/dev/tiusb{:d}".format(minor - 16) elif major == 117: return "/dev/cosa{:d}c{:d}".format(minor // 16, minor % 16) elif major == 118: if minor == 0: return "/dev/ica" elif 1 <= minor: return "/dev/ica{:d}".format(minor - 1) elif major == 145: if minor % 64 == 0: return "/dev/sam{:d}_mixer".format(minor // 64) elif minor % 64 == 1: return "/dev/sam{:d}_sequencer".format(minor // 64) elif minor % 64 == 2: return "/dev/sam{:d}_midi00".format(minor // 64) elif minor % 64 == 3: return "/dev/sam{:d}_dsp".format(minor // 64) elif minor % 64 == 4: return "/dev/sam{:d}_audio".format(minor // 64) elif minor % 64 == 6: return "/dev/sam{:d}_sndstat".format(minor // 64) elif minor % 64 == 18: return "/dev/sam{:d}_midi01".format(minor // 64) elif minor % 64 == 34: return "/dev/sam{:d}_midi02".format(minor // 64) elif minor % 64 == 50: return "/dev/sam{:d}_midi03".format(minor // 64) elif major == 156: return "/dev/ttySR{:d}".format(minor + 256) elif major == 157: return "/dev/cusr{:d}".format(minor + 256) elif major == 161: if 0 <= minor < 16: return "/dev/ircomm{:d}".format(minor) elif 16 <= minor < 32: return "/dev/irlpt{:d}".format(minor - 16) elif major == 162: if minor == 0: return "/dev/rawctl", elif 1 <= minor: return "/dev/raw/raw{:d}".format(minor) elif major == 164: return "/dev/ttyCH{:d}".format(minor) elif major == 165: if 0 <= minor < 64: return "/dev/cuch{:d}".format(minor) elif major == 172: if 0 <= minor < 128: return "/dev/ttyMX{:d}".format(minor) elif minor == 128: return "/dev/moxactl" elif major == 173: if 0 <= minor < 128: return "/dev/cumx{:d}".format(minor) elif major == 177: if 0 <= minor < 16: return "/dev/pcilynx/aux{:d}".format(minor) elif 16 <= minor < 32: return "/dev/pcilynx/rom{:d}".format(minor - 16) elif 32 <= minor < 48: return "/dev/pcilynx/ram{:d}".format(minor - 32) elif major == 180: if 0 <= minor < 16: return "/dev/usb/lp{:d}".format(minor) elif 48 <= minor < 64: return "/dev/usb/scanner{:d}".format(minor - 48) elif minor == 64: return "/dev/usb/rio500" elif minor == 65: return "/dev/usb/usblcd" elif minor == 66: return "/dev/usb/cpad0" elif 96 <= minor < 112: return "/dev/usb/hiddev{:d}".format(minor - 96) elif 112 <= minor < 128: return "/dev/usb/auer{:d}".format(minor - 112) elif 128 <= minor < 132: return "/dev/usb/brlvgr{:d}".format(minor - 128) elif minor == 132: return "/dev/usb/idmouse" elif 133 <= minor < 141: return "/dev/usb/sisusbvga{:d}".format(minor - 133 + 1) elif minor == 144: return "/dev/usb/lcd" elif 160 <= minor < 176: return "/dev/usb/legousbtower{:d}".format(minor - 160) elif 176 <= minor < 192: return "/dev/usb/usbtmc{:d}".format(minor - 176 + 1) elif 192 <= minor < 210: return "/dev/usb/yurex{:d}".format(minor - 192 + 1) elif major == 192: if minor == 0: return "/dev/profile" elif 1 <= minor: return "/dev/profile{:d}".format(minor - 1) elif major == 193: if minor == 0: return "/dev/trace" elif 1 <= minor: return "/dev/trace{:d}".format(minor - 1) elif major == 194: if minor % 16 == 0: return "/dev/mvideo/status{:d}".format(minor // 16) elif minor % 16 == 1: return "/dev/mvideo/stream{:d}".format(minor // 16) elif minor % 16 == 2: return "/dev/mvideo/frame{:d}".format(minor // 16) elif minor % 16 == 3: return "/dev/mvideo/rawframe{:d}".format(minor // 16) elif minor % 16 == 4: return "/dev/mvideo/codec{:d}".format(minor // 16) elif minor % 16 == 5: return "/dev/mvideo/video4linux{:d}".format(minor // 16) elif major == 195: if 0 <= minor < 255: return "/dev/nvidia{:d}".format(minor) elif minor == 255: return "/dev/nvidiactl" elif major == 197: if 0 <= minor < 128: return "/dev/tnf/t{:d}".format(minor) elif minor == 128: return "/dev/tnf/status" elif minor == 130: return "/dev/tnf/trace" elif major == 204: if 0 <= minor < 4: return "/dev/ttyLU{:d}".format(minor) elif minor == 4: return "/dev/ttyFB0" elif 5 <= minor < 8: return "/dev/ttySA{:d}".format(minor - 5) elif 8 <= minor < 12: return "/dev/ttySC{:d}".format(minor - 8) elif 12 <= minor < 16: return "/dev/ttyFW{:d}".format(minor - 12) elif 16 <= minor < 32: return "/dev/ttyAM{:d}".format(minor - 16) elif 32 <= minor < 40: return "/dev/ttyDB{:d}".format(minor - 32) elif minor == 40: return "/dev/ttySG0" elif 41 <= minor < 44: return "/dev/ttySMX{:d}".format(minor - 41) elif 44 <= minor < 46: return "/dev/ttyMM{:d}".format(minor - 44) elif 46 <= minor < 50: return "/dev/ttyCPM{:d}".format(minor - 46) elif 50 <= minor < 82: return "/dev/ttyIOC{:d}".format(minor - 50) elif 82 <= minor < 84: return "/dev/ttyVR{:d}".format(minor - 82) elif 84 <= minor < 116: return "/dev/ttyIOC{:d}".format(minor) elif 116 <= minor < 148: return "/dev/ttySIOC{:d}".format(minor - 116) elif 148 <= minor < 154: return "/dev/ttyPSC{:d}".format(minor - 148) elif 154 <= minor < 170: return "/dev/ttyAT{:d}".format(minor - 154) elif 170 <= minor < 186: return "/dev/ttyNX{:d}".format(minor - 170) elif minor == 186: return "/dev/ttyJ0" elif 187 <= minor < 190: return "/dev/ttyUL{:d}".format(minor - 187) elif minor == 191: return "/dev/xvc0" elif 192 <= minor < 196: return "/dev/ttyPZ{:d}".format(minor - 192) elif 196 <= minor < 204: return "/dev/ttyTX{:d}".format(minor - 196) elif 205 <= minor < 209: return "/dev/ttySC{:d}".format(minor - 205) elif 209 <= minor < 213: return "/dev/ttyMAX{:d}".format(minor - 209) elif major == 205: if 0 <= minor < 4: return "/dev/culu{:d}".format(minor) elif minor == 4: return "/dev/cufb0" elif 5 <= minor < 8: return "/dev/cusa{:d}".format(minor - 5) elif 8 <= minor < 12: return "/dev/cusc{:d}".format(minor - 8) elif 12 <= minor < 16: return "/dev/cufw{:d}".format(minor - 12) elif 16 <= minor < 32: return "/dev/cuam{:d}".format(minor - 16) elif 32 <= minor < 40: return "/dev/cudb{:d}".format(minor - 32) elif minor == 40: return "/dev/cusg0" elif 41 <= minor < 44: return "/dev/ttycusmx{:d}".format(minor - 41) elif 46 <= minor < 50: return "/dev/cucpm{:d}".format(minor - 46) elif 50 <= minor < 82: return "/dev/cuioc4{:d}".format(minor - 50) elif 82 <= minor < 84: return "/dev/cuvr{:d}".format(minor - 82) elif major == 206: if 0 <= minor < 32: return "/dev/osst{:d}".format(minor) elif 32 <= minor < 64: return "/dev/osst{:d}l".format(minor - 32) elif 64 <= minor < 96: return "/dev/osst{:d}m".format(minor - 64) elif 96 <= minor < 128: return "/dev/osst{:d}a".format(minor - 96) elif 128 <= minor < 160: return "/dev/nosst{:d}".format(minor - 128) elif 160 <= minor < 192: return "/dev/nosst{:d}l".format(minor - 160) elif 192 <= minor < 224: return "/dev/nosst{:d}m".format(minor - 192) elif 224 <= minor < 256: return "/dev/nosst{:d}a".format(minor - 224) elif major == 210: if minor % 10 == 0: return "/dev/sbei/wxcfg{:d}".format(minor // 10) elif minor % 10 == 1: return "/dev/sbei/dld{:d}".format(minor // 10) elif 2 <= minor % 10 < 6: return "/dev/sbei/wan{:d}{:d}".format(minor // 10, minor % 10) elif 6 <= minor % 10 < 10: return "/dev/sbei/wanc{:d}{:d}".format(minor // 10, minor % 10) elif major == 212: if minor % 64 % 9 == 0: return "/dev/dvb/adapter{:d}/video{:d}".format(minor // 64, minor % 64 // 9) elif minor % 64 % 9 == 1: return "/dev/dvb/adapter{:d}/audio{:d}".format(minor // 64, minor % 64 // 9) elif minor % 64 % 9 == 2: return "/dev/dvb/adapter{:d}/sec{:d}".format(minor // 64, minor % 64 // 9) elif minor % 64 % 9 == 3: return "/dev/dvb/adapter{:d}/frontend{:d}".format(minor // 64, minor % 64 // 9) elif minor % 64 % 9 == 4: return "/dev/dvb/adapter{:d}/demux{:d}".format(minor // 64, minor % 64 // 9) elif minor % 64 % 9 == 5: return "/dev/dvb/adapter{:d}/dvr{:d}".format(minor // 64, minor % 64 // 9) elif minor % 64 % 9 == 6: return "/dev/dvb/adapter{:d}/ca{:d}".format(minor // 64, minor % 64 // 9) elif minor % 64 % 9 == 7: return "/dev/dvb/adapter{:d}/net{:d}".format(minor // 64, minor % 64 // 9) elif minor % 64 % 9 == 8: return "/dev/dvb/adapter{:d}/osd{:d}".format(minor // 64, minor % 64 // 9) elif major == 220: if minor % 2 == 0: return "/dev/myricom/gm{:d}".format(minor // 2) elif minor % 2 == 1: return "/dev/myricom/gmp{:d}".format(minor // 2) elif major == 221: if 0 <= minor < 4: return "/dev/bus/vme/m{:d}".format(minor) elif 4 <= minor < 8: return "/dev/bus/vme/s{:d}".format(minor - 4) elif minor == 8: return "/dev/bus/vme/ctl" elif major == 227: if 1 <= minor: return "/dev/3270/tty{:d}".format(minor) elif major == 228: if minor == 0: return "/dev/3270/tub" elif 1 <= minor: return "/dev/3270/tub{:d}".format(minor) elif major == 230: if 0 <= minor < 32: return "/dev/iseries/vt{:d}".format(minor) elif 32 <= minor < 64: return "/dev/iseries/vt{:d}l".format(minor - 32) elif 64 <= minor < 96: return "/dev/iseries/vt{:d}m".format(minor - 64) elif 96 <= minor < 128: return "/dev/iseries/vt{:d}a".format(minor - 96) elif 128 <= minor < 160: return "/dev/iseries/nvt{:d}".format(minor - 128) elif 160 <= minor < 192: return "/dev/iseries/nvt{:d}l".format(minor - 160) elif 192 <= minor < 224: return "/dev/iseries/nvt{:d}m".format(minor - 192) elif 224 <= minor < 256: return "/dev/iseries/nvt{:d}a".format(minor - 224) elif major == 231: if 0 <= minor < 64: return "/dev/infiniband/umad{:d}".format(minor) elif 64 <= minor < 128: return "/dev/infiniband/issm{:d}".format(minor - 64) elif 192 <= minor < 224: return "/dev/infiniband/uverbs{:d}".format(minor - 192) elif major == 232: if minor % 10 == 0: return "/dev/biometric/sensor{:d}/fingerprint".format(minor // 10) elif minor % 10 == 1: return "/dev/biometric/sensor{:d}/iris".format(minor // 10) elif minor % 10 == 2: return "/dev/biometric/sensor{:d}/retina".format(minor // 10) elif minor % 10 == 3: return "/dev/biometric/sensor{:d}/voiceprint".format(minor // 10) elif minor % 10 == 4: return "/dev/biometric/sensor{:d}/facial".format(minor // 10) elif minor % 10 == 5: return "/dev/biometric/sensor{:d}/hand".format(minor // 10) elif major == 233: if minor == 0: return "/dev/ipath" elif 1 <= minor < 5: return "/dev/ipath{:d}".format(minor - 1) elif minor == 129: return "/dev/ipath_sma" elif minor == 130: return "/dev/ipath_diag" return "???" def get_chrdev_list(self): # [chrdev, chrdev, chrdev, ...] """ #define CHRDEV_MAJOR_HASH_SIZE 255 static struct char_device_struct { struct char_device_struct *next; unsigned int major; unsigned int baseminor; int minorct; char name[64]; struct cdev *cdev; } *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; """ chrdevs = KernelAddressHeuristicFinder.get_chrdevs() if chrdevs is None: self.quiet_err("Could not find chrdevs") return None self.quiet_info("chrdevs: {:#x}".format(chrdevs)) chrdev_addrs = [] for i in range(255): chrdevs_i = chrdevs + i * current_arch.ptrsize if not is_valid_addr(chrdevs_i): self.quiet_err("Memory read error") return None addr = read_int_from_memory(chrdevs_i) while addr and addr not in chrdev_addrs: chrdev_addrs.append(addr) if not is_valid_addr(addr): self.quiet_err("Memory read error") return None addr = read_int_from_memory(addr) return chrdev_addrs def get_cdev_list(self): # [[cdev, major, minor], [...] ...] """ struct kobj_map { struct probe { struct probe *next; dev_t dev; unsigned long range; struct module *owner; kobj_probe_t *get; int (*lock)(dev_t, void *); void *data; // -> cdev } *probes[255]; struct mutex *lock; }; static struct kobj_map *cdev_map; struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; } __randomize_layout; struct kobject { const char *name; struct list_head entry; struct kobject *parent; struct kset *kset; const struct kobj_type *ktype; struct kernfs_node *sd; struct kref kref; #ifdef CONFIG_DEBUG_KOBJECT_RELEASE struct delayed_work release; #endif unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; unsigned int state_remove_uevent_sent:1; unsigned int uevent_suppress:1; }; """ cdev_map = KernelAddressHeuristicFinder.get_cdev_map() if cdev_map is None: self.quiet_err("Could not find cdev_map") return None self.quiet_info("cdev_map: {:#x}".format(cdev_map)) try: cdev_map_ = read_int_from_memory(cdev_map) self.quiet_info("*cdev_map: {:#x}".format(cdev_map_)) except Exception: self.quiet_err("cdev_map is not initialized") return None cdev_addrs = [] seen = [] for i in range(255): addr = read_int_from_memory(cdev_map_ + i * current_arch.ptrsize) while addr: cdev = read_int_from_memory(addr + 6 * current_arch.ptrsize) dev = read_int32_from_memory(addr + current_arch.ptrsize) major = dev >> 20 minor = dev & ((1 << 20) - 1) if cdev and cdev not in seen: cdev_addrs.append([cdev, major, minor]) seen.append(cdev) addr = read_int_from_memory(addr) return cdev_addrs def get_offset_ops(self, cdevs): for i in range(3, 0x20): offset_list = i * current_arch.ptrsize valid = True for cdev in cdevs: pos_next = cdev + offset_list pos_prev = cdev + offset_list + current_arch.ptrsize list_entry_next = [pos_next] list_entry_prev = [pos_prev] while valid: # read check try: pos_next = read_int_from_memory(pos_next) pos_prev = read_int_from_memory(pos_prev) + current_arch.ptrsize except gdb.MemoryError: # memory read error valid = False break # list validate if pos_next in list_entry_next[1:]: # incomplete infinity loop detected valid = False break if pos_prev in list_entry_prev[1:]: # incomplete infinity loop detected valid = False break if pos_next == list_entry_next[0] and pos_prev == list_entry_prev[0]: break list_entry_next.append(pos_next) list_entry_prev.append(pos_prev) if not valid: break else: # for loop is finished until last element if valid: offset_ops = offset_list - current_arch.ptrsize self.quiet_info("offsetof(cdev, ops): {:#x}".format(offset_ops)) return offset_ops self.quiet_err("Could not find offsetof(cdev, ops)") return None @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") chrdev_addrs = self.get_chrdev_list() if chrdev_addrs is None: return cdev_addrs = self.get_cdev_list() if cdev_addrs is None: return # merge chrdev (from chrdevs) merged = {} for chrdev in chrdev_addrs: major = read_int32_from_memory(chrdev + current_arch.ptrsize) minor = read_int32_from_memory(chrdev + current_arch.ptrsize + 4) name_string = read_cstring_from_memory(chrdev + current_arch.ptrsize + 4 * 3) or "" off = chrdev + current_arch.ptrsize + 4 * 3 + 64 while off % current_arch.ptrsize: # align off += 1 cdev = read_int_from_memory(off) merged[major, minor] = {"chrdev": chrdev, "name": name_string, "cdev": cdev} # merge cdev (from cdev_map) for cdev, major, minor in cdev_addrs: kobj = read_int_from_memory(cdev) name_string = read_cstring_from_memory(kobj) or "" if (major, minor) in merged: if merged[major, minor]["cdev"] == 0: merged[major, minor]["cdev"] = cdev if merged[major, minor]["name"] == "": merged[major, minor]["name"] = name_string else: merged[major, minor] = {"chrdev": 0x0, "name": name_string, "cdev": cdev} # add ops info off_ops = self.get_offset_ops([v["cdev"] for k, v in merged.items() if v["cdev"]]) if off_ops is None: return for k in merged.keys(): if merged[k]["cdev"]: merged[k]["ops"] = read_int_from_memory(merged[k]["cdev"] + off_ops) else: merged[k]["ops"] = 0x0 merged[k]["ops_sym"] = Symbol.get_symbol_string(merged[k]["ops"]) # add parent info for k in merged.keys(): if merged[k]["cdev"]: parent = read_int_from_memory(merged[k]["cdev"] + current_arch.ptrsize * 3) merged[k]["parent"] = parent if parent: if not is_valid_addr(parent): merged[k]["parent_name"] = "???" else: name = read_int_from_memory(parent) if name: merged[k]["parent_name"] = read_cstring_from_memory(name) or "" else: merged[k]["parent_name"] = "" else: merged[k]["parent_name"] = "" else: merged[k]["parent"] = 0x0 merged[k]["parent_name"] = "" # print self.out = [] if not args.quiet: fmt = "{:<18s} {:<18s} {:<24s} {:<6s} {:<6s} {:<18s} {:<18s} {:18s} {:kobj.parent", "parent_name", "cdev->ops", ] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for (major, minor), m in sorted(merged.items()): guessed_name = KernelCharacterDevicesCommand.get_cdev_name(major, minor) if not args.verbose: if m["chrdev"] == 0: continue self.out.append("{:#018x} {:<18s} {:<24s} {:<6d} {:<6d} {:#018x} {:#018x} {:<18s} {:#018x}{:s}".format( m["chrdev"], m["name"], guessed_name, major, minor, m["cdev"], m["parent"], m["parent_name"], m["ops"], m["ops_sym"], )) self.print_output(check_terminal_size=True) return @register_command class KernelOperationsCommand(GenericCommand, BufferingOutput): """Display the members of commonly used function table (like struct file_operations) in the kernel.""" _cmdline_ = "kops" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" types = [ "address_space_operations", "ata_port_operations", "btf_kind_operations", "block_device_operations", "clk_ops", "configfs_item_operations", "configfs_group_operations", "damon_operations", "dentry_operations", "dev_pm_ops", "dma_buf_ops", "export_operations", "file_operations", "fs_context_operations", "inode_operations", "kobj_ns_type_operations", "media_entity_operations", "movable_operations", "net_device_ops", "page_ext_operations", "parport_operations", "pernet_operations", "pipe_buf_operations", "proc_ns_operations", "proc_ops", "regulator_ops", "seq_operations", "smp_operations", # ARM only "super_operations", "tty_ldisc_ops", "tty_operations", "tty_port_operations", "ucsi_operations", "vm_operations_struct", ] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("name", metavar="STRUCT_NAME", choices=types, help="the structure name.") parser.add_argument("address", metavar="ADDRESS", nargs="?", type=AddressUtil.parse_address, help="the address interpreted as ops.") parser.add_argument("-V", "--version", help="use specific kernel version. (default: detected kernel version)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} file_operations", "{0:s} -V 6.6.0 file_operations", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "", "Currently it supports from 3.0 to 6.16.", "", "Supported structure:", ] for t_grp in slicer(types, 4): _note_.append(" " + ", ".join(t_grp) + ",") _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.types: # already matched return [] if text == "": # no prefix: example: `kops TAB` return [s for s in self.types if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.types if s and s.startswith(text.strip())] def initialize(self, kversion): if kversion.major < 3: err("Unsupported before v3.0") return False self.members = {} def adapt_to_kernel_version(ops): out = [] for entry in ops: if len(entry) == 5: typ, name, minver, maxver, enabled = entry if not enabled: continue else: typ, name, minver, maxver = entry if minver and kversion < minver: continue if maxver and maxver <= kversion: continue out.append((typ, name)) return out file_operations = [ # type name minver maxver ["ptr", "owner", None, None], ["int", "flags", "6.10.0", None], ["func_ptr", "llseek", None, None], ["func_ptr", "read", None, None], ["func_ptr", "write", None, None], ["func_ptr", "aio_read", None, "4.1.0"], ["func_ptr", "aio_write", None, "4.1.0"], ["func_ptr", "read_iter", "3.16.0", None], ["func_ptr", "write_iter", "3.16.0", None], ["func_ptr", "readdir", None, "3.11.0"], ["func_ptr", "iopoll", "5.1.0", None], ["func_ptr", "iterate", "3.11.0", "6.5.0"], ["func_ptr", "iterate_shared", "4.7.0", None], ["func_ptr", "poll", None, None], ["func_ptr", "unlocked_ioctl", None, None], ["func_ptr", "compat_ioctl", None, None], ["func_ptr", "mmap", None, None], ["func_ptr", "mremap", "3.19.0", "4.3.0"], ["ulong", "mmap_supported_flags", "4.15.0", "6.10.0"], ["func_ptr", "open", None, None], ["func_ptr", "flush", None, None], ["func_ptr", "release", None, None], ["func_ptr", "fsync", None, None], ["func_ptr", "aio_fsync", None, "4.9.0"], ["func_ptr", "fasync", None, None], ["func_ptr", "lock", None, None], ["func_ptr", "sendpage", None, "6.5.0"], ["func_ptr", "get_unmapped_area", None, None], ["func_ptr", "check_flags", None, None], ["func_ptr", "flock", None, None], ["func_ptr", "splice_write", None, None], ["func_ptr", "splice_read", None, None], ["func_ptr", "splice_eof", "6.5.0", None], ["func_ptr", "setlease", None, None], ["func_ptr", "fallocate", None, None], ["func_ptr", "show_fdinfo", "3.8.0", None], ["func_ptr", "copy_file_range", "4.5.0", None], ["func_ptr", "clone_file_range", None, "4.19.289"], ["func_ptr", "dedupe_file_range", None, "4.19.289"], ["func_ptr", "remap_file_range", "4.20.0", None], ["func_ptr", "fadvise", "4.19.0", None], ["func_ptr", "uring_cmd", "5.19.0", None], ["func_ptr", "uring_cmd_iopoll", "6.1.0", None], ["func_ptr", "mmap_prepare", "6.16.0", None], ] self.members["file_operations"] = adapt_to_kernel_version(file_operations) tty_operations = [ # type name minver maxver ["func_ptr", "lookup", None, None], ["func_ptr", "install", None, None], ["func_ptr", "remove", None, None], ["func_ptr", "open", None, None], ["func_ptr", "close", None, None], ["func_ptr", "shutdown", None, None], ["func_ptr", "cleanup", None, None], ["func_ptr", "write", None, None], ["func_ptr", "put_char", None, None], ["func_ptr", "flush_chars", None, None], ["func_ptr", "write_room", None, None], ["func_ptr", "chars_in_buffer", None, None], ["func_ptr", "ioctl", None, None], ["func_ptr", "compat_ioctl", None, None], ["func_ptr", "set_termios", None, None], ["func_ptr", "throttle", None, None], ["func_ptr", "unthrottle", None, None], ["func_ptr", "stop", None, None], ["func_ptr", "start", None, None], ["func_ptr", "hangup", None, None], ["func_ptr", "break_ctl", None, None], ["func_ptr", "flush_buffer", None, None], ["func_ptr", "ldisc_ok", "6.1.0", None], ["func_ptr", "set_ldisc", None, None], ["func_ptr", "wait_until_sent", None, None], ["func_ptr", "send_xchar", None, None], ["func_ptr", "tiocmget", None, None], ["func_ptr", "tiocmset", None, None], ["func_ptr", "resize", None, None], ["func_ptr", "set_termiox", None, "5.10.0"], ["func_ptr", "get_icount", None, None], ["func_ptr", "get_serial", "4.19.0", None], ["func_ptr", "set_serial", "4.19.0", None], ["func_ptr", "show_fdinfo", "4.14.0", None], ["func_ptr", "poll_init (CONFIG_CONSOLE_POLL=y)", None, None], ["func_ptr", "poll_get_char (CONFIG_CONSOLE_POLL=y)", None, None], ["func_ptr", "poll_put_char (CONFIG_CONSOLE_POLL=y)", None, None], ["func_ptr", "proc_show", "4.18.0", None], ["ptr", "proc_fops", None, "4.18.0"], ] self.members["tty_operations"] = adapt_to_kernel_version(tty_operations) tty_ldisc_ops = [ # type name minver maxver additional_flag ["int", "magic", None, "5.13.0"], ["char*", "name", None, None], ["int", "num", "5.16.0", None], ["int", "num", None, "5.15.121", is_32bit()], ["int", "flags", None, "5.15.121", is_32bit()], ["int, int", "flags, num", None, "5.15.121", is_64bit()], ["func_ptr", "open", None, None], ["func_ptr", "close", None, None], ["func_ptr", "flush_buffer", None, None], ["func_ptr", "read", None, None], ["func_ptr", "write", None, None], ["func_ptr", "ioctl", None, None], ["func_ptr", "compat_ioctl", None, None], ["func_ptr", "set_termios", None, None], ["func_ptr", "poll", None, None], ["func_ptr", "hangup", None, None], ["func_ptr", "receive_buf", None, None], ["func_ptr", "write_wakeup", None, None], ["func_ptr", "dcd_change", None, None], ["func_ptr", "fasync", "3.11.0", "4.6.0"], ["func_ptr", "receive_buf2", "3.12.0", None], ["func_ptr", "lookahead_buf", "5.16.0", None], ["ptr", "owner", None, None], ["int", "refcount", None, "5.14.0"], ] self.members["tty_ldisc_ops"] = adapt_to_kernel_version(tty_ldisc_ops) seq_operations = [ # type name minver maxver ["func_ptr", "start", None, None], ["func_ptr", "stop", None, None], ["func_ptr", "next", None, None], ["func_ptr", "show", None, None], ] self.members["seq_operations"] = adapt_to_kernel_version(seq_operations) inode_operations = [ # type name minver maxver ["func_ptr", "lookup", None, None], ["func_ptr", "get_link", "4.5.0", None], ["func_ptr", "follow_link", None, "4.5.0"], ["func_ptr", "permission", None, None], ["func_ptr", "get_inode_acl", "6.2.0", None], ["func_ptr", "get_acl", "3.1.0", "6.1.39"], ["func_ptr", "check_acl", None, "3.1.0"], ["func_ptr", "readlink", None, None], ["func_ptr", "put_link", None, "4.5.0"], ["func_ptr", "create", None, None], ["func_ptr", "link", None, None], ["func_ptr", "unlink", None, None], ["func_ptr", "symlink", None, None], ["func_ptr", "mkdir", None, None], ["func_ptr", "rmdir", None, None], ["func_ptr", "mknod", None, None], ["func_ptr", "rename", None, None], ["func_ptr", "truncate", None, "3.8.0"], ["func_ptr", "rename2", "3.15.0", "4.9.0"], ["func_ptr", "setattr", None, None], ["func_ptr", "getattr", None, None], ["func_ptr", "setxattr", None, "4.9.0"], ["func_ptr", "getxattr", None, "4.9.0"], ["func_ptr", "listxattr", None, None], ["func_ptr", "removexattr", None, "4.9.0"], ["func_ptr", "fiemap", None, None], ["func_ptr", "update_time", "3.5.0", None], ["func_ptr", "atomic_open", "3.6.0", None], ["func_ptr", "tmpfile", "3.11.0", None], ["func_ptr", "get_acl", "6.2.0", None], ["func_ptr", "set_acl", "3.14.0", None], ["func_ptr", "dentry_open", None, "4.2.0"], ["func_ptr", "fileattr_set", "5.13.0", None], ["func_ptr", "fileattr_get", "5.13.0", None], ["func_ptr", "get_offset_ctx", "6.6.0", None], ] self.members["inode_operations"] = adapt_to_kernel_version(inode_operations) pernet_operations = [ # type name minver maxver ["ptr", "list.next", None, None], ["ptr", "list.prev", None, None], ["func_ptr", "init", None, None], ["func_ptr", "pre_exit", "5.3.0", None], ["func_ptr", "exit", None, None], ["func_ptr", "exit_batch", None, None], ["func_ptr", "exit_batch_rtnl", "6.9.0", "6.15.9"], ["func_ptr", "exit_rtnl", "6.16.0", None], ["ptr", "id", None, None], ["long", "size", None, None], ] self.members["pernet_operations"] = adapt_to_kernel_version(pernet_operations) address_space_operations = [ # type name minver maxver ["func_ptr", "writepage", None, "6.15.9"], ["func_ptr", "read_folio", "5.19.0", None], ["func_ptr", "readpage", None, "5.19.0"], ["func_ptr", "writepages", None, None], ["func_ptr", "dirty_folio", "5.18.0", None], ["func_ptr", "set_page_dirty", None, "5.18.0"], ["func_ptr", "readpages", None, "5.18.0"], ["func_ptr", "readahead", "5.8.0", None], ["func_ptr", "write_begin", None, None], ["func_ptr", "write_end", None, None], ["func_ptr", "bmap", None, None], ["func_ptr", "invalidate_folio", "5.18.0", None], ["func_ptr", "invalidatepage", None, "5.18.0"], ["func_ptr", "release_folio", "5.19.0", None], ["func_ptr", "releasepage", None, "5.19.0"], ["func_ptr", "free_folio", "5.19.0", None], ["func_ptr", "freepage", None, "5.19.0"], ["func_ptr", "direct_IO", None, None], ["func_ptr", "get_xip_mem", None, "4.0.0"], ["func_ptr", "migrate_folio", "6.0.0", None], ["func_ptr", "migratepage", None, "5.20.0"], ["func_ptr", "isolate_page", "4.8.0", "5.20.0"], ["func_ptr", "putback_page", "4.8.0", "5.20.0"], ["func_ptr", "launder_folio", "5.18.0", None], ["func_ptr", "launder_page", None, "5.18.0"], ["func_ptr", "is_partially_uptodate", None, None], ["func_ptr", "is_dirty_writeback", "3.11.0", None], ["func_ptr", "error_remove_page", None, "6.8.0"], ["func_ptr", "error_remove_folio", "6.8.0", None], ["func_ptr", "swap_activate", "3.6.0", None], ["func_ptr", "swap_deactivate", "3.6.0", None], ["func_ptr", "swap_rw", "5.19.0", None], ] self.members["address_space_operations"] = adapt_to_kernel_version(address_space_operations) vm_operations_struct = [ # type name minver maxver ["func_ptr", "open", None, None], ["func_ptr", "close", None, None], ["func_ptr", "may_split", "5.11.0", None], ["func_ptr", "split", "4.14.0", "5.10.187"], ["func_ptr", "mremap", "4.3.9", None], ["func_ptr", "mprotect", "5.11.0", None], ["func_ptr", "fault", None, None], ["func_ptr", "huge_fault", "4.11.0", None], ["func_ptr", "pmd_fault", "4.3.0", "4.11.0"], ["func_ptr", "map_pages", "3.15.0", None], ["func_ptr", "pagesize", "4.17.0", None], ["func_ptr", "page_mkwrite", None, None], ["func_ptr", "pfn_mkwrite", "4.1.0", None], ["func_ptr", "access", None, None], ["func_ptr", "name", "3.16.0", None], ["func_ptr", "set_policy (CONFIG_NUMA=y)", None, None], ["func_ptr", "get_policy (CONFIG_NUMA=y)", None, None], ["func_ptr", "migrate (CONFIG_NUMA=y)", None, "3.19.0"], ["func_ptr", "find_special_page", "4.0.0", "6.17.8"], ["func_ptr", "find_normal_page (CONFIG_FIND_NORMAL_PAGE=y)", "6.18.0", None], ["func_ptr", "remap_pages", "3.17.0", "4.0.0"], ["func_ptr", "remap_pages", "3.7.0", "3.16.59"], ] self.members["vm_operations_struct"] = adapt_to_kernel_version(vm_operations_struct) super_operations = [ # type name minver maxver ["func_ptr", "alloc_inode", None, None], ["func_ptr", "destroy_inode", None, None], ["func_ptr", "free_inode", "5.2.0", None], ["func_ptr", "dirty_inode", None, None], ["func_ptr", "write_inode", None, None], ["func_ptr", "drop_inode", None, None], ["func_ptr", "evict_inode", None, None], ["func_ptr", "put_super", None, None], ["func_ptr", "write_super", None, "3.6.0"], ["func_ptr", "sync_fs", None, None], ["func_ptr", "freeze_super", "3.19.0", None], ["func_ptr", "freeze_fs", None, None], ["func_ptr", "thaw_super", "3.19.0", None], ["func_ptr", "unfreeze_fs", None, None], ["func_ptr", "statfs", None, None], ["func_ptr", "remount_fs", None, None], ["func_ptr", "umount_begin", None, None], ["func_ptr", "show_options", None, None], ["func_ptr", "show_devname", None, None], ["func_ptr", "show_path", None, None], ["func_ptr", "show_stats", None, None], ["func_ptr", "quota_read (CONFIG_QUOTA=y)", None, None], ["func_ptr", "quota_write (CONFIG_QUOTA=y)", None, None], ["func_ptr", "get_dquots (CONFIG_QUOTA=y)", "3.19.0", None], ["func_ptr", "bdev_try_to_free_page", None, "5.14.0"], ["func_ptr", "nr_cached_objects", "3.1.0", None], ["func_ptr", "free_cached_objects", "3.1.0", None], ["func_ptr", "remove_bdev", "6.17.0", None], ["func_ptr", "shutdown", "6.5.0", None], ] self.members["super_operations"] = adapt_to_kernel_version(super_operations) dentry_operations = [ # type name minver maxver ["func_ptr", "d_revalidate", None, None], ["func_ptr", "d_weak_revalidate", "3.9.0", None], ["func_ptr", "d_hash", None, None], ["func_ptr", "d_compare", None, None], ["func_ptr", "d_delete", None, None], ["func_ptr", "d_init", "4.8.0", None], ["func_ptr", "d_release", None, None], ["func_ptr", "d_prune", "3.2.0", None], ["func_ptr", "d_iput", None, None], ["func_ptr", "d_dname", None, None], ["func_ptr", "d_automount", None, None], ["func_ptr", "d_manage", None, None], ["func_ptr", "d_select_inode", "4.1.0", "4.8.0"], ["func_ptr", "d_real", "4.4.0", None], ["func_ptr", "d_select_inode", "3.18.23", "3.19.0"], ["func_ptr", "d_unalias_trylock", "6.14.0", None], ["func_ptr", "d_unalias_unlock", "6.14.0", None], ] self.members["dentry_operations"] = adapt_to_kernel_version(dentry_operations) block_device_operations = [ # type name minver maxver ["func_ptr", "submit_bio", "5.9.0", None], ["func_ptr", "poll_bio", "5.18.0", None], ["func_ptr", "open", None, None], ["func_ptr", "release", None, None], ["func_ptr", "rw_page", None, "6.3.0"], ["func_ptr", "ioctl", None, None], ["func_ptr", "compat_ioctl", None, None], ["func_ptr", "direct_access", None, "4.12.0"], ["func_ptr", "check_events", None, None], ["func_ptr", "media_changed", None, "5.9.0"], ["func_ptr", "unlock_native_capacity", None, None], ["func_ptr", "revalidate_disk", None, "5.13.0"], ["func_ptr", "getgeo", None, None], ["func_ptr", "set_read_only", "5.11.0", None], ["func_ptr", "free_disk", "5.18.0", None], ["func_ptr", "swap_slot_free_notify", None, None], ["func_ptr", "report_zones", "4.20.0", None], ["func_ptr", "devnode", "5.7.0", None], ["func_ptr", "get_unique_id", "5.16.0", None], ["ptr", "owner", None, None], ["ptr", "pr_ops", "4.4.0", None], ["func_ptr", "alternative_gpt_sector", "5.15.0", None], ] self.members["block_device_operations"] = adapt_to_kernel_version(block_device_operations) pipe_buf_operations = [ # type name minver maxver ["int", "can_merge", None, "5.1.0"], ["func_ptr", "map", None, "3.15.0"], ["func_ptr", "unmap", None, "3.15.0"], ["func_ptr", "confirm", None, None], ["func_ptr", "release", None, None], ["func_ptr", "try_steal", None, None], ["func_ptr", "get", None, None], ] self.members["pipe_buf_operations"] = adapt_to_kernel_version(pipe_buf_operations) smp_operations = [ # type name minver maxver ["func_ptr", "smp_init_cpus (CONFIG_SMP=y)", "3.7.0", None], ["func_ptr", "smp_prepare_cpus (CONFIG_SMP=y)", "3.7.0", None], ["func_ptr", "smp_secondary_init (CONFIG_SMP=y)", "3.7.0", None], ["func_ptr", "smp_boot_secondary (CONFIG_SMP=y)", "3.7.0", None], ["func_ptr", "cpu_kill (CONFIG_HOTPLUG_CPU=y)", "3.7.0", None], ["func_ptr", "cpu_die (CONFIG_HOTPLUG_CPU=y)", "3.7.0", None], ["func_ptr", "cpu_can_disable (CONFIG_HOTPLUG_CPU=y)", "4.3.0", None], ["func_ptr", "cpu_disable (CONFIG_HOTPLUG_CPU=y)", "3.7.0", None], ] self.members["smp_operations"] = adapt_to_kernel_version(smp_operations) dma_buf_ops = [ # type name minver maxver ["bool", "cache_sgt_mapping", "5.7.0", "6.15.9"], ["bool, bool", "cache_sgt_mapping, dynamic_mapping", "5.5.0", "5.7.0"], ["bool", "cache_sgt_mapping", "5.3.0", "5.4.265"], ["func_ptr", "attach", "3.2.0", None], ["func_ptr", "detach", "3.2.0", None], ["func_ptr", "pin", "5.7.0", None], ["func_ptr", "unpin", "5.7.0", None], ["func_ptr", "map_dma_buf", "3.2.0", None], ["func_ptr", "unmap_dma_buf", "3.2.0", None], ["func_ptr", "release", "3.2.0", None], ["func_ptr", "begin_cpu_access", "3.4.0", None], ["func_ptr", "end_cpu_access", "3.4.0", None], ["func_ptr", "mmap", "5.3.0", None], ["func_ptr", "map_atomic", "4.12.0", "4.19.0"], ["func_ptr", "unmap_atomic", "4.12.0", "4.19.0"], ["func_ptr", "kmap_atomic", "3.4.0", "4.12.0"], ["func_ptr", "kunmap_atomic", "3.4.0", "4.12.0"], ["func_ptr", "map", "4.12.0", "5.6.0"], ["func_ptr", "unmap", "4.12.0", "5.6.0"], ["func_ptr", "kmap", "3.4.0", "4.12.0"], ["func_ptr", "kunmap", "3.4.0", "4.12.0"], ["func_ptr", "mmap", "3.5.0", "5.3.0"], ["func_ptr", "vmap", "3.5.0", None], ["func_ptr", "vunmap", "3.5.0", None], ] self.members["dma_buf_ops"] = adapt_to_kernel_version(dma_buf_ops) ata_port_operations = [ # type name minver maxver ["func_ptr", "qc_defer", None, None], ["func_ptr", "check_atapi_dma", None, None], ["func_ptr", "qc_prep", None, None], ["func_ptr", "qc_issue", None, None], ["func_ptr", "qc_fill_rtf", None, None], ["func_ptr", "qc_ncq_fill_rtf", "6.3.0", None], ["func_ptr", "cable_detect", None, None], ["func_ptr", "mode_filter", None, None], ["func_ptr", "set_piomode", None, None], ["func_ptr", "set_dmamode", None, None], ["func_ptr", "set_mode", None, None], ["func_ptr", "read_id", None, None], ["func_ptr", "dev_config", None, None], ["func_ptr", "freeze", None, None], ["func_ptr", "thaw", None, None], ["func_ptr", "prereset", None, "6.16.5"], ["func_ptr", "softreset", None, "6.16.5"], ["func_ptr", "hardreset", None, "6.16.5"], ["func_ptr", "postreset", None, "6.16.5"], ["func_ptr", "reset.prereset", "6.17.0", None], ["func_ptr", "reset.softreset", "6.17.0", None], ["func_ptr", "reset.hardreset", "6.17.0", None], ["func_ptr", "reset.postreset", "6.17.0", None], ["func_ptr", "pmp_prereset", None, "6.16.5"], ["func_ptr", "pmp_softreset", None, "6.16.5"], ["func_ptr", "pmp_hardreset", None, "6.16.5"], ["func_ptr", "pmp_postreset", None, "6.16.5"], ["func_ptr", "pmp_reset.pmp_prereset", "6.17.0", None], ["func_ptr", "pmp_reset.pmp_softreset", "6.17.0", None], ["func_ptr", "pmp_reset.pmp_hardreset", "6.17.0", None], ["func_ptr", "pmp_reset.pmp_postreset", "6.17.0", None], ["func_ptr", "error_handler", None, None], ["func_ptr", "lost_interrupt", None, None], ["func_ptr", "post_internal_cmd", None, None], ["func_ptr", "sched_eh", "3.6.0", None], ["func_ptr", "end_eh", "3.6.0", None], ["func_ptr", "scr_read", None, None], ["func_ptr", "scr_write", None, None], ["func_ptr", "pmp_attach", None, None], ["func_ptr", "pmp_detach", None, None], ["func_ptr", "set_lpm", None, None], ["func_ptr", "port_suspend", None, None], ["func_ptr", "port_resume", None, None], ["func_ptr", "port_start", None, None], ["func_ptr", "port_stop", None, None], ["func_ptr", "host_stop", None, None], ["func_ptr", "sff_dev_select (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_set_devctl (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_check_status (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_check_altstatus (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_tf_load (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_tf_read (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_exec_command (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_data_xfer (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_irq_on (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_irq_check (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_irq_clear (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "sff_drain_fifo (CONFIG_ATA_SFF=y)", None, None], ["func_ptr", "bmdma_setup (CONFIG_ATA_BMDMA=y)", None, None], ["func_ptr", "bmdma_start (CONFIG_ATA_BMDMA=y)", None, None], ["func_ptr", "bmdma_stop (CONFIG_ATA_BMDMA=y)", None, None], ["func_ptr", "bmdma_status (CONFIG_ATA_BMDMA=y)", None, None], ["func_ptr", "em_show", None, None], ["func_ptr", "em_store", None, None], ["func_ptr", "sw_activity_show", None, None], ["func_ptr", "sw_activity_store", None, None], ["func_ptr", "transmit_led_message", "3.11.0", None], ["func_ptr", "phy_reset", None, "6.6.0"], ["func_ptr", "eng_timeout", None, "6.6.0"], ["ptr", "inherits", None, None], ] self.members["ata_port_operations"] = adapt_to_kernel_version(ata_port_operations) media_entity_operations = [ # type name minver maxver ["func_ptr", "get_fwnode_pad", "4.13.0", None], ["func_ptr", "link_setup", None, None], ["func_ptr", "link_validate", "3.5.0", None], ["func_ptr", "has_pad_interdep", "6.1.0", None], ] self.members["media_entity_operations"] = adapt_to_kernel_version(media_entity_operations) configfs_item_operations = [ # type name minver maxver ["func_ptr", "release", None, None], ["func_ptr", "show_attribute", None, "4.4.0"], ["func_ptr", "store_attribute", None, "4.4.0"], ["func_ptr", "allow_link", None, None], ["func_ptr", "drop_link", None, None], ] self.members["configfs_item_operations"] = adapt_to_kernel_version(configfs_item_operations) configfs_group_operations = [ # type name minver maxver ["func_ptr", "make_item", None, None], ["func_ptr", "make_group", None, None], ["func_ptr", "commit_item", None, "6.1.113"], ["func_ptr", "disconnect_notify", None, None], ["func_ptr", "drop_item", None, None], ["func_ptr", "is_visible", "6.11.0", None], ["func_ptr", "is_bin_visible", "6.11.0", None], ] self.members["configfs_group_operations"] = adapt_to_kernel_version(configfs_group_operations) fs_context_operations = [ # type name minver maxver ["func_ptr", "free", "5.1.0", None], ["func_ptr", "dup", "5.1.0", None], ["func_ptr", "parse_param", "5.1.0", None], ["func_ptr", "parse_monolithic", "5.1.0", None], ["func_ptr", "get_tree", "5.1.0", None], ["func_ptr", "reconfigure", "5.1.0", None], ] self.members["fs_context_operations"] = adapt_to_kernel_version(fs_context_operations) export_operations = [ # type name minver maxver ["func_ptr", "encode_fh", None, None], ["func_ptr", "fh_to_dentry", None, None], ["func_ptr", "fh_to_parent", None, None], ["func_ptr", "get_name", None, None], ["func_ptr", "get_parent", None, None], ["func_ptr", "commit_metadata", None, None], ["func_ptr", "get_uuid", "4.0.0", None], ["func_ptr", "map_blocks", "4.0.0", None], ["func_ptr", "commit_blocks", "4.0.0", None], ["func_ptr", "fetch_iversion", "5.10.0", "6.3.0"], ["func_ptr", "permission", "6.14.0", None], ["func_ptr", "open", "6.14.0", None], ["long", "flags", "5.10.0", None], ] self.members["export_operations"] = adapt_to_kernel_version(export_operations) dev_pm_ops = [ # type name minver maxver ["func_ptr", "prepare", None, None], ["func_ptr", "complete", None, None], ["func_ptr", "suspend", None, None], ["func_ptr", "resume", None, None], ["func_ptr", "freeze", None, None], ["func_ptr", "thaw", None, None], ["func_ptr", "poweroff", None, None], ["func_ptr", "restore", None, None], ["func_ptr", "suspend_late", "3.4.0", None], ["func_ptr", "resume_early", "3.4.0", None], ["func_ptr", "freeze_late", "3.4.0", None], ["func_ptr", "thaw_early", "3.4.0", None], ["func_ptr", "poweroff_late", "3.4.0", None], ["func_ptr", "restore_early", "3.4.0", None], ["func_ptr", "suspend_noirq", None, None], ["func_ptr", "resume_noirq", None, None], ["func_ptr", "freeze_noirq", None, None], ["func_ptr", "thaw_noirq", None, None], ["func_ptr", "poweroff_noirq", None, None], ["func_ptr", "restore_noirq", None, None], ["func_ptr", "runtime_suspend", None, None], ["func_ptr", "runtime_resume", None, None], ["func_ptr", "runtime_idle", None, None], ] self.members["dev_pm_ops"] = adapt_to_kernel_version(dev_pm_ops) clk_ops = [ # type name minver maxver ["func_ptr", "prepare", "3.4.0", None], ["func_ptr", "unprepare", "3.4.0", None], ["func_ptr", "is_prepared", "3.10.0", None], ["func_ptr", "unprepare_unused", "3.10.0", None], ["func_ptr", "init (CONFIG_SH_CLK_CPG_LEGACY=y)", None, "3.4.0"], ["func_ptr", "enable", None, None], ["func_ptr", "disable", None, None], ["func_ptr", "is_enabled", "3.4.0", None], ["func_ptr", "disable_unused", "3.8.0", None], ["func_ptr", "save_context", "4.20.0", None], ["func_ptr", "restore_context", "4.20.0", None], ["func_ptr", "recalc_rate", "3.4.0", None], ["func_ptr", "recalc", None, "3.4.0"], ["func_ptr", "round_rate", "3.4.0", None], ["func_ptr", "determine_rate", "3.12.0", None], ["func_ptr", "set_parent", "3.4.0", None], ["func_ptr", "get_parent", "3.4.0", None], ["func_ptr", "set_rate", None, None], ["func_ptr", "set_rate_and_parent", "3.14.0", None], ["func_ptr", "set_parent", None, "3.4.0"], ["func_ptr", "round_rate", None, "3.4.0"], ["func_ptr", "recalc_accuracy", "3.14.0", None], ["func_ptr", "get_phase", "3.18.0", None], ["func_ptr", "set_phase", "3.18.0", None], ["func_ptr", "get_duty_cycle", "4.19.0", None], ["func_ptr", "set_duty_cycle", "4.19.0", None], ["func_ptr", "init", "3.4.0", None], ["func_ptr", "terminate", "5.6.0", None], ["func_ptr", "debug_init", "3.15.0", None], ] self.members["clk_ops"] = adapt_to_kernel_version(clk_ops) parport_operations = [ # type name minver maxver ["func_ptr", "write_data", None, None], ["func_ptr", "read_data", None, None], ["func_ptr", "write_control", None, None], ["func_ptr", "read_control", None, None], ["func_ptr", "frob_control", None, None], ["func_ptr", "read_status", None, None], ["func_ptr", "enable_irq", None, None], ["func_ptr", "disable_irq", None, None], ["func_ptr", "data_forward", None, None], ["func_ptr", "data_reverse", None, None], ["func_ptr", "init_state", None, None], ["func_ptr", "save_state", None, None], ["func_ptr", "restore_state", None, None], ["func_ptr", "epp_write_data", None, None], ["func_ptr", "epp_read_data", None, None], ["func_ptr", "epp_write_addr", None, None], ["func_ptr", "epp_read_addr", None, None], ["func_ptr", "ecp_write_data", None, None], ["func_ptr", "ecp_read_data", None, None], ["func_ptr", "ecp_write_addr", None, None], ["func_ptr", "compat_write_data", None, None], ["func_ptr", "nibble_read_data", None, None], ["func_ptr", "byte_read_data", None, None], ["ptr", "owner", None, None], ] self.members["parport_operations"] = adapt_to_kernel_version(parport_operations) proc_ns_operations = [ # type name minver maxver ["char*", "name", None, None], ["char*", "real_ns_name", "4.12.0", None], ["int", "type", None, "6.17.8"], ["func_ptr", "get", None, None], ["func_ptr", "put", None, None], ["func_ptr", "install", None, None], ["func_ptr", "owner", "4.9.0", None], ["func_ptr", "get_parent", "4.9.0", None], ["func_ptr", "inum", "3.8.0", "3.19.0"], ] self.members["proc_ns_operations"] = adapt_to_kernel_version(proc_ns_operations) net_device_ops = [ # type name minver maxver ["func_ptr", "ndo_init", None, None], ["func_ptr", "ndo_uninit", None, None], ["func_ptr", "ndo_open", None, None], ["func_ptr", "ndo_stop", None, None], ["func_ptr", "ndo_start_xmit", None, None], ["func_ptr", "ndo_features_check", "4.5.0", None], ["func_ptr", "ndo_select_queue", None, None], ["func_ptr", "ndo_change_rx_flags", None, None], ["func_ptr", "ndo_set_rx_mode", None, None], ["func_ptr", "ndo_set_multicast_list", None, "3.2.0"], ["func_ptr", "ndo_set_mac_address", None, None], ["func_ptr", "ndo_validate_addr", None, None], ["func_ptr", "ndo_do_ioctl", None, None], ["func_ptr", "ndo_eth_ioctl", "5.15.0", None], ["func_ptr", "ndo_siocbond", "5.15.0", None], ["func_ptr", "ndo_siocwandev", "5.15.0", None], ["func_ptr", "ndo_siocdevprivate", "5.15.0", None], ["func_ptr", "ndo_set_config", None, None], ["func_ptr", "ndo_change_mtu", None, None], ["func_ptr", "ndo_neigh_setup", None, None], ["func_ptr", "ndo_tx_timeout", None, None], ["func_ptr", "ndo_get_stats64", None, None], ["func_ptr", "ndo_has_offload_stats", "4.9.0", None], ["func_ptr", "ndo_get_offload_stats", "4.9.0", None], ["func_ptr", "ndo_get_stats", None, None], ["func_ptr", "ndo_vlan_rx_register", None, "3.1.0"], ["func_ptr", "ndo_vlan_rx_add_vid", None, None], ["func_ptr", "ndo_vlan_rx_kill_vid", None, None], ["func_ptr", "ndo_poll_controller (CONFIG_NET_POLL_CONTROLLER=y)", None, None], ["func_ptr", "ndo_netpoll_setup (CONFIG_NET_POLL_CONTROLLER=y)", None, None], ["func_ptr", "ndo_netpoll_cleanup (CONFIG_NET_POLL_CONTROLLER=y)", None, None], ["func_ptr", "ndo_busy_poll (CONFIG_NET_RX_BUSY_POLL=y)", "3.11.0", "4.11.0"], ["func_ptr", "ndo_set_vf_mac", None, None], ["func_ptr", "ndo_set_vf_vlan", None, None], ["func_ptr", "ndo_set_vf_rate", "3.16.0", None], ["func_ptr", "ndo_set_vf_tx_rate", None, "3.16.0"], ["func_ptr", "ndo_set_vf_spoofchk", "3.2.0", None], ["func_ptr", "ndo_set_vf_trust", "4.4.0", None], ["func_ptr", "ndo_get_vf_config", None, None], ["func_ptr", "ndo_set_vf_link_state", "3.11.0", None], ["func_ptr", "ndo_get_vf_stats", "4.2.0", None], ["func_ptr", "ndo_set_vf_port", None, None], ["func_ptr", "ndo_get_vf_port", None, None], ["func_ptr", "ndo_set_vf_rss_query_en", "3.18.21", "3.19.0"], ["func_ptr", "ndo_get_vf_guid", "5.5.0", None], ["func_ptr", "ndo_set_vf_guid", "4.6.0", None], ["func_ptr", "ndo_set_vf_rss_query_en", "4.1.0", None], ["func_ptr", "ndo_setup_tc", None, None], ["func_ptr", "ndo_fcoe_enable (CONFIG_FCOE=y)", None, None], ["func_ptr", "ndo_fcoe_disable (CONFIG_FCOE=y)", None, None], ["func_ptr", "ndo_fcoe_ddp_setup (CONFIG_FCOE=y)", None, None], ["func_ptr", "ndo_fcoe_ddp_done (CONFIG_FCOE=y)", None, None], ["func_ptr", "ndo_fcoe_ddp_target CONFIG_FCOE=y)", None, None], ["func_ptr", "ndo_fcoe_get_hbainfo (CONFIG_FCOE=y)", "3.3.0", None], ["func_ptr", "ndo_fcoe_get_wwn (CONFIG_LIBFCOE=y)", "3.2.0", None], ["func_ptr", "ndo_fcoe_get_wwn (CONFIG_FCOE=y)", None, "3.2.0"], ["func_ptr", "ndo_rx_flow_steer (CONFIG_RFS_ACCEL=y)", None, None], ["func_ptr", "ndo_add_slave", None, None], ["func_ptr", "ndo_del_slave", None, None], ["func_ptr", "ndo_get_xmit_slave", "5.8.0", None], ["func_ptr", "ndo_sk_get_lower_dev", "5.12.0", None], ["func_ptr", "ndo_fix_features", None, None], ["func_ptr", "ndo_set_features", None, None], ["func_ptr", "ndo_neigh_construct", "3.3.0", None], ["func_ptr", "ndo_neigh_destroy", "3.3.0", None], ["func_ptr", "ndo_fdb_add", "3.5.0", None], ["func_ptr", "ndo_fdb_del", "3.5.0", None], ["func_ptr", "ndo_fdb_del_bulk", "5.19.0", None], ["func_ptr", "ndo_fdb_dump", "3.5.0", None], ["func_ptr", "ndo_fdb_get", "5.0.0", None], ["func_ptr", "ndo_mdb_add", "6.4.0", None], ["func_ptr", "ndo_mdb_del", "6.4.0", None], ["func_ptr", "ndo_mdb_del_bulk", "6.8.0", None], ["func_ptr", "ndo_mdb_dump", "6.4.0", None], ["func_ptr", "ndo_mdb_get", "6.7.0", None], ["func_ptr", "ndo_bridge_setlink", "3.8.0", None], ["func_ptr", "ndo_bridge_getlink", "3.8.0", None], ["func_ptr", "ndo_bridge_dellink", "3.9.0", None], ["func_ptr", "ndo_change_carrier", "3.9.0", None], ["func_ptr", "ndo_get_phys_port_id", "3.12.0", None], ["func_ptr", "ndo_get_port_parent_id", "5.1.0", None], ["func_ptr", "ndo_get_phys_port_name", "4.1.0", None], ["func_ptr", "ndo_udp_tunnel_add", "4.8.0", "5.12.0"], ["func_ptr", "ndo_udp_tunnel_del", "4.8.0", "5.12.0"], ["func_ptr", "ndo_add_vxlan_port", "3.12.0", "4.8.0"], ["func_ptr", "ndo_del_vxlan_port", "3.12.0", "4.8.0"], ["func_ptr", "ndo_add_geneve_port", "4.5.0", "4.8.0"], ["func_ptr", "ndo_del_geneve_port", "4.5.0", "4.8.0"], ["func_ptr", "ndo_dfwd_add_station", "3.13.0", None], ["func_ptr", "ndo_dfwd_del_station", "3.13.0", None], ["func_ptr", "ndo_dfwd_start_xmit", "3.13.0", "4.13.0"], ["func_ptr", "ndo_get_lock_subclass", "3.14.5", "5.4.0"], ["func_ptr", "ndo_features_check", "3.18.4", "4.5.0"], ["func_ptr", "ndo_gso_check", "3.18.0", "3.18.4"], ["func_ptr", "ndo_set_tx_maxrate", "4.1.0", None], ["func_ptr", "ndo_get_iflink", "4.1.0", None], ["func_ptr", "ndo_switch_parent_id_get (CONFIG_NET_SWITCHDEV=y)", "3.19.0", "4.1.0"], ["func_ptr", "ndo_switch_port_stp_update (CONFIG_NET_SWITCHDEV=y)", "3.19.0", "4.1.0"], ["func_ptr", "ndo_change_proto_down", "4.3.0", "5.17.0"], ["func_ptr", "ndo_fill_metadata_dst", "4.3.0", None], ["func_ptr", "ndo_set_rx_headroom", "4.6.0", None], ["func_ptr", "ndo_bpf", "4.15.0", None], ["func_ptr", "ndo_xdp", "4.8.0", "4.15.0"], ["func_ptr", "ndo_xdp_xmit", "4.14.0", None], ["func_ptr", "ndo_xdp_get_xmit_slave", "5.15.0", None], ["func_ptr", "ndo_xsk_wakeup", "5.4.0", None], ["func_ptr", "ndo_xsk_async_xmit", "4.18.0", "5.4.0"], ["func_ptr", "ndo_xdp_flush", "4.14.0", "4.18.0"], ["func_ptr", "ndo_get_devlink_port", "5.2.0", "6.1.115"], ["func_ptr", "ndo_get_devlink", "5.1.0", "5.2.0"], ["func_ptr", "ndo_tunnel_ctl", "5.8.0", None], ["func_ptr", "ndo_get_peer_dev", "5.10.0", None], ["func_ptr", "ndo_fill_forward_path", "5.13.0", None], ["func_ptr", "ndo_get_tstamp", "5.19.0", None], ["func_ptr", "ndo_hwtstamp_get", "6.6.0", None], ["func_ptr", "ndo_hwtstamp_set", "6.6.0", None], ["ptr", "net_shaper_ops (CONFIG_NET_SHAPER=y)", "6.12.0", None], ] self.members["net_device_ops"] = adapt_to_kernel_version(net_device_ops) page_ext_operations = [ # type name minver maxver ["long", "offset", "4.9.0", None], ["long", "size", "4.9.0", None], ["func_ptr", "need", "3.19.0", None], ["func_ptr", "init", "3.19.0", None], ["bool", "need_shared_flags", "6.3.0", None], ] self.members["page_ext_operations"] = adapt_to_kernel_version(page_ext_operations) ucsi_operations = [ # type name minver maxver ["func_ptr", "read_version", "6.11.0", None], ["func_ptr", "read_cci", "6.11.0", None], ["func_ptr", "poll_cci", "6.14.0", None], ["func_ptr", "read_message_in", "6.11.0", None], ["func_ptr", "sync_control", "6.11.0", None], ["func_ptr", "async_control", "6.11.0", None], ["func_ptr", "read", "5.4.0", "6.11.0"], ["func_ptr", "sync_write", "5.4.0", "6.11.0"], ["func_ptr", "async_write", "5.4.0", "6.11.0"], ["func_ptr", "update_altmodes", "5.6.0", None], ["func_ptr", "update_connector", "6.10.0", None], ["func_ptr", "connector_status", "6.10.0", None], ] self.members["ucsi_operations"] = adapt_to_kernel_version(ucsi_operations) movable_operations = [ # type name minver maxver ["func_ptr", "isolate_page", "6.0.0", None], ["func_ptr", "migrate_page", "6.0.0", None], ["func_ptr", "pushback_page", "6.0.0", None], ] self.members["movable_operations"] = adapt_to_kernel_version(movable_operations) damon_operations = [ # type name minver maxver ["int", "id", "5.18.0", None], ["func_ptr", "init", "5.18.0", None], ["func_ptr", "update", "5.18.0", None], ["func_ptr", "prepare_access_checks", "5.18.0", None], ["func_ptr", "check_accesses", "5.18.0", None], ["func_ptr", "reset_aggregated", "5.18.0", "6.15.0"], ["func_ptr", "get_scheme_score", "5.18.0", None], ["func_ptr", "apply_scheme", "5.18.0", None], ["func_ptr", "target_valid", "5.18.0", None], ["func_ptr", "cleanup_target", "6.17.0", None], ["func_ptr", "cleanup", "5.18.0", None], ] self.members["damon_operations"] = adapt_to_kernel_version(damon_operations) proc_ops = [ # type name minver maxver ["int", "proc_flags", "5.7.0", None], ["func_ptr", "proc_open", "5.6.0", None], ["func_ptr", "proc_read", "5.6.0", None], ["func_ptr", "proc_read_iter", "5.10.0", None], ["func_ptr", "proc_write", "5.6.0", None], ["func_ptr", "proc_lseek", "5.6.0", None], ["func_ptr", "proc_release", "5.6.0", None], ["func_ptr", "proc_poll", "5.6.0", None], ["func_ptr", "proc_ioctl", "5.6.0", None], ["func_ptr", "proc_compat_ioctl (CONFIG_COMPAT=y)", "5.6.0", None], ["func_ptr", "proc_mmap", "5.6.0", None], ["func_ptr", "proc_get_unmapped_area", "5.6.0", None], ] self.members["proc_ops"] = adapt_to_kernel_version(proc_ops) regulator_ops = [ # type name minver maxver ["func_ptr", "list_voltage", None, None], ["func_ptr", "set_voltage", None, None], ["func_ptr", "map_voltage", None, None], ["func_ptr", "set_voltage_sel", None, None], ["func_ptr", "get_voltage", None, None], ["func_ptr", "get_voltage_sel", None, None], ["func_ptr", "set_current_limit", None, None], ["func_ptr", "get_current_limit", None, None], ["func_ptr", "set_input_current_limit", "4.2.0", None], ["func_ptr", "set_over_current_protection", "4.3.0", None], ["func_ptr", "set_over_voltage_protection", "5.14.0", None], ["func_ptr", "set_under_voltage_protection", "5.14.0", None], ["func_ptr", "set_thermal_protection", "5.14.0", None], ["func_ptr", "set_active_discharge", "4.6.0", None], ["func_ptr", "enable", None, None], ["func_ptr", "disable", None, None], ["func_ptr", "is_enabled", None, None], ["func_ptr", "set_mode", None, None], ["func_ptr", "get_mode", None, None], ["func_ptr", "get_error_flags", "4.10.0", None], ["func_ptr", "enable_time", None, None], ["func_ptr", "set_ramp_delay", "3.6.0", None], ["func_ptr", "set_voltage_time", "4.9.0", None], ["func_ptr", "set_voltage_time_sel", None, None], ["func_ptr", "set_soft_start", "4.2.0", None], ["func_ptr", "get_status", None, None], ["func_ptr", "get_optimum_mode", None, None], ["func_ptr", "set_load", "4.1.0", None], ["func_ptr", "set_bypass", "3.7.0", None], ["func_ptr", "get_bypass", "3.7.0", None], ["func_ptr", "set_suspend_voltage", None, None], ["func_ptr", "set_suspend_enable", None, None], ["func_ptr", "set_suspend_disable", None, None], ["func_ptr", "set_suspend_mode", None, None], ["func_ptr", "resume", "4.19.0", None], ["func_ptr", "resume_early", "4.16.0", "4.19.0"], ["func_ptr", "set_pull_down", "4.2.0", None], ] self.members["regulator_ops"] = adapt_to_kernel_version(regulator_ops) tty_port_operations = [ # type name minver maxver ["func_ptr", "carrier_raised", "5.15.0", None], ["func_ptr", "dtr_rts", "5.15.0", None], ["func_ptr", "shutdown", "5.15.0", None], ["func_ptr", "activate", "5.15.0", None], ["func_ptr", "destruct", "5.15.0", None], ] self.members["tty_port_operations"] = adapt_to_kernel_version(tty_port_operations) kobj_ns_type_operations = [ # type name minver maxver ["int", "type", None, None], ["func_ptr", "current_may_mount", "3.12.0", None], ["func_ptr", "grab_current_ns", None, None], ["func_ptr", "netlink_ns", None, None], ["func_ptr", "initial_ns", None, None], ["func_ptr", "drop_ns", None, None], ] self.members["kobj_ns_type_operations"] = adapt_to_kernel_version(kobj_ns_type_operations) btf_kind_operations = [ # type name minver maxver ["func_ptr", "check_meta", "4.18.0", None], ["func_ptr", "resolve", "4.18.0", None], ["func_ptr", "check_member", "4.18.0", None], ["func_ptr", "check_kflag_member", "5.0.0", None], ["func_ptr", "log_details", "4.18.0", None], ["func_ptr", "show", "5.10.0", None], ["func_ptr", "seq_show", "4.18.0", "5.10.0"], ] self.members["btf_kind_operations"] = adapt_to_kernel_version(btf_kind_operations) assert set(self.members.keys()) == set(self.types) return True @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): # parse version if args.version: r = re.search(r"(\d)\.(\d+)(?:\.(\d+))?", args.version) if r: major, minor, patch = int(r.group(1)), int(r.group(2)), int(r.group(3) or 0) else: err("Failed to parse version string") return kversion = Kernel.KernelVersion(None, args.version, major, minor, patch) else: self.quiet_info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return self.quiet_info("Kernel version: {:d}.{:d}.{:d}".format(kversion.major, kversion.minor, kversion.patch)) # initialize if self.initialize(kversion) is False: return # get member members = self.members[args.name] if not members: warn("Not defined in this version") return # print self.out = [] if args.address: # show permission if not args.quiet: kinfo = Kernel.get_kernel_layout() for vaddr, size, perm in kinfo.maps: if vaddr <= args.address and args.address < vaddr + size: perm_str = perm break else: perm_str = "???" self.out.append("Address: {:#x} Permission: {:s}".format(args.address, perm_str)) # get name width name_width = max(len(m[1]) for m in members) try: addrs = [read_int_from_memory(args.address + current_arch.ptrsize * i) for i in range(len(members))] except gdb.MemoryError: self.quiet_err("Memory read error") return # legend if not args.quiet: fmt = "{:5s} {:<10s} {:<{:d}s} {:s}" legend = ["Index", "Type", "Name", name_width, "Value"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # each entries width = AddressUtil.get_format_address_width() for idx, ((type_name, name), address) in enumerate(zip(members, addrs)): if type_name == "char*": sym = " {!r}".format(read_cstring_from_memory(address)) else: sym = Symbol.get_symbol_string(address) self.out.append("{:<5d} {:10s} {:{:d}s} {:#0{:d}x}{:s}".format( idx, type_name, name, name_width, address, width, sym, )) else: # legend if not args.quiet: fmt = "{:5s} {:<10s} {:s}" legend = ["Index", "Type", "Name"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # each entries for idx, (type_name, name) in enumerate(members): self.out.append("{:<5d} {:10s} {:s}".format(idx, type_name, name)) self.print_output(check_terminal_size=True) return @register_command class KernelSysctlCommand(GenericCommand, BufferingOutput): """Dump the sysctl parameters.""" _cmdline_ = "ksysctl" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="REGEXP filter.") parser.add_argument("-s", "--skip-symlink", action="store_true", help="do not follow symlink (net.* and user.*).") parser.add_argument("-e", "--exact", action="store_true", help="use exact match.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-v", "--verbose", action="store_true", help="dump zero-sized entries too.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -q", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "", "Simplified sysctl_table structure:", "", " +-sysctl_table_root-+ +----->+-ctl_dir------+", " | default_set | | | header |", " | ... | | | ctl_table |---+", " | dir | | | ... | |", " | header | | | parent |---|-->parent ctl_node", " | ctl_table | | | ... | |", " | ... | | | root | |", " | parent | | | rb_node |---|-->ctl_node", " | ... | | +--------------+ |", " | root | | |", " | rb_node |----+ | +---------------------+", " | ... | | | |", " +-------------------+ | | +->+-ctl_table(array)-+", " | | | procname |-->name[]", "+---------------------------+ | | data |-->data[max_len]", "| | | maxlen |", "+->+-ctl_node-----+ | | mode |", " | rb_node | | | proc_handler |", " | color | | +------------------+", " | right |--->ctl_node | | procname |-->name[]", " | left |--->ctl_node | | data |-->data[max_len]", " | header |---------------+ | maxlen |", " +--------------+ | mode |", " | proc_handler |", " +------------------+", " | ... |", " +------------------+", ] _note_ = "\n".join(_note_) # Because this may be called repeatedly with different filter conditions, # GEF caches the results for a short time. @Cache.cache_until_next def read_int_from_memory(self, addr): return read_int_from_memory(addr) @Cache.cache_until_next def read_int8_from_memory(self, addr): return read_int8_from_memory(addr) @Cache.cache_until_next def read_int32_from_memory(self, addr): return read_int32_from_memory(addr) @Cache.cache_until_next def read_int64_from_memory(self, addr): return read_int64_from_memory(addr) @Cache.cache_until_next def read_cstring_from_memory(self, addr): return read_cstring_from_memory(addr) @Cache.cache_until_next def is_valid_addr(self, addr): return is_valid_addr(addr) def should_be_print(self, procname): if self.args.filter == []: return True if self.args.exact: for filt in self.args.filter: if filt.pattern == procname: self.exact_found = True return True return False else: for re_pattern in self.args.filter: if re_pattern.search(procname): return True return False def dump_data(self, ctl_table, param_path, mode): if not self.should_be_print(param_path): return maxlen = self.read_int32_from_memory(ctl_table + self.offset_maxlen) # data data_addr = self.read_int_from_memory(ctl_table + current_arch.ptrsize) if data_addr and self.is_valid_addr(data_addr): # type from handler handler = self.read_int_from_memory(ctl_table + self.offset_handler) # data length if handler in self.str_types: data_val = self.read_cstring_from_memory(data_addr) self.out.append("{:<56s} {:#018x} {:#07x} {:#010o} {!r}".format( param_path, data_addr, maxlen, mode, data_val, )) # allow None elif maxlen == 4: data_val = self.read_int32_from_memory(data_addr) self.out.append("{:<56s} {:#018x} {:#07x} {:#010o} {:#018x}".format( param_path, data_addr, maxlen, mode, data_val, )) elif maxlen == 8: data_val = self.read_int64_from_memory(data_addr) self.out.append("{:<56s} {:#018x} {:#07x} {:#010o} {:#018x}".format( param_path, data_addr, maxlen, mode, data_val, )) elif maxlen == 1: data_val = self.read_int8_from_memory(data_addr) self.out.append("{:<56s} {:#018x} {:#07x} {:#010o} {:#018x}".format( param_path, data_addr, maxlen, mode, data_val, )) elif maxlen == 0: if self.args.verbose: self.out.append("{:<56s} {:#018x} {:#07x} {:#010o}".format( param_path, data_addr, maxlen, mode, )) else: # type from heuristic data_val = self.read_cstring_from_memory(data_addr) if data_val and data_val.isprintable() and len(data_val) >= 2: self.out.append("{:<56s} {:#018x} {:#07x} {:#010o} {!r}".format( param_path, data_addr, maxlen, mode, data_val, )) else: data_val = self.read_int_from_memory(data_addr) self.out.append("{:<56s} {:#018x} {:#07x} {:#010o} {:#018x}".format( param_path, data_addr, maxlen, mode, data_val, )) else: if self.args.verbose: self.out.append("{:<56s} {:#018x} {:#07x} {:#010o}".format( param_path, data_addr, maxlen, mode, )) return def redirect_root_for_symlink(self, ctl_table, pbar): if self.args.skip_symlink: return ctset = None root = self.read_int_from_memory(ctl_table + current_arch.ptrsize) if self.is_valid_addr(root + self.offset_lookup): lookup = self.read_int_from_memory(root + self.offset_lookup) if lookup == Symbol.get_ksymaddr("net_ctl_header_lookup"): # net.* ctset = self.net_ctset elif lookup == Symbol.get_ksymaddr("set_lookup"): # user.* ctset = self.user_ctset if ctset: symlink_rb_node = self.read_int_from_memory(ctset + current_arch.ptrsize + self.offset_rb_node) if ctset not in self.seen_ctset: self.seen_ctset.add(ctset) self.sysctl_dump(symlink_rb_node, pbar) return def get_param_path(self, ctl_dir, ctl_table, parent_path): procname = self.read_int_from_memory(ctl_table) if procname == 0: return None procname_str = self.read_cstring_from_memory(procname) if not procname_str: # None or "" return None param_path = (parent_path + "." + procname_str).lstrip(".") self.parent_paths[ctl_dir] = param_path return param_path def sysctl_dump(self, rb_node, pbar): if not rb_node: return if self.args.exact and self.exact_found: return if pbar is not None: pbar.update(1) # ctl_node.header (=ctl_dir) ctl_dir = self.read_int_from_memory(rb_node + current_arch.ptrsize * 3) if ctl_dir not in self.seen_ctl_dir: self.seen_ctl_dir.add(ctl_dir) # parent parent = self.read_int_from_memory(ctl_dir + self.offset_parent) parent_path = self.parent_paths.get(parent, "") # ctl_table(s) ctl_table = self.read_int_from_memory(ctl_dir) while ctl_table not in self.seen_ctl_table: self.seen_ctl_table.add(ctl_table) # param_path param_path = self.get_param_path(ctl_dir, ctl_table, parent_path) if param_path is None: break # mode mode = self.read_int32_from_memory(ctl_table + self.offset_mode) # dump if (mode & 0o0120000) == 0o0120000: # symlink # `net.*` and `user.*` have a symlink attribute and they are redirected to another location. # These must be traced from another root. self.redirect_root_for_symlink(ctl_table, pbar) elif (mode & 0o0040000) == 0o0040000: # directory pass elif mode > 0o777: break else: # If it's not a directory, it should hold data, so dump it. self.dump_data(ctl_table, param_path, mode) if self.args.exact and self.exact_found: return # next array element ctl_table += self.sizeof_ctl_table # ctl_dir.rb_root->rb_node ctl_dir_rb_node = self.read_int_from_memory(ctl_dir + self.offset_rb_node) & ~1 # remove RB_BLACK self.sysctl_dump(ctl_dir_rb_node, pbar) # ctl_node.node.rb_right right = self.read_int_from_memory(rb_node + current_arch.ptrsize * 1) & ~1 # remove RB_BLACK self.sysctl_dump(right, pbar) # ctl_node.node.rb_left left = self.read_int_from_memory(rb_node + current_arch.ptrsize * 2) & ~1 # remove RB_BLACK self.sysctl_dump(left, pbar) return def initialize(self): if hasattr(self, "initialized") and self.initialized: return True self.sysctl_table_root = KernelAddressHeuristicFinder.get_sysctl_table_root() if self.sysctl_table_root is None: self.quiet_err("Could not find sysctl_table_root") return False self.quiet_info("sysctl_table_root: {:#x}".format(self.sysctl_table_root)) """ struct ctl_table_root { struct ctl_table_set { int (*is_seen)(struct ctl_table_set *); struct ctl_dir dir; } default_set; struct ctl_table_set *(*lookup)(struct ctl_table_root *root); void (*set_ownership)(struct ctl_table_header *head, struct ctl_table *table, kuid_t *uid, kgid_t *gid); int (*permissions)(struct ctl_table_header *head, struct ctl_table *table); }; struct ctl_dir { struct ctl_table_header { union { struct { struct ctl_table *ctl_table; int ctl_table_size; // v6.6~ int used; int count; int nreg; }; struct rcu_head { struct callback_head *next; void (*func)(struct callback_head *head); } rcu; }; struct completion *unregistering; struct ctl_table *ctl_table_arg; struct ctl_table_root *root; struct ctl_table_set *set; struct ctl_dir *parent; struct ctl_node *node; struct hlist_head inodes; // v4.12.2~ struct list_head inodes; // v4.11~v4.12.1 struct hlist_head inodes; // v4.9.120~v4.9.337 enum { SYSCTL_TABLE_TYPE_DEFAULT, SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY, } type; // v6.10~ } header; struct rb_root { struct rb_node *rb_node; } root; }; struct ctl_node { struct rb_node { unsigned long __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; } node; struct ctl_table_header *header; }; struct ctl_table { const char *procname; void *data; int maxlen; umode_t mode; struct ctl_table *child; // ~v6.4 enum { SYSCTL_TABLE_TYPE_DEFAULT, SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY } type; // v6.5~v6.10 proc_handler *proc_handler; struct ctl_table_poll *poll; void *extra1; void *extra2; }; """ kversion = Kernel.kernel_version() if is_64bit(): # struct ctl_dir if kversion < "4.9.120": self.offset_rb_node = 0x48 elif "4.9.120" <= kversion < "4.10": self.offset_rb_node = 0x50 elif "4.10" <= kversion < "4.11": self.offset_rb_node = 0x48 elif "4.11" <= kversion < "4.12.2": self.offset_rb_node = 0x58 elif "4.12.2" <= kversion < "6.10": self.offset_rb_node = 0x50 elif "6.10" <= kversion: self.offset_rb_node = 0x58 self.offset_parent = 0x38 # struct ctl_table self.offset_maxlen = 0x10 self.offset_mode = 0x14 if kversion < "6.10": self.offset_handler = 0x20 self.sizeof_ctl_table = 0x40 else: self.offset_handler = 0x18 self.sizeof_ctl_table = 0x38 else: # struct ctl_dir if kversion < "4.9.120": self.offset_rb_node = 0x28 self.offset_parent = 0x20 elif "4.9.120" <= kversion < "4.10": self.offset_rb_node = 0x2c self.offset_parent = 0x20 elif "4.10" <= kversion < "4.11": self.offset_rb_node = 0x28 self.offset_parent = 0x20 elif "4.11" <= kversion < "4.12.2": self.offset_rb_node = 0x30 self.offset_parent = 0x20 elif "4.12.2" <= kversion < "6.6": self.offset_rb_node = 0x2c self.offset_parent = 0x20 elif "6.6" <= kversion < "6.10": self.offset_rb_node = 0x30 self.offset_parent = 0x24 elif "6.10" <= kversion: self.offset_rb_node = 0x34 self.offset_parent = 0x24 # struct ctl_table self.offset_maxlen = 0x8 self.offset_mode = 0xc if kversion < "6.10": self.offset_handler = 0x14 self.sizeof_ctl_table = 0x24 else: self.offset_handler = 0x10 self.sizeof_ctl_table = 0x20 # struct ctl_table_root self.offset_lookup = current_arch.ptrsize + self.offset_rb_node + current_arch.ptrsize # the root for `net.*`; init_nsproxy.net_ns.sysctls self.net_ctset = None init_net = KernelAddressHeuristicFinder.get_init_net() if init_net: current = init_net is_seen = Symbol.get_ksymaddr("is_seen") if is_seen: while True: v = self.read_int_from_memory(current) if v == is_seen: self.net_ctset = current break current += current_arch.ptrsize # the root for `user.*`; init_user_ns.set self.user_ctset = None init_user_ns = KernelAddressHeuristicFinder.get_init_user_ns() if init_user_ns: current = init_user_ns # set_is_seen is found in 3 places (v5.19~), so Symbol.get_ksymaddr should not be used. set_is_seen = Symbol.get_ksymaddr_multiple("set_is_seen") if set_is_seen: while True: v = self.read_int_from_memory(current) if v in set_is_seen: self.user_ctset = current break current += current_arch.ptrsize # handle functions known_str_types_handlers = [ "addrconf_sysctl_stable_secret", "cdrom_sysctl_info", "devkmsg_sysctl_set_loglvl", "numa_zonelist_order_handler", "proc_allowed_congestion_control", "proc_do_uts_string", "proc_dostring", "proc_dostring_coredump", "proc_tcp_available_congestion_control", "proc_tcp_available_ulp", "seccomp_actions_logged_handler", "set_default_qdisc", ] self.str_types = [] for handler in known_str_types_handlers: handler_addr = Symbol.get_ksymaddr(handler) if handler_addr: self.str_types.append(handler_addr) self.root_ctl_dir = self.sysctl_table_root + current_arch.ptrsize self.root_rb_node = self.read_int_from_memory(self.root_ctl_dir + self.offset_rb_node) self.quiet_info("root_ctl_dir: {:#x}".format(self.root_ctl_dir)) self.quiet_info("root_rb_node: {:#x}".format(self.root_rb_node)) self.initialized = True return True @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.exact_found = False if args.exact and not args.filter: self.quiet_err("Filter string is needed") return if args.rescan: self.initialized = False Cache.reset_gef_caches(all=True) self.quiet_info("Wait for memory scan") if not self.initialize(): return # legend self.out = [] if not args.quiet: fmt = "{:<56s} {:<18s} {:<7s} {:<10s} {:+-file_system_type-+ +-->+-file_system_type-+ +-->...", " | | name | | | name | |", "+--------------+ | | ... | | | ... | |", "| file_systems |--+ | next |--+ | next |--+", "+--------------+ | fs_supers |--+ | fs_supers |", " | ... | | | ... |", " +------------------+ | +------------------+", " |", " +----------------------------------------+", " |", " | +-super_block-+ +-super_block-+ +-mount--------+", " | | s_list | | s_list | | ... |", " | | ... | | ... | | mnt |", " | | s_mounts | | s_mounts |---------+ | mnt_root |", " | | ... | | ... | | | ... |", " +-->| s_instances |-->| s_instances |-->... | | ... |", " | ... | | ... | +-->| mnt_instance |", " +-------------+ +-------------+ | ... |", " +--------------+", ] _note_ = "\n".join(_note_) def initialize(self): if hasattr(self, "initialized") and self.initialized: return True # file_systems self.file_systems = KernelAddressHeuristicFinder.get_file_systems() if self.file_systems is None: self.quiet_err("Could not find file_systems") return self.quiet_info("file_systems: {:#x}".format(self.file_systems)) """ struct file_system_type { const char *name; int fs_flags; int (*init_fs_context)(struct fs_context *); // v5.1~ const struct fs_parameter_spec *parameters; // v5.1~ struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); void (*kill_sb) (struct super_block *); struct module *owner; struct file_system_type * next; struct hlist_head fs_supers; struct lock_class_key s_lock_key; struct lock_class_key s_umount_key; struct lock_class_key s_vfs_rename_key; struct lock_class_key s_writers_key[SB_FREEZE_LEVELS]; // v3.6~ struct lock_class_key i_lock_key; struct lock_class_key i_mutex_key; struct lock_class_key invalidate_lock_key; // v5.15~ struct lock_class_key i_mutex_dir_key; struct lock_class_key i_alloc_sem_key; // ~v3.0 }; """ # file_system_type->name self.offset_name = 0 self.quiet_info("offsetof(file_system_type, name): {:#x}".format(self.offset_name)) # file_system_type->next for i in range(10): offset_next = current_arch.ptrsize * i valid = True current = read_int_from_memory(self.file_systems) seen = [] while current != 0: if not is_valid_addr(current): valid = False break seen.append(current) name_addr = read_int_from_memory(current) if not is_valid_addr(name_addr): valid = False break name = read_cstring_from_memory(name_addr) if len(name) == 0: valid = False break current = read_int_from_memory(current + offset_next) if current in seen: valid = False break if len(seen) == 1: valid = False if valid: self.offset_next = offset_next break else: self.quiet_err("Could not find file_system_type->next") return False self.quiet_info("offsetof(file_system_type, next): {:#x}".format(self.offset_next)) self.offset_fs_supers = self.offset_next + current_arch.ptrsize self.quiet_info("offsetof(file_system_type, fs_supers): {:#x}".format(self.offset_fs_supers)) """ struct super_block { struct list_head s_list; dev_t s_dev; // u32 unsigned char s_dirt; // ~v3.5 unsigned char s_blocksize_bits; unsigned long s_blocksize; ... struct hlist_node s_instances; <-- fs_supers points here ... } __randomize_layout; """ # super_block->s_dev self.offset_s_dev = current_arch.ptrsize * 2 self.quiet_info("offsetof(super_block, s_dev): {:#x}".format(self.offset_s_dev)) # super_block->s_instances current = read_int_from_memory(self.file_systems) while True: if current == 0: self.quiet_err("Could not find file_systems who has valid fs_supers") return False fs_supers = read_int_from_memory(current + self.offset_fs_supers) if is_valid_addr(fs_supers): break current = read_int_from_memory(current + self.offset_next) for i in range(1, 100): offset_base = current_arch.ptrsize * i """ 0xffff8cb085375800|+0x0000|+000: 0xffff8cb085373000 -> // s_list.next 0xffff8cb085375808|+0x0008|+001: 0xffff8cb088b77000 -> // s_list.prev 0xffff8cb085375810|+0x0010|+002: 0x0000000c00000021 // s_blocksize_bits, s_dev 0xffff8cb085375818|+0x0018|+003: 0x0000000000001000 // s_blocksize 0xffff8cb085375820|+0x0020|+004: 0x7fffffffffffffff 0xffff8cb085375828|+0x0028|+005: 0xffffffff8c33f260 0xffff8cb085375830|+0x0030|+006: 0xffffffff8ba36da0 """ # check s_list if not is_double_link_list(fs_supers - offset_base): continue # check s_blocksize x = read_int_from_memory(fs_supers - offset_base + current_arch.ptrsize * 2 + 4 * 2) if x == 0x1000: self.offset_s_instances = offset_base break else: self.quiet_err("Could not find super_block->s_instances") return False self.quiet_info("offsetof(super_block, s_instances): {:#x}".format(self.offset_s_instances)) """ struct super_block { // ~v3.11 ... struct list_head s_mounts; // v3.3~ <-- double link list struct list_head s_dentry_lru; <-- double link list int s_nr_dentry_unused; spinlock_t s_inode_lru_lock ____cacheline_aligned_in_smp; struct list_head s_inode_lru; <-- double link list int s_nr_inodes_unused; struct block_device *s_bdev; struct backing_dev_info *s_bdi; struct mtd_info *s_mtd; struct hlist_node s_instances; <-- fs_supers points here ... }; struct super_block { // v3.12~ ... struct list_head s_mounts; // v3.12~v6.17 struct mount *s_mounts; // v6.18~ struct block_device *s_bdev; struct bdev_handle *s_bdev_handle; // v6.6.47~v6.8 struct file *s_bdev_file; // v6.9~ struct backing_dev_info *s_bdi; struct mtd_info *s_mtd; struct hlist_node s_instances; <-- fs_supers points here ... }; // ~v4.12 } __randomize_layout; // v4.13~ """ # super_block->s_mounts kversion = Kernel.kernel_version() if kversion < "3.12": current = fs_supers - current_arch.ptrsize * 2 double_link_list_count = 0 while True: if is_double_link_list(current): double_link_list_count += 1 if double_link_list_count == 3: difference = fs_supers - current self.offset_s_mounts = self.offset_s_instances - difference break current -= current_arch.ptrsize elif kversion < "6.6.47": self.offset_s_mounts = self.offset_s_instances - current_arch.ptrsize * 5 elif kversion < "6.18": self.offset_s_mounts = self.offset_s_instances - current_arch.ptrsize * 6 else: self.offset_s_mounts = self.offset_s_instances - current_arch.ptrsize * 5 self.quiet_info("offsetof(super_block, s_mounts): {:#x}".format(self.offset_s_mounts)) """ struct mount { // <-- s_mounts points here (v6.18~) struct hlist_node mnt_hash; // v3.13~ // ptrsize * 2 struct list_node mnt_hash; // ~v3.12 // ptrsize * 2 struct mount *mnt_parent; struct dentry *mnt_mountpoint; struct vfsmount { struct dentry *mnt_root; struct super_block *mnt_sb; int mnt_flags; struct user_namespace *mnt_userns; // v5.12~v6.1 struct mnt_idmap *mnt_idmap; // v6.2~ } mnt; union { struct rb_node mnt_node; // v6.12~ // ptrsize * 3 struct rcu_head mnt_rcu; // v3.13~ // ptrsize * 2 struct llist_node mnt_llist; // v3.18~ // ptrsize }; #ifdef CONFIG_SMP struct mnt_pcp __percpu *mnt_pcp; #else int mnt_count; int mnt_writers; #endif struct list_head mnt_mounts; struct list_head mnt_child; struct list_head mnt_instance; // ~v6.17 // <-- s_mounts points here (~v6.17) const char *mnt_devname; ... } __randomize_layout; """ # mount->mnt_instance if kversion < "6.18": common1 = current_arch.ptrsize * 4 # mnt_hash ~ mnt_mount_point if kversion < "5.12": sizeof_vfsmount = current_arch.ptrsize * 3 else: sizeof_vfsmount = current_arch.ptrsize * 4 if kversion < "3.13": sizeof_union = 0 elif kversion < "6.12": sizeof_union = current_arch.ptrsize * 2 else: sizeof_union = current_arch.ptrsize * 3 sizeof_ifdef = current_arch.ptrsize # for x86/x64/ARM/ARM64, CONFIG_SMP is 'y' in almost all cases common2 = current_arch.ptrsize * 4 # mnt_mounts ~ mnt_child self.offset_mount_mnt_instance = common1 + sizeof_vfsmount + sizeof_union + sizeof_ifdef + common2 else: self.offset_mount_mnt_instance = 0 # mount->{mnt_parent,mnt_mountpoint,mnt} self.offset_mount_mnt_parent = current_arch.ptrsize * 2 self.offset_mount_mnt_mountpoint = current_arch.ptrsize * 3 self.offset_mount_mnt = current_arch.ptrsize * 4 # vfsmount->mnt_root self.offset_vfsmount_mnt_root = 0 self.initialized = True return True def get_fst_name(self, fst): name_addr = read_int_from_memory(fst + self.offset_name) name = read_cstring_from_memory(name_addr) return name def get_dev_num(self, dev): major = dev >> 20 minor = dev & ((1 << 20) - 1) name = KernelBlockDevicesCommand.get_bdev_name(major, minor) return major, minor, name def get_offset_d_iname(self, dentry): if hasattr(self, "offset_d_iname") and self.offset_d_iname is not None: return self.offset_d_iname """ struct dentry { unsigned int d_flags; seqcount_spinlock_t d_seq; struct hlist_bl_node d_hash; struct dentry *d_parent; struct qstr { union { struct { HASH_LEN_DECLARE; }; u64 hash_len; }; const unsigned char *name; // this points d_iname } d_name; struct inode *d_inode; unsigned char d_iname[DNAME_INLINE_LEN]; ... }; """ current = dentry while True: name = read_int_from_memory(current) if 0 < name - current <= 0x20: offset_d_iname = name - dentry break current += current_arch.ptrsize self.offset_d_iname = offset_d_iname return offset_d_iname def get_offset_d_parent(self, dentry, offset_d_iname): if hasattr(self, "offset_d_parent") and self.offset_d_parent is not None: return self.offset_d_parent offset_dname_name = offset_d_iname - current_arch.ptrsize * 2 if read_int_from_memory(dentry + offset_dname_name) == 0: # skip if padding offset_dname_name -= current_arch.ptrsize offset_d_parent = offset_dname_name - 0x8 - current_arch.ptrsize if read_int_from_memory(dentry + offset_d_parent) == 0: # skip if padding offset_d_parent -= current_arch.ptrsize self.offset_d_parent = offset_d_parent return offset_d_parent def get_mount(self, mnt_instance): mount = mnt_instance - self.offset_mount_mnt_instance return mount def get_mount_point(self, mnt_instance): mount = self.get_mount(mnt_instance) vfsmnt = mount + self.offset_mount_mnt dentry = read_int_from_memory(vfsmnt + self.offset_vfsmount_mnt_root) if not is_valid_addr(dentry): return None offset_d_iname = self.get_offset_d_iname(dentry) offset_d_parent = self.get_offset_d_parent(dentry, offset_d_iname) def is_root(dentry): return dentry == read_int_from_memory(dentry + offset_d_parent) filepath = [] switched = False while True: if not is_valid_addr(vfsmnt): return None if not is_valid_addr(dentry): return None mnt_root = read_int_from_memory(vfsmnt + self.offset_vfsmount_mnt_root) if dentry == mnt_root or is_root(dentry): parent = read_int_from_memory(mount + self.offset_mount_mnt_parent) # Global root? if mount != parent: dentry = read_int_from_memory(mount + self.offset_mount_mnt_mountpoint) mount = parent vfsmnt = mount + self.offset_mount_mnt switched = True continue name = read_cstring_from_memory(dentry + offset_d_iname) if name is None: name_ptr = read_int_from_memory(dentry + offset_d_iname - current_arch.ptrsize * 2) name = read_cstring_from_memory(name_ptr) filepath.append(name) break name = read_cstring_from_memory(dentry + offset_d_iname) if name is None: name_ptr = read_int_from_memory(dentry + offset_d_iname - current_arch.ptrsize * 2) name = read_cstring_from_memory(name_ptr) filepath.append(name) parent = read_int_from_memory(dentry + offset_d_parent) dentry = parent filepath = os.path.join(*filepath[::-1]) # The reason is unclear, but this works. if switched is False and filepath == "/": next_mnt_instance = read_int_from_memory(mnt_instance) if next_mnt_instance: ret = self.get_mount_point(next_mnt_instance) if ret: return ret return "-", "-" mount = self.get_mount(mnt_instance) return mount, filepath def get_dev_name(self, mnt_instance): offset_mount_mnt_instance = current_arch.ptrsize * 14 offset_mount_mnt_devname = current_arch.ptrsize * 16 mnt = mnt_instance - offset_mount_mnt_instance devname_p = read_int_from_memory(mnt + offset_mount_mnt_devname) devname = read_cstring_from_memory(devname_p) return devname def parse_super_block(self, fst, super_block): # name name = self.get_fst_name(fst) # dev dev = read_int32_from_memory(super_block + self.offset_s_dev) major, minor, devname = self.get_dev_num(dev) # mount s_mounts = read_int_from_memory(super_block + self.offset_s_mounts) mount = self.get_mount(s_mounts) # mount points if self.args.skip_mount_path: parsed_mount, mount_point = "-", "???" else: ret = self.get_mount_point(s_mounts) if ret is None: parsed_mount, mount_point = "-", "???" else: parsed_mount, mount_point = ret if isinstance(parsed_mount, int): parsed_mount = "{:#018x}".format(parsed_mount) # devname if devname == "???": devname = self.get_dev_name(s_mounts) else: devname += " (guessed)" # dump self.out.append("{:#018x} {:12s} {:#018x} {:20s} {:<6d} {:<6d} {:#018x} {:18s} {:s}".format( fst, name, super_block, devname, major, minor, mount, parsed_mount, mount_point, )) return def parse_file_system_type(self, fst): # fs_supers fs_supers = read_int_from_memory(fst + self.offset_fs_supers) if fs_supers == 0: # fast return name = self.get_fst_name(fst) self.out.append("{:#018x} {:12s} {:18s} {:20s} {:6s} {:6s} {:18s} {:18s} {:s}".format( fst, name, "-", "-", "-", "-", "-", "-", "-", )) return s_instances = fs_supers # parse more while s_instances: super_block = s_instances - self.offset_s_instances # parse super_block self.parse_super_block(fst, super_block) # go to next s_instances = read_int_from_memory(s_instances) return def parse_file_systems(self): if not self.args.quiet: fmt = "{:18s} {:12s} {:18s} {:20s} {:6s} {:6s} {:18s} {:18s} {:s}" legend = [ "file_system_type", "fsname", "super_block", "devname", "major", "minor", "s_mount", "(parsed) mount", "mount_point", ] self.out.append(GefUtil.make_legend(fmt.format(*legend))) fst = read_int_from_memory(self.file_systems) while fst != 0: # parse file_system_type self.parse_file_system_type(fst) # go to next fst = read_int_from_memory(fst + self.offset_next) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return if kversion < "3.3": err("Unsupported before v3.3") return if not self.initialize(): return self.out = [] self.parse_file_systems() self.print_output(check_terminal_size=True) return @register_command class KernelClockSourceCommand(GenericCommand, BufferingOutput): """Dump the clocksource list.""" _cmdline_ = "kclock-source" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _note_ = [ "Simplified clocksource structure:", "", " +-clocksource-+", " | read |", "+-clocksource_list-+ | ... |", "| list_head |--->| list |--->...", "+------------------+ | ... |", " +-------------+", ] _note_ = "\n".join(_note_) def get_offset_list(self, clocksource): """ struct clocksource { u64 (*read)(struct clocksource *cs); u64 mask; u32 mult; u32 shift; u64 max_idle_ns; u32 maxadj; u32 uncertainty_margin; #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA struct arch_clocksource_data archdata; #endif u64 max_cycles; const char *name; struct list_head list; int rating; enum clocksource_ids id; enum vdso_clock_mode vdso_clock_mode; unsigned long flags; int (*enable)(struct clocksource *cs); void (*disable)(struct clocksource *cs); void (*suspend)(struct clocksource *cs); void (*resume)(struct clocksource *cs); void (*mark_unstable)(struct clocksource *cs); void (*tick_stable)(struct clocksource *cs); #ifdef CONFIG_CLOCKSOURCE_WATCHDOG struct list_head wd_list; u64 cs_last; u64 wd_last; #endif struct module *owner; }; """ current = read_int_from_memory(clocksource) for i in range(7, 20): candidate_offset = i * current_arch.ptrsize v = read_int_from_memory(current - candidate_offset) if is_valid_addr(v): return candidate_offset return None @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") clocksource_list = KernelAddressHeuristicFinder.get_clocksource_list() if clocksource_list is None: self.quiet_err("Could not find clocksource_list") return self.quiet_info("clocksource_list: {:#x}".format(clocksource_list)) offset_list = self.get_offset_list(clocksource_list) if offset_list is None: return self.quiet_info("offsetof(clocksource, list): {:#x}".format(offset_list)) self.out = [] width = AddressUtil.get_format_address_width() if not args.quiet: fmt = "{:<{:d}s} {:20s} {:<{:d}s} {:<{:d}s}" legend = ["address", width, "name", "read", width, "symbol", width] self.out.append(GefUtil.make_legend(fmt.format(*legend))) current = read_int_from_memory(clocksource_list) while current != clocksource_list: cs = current - offset_list read = read_int_from_memory(cs) read_sym = Symbol.get_symbol_string(read, nosymbol_string=" ") name_addr = read_int_from_memory(current - current_arch.ptrsize) name = read_cstring_from_memory(name_addr) self.out.append("{:#0{:d}x} {:20s} {:#0{:d}x}{:s}".format(cs, width, name, read, width, read_sym)) current = read_int_from_memory(current) self.print_output(check_terminal_size=True) return @register_command class KernelTimerCommand(GenericCommand, BufferingOutput): """Dump the timer.""" _cmdline_ = "ktimer" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _note_ = [ "Simplified timer structure (per-cpu):", "", "+-timer_bases[0]----+ +-timer_list--+ +-timer_list--+", "| ... | | entry | | entry |", "| vectors[0] |--->| next |--->| next |--->...", "| ... | | pprev | | pprev |", "| vectors[512or576] | | expires | | expires |", "| ... | | function | | function |", "+-timer_bases[1]----+ | ... | | ... |", "| ... | +-------------+ +-------------+", "| vectors[0] |", "| ... |", "| vectors[512or576] |", "| ... |", "+-------------------+", "", "Simplified hrtimer structure (per-cpu):", "", "+-hrtimer_cpu_bases-+", "| ... |", "| clock_bases[0] | +--->+-hrtimer------+", "| ... | | | node |", "| clockid | | | node |", "| ... | | | color |", "| active | | | right |--->hrtimer", "| rb_root | | | left |--->hrtimer", "| rb_root |---+ | expires |", "| ... | | ... |", "| get_time | | function |", "| ... | | ... |", "| ... | +--------------+", "| clock_bases[8] |", "| ... |", "+-------------------+", ] _note_ = "\n".join(_note_) def initialize(self): if hasattr(self, "initialized") and self.initialized: return True # resolve __per_cpu_offset __per_cpu_offset = KernelAddressHeuristicFinder.get_per_cpu_offset() if __per_cpu_offset is None: self.quiet_info("__per_cpu_offset: Not found") self.cpu_offset = [] else: self.quiet_info("__per_cpu_offset: {:#x}".format(__per_cpu_offset)) self.cpu_offset = KernelCurrentCommand.get_each_cpu_offset(__per_cpu_offset) ### classic timer (unit: tick) # timer_bases self.timer_bases = KernelAddressHeuristicFinder.get_timer_bases() if not self.timer_bases: self.quiet_err("timer_bases: Not found") return False self.quiet_info("timer_bases: {:#x}".format(self.timer_bases)) # per_cpu_timer_bases if self.cpu_offset == []: self.per_cpu_timer_bases = [self.timer_bases] else: self.per_cpu_timer_bases = [AddressUtil.align_address(x + self.timer_bases) for x in self.cpu_offset] # len(timer_bases) if Symbol.get_ksymaddr("sysctl_timer_migration"): self.nr_bases = 2 else: self.nr_bases = 1 self.quiet_info("nr_bases: {:d}".format(self.nr_bases)) # sizeof(struct timer_base) """ struct timer_base { raw_spinlock_t lock; struct timer_list *running_timer; #ifdef CONFIG_PREEMPT_RT spinlock_t expiry_lock; atomic_t timer_waiters; #endif unsigned long clk; unsigned long next_expiry; unsigned int cpu; bool next_expiry_recalc; bool is_idle; bool timers_pending; DECLARE_BITMAP(pending_map, WHEEL_SIZE); struct hlist_head vectors[WHEEL_SIZE]; } ____cacheline_aligned; """ self.roughly_sizeof_timer_base = 0 if self.nr_bases == 2: timer_base = self.per_cpu_timer_bases[0] i = 512 while True: v = read_int_from_memory(timer_base + current_arch.ptrsize * i) if v != 0 and not is_valid_addr(v): self.roughly_sizeof_timer_base = current_arch.ptrsize * i break i += 1 # jiffies self.jiffies = KernelAddressHeuristicFinder.get_jiffies() if not self.jiffies: self.quiet_err("jiffies: Not found") return False self.quiet_info("jiffies: {:#x}".format(self.jiffies)) ### High-resolution kernel timer (unit: nano seconds) # hrtimer_bases self.hrtimer_bases = KernelAddressHeuristicFinder.get_hrtimer_bases() if not self.hrtimer_bases: self.quiet_err("hrtimer_bases: Not found") return False self.quiet_info("hrtimer_bases: {:#x}".format(self.hrtimer_bases)) # per_cpu_hrtimer_bases if self.cpu_offset == []: self.per_cpu_hrtimer_cpu_bases = [self.hrtimer_bases] else: self.per_cpu_hrtimer_cpu_bases = [AddressUtil.align_address(x + self.hrtimer_bases) for x in self.cpu_offset] """ struct hrtimer_cpu_base { raw_spinlock_t lock; unsigned int cpu; unsigned int active_bases; unsigned int clock_was_set_seq; unsigned int hres_active : 1, in_hrtirq : 1, hang_detected : 1, softirq_activated : 1; #ifdef CONFIG_HIGH_RES_TIMERS unsigned int nr_events; unsigned short nr_retries; unsigned short nr_hangs; unsigned int max_hang_time; #endif #ifdef CONFIG_PREEMPT_RT spinlock_t softirq_expiry_lock; atomic_t timer_waiters; #endif ktime_t expires_next; struct hrtimer *next_timer; ktime_t softirq_expires_next; struct hrtimer *softirq_next_timer; struct hrtimer_clock_base { struct hrtimer_cpu_base *cpu_base; unsigned int index; clockid_t clockid; seqcount_raw_spinlock_t seq; // v4.16~ struct hrtimer *running; // v4.16~ struct timerqueue_head { struct rb_root_cached { // v5.4~ struct rb_root rb_root; // v5.4~ struct rb_node *rb_leftmost; // v5.4~ } rb_root; // v5.4~ struct rb_root head; // ~v5.3 struct timerqueue_node *next; // ~v5.3 } active; ktime_t (*get_time)(void); // ~v6.17 ktime_t offset; } __hrtimer_clock_base_align clock_base[HRTIMER_MAX_CLOCK_BASES]; } ____cacheline_aligned; DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = { .lock = __RAW_SPIN_LOCK_UNLOCKED(hrtimer_bases.lock), .clock_base = { { .index = HRTIMER_BASE_MONOTONIC, .clockid = CLOCK_MONOTONIC, .get_time = &ktime_get, ------- }, ^ { | calc this .index = HRTIMER_BASE_REALTIME, | .clockid = CLOCK_REALTIME, v .get_time = &ktime_get_real, ------- }, { .index = HRTIMER_BASE_BOOTTIME, .clockid = CLOCK_BOOTTIME, .get_time = &ktime_get_boottime, }, { .index = HRTIMER_BASE_TAI, .clockid = CLOCK_TAI, .get_time = &ktime_get_clocktai, }, { // v4.16~ .index = HRTIMER_BASE_MONOTONIC_SOFT, .clockid = CLOCK_MONOTONIC, .get_time = &ktime_get, }, { // v4.16~ .index = HRTIMER_BASE_REALTIME_SOFT, .clockid = CLOCK_REALTIME, .get_time = &ktime_get_real, }, { // v4.16~ .index = HRTIMER_BASE_BOOTTIME_SOFT, .clockid = CLOCK_BOOTTIME, .get_time = &ktime_get_boottime, }, { // v4.16~ .index = HRTIMER_BASE_TAI_SOFT, .clockid = CLOCK_TAI, .get_time = &ktime_get_clocktai, }, } }; """ hrtimer_cpu_base = self.per_cpu_hrtimer_cpu_bases[0] ktime_get = Symbol.get_ksymaddr("ktime_get") ktime_get_real = Symbol.get_ksymaddr("ktime_get_real") ktime_get_ofs = None ktime_get_real_ofs = None i = 0 while True: ofs = current_arch.ptrsize * i try: v = read_int_from_memory(hrtimer_cpu_base + ofs) except gdb.MemoryError: self.quiet_err("Memory read error") return False if v == ktime_get: ktime_get_ofs = ofs elif v == ktime_get_real: ktime_get_real_ofs = ofs if ktime_get_ofs and ktime_get_real_ofs: break i += 1 self.sizeof_hrtimer_clock_base = ktime_get_real_ofs - ktime_get_ofs clock_base_1 = ktime_get_ofs + current_arch.ptrsize + 8 # get_time, offset self.offset_clock_base = clock_base_1 - self.sizeof_hrtimer_clock_base self.offset_clockid = current_arch.ptrsize + 4 # cpu_base, index self.offset_get_time = ktime_get_ofs - self.offset_clock_base self.offset_rb_root = self.offset_get_time - current_arch.ptrsize * 2 kversion = Kernel.kernel_version() if kversion < "4.16": self.num_of_clock_base = 4 else: self.num_of_clock_base = 8 self.initialized = True return True def parse_rb_node(self, rb_node): if not rb_node or not is_valid_addr(rb_node): return [] right = read_int_from_memory(rb_node + current_arch.ptrsize * 1) & ~1 # remove RB_BLACK left = read_int_from_memory(rb_node + current_arch.ptrsize * 2) & ~1 # remove RB_BLACK ret = [rb_node] if right: ret += self.parse_rb_node(right) if left: ret += self.parse_rb_node(left) return ret def dump_hrtimer(self): """ struct hrtimer { struct timerqueue_node { struct rb_node { unsigned long __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; } node; ktime_t expires; } node; ktime_t _softexpires; enum hrtimer_restart (*function)(struct hrtimer *); struct hrtimer_clock_base *base; u8 state; u8 is_rel; u8 is_soft; u8 is_hard; }; """ clockid_dict = { 0: "CLOCK_REALTIME", 1: "CLOCK_MONOTONIC", 2: "CLOCK_PROCESS_CPUTIME_ID", 3: "CLOCK_THREAD_CPUTIME_ID", 4: "CLOCK_MONOTONIC_RAW", 5: "CLOCK_REALTIME_COARSE", 6: "CLOCK_MONOTONIC_COARSE", 7: "CLOCK_BOOTTIME", 8: "CLOCK_REALTIME_ALARM", 9: "CLOCK_BOOTTIME_ALARM", 10: "CLOCK_SGI_CYCLE", 11: "CLOCK_TAI", } for cpu, hrtimer_cpu_base in enumerate(self.per_cpu_hrtimer_cpu_bases): clock_base = hrtimer_cpu_base + self.offset_clock_base for base_n in range(self.num_of_clock_base): htb = clock_base + self.sizeof_hrtimer_clock_base * base_n clockid = read_int32_from_memory(htb + self.offset_clockid) get_time = read_int_from_memory(htb + self.offset_get_time) self.out.append(titlify("cpu{:d} hrtimer_clock_base[{:d}]: {:#x} [{:s}; get_time: {:#x}{:s}]".format( cpu, base_n, htb, clockid_dict.get(clockid, "UNKNOWN"), get_time, Symbol.get_symbol_string(get_time, nosymbol_string=" "), ).rstrip())) # print legend if not self.args.quiet: fmt = "{:18s} {:18s} {:23s} {:18s} {:s}" legend = ["hrtimer", "expires", "time_to_expired", "function", "symbol"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) rb_node = read_int_from_memory(htb + self.offset_rb_root) for hrtimer in self.parse_rb_node(rb_node): expires = read_int64_from_memory(hrtimer + current_arch.ptrsize * 3) function = read_int_from_memory(hrtimer + current_arch.ptrsize * 3 + 8 * 2) if is_32bit() and not is_valid_addr(function): expires = read_int64_from_memory(hrtimer + current_arch.ptrsize * 3 + 4) function = read_int_from_memory(hrtimer + current_arch.ptrsize * 3 + 4 + 8 * 2) self.out.append("{:#018x} {:#018x} {:23s} {:#018x}{:s}".format( hrtimer, expires, "? (too hard to calc)", function, Symbol.get_symbol_string(function, nosymbol_string=" "), ).rstrip()) return def dump_timer(self): """ struct timer_list { struct hlist_node entry; unsigned long expires; void (*function)(struct timer_list *); u32 flags; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; """ jiffies = read_int_from_memory(self.jiffies) for cpu, timer_base in enumerate(self.per_cpu_timer_bases): # dump timer_list for base_n in range(self.nr_bases): tb = timer_base + self.roughly_sizeof_timer_base * base_n self.out.append(titlify("cpu{:d} timer_base[{:d}]: {:#x}".format(cpu, base_n, tb))) # print legend if not self.args.quiet: fmt = "{:18s} {:18s} {:23s} {:18s} {:s}" legend = ["timer_list", "expires", "time_to_expired", "function", "symbol"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) i = 0 while True: addr = tb + current_arch.ptrsize * i try: v = read_int_from_memory(addr) except gdb.MemoryError: self.err_add_out("Memory read error") return if v == 0: i += 1 continue if i < 512: if not is_valid_addr(v): i += 1 continue if read_int_from_memory(v + current_arch.ptrsize) != addr: i += 1 continue else: if not is_valid_addr(v): break if read_int_from_memory(v + current_arch.ptrsize) != addr: break timer_list = v expires = read_int_from_memory(timer_list + current_arch.ptrsize * 2) function = read_int_from_memory(timer_list + current_arch.ptrsize * 3) sym = Symbol.get_symbol_string(function, nosymbol_string=" ") tte = expires - jiffies self.out.append("{:#018x} {:#018x} {:#018x} tick {:#018x}{:s}".format( v, expires, tte, function, sym, ).rstrip()) i += 1 return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return if kversion < "4.8": err("Unsupported before v4.8") return if "6.18" <= kversion: # Read-write function pointers have been removed, # so there is no longer any point in displaying them with this command. err("Unsupported after v6.18") return self.quiet_info("Wait for memory scan") if not self.initialize(): return self.out = [] self.dump_timer() self.dump_hrtimer() self.print_output(check_terminal_size=True) return @register_command class KernelPciDeviceCommand(GenericCommand, BufferingOutput): """Dump the PCI devices.""" _cmdline_ = "kpcidev" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose mode.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _note_ = [ "Simplified pcidev structure:", "", "+----------------+ +-pci_bus--------+", "| pci_root_buses |-->| node.next |-->...", "+----------------+ | node.prev |", " | parent | +-pci_dev----------+", " | children.next | +->| bus_list.next |-->...", " | children.prev | | | bus_list.prev |", " | devices.next |-+ | ... |", " | devices.prev | | vendor |", " | ... | | device |", " | dev | | subsystem_vendor |", " | kobj | | subsystem_device |", " | name | | class |", " | ... | | revision |", " +----------------+ | dev |", " | kobj |", " | name |", " | ... |", " | +-resource[0]-+ |", " | | start | |", " | | end | |", " | | name | |", " | | flags | |", " | | ... | |", " | +-resource[1]-+ |", " | | ... | |", " | +-------------+ |", " | | ... | |", " | +-------------+ |", " | ... |", " +------------------+", ] _note_ = "\n".join(_note_) def initialize(self): if hasattr(self, "initialized") and self.initialized: return True # pci_root_buses self.pci_root_buses = KernelAddressHeuristicFinder.get_pci_root_buses() if not self.pci_root_buses: self.quiet_err("Could not find pci_root_buses (maybe, CONFIG_PCI is not set)") return False self.quiet_info("pci_root_buses: {:#x}".format(self.pci_root_buses)) first_root_bus = read_int_from_memory(self.pci_root_buses) if self.pci_root_buses == first_root_bus: warn("No PCI devices found") return False # pci_bus->{node,children,devices} """ struct pci_bus { struct list_head node; struct pci_bus *parent; struct list_head children; struct list_head devices; struct pci_dev *self; struct list_head slots; struct resource *resource[PCI_BRIDGE_RESOURCE_NUM]; struct list_head resources; struct resource busn_res; struct pci_ops *ops; struct msi_controller *msi; void *sysdata; struct proc_dir_entry *procdir; unsigned char number; unsigned char primary; unsigned char max_bus_speed; unsigned char cur_bus_speed; #ifdef CONFIG_PCI_DOMAINS_GENERIC int domain_nr; #endif char name[48]; unsigned short bridge_ctl; pci_bus_flags_t bus_flags; struct device *bridge; struct device { struct kobject { const char *name; <-- search for this ... } kobj; ... } dev; struct bin_attribute *legacy_io; struct bin_attribute *legacy_mem; unsigned int is_added:1; }; """ self.offset_pci_bus_node = 0 self.offset_pci_bus_children = current_arch.ptrsize * 3 self.offset_pci_bus_devices = current_arch.ptrsize * 5 # pci_bus->dev for i in range(100): v = read_int_from_memory(first_root_bus + current_arch.ptrsize * i) if is_valid_addr(v): if read_cstring_from_memory(v) == "0000:00": self.offset_pci_bus_dev = current_arch.ptrsize * i self.quiet_info("offsetof(pci_bus, dev): {:#x}".format(self.offset_pci_bus_dev)) break else: self.quiet_err("Could not find pci_bus->dev") return False # pci_dev->{bus_list,vendor,device,subsystem_vendor,subsystem_device,class,revision} """ struct pci_dev { struct list_head bus_list; struct pci_bus *bus; struct pci_bus *subordinate; void *sysdata; struct proc_dir_entry *procent; struct pci_slot *slot; unsigned int devfn; unsigned short vendor; unsigned short device; unsigned short subsystem_vendor; unsigned short subsystem_device; unsigned int class; u8 revision; u8 hdr_type; ... struct device { struct kobject { const char *name; <-- search for this ... } kobj; ... } dev; int cfg_size; unsigned int irq; struct resource resource[DEVICE_COUNT_RESOURCE]; ... }; """ self.offset_pci_dev_bus_list = 0 self.offset_pci_dev_vendor = current_arch.ptrsize * 7 + 4 self.offset_pci_dev_device = self.offset_pci_dev_vendor + 2 self.offset_pci_dev_subsystem_vendor = self.offset_pci_dev_device + 2 self.offset_pci_dev_subsystem_device = self.offset_pci_dev_subsystem_vendor + 2 self.offset_pci_dev_class = self.offset_pci_dev_subsystem_device + 2 self.offset_pci_dev_revision = self.offset_pci_dev_class + 4 # pci_dev->dev first_dev = read_int_from_memory(first_root_bus + self.offset_pci_bus_devices) for i in range(100): v = read_int_from_memory(first_dev + current_arch.ptrsize * i) if is_valid_addr(v): if read_cstring_from_memory(v) == "0000:00:00.0": self.offset_pci_dev_dev = current_arch.ptrsize * i self.quiet_info("offsetof(pci_dev, dev): {:#x}".format(self.offset_pci_dev_dev)) break else: self.quiet_err("Could not find pci_dev->dev") return False # pci_dev->resource """ struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; unsigned long desc; struct resource *parent, *sibling, *child; }; """ ofs_base = self.offset_pci_dev_dev + current_arch.ptrsize for i in range(200): v = read_int_from_memory(first_dev + ofs_base + current_arch.ptrsize * i) if is_valid_addr(v): if read_cstring_from_memory(v) == "0000:00:00.0": self.offset_pci_dev_resource = ofs_base + current_arch.ptrsize * i - 0x10 self.sizeof_resource = 0x10 + current_arch.ptrsize * 6 self.quiet_info("offsetof(pci_dev, resource): {:#x}".format(self.offset_pci_dev_resource)) break else: self.quiet_err("Could not find pci_dev->resource") return False # pci.ids pci_ids_file_name = "/usr/share/misc/pci.ids" if os.path.exists(pci_ids_file_name): self.quiet_info("use {:s}".format(pci_ids_file_name)) content = open(pci_ids_file_name).read() else: pci_ids_file_name = os.path.join(GEF_TEMP_DIR, "pci.ids") if os.path.exists(pci_ids_file_name): self.quiet_info("use {:s}".format(pci_ids_file_name)) content = open(pci_ids_file_name).read() else: url = "https://raw.githubusercontent.com/pciutils/pciids/master/pci.ids" self.quiet_info("use {:s}".format(url)) content = String.bytes2str(http_get(url) or "") if not content: self.quiet_info("Connection timed out: {:s}".format(url)) open(pci_ids_file_name, "w").write(content) self.pci_ids = self.parse_pci_ids(content) self.initialized = True return True def parse_pci_ids(self, content): dic = {} class_mode = False for line in content.splitlines(): if not line or line.startswith("#"): continue if class_mode is False and line.startswith("C"): class_mode = True if class_mode is False: # device mode if not line.startswith("\t"): vendor, *desc = line.split(" ") vendor = int(vendor, 16) dic[vendor] = " ".join(desc) continue if line.startswith("\t") and not line.startswith("\t\t"): device, *desc = line[1:].split(" ") device = int(device, 16) # Use the previous value for `vendor`. dic[vendor, device] = " ".join(desc) continue if line.startswith("\t\t"): subsystem, *desc = line[2:].split(" ") subv, subd = subsystem.split() subv = int(subv, 16) subd = int(subd, 16) # Use the previous value for `vendor` and `device`. dic[vendor, device, subv, subd] = " ".join(desc) continue else: # class mode if not line.startswith("\t"): base_class, *desc = line.split(" ") base_class = int(base_class[2:], 16) dic["C", base_class] = " ".join(desc) continue if line.startswith("\t") and not line.startswith("\t\t"): sub_class, *desc = line[1:].split(" ") sub_class = int(sub_class, 16) # Use the previous value for `base_class`. dic["C", base_class, sub_class] = " ".join(desc) continue if line.startswith("\t\t"): prgif, *desc = line[2:].split(" ") prgif = int(prgif, 16) # Use the previous value for `base_class` and `sub_class`. dic["C", base_class, sub_class, prgif] = " ".join(desc) continue return dic def get_description(self, base_class, sub_class, prgif, vendor, device, subv, subd): # The information provided by the programming interface (prgif) is too detailed, # so it is not used. class_str = self.pci_ids.get(("C", base_class, sub_class), None) if class_str is None: class_str = self.pci_ids.get(("C", base_class), None) if class_str is None: class_str = "???" device_str = self.pci_ids.get((vendor, device, subv, subd), None) if device_str is None: device_str = self.pci_ids.get((vendor, device), None) if device_str is None: device_str = self.pci_ids.get(vendor, None) if device_str is None: device_str = "???" qemu_monitor_out = "" if is_qemu_system(): dev = "" res = gdb.execute("monitor info qtree", to_string=True) target = "pci id {:04x}:{:04x} (sub {:04x}:{:04x})".format(vendor, device, subv, subd) for line in res.splitlines(): m = re.search('dev: (.+), id ".*"', line) if m: dev = m.group(1) continue if target in line: qemu_monitor_out = " ({:s})".format(Color.boldify(dev)) break return "{:s} / {:s}".format(class_str, device_str) + qemu_monitor_out @staticmethod def get_flags_str(flags_value): flags_dic = { 0x80000000: "IORESOURCE_BUSY", 0x40000000: "IORESOURCE_AUTO", 0x20000000: "IORESOURCE_UNSET", 0x10000000: "IORESOURCE_DISABLED", 0x08000000: "IORESOURCE_EXCLUSIVE", 0x04000000: "IORESOURCE_SYSRAM_MERGEABLE", 0x02000000: "IORESOURCE_SYSRAM_DRIVER_MANAGED", 0x01000000: "IORESOURCE_SYSRAM", 0x00400000: "IORESOURCE_MUXED", 0x00200000: "IORESOURCE_WINDOW", 0x00100000: "IORESOURCE_MEM_64", 0x00080000: "IORESOURCE_STARTALIGN", 0x00040000: "IORESOURCE_SIZEALIGN", 0x00020000: "IORESOURCE_SHADOWABLE", 0x00010000: "IORESOURCE_RANGELENGTH", 0x00008000: "IORESOURCE_CACHEABLE", 0x00004000: "IORESOURCE_READONLY", 0x00002000: "IORESOURCE_PREFETCH", 0x00001000: "IORESOURCE_BUS", 0x00000800: "IORESOURCE_DMA", 0x00000400: "IORESOURCE_IRQ", 0x00000200: "IORESOURCE_MEM", 0x00000100: "IORESOURCE_IO", } flags = [] for k, v in flags_dic.items(): if flags_value & k: flags.append(v) if "IORESOURCE_IO" in flags and "IORESOURCE_MEM" in flags: flags.remove("IORESOURCE_IO") flags.remove("IORESOURCE_MEM") flags.append("IORESOURCE_REG") flags_str = " | ".join(flags) if flags_str == "": flags_str = "none" return flags_str.replace("IORESOURCE_", "") def search_label(self, start, end): if not is_qemu_system(): return [] label_list = [] res = gdb.execute("monitor info mtree -f", to_string=True) for line in res.splitlines(): if not line.startswith(" "): continue m = re.search(r" ([0-9a-f]+)-([0-9a-f]+)", line) if not m: continue s = int(m.group(1), 16) e = int(m.group(2), 16) if not (start <= s and e <= end): continue line = " -> " + line.lstrip() if line not in label_list: label_list.append(line) if label_list == []: label_list.append(" -> No results found from `monitor info mtree -f`") return label_list def walk_devices(self, dev): if not self.args.quiet: fmt = "{:18s} {:12s} {:7s} {:10s} {:10s} {:3s} {:s}" legend = ["pci_dev", "name", "class", "vendor:dev", "subsystem", "rev", "description"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) if not dev: return while dev not in self.seen_dev: self.seen_dev.append(dev) # parse device info dev_name = read_cstring_from_memory(read_int_from_memory(dev + self.offset_pci_dev_dev)) vendor = read_int16_from_memory(dev + self.offset_pci_dev_vendor) device = read_int16_from_memory(dev + self.offset_pci_dev_device) sub_vendor = read_int16_from_memory(dev + self.offset_pci_dev_subsystem_vendor) sub_device = read_int16_from_memory(dev + self.offset_pci_dev_subsystem_device) revision = read_int8_from_memory(dev + self.offset_pci_dev_revision) # u32:class = u8:unused || u8:base_class || u8:sub_class || u8:programming-interface class_val = read_int32_from_memory(dev + self.offset_pci_dev_class) prgif = class_val & 0xff sub_class = (class_val >> 8) & 0xff base_class = (class_val >> 16) & 0xff desc = self.get_description(base_class, sub_class, prgif, vendor, device, sub_vendor, sub_device) self.out.append("{:#018x} {:s} {:02x}{:02x}:{:02x} {:04x}:{:04x} {:04x}:{:04x} {:02x} {:s}".format( dev, dev_name, base_class, sub_class, prgif, vendor, device, sub_vendor, sub_device, revision, desc, )) if self.args.verbose: # parse resource i = 0 while True: resource_i = dev + self.offset_pci_dev_resource + self.sizeof_resource * i # parse resource name resource_i_name = read_int_from_memory(resource_i + 8 * 2) if not is_valid_addr(resource_i_name): break if read_cstring_from_memory(resource_i_name) != dev_name: break # parse resource address resource_i_start = read_int_from_memory(resource_i + 8 * 0) resource_i_end = read_int_from_memory(resource_i + 8 * 1) resource_i_size = resource_i_end - resource_i_start if resource_i_start != 0 and resource_i_end != 0: # parse resource flags resource_i_flags = read_int_from_memory(resource_i + 8 * 2 + current_arch.ptrsize) flag_str = KernelPciDeviceCommand.get_flags_str(resource_i_flags) if (resource_i_flags & 0x300) == 0x300: type_str = "RegOffs" elif resource_i_flags & 0x100: type_str = "I/O-Mem" elif resource_i_flags & 0x200: type_str = "PhysMem" else: type_str = "???" self.out.append(" [{:d}] {:7s}: {:#010x}-{:#010x} ({:#010x}) flags:{:#x} ({:s})".format( i, type_str, resource_i_start, resource_i_end, resource_i_size, resource_i_flags, flag_str, )) # add more details ret = self.search_label(resource_i_start, resource_i_end) self.out.extend(ret) i += 1 # goto next dev = read_int_from_memory(dev + self.offset_pci_dev_bus_list) return def walk_pci_bus(self, bus): if not bus: return while bus not in self.seen_bus: self.seen_bus.append(bus) bus_name = read_cstring_from_memory(read_int_from_memory(bus + self.offset_pci_bus_dev)) self.out.append(titlify("Bus {:s}: {:#x}".format(bus_name, bus))) # parse device self.seen_dev.append(bus + self.offset_pci_bus_devices) dev = read_int_from_memory(bus + self.offset_pci_bus_devices) self.walk_devices(dev) # parse child self.seen_bus.append(bus + self.offset_pci_bus_children) first_child_bus = read_int_from_memory(bus + self.offset_pci_bus_children) self.walk_pci_bus(first_child_bus) # goto next bus = read_int_from_memory(bus + self.offset_pci_bus_node) return def dump_pci(self): first_root_bus = read_int_from_memory(self.pci_root_buses) self.seen_bus = [self.pci_root_buses] self.seen_dev = [] self.walk_pci_bus(first_root_bus) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") if not self.initialize(): return self.out = [] self.dump_pci() self.print_output(check_terminal_size=True) return @register_command class KernelConfigCommand(GenericCommand, BufferingOutput): """Dump the kernel config if available.""" _cmdline_ = "kconfig" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="REGEXP include filter.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() def get_config(self): kinfo = Kernel.get_kernel_layout() if kinfo.ro_base is None: err("Not recognized .rodata") return False if is_kgdb(): info("The config is often near the top of .rodata; once found, the search stops early.") ro_data = b"" tqdm = GefUtil.get_tqdm(not self.args.quiet) for pos in tqdm(range(0, kinfo.ro_size, 0x1000), leave=False): if not is_valid_addr(kinfo.ro_base + pos): err("Memory read error") return ro_data += read_memory(kinfo.ro_base + pos, 0x1000) if ro_data.find(b"IKCFG_ST") >= 0 and ro_data.find(b"IKCFG_ED") >= 0: break else: if not is_valid_addr(kinfo.ro_base): err("Memory read error") return ro_data = read_memory(kinfo.ro_base, kinfo.ro_size) start_pos = ro_data.find(b"IKCFG_ST") if start_pos == -1: err("Could not find IKCFG_ST, this kernel may be built as CONFIG_IKCONFIG_PROC=n") return False end_pos = ro_data.find(b"IKCFG_ED") info("IKCFG_ST: {:#x}".format(kinfo.ro_base + start_pos)) info("IKCFG_ED: {:#x}".format(kinfo.ro_base + end_pos)) configz = ro_data[start_pos + len("IKCFG_ST"):end_pos] import gzip try: self.configs = String.bytes2str(gzip.decompress(configz)) except gzip.BadGzipFile: err("Gzip decompress error") return False return True @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") if not hasattr(self, "configs"): self.configs = None if args.rescan: self.configs = None if self.configs is None: ret = self.get_config() if not ret: return self.out = self.configs.splitlines() if args.filter: out = [] for line in self.out: for filt in args.filter: if filt.search(line): out.append(line) break self.out = out self.print_output() return @register_command class KernelSearchCodePtrCommand(GenericCommand, BufferingOutput): """Search the code pointer in kernel data area.""" _cmdline_ = "ksearch-code-ptr" _category_ = "06-k. Qemu-system/KGDB Cooperation - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-d", "--depth", type=int, default=1, help="depth of reference. (default: %(default)s)") parser.add_argument("-r", "--max-range", type=AddressUtil.parse_address, default=0, help="allowable offset range for each reference. (default: %(default)s)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() @Cache.cache_until_next def read_int_from_memory(self, addr): return read_int_from_memory(addr) def get_permission(self, addr): for vaddr, size, perm in self.kinfo.maps: if vaddr <= addr and addr < vaddr + size: return perm return "???" def search(self, backtrack_info, addr, max_range, depth): if depth == 0: if not (self.kinfo.text_base <= addr < self.kinfo.text_end): return False # backtrack new_backtrack_info = backtrack_info + [(addr, 0)] msg = [] for addr, offset in new_backtrack_info: addr_sym = Symbol.get_symbol_string(addr + offset, nosymbol_string=" ") m = "{:#x}+{:#x}{:s} [{:s}]".format(addr, offset, addr_sym, self.get_permission(addr + offset)) msg.append(m) # create message self.out.append("\n -> ".join(msg) + "\n") return True valid = False if depth not in self.invalid_addrs: self.invalid_addrs[depth] = [] for offset in range(0, max_range + current_arch.ptrsize, current_arch.ptrsize): # align to 32bit / 64bit cur = AddressUtil.align_address(addr + offset) # is aligned? if cur & 0x7 != 0: continue # is kernel address? # TODO: more suitable check for kernel address if (cur >> (current_arch.ptrsize * 8 - 1)) == 0: continue # is accessible? if not is_valid_addr(cur): continue # check result of previous recursive v = self.read_int_from_memory(cur) if v in self.invalid_addrs[depth]: continue # add to backtrack new_backtrack_info = backtrack_info + [(addr, offset)] # recursive ret = self.search(new_backtrack_info, v, max_range, depth - 1) if ret is False: self.invalid_addrs[depth].append(v) valid |= ret return valid @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): if args.max_range and args.max_range % current_arch.ptrsize: err("The range must be a multiple of the pointer size") return if args.depth <= 0: err("The depth must be larger than 0") return info("Wait for memory scan") self.kinfo = Kernel.get_kernel_layout() if self.kinfo.has_none or self.kinfo.rwx: err("Unsupported environment which has RWX data area") return self.invalid_addrs = {} self.out = [] if not is_valid_addr(self.kinfo.rw_base): err("Memory read error") return rw_data = read_memory(self.kinfo.rw_base, self.kinfo.rw_size) rw_data = slice_unpack(rw_data, current_arch.ptrsize) tqdm = GefUtil.get_tqdm() for i, rw_d in tqdm(enumerate(rw_data), leave=False, total=len(rw_data)): rw_addr = self.kinfo.rw_base + i * current_arch.ptrsize backtrack_info = [(rw_addr, 0)] self.search(backtrack_info, rw_d, args.max_range, args.depth - 1) self.print_output(check_terminal_size=True) return @register_command class KernelDmesgCommand(GenericCommand, BufferingOutput): """Dump the ring buffer of the dmesg area.""" _cmdline_ = "kdmesg" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-c", "--use-cache", action="store_true", help="use previous result.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -q", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "The information such as [T1] is the thread ID.", "Originally, this information is displayed when CONFIG_PRINTK_CALLER=y.", "However it is always displayed because it is useful.", "", "Simplified dmesg structure (5.10~):", "", "+-----+", "| prb |--+", "+-----+ |", " |", "+--------+", "|", "+->+-printk_rb_static-+ +-------------------------->+-prb_desc[]----+", " | desc_ring | | +---| state_var |---+", " | count_bits | | +->+-printk_info[]-+ | | ... | |", " | descs |--+ | | seq | | +---------------+ |", " | infos |----+ | ts_nsec | | | state_var | |", " | head_id | | text_len | | | ... | |", " | tail_id | | facility | | +---------------+ |", " | ... | | flags, level | | | ... | |", " | text_data_ring | | caller_id | | +---------------+<--+", " | size_bits | | dev_info | | | state_var |", " | data |--+ +---------------+ | | text_blk_lpos |", " | head_lpos | | | seq | | | begin |(=text block start offset)", " | tail_lpos | | | ts_nsec | | | next |(=text block end offset)", " | fail | | | text_len | | +---------------+", " +------------------+ | | facility | | | state_var |", " | | flags, level | | | text_blk_lpos |", "+------------------------+ | caller_id | | | begin |", "| | dev_info | | | next |", "+->+-printk_record-+ +---------------+ | +---------------+", " | info | | ... | |", " | text_buf |-->text +---------------+<-+", " | text_buf_size | | seq |", " +---------------+ | ... |", " +---------------+", "* prb_desc and printk_info are accessed in two ways. One is seq number based access which is simply incremented", " and the other is id number based access by lower bit of state_var.", " 1-A. (Seq-based prb_desc): Preserving entry state and entry index (=id).", " 1-B. (Id-based prb_desc): Preserving begin and next.", " 2-A. (Seq-based printk_info): Preserving text data length, time, thread ID, etc. for each entry.", " 2-B. (Id-based printk_info): Preserving seq for ring buffer reuse.", "", "Simplified dmesg structure (~5.10):", "", "+-----------+", "| __log_buf |-------->+-log_buffer-----+ ^ ^", "+-----------+ | ts_nsec | | |", " | len |-->| |", " | text_len | | |", " | ... | | |", " | text[text_len] | | |", " +----------------+ v |", "+---------------+ | ... | |", "| log_first_idx |---->+----------------+ |", "+---------------+ | ts_nsec | |", " =start | len | | +-------------+", " | text_len | |<---| log_buf_len |", " | ... | | +-------------+", " | text[text_len] | |", " +----------------+ |", "+---------------+ | ... | |", "| log_next_idx |---->+----------------+ |", "+---------------+ | ts_nsec | |", " =end | len | |", " | text_len | |", " | ... | |", " | text[text_len] | |", " +----------------+ v", ] _note_ = "\n".join(_note_) def dump_printk_ringbuffer(self, ring_buffer_name, ring_buffer_address): """ # [v5.10~] struct printk_ringbuffer { struct prb_desc_ring { unsigned int count_bits; struct prb_desc* descs; struct printk_info* infos; atomic_long_t head_id; atomic_long_t tail_id; atomic_long_t last_finalized_id; // v5.18~ } desc_ring; struct prb_data_ring { unsigned int size_bits; char* data; atomic_long_t head_lpos; atomic_long_t tail_lpos; } text_data_ring; atomic_long_t fail; }; """ current = ring_buffer_address rb = {} rb["desc_ring"] = {} rb["desc_ring"]["count_bits"] = read_int_from_memory(current) current += current_arch.ptrsize rb["desc_ring"]["descs"] = read_int_from_memory(current) current += current_arch.ptrsize rb["desc_ring"]["infos"] = read_int_from_memory(current) current += current_arch.ptrsize rb["desc_ring"]["head_id"] = read_int_from_memory(current) current += current_arch.ptrsize rb["desc_ring"]["tail_id"] = read_int_from_memory(current) current += current_arch.ptrsize rb["text_data_ring"] = {} size_bits = read_int_from_memory(current) if size_bits > current_arch.ptrsize * 8: current += current_arch.ptrsize # last_finalized_id size_bits = read_int_from_memory(current) rb["text_data_ring"]["size_bits"] = size_bits current += current_arch.ptrsize rb["text_data_ring"]["data"] = read_int_from_memory(current) current += current_arch.ptrsize rb["text_data_ring"]["head_lpos"] = read_int_from_memory(current) current += current_arch.ptrsize rb["text_data_ring"]["tail_lpos"] = read_int_from_memory(current) current += current_arch.ptrsize rb["fail"] = read_int_from_memory(current) self.quiet_info("name: {:s}".format(ring_buffer_name)) self.quiet_info("address: {:#x}".format(ring_buffer_address)) self.quiet_info("desc_ring.count_bits: {:#x}".format(rb["desc_ring"]["count_bits"])) self.quiet_info("desc_ring.descs: {:#x}".format(rb["desc_ring"]["descs"])) self.quiet_info("desc_ring.infos: {:#x}".format(rb["desc_ring"]["infos"])) self.quiet_info("desc_ring.head_id: {:#x}".format(rb["desc_ring"]["head_id"])) self.quiet_info("desc_ring.tail_id: {:#x}".format(rb["desc_ring"]["tail_id"])) self.quiet_info("text_data_ring.size_bits: {:#x}".format(rb["text_data_ring"]["size_bits"])) self.quiet_info("text_data_ring.data: {:#x}".format(rb["text_data_ring"]["data"])) self.quiet_info("text_data_ring.head_lpos: {:#x}".format(rb["text_data_ring"]["head_lpos"])) self.quiet_info("text_data_ring.tail_lpos: {:#x}".format(rb["text_data_ring"]["tail_lpos"])) self.quiet_info("fail: {:#x}".format(rb["fail"])) def read_desc_i(descs_addr, seq): """ struct prb_desc { atomic_long_t state_var; struct prb_data_blk_lpos { unsigned long begin; unsigned long next; } text_blk_lpos; }; """ sizeof_desc = current_arch.ptrsize * 3 current = descs_addr + sizeof_desc * seq if not is_valid_addr(current): return False desc = {} desc["state_var"] = read_int_from_memory(current) desc["text_blk_lpos"] = {} current += current_arch.ptrsize desc["text_blk_lpos"]["begin"] = read_int_from_memory(current) current += current_arch.ptrsize desc["text_blk_lpos"]["next"] = read_int_from_memory(current) current += current_arch.ptrsize return desc def read_info_i(infos_addr, seq): """ struct printk_info { u64 seq; /* sequence number */ u64 ts_nsec; /* timestamp in nanoseconds */ u16 text_len; /* length of text message */ u8 facility; /* syslog facility */ u8 flags:5; /* internal record flags */ u8 level:3; /* syslog level */ u32 caller_id; /* thread id or processor id */ struct dev_printk_info dev_info; }; """ sizeof_info = 8 + 8 + 2 + 1 + 1 + 4 + 16 + 48 current = infos_addr + sizeof_info * seq if not is_valid_addr(current): return False info = {} info["seq"] = read_int64_from_memory(current) current += 8 info["ts_nsec"] = read_int64_from_memory(current) current += 8 info["text_len"] = read_int16_from_memory(current) current += 2 info["facility"] = read_int8_from_memory(current) current += 1 info["flags"] = read_int8_from_memory(current) & 0b11111 info["level"] = (read_int8_from_memory(current) >> 5) & 0b111 current += 1 info["caller_id"] = read_int32_from_memory(current) current += 4 info["dev_info"] = {} info["dev_info"]["subsystem"] = read_memory(current, 16) current += 16 info["dev_info"]["device"] = read_memory(current, 48) current += 48 return info seq_mask = (1 << rb["desc_ring"]["count_bits"]) - 1 state_var_id_mask = ~(3 << (current_arch.ptrsize * 8 - 2)) get_desc_state = lambda sv: (sv >> (current_arch.ptrsize * 8 - 2)) & 3 size_bits = rb["text_data_ring"]["size_bits"] data_size_mask = (1 << size_bits) - 1 info("Wait for reading records...") seq = 0 while True: # prb_read # - Read prb_desc and printk_info based on seq number. seq_based_desc = read_desc_i(rb["desc_ring"]["descs"], seq & seq_mask) seq_based_info = read_info_i(rb["desc_ring"]["infos"], seq & seq_mask) # desc_read_finalized_seq, desc_read # - Read prb_desc and printk_info based on id number. id = seq_based_desc["state_var"] & state_var_id_mask id_based_desc = read_desc_i(rb["desc_ring"]["descs"], id & seq_mask) id_based_info = read_info_i(rb["desc_ring"]["infos"], id & seq_mask) # - Determine whether it is the last entry based on the state and seq values. if (id_based_desc["state_var"] & state_var_id_mask) != id: # desc_miss break if get_desc_state(id_based_desc["state_var"]) in [0, 1]: # desc_reserved, desc_commited break if id_based_info["seq"] != seq: if seq == 0: # ring buffer is already looping seq = id_based_info["seq"] else: break if get_desc_state(id_based_desc["state_var"]) == 2: # desc_reusable if (id_based_desc["text_blk_lpos"]["begin"], id_based_desc["text_blk_lpos"]["next"]) == (1, 1): break # copy_data, get_data # - Calculates the start address of text data from the begin and next values. begin = id_based_desc["text_blk_lpos"]["begin"] next = id_based_desc["text_blk_lpos"]["next"] if (begin >> size_bits) == (next >> size_bits) and (begin < next): src = rb["text_data_ring"]["data"] + (begin & data_size_mask) elif ((begin + (1 << size_bits)) >> size_bits) == (next >> size_bits): src = rb["text_data_ring"]["data"] else: raise size = seq_based_info["text_len"] src += current_arch.ptrsize if size: entry = String.bytes2str(read_memory(src, size)) else: entry = "" # timestamp sec = seq_based_info["ts_nsec"] // 1000 // 1000 // 1000 nsec = seq_based_info["ts_nsec"] % (1000 * 1000 * 1000) nsec_str = "{:09d}".format(nsec)[:6] # thread id. This is displayed when CONFIG_PRINTK_CALLER=y, but always displayed because it is useful. caller_id_str = "T{:d}".format(seq_based_info["caller_id"]) # output formatted_entry = "[{:5d}.{:s}] [{:>6s}] {:s}".format(sec, nsec_str, caller_id_str, entry) self.out.append(formatted_entry) seq += 1 return def dump_printk_log_buffer(self, log_first_idx, log_end_idx, buf_start, buf_end): """ # [~v5.9] struct printk_log { u64 ts_nsec; /* timestamp in nanoseconds */ u16 len; /* length of entire record */ u16 text_len; /* length of text buffer */ u16 dict_len; /* length of dictionary buffer */ u8 facility; /* syslog facility */ u8 flags:5; /* internal record flags */ u8 level:3; /* syslog level */ #ifdef CONFIG_PRINTK_CALLER u32 caller_id; /* thread id or processor id */ #endif }; """ CONFIG_PRINTK_CALLER = Symbol.get_ksymaddr("print_caller") is not None length_of_caller_id = 4 if CONFIG_PRINTK_CALLER else 0 sizeof_printk_log = 16 + length_of_caller_id pos = buf_start + log_first_idx log_end_pos = buf_start + log_end_idx while pos != log_end_pos: x = read_memory(pos, 16) ts_nsec = u64(x[:8]) rec_len = u16(x[8:10]) if rec_len == 0: pos = buf_start continue text_len = u16(x[10:12]) #dict_len = u16(x[12:14]) #facility = u8(x[14:15]) #flags = (u8(x[15:16]) >> 0) & 0b11111 #level = (u8(x[15:16]) >> 5) & 0b111 text = read_memory(pos + sizeof_printk_log, text_len) sec = ts_nsec // 1000 // 1000 // 1000 nsec = ts_nsec % (1000 * 1000 * 1000) nsec_str = "{:09d}".format(nsec)[:6] # split from multi-line message for t in String.bytes2str(text).splitlines(): formatted_entry = "[{:5d}.{:s}] {:s}".format(sec, nsec_str, t) self.out.append(formatted_entry) pos += rec_len if pos >= buf_end: break # something is wrong return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") if args.use_cache and hasattr(self, "cache") and self.cache: self.out = self.cache[::] self.print_output() return self.out = [] kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return if "5.10" <= kversion: # new structure printk_rb_static = KernelAddressHeuristicFinder.get_printk_rb_static() if printk_rb_static is None: err("Could not find printk_rb_static") return self.dump_printk_ringbuffer("printk_rb_static", printk_rb_static) else: # old structure log_first_idx_ptr = KernelAddressHeuristicFinder.get_log_first_idx() if log_first_idx_ptr is None: err("Could not find log_first_idx") return self.quiet_info("log_first_idx: {:#x}".format(log_first_idx_ptr)) log_next_idx_ptr = KernelAddressHeuristicFinder.get_log_next_idx() if log_next_idx_ptr is None: err("Could not find log_next_idx") return self.quiet_info("log_next_idx: {:#x}".format(log_next_idx_ptr)) log_buf_start = KernelAddressHeuristicFinder.get___log_buf() if log_buf_start is None: err("Could not find __log_buf") return self.quiet_info("__log_buf: {:#x}".format(log_buf_start)) log_buf_len_ptr = KernelAddressHeuristicFinder.get_log_buf_len() if log_buf_len_ptr is None: err("Could not find log_buf_len") return self.quiet_info("log_buf_len: {:#x}".format(log_buf_len_ptr)) log_first_idx = read_int32_from_memory(log_first_idx_ptr) log_next_idx = read_int32_from_memory(log_next_idx_ptr) log_buf_len = read_int32_from_memory(log_buf_len_ptr) log_buf_end = log_buf_start + log_buf_len self.quiet_info("*log_first_idx: {:#x}".format(log_first_idx)) self.quiet_info("*log_next_idx: {:#x}".format(log_next_idx)) self.quiet_info("*log_buf_len: {:#x}".format(log_buf_len)) self.dump_printk_log_buffer(log_first_idx, log_next_idx, log_buf_start, log_buf_end) self.print_output() self.cache = self.out[::] return @register_command class StringsCommand(GenericCommand, BufferingOutput): """Search ASCII string (recursively) from specific location.""" _cmdline_ = "strings" _category_ = "03-a. Memory - Search" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="the start location to search for.") parser.add_argument("end_location", metavar="END_LOCATION", type=AddressUtil.parse_address, nargs="?", help="the end location to search for. (default: end of region or LOCATION+0x1000)") parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="REGEXP include filter.") parser.add_argument("-e", "--exclude", action="append", type=re.compile, default=[], help="REGEXP exclude filter.") parser.add_argument("-d", "--depth", type=int, default=0, help="recursive depth. (default: %(default)s)") parser.add_argument("-r", "--range", type=AddressUtil.parse_address, default=0x40, help="search range for recursively. (default: %(default)s)") parser.add_argument("-s", "--skip-save", action="store_true", help="do not save the output.") parser.add_argument("-m", "--minlen", type=int, default=8, help="minimum string length (default: %(default)s)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x00007ffffffde000 0x00007ffffffff000 # exact specification", "{0:s} 0x00007ffffffde000 # guess the search end location", "{0:s} -m 10 0x00007ffffffde000 0x00007ffffffff000 # filter by length", "{0:s} -d 1 0x00007ffffffde000 0x00007ffffffff000 # if an address is found, it will be followed up", '{0:s} -f "GLIBC" 0x00007ffffffde000 0x00007ffffffff000 # filter by keywords (-f, -e). need double-escape', ] _example_ = "\n".join(_example_).format(_cmdline_) def strings(self, data, len_threshold): strings_result = [] for m in re.finditer(b"[\x20-\x7E]+\x00", data): s = m.group(0).rstrip(b"\0") if len(s) >= len_threshold: strings_result.append((m.span(0)[0], String.bytes2str(s))) return strings_result def search_ascii(self, queue): seen_addr = [] seen_cstr = [] while queue: location, search_range, depth = queue.pop(0) # get data data = b"" try: # read range data += read_memory(location, search_range) # read extra while data and data[-1] in range(0x20, 0x7f): data += read_memory(location + len(data), 1) except gdb.MemoryError: pass # search for string for offset, cstr in self.strings(data, self.args.minlen): address = location + offset seen = False for seen_addr_start, seen_addr_end in seen_cstr: if seen_addr_start <= address < seen_addr_end: seen = True break if seen: continue if not self.args.filter or any(filt.search(cstr) for filt in self.args.filter): if not self.args.exclude or not any(ex.search(cstr) for ex in self.args.exclude): self.out.append("{!s}: {:s}".format(ProcessMap.lookup_address(address), cstr)) seen_cstr.append((address, address + len(cstr) + 1)) if depth == 0: continue # search for the pointer for recursive aligned_data = data[current_arch.ptrsize - location % current_arch.ptrsize:] if len(aligned_data) % 8: aligned_data = aligned_data[:-(len(aligned_data) % current_arch.ptrsize)] for addr in slice_unpack(aligned_data, current_arch.ptrsize): if addr in seen_addr: continue if is_valid_addr(addr): queue.append((addr, self.args.range, depth - 1)) seen_addr.append(addr) return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.end_location: first_range = args.end_location - args.location else: loc = ProcessMap.lookup_address(args.location) if loc.valid: first_range = loc.section.page_end - args.location else: first_range = get_pagesize() self.out = [] queue = [(args.location, first_range, args.depth)] self.search_ascii(queue) if not args.skip_save: tmp_fd, tmp_path = GefUtil.mkstemp(prefix="strings", suffix=".txt") os.fdopen(tmp_fd, "w").write("\n".join(self.out)) info("The output is saved to {:s}".format(tmp_path)) self.print_output() return @register_command class SyscallTableViewCommand(GenericCommand, BufferingOutput): """Display syscall_table entries.""" _cmdline_ = "syscall-table-view" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" _aliases_ = ["kst"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="REGEXP filter.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", "{0:s} --filter write", ] _example_ = "\n".join(_example_).format(_cmdline_) @switch_to_intel_syntax def parse_syscall_table(self, sys_call_table_addr): # scan cached_table = [] i = 0 while True: addr = sys_call_table_addr + i * current_arch.ptrsize if not is_valid_addr(addr): break syscall_function_addr = read_int_from_memory(addr) if (is_arm32() or is_arm64()) and syscall_function_addr % 4: # should be aligned break if not is_valid_addr(syscall_function_addr): # if entry is valid, no error break # check symbol symbol = Symbol.get_symbol_string(syscall_function_addr) if symbol is None: symbol = Symbol.get_ksymaddr_symbol(syscall_function_addr) if symbol is None: symbol = " " elif "+" in symbol: break # check if valid insn or not insn = get_insn(syscall_function_addr) insn2 = get_insn_next(syscall_function_addr) if insn is None or insn2 is None: break if is_x86(): # detect endbr, so slide if insn.mnemonic in ["endbr64", "endbr32"]: codelen = len(insn.opcodes) insn2 = get_insn_next(insn.address + codelen) insn = get_insn(insn.address + codelen) # detect `call non-essential-function` e.g., perf, trace, debug, ... while insn and insn2 and insn.mnemonic == "call": codelen = len(insn.opcodes) insn2 = get_insn_next(insn.address + codelen) insn = get_insn(insn.address + codelen) elif is_arm64(): # detect bti, so slide if insn.mnemonic in ["bti"]: codelen = len(insn.opcodes) insn2 = get_insn_next(insn.address + codelen) insn = get_insn(insn.address + codelen) # detect `bl non-essential-function` e.g., perf, trace, debug, ... while insn and insn2 and insn.mnemonic == "bl": codelen = len(insn.opcodes) insn2 = get_insn_next(insn.address + codelen) insn = get_insn(insn.address + codelen) elif is_arm32(): # detect `bl non-essential-function` e.g., perf, trace, debug, ... while insn and insn2 and insn.mnemonic in ["bl", "blx"]: codelen = len(insn.opcodes) insn2 = get_insn_next(insn.address + codelen) insn = get_insn(insn.address + codelen) # check again if insn is None or insn2 is None: break # check if the target system call is disabled is_valid = True if is_x86(): if is_x86_64(): err = "0xffffffffffffffda" else: err = "0xffffffda" if len(insn.operands) == 2 and insn.operands[-1] == err: if insn2.mnemonic == "ret": is_valid = False elif insn2.mnemonic == "jmp": try: insn3 = get_insn(AddressUtil.parse_address(insn2.operands[-1])) if insn3 and insn3.mnemonic == "ret": is_valid = False except (gdb.error, ValueError): pass elif is_arm64(): if len(insn.operands) == 2 and insn.operands[-1].split("\t")[0].strip() == "#0xffffffffffffffda": is_valid = False elif len(insn.operands) == 3 and insn.operands[-1] == "// #-38": is_valid = False cached_table.append([i, addr, syscall_function_addr, symbol, is_valid]) i += 1 return cached_table def syscall_table_view(self, orig_tag, sys_call_table_addr, syscall_list, nr_base=0): if syscall_list is None: self.quiet_add_out("{} {}".format(Color.colorify("[+]", "bold red"), "Could not find the syscall table")) return if sys_call_table_addr is None: self.quiet_add_out("{} {}".format(Color.colorify("[+]", "bold red"), "Could not find the symbol")) return # It maintains the cache both when running with and without symbols. try: AddressUtil.parse_address("_stext") tag = "symboled_" + orig_tag except gdb.error: tag = orig_tag # parse if tag not in self.cached_table: self.cached_table[tag] = self.parse_syscall_table(sys_call_table_addr) # print legend if not self.args.quiet: fmt = "{:8s} {:5s} {:7s} {:30s} {:18s} {:18s} {:s}" legend = ["Tag", "Index", "IsValid", "Syscall Name", "Table Address", "Function Address", "Symbol"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # for duplication check seen_count = {} for _, _, syscall_function_addr, _, _ in self.cached_table[tag]: seen_count[syscall_function_addr] = seen_count.get(syscall_function_addr, 0) + 1 # print for i, addr, syscall_function_addr, symbol, is_valid in self.cached_table[tag]: nr = nr_base + i if nr in syscall_list.nr_table: expected_name = syscall_list.nr_table[nr].name else: expected_name = "" fmt = "{:8s} [{:03d}] {:7s} {:30s} {:#018x} {:#018x}{:s}" if seen_count[syscall_function_addr] == 1 and is_valid: # valid entry msg = fmt.format(orig_tag, i, "valid", expected_name, addr, syscall_function_addr, symbol) if seen_count[syscall_function_addr] > 1 or not is_valid: # invalid entry msg = fmt.format(orig_tag, i, "invalid", expected_name, addr, syscall_function_addr, symbol) msg = Color.grayify(msg) if not self.args.filter: self.out.append(msg) else: for re_pattern in self.args.filter: if re_pattern.search(msg): self.out.append(msg) return def dump_syscall_table(self): if is_x86_32(): self.quiet_add_out(titlify("sys_call_table (x86)")) sys_call_table_addr = KernelAddressHeuristicFinder.get_sys_call_table_x86() self.syscall_table_view("x86", sys_call_table_addr, get_syscall_table("X86", "N32")) elif is_x86_64(): self.quiet_add_out(titlify("sys_call_table (x64)")) sys_call_table_addr = KernelAddressHeuristicFinder.get_sys_call_table_x64() self.syscall_table_view("x86_64", sys_call_table_addr, get_syscall_table("X86", "64")) kversion = Kernel.kernel_version() self.quiet_add_out(titlify("ia32_sys_call_table")) if kversion < "6.6.26": sys_call_table_addr = KernelAddressHeuristicFinder.get_sys_call_table_x86() self.syscall_table_view("x86_32", sys_call_table_addr, get_syscall_table("X86", "32")) else: self.quiet_add_out("ia32_sys_call_table is removed from 6.6.26.") self.quiet_add_out("each entry is embedded in `ia32_sys_call()` as call instruction.") self.quiet_add_out(titlify("x32_sys_call_table")) if kversion < "6.6.26": sys_call_table_addr = KernelAddressHeuristicFinder.get_sys_call_table_x32() self.syscall_table_view("x86_x32", sys_call_table_addr, get_syscall_table("X86", "64"), nr_base=0x4000_0000) else: self.quiet_add_out("x32_sys_call_table is removed from 6.6.26.") self.quiet_add_out("each entry is embedded in `x32_sys_call()` as call instruction.") elif is_arm32(): self.quiet_add_out(titlify("sys_call_table (arm32)")) sys_call_table_addr = KernelAddressHeuristicFinder.get_sys_call_table_arm32() self.syscall_table_view("arm32", sys_call_table_addr, get_syscall_table("ARM", "N32")) elif is_arm64(): self.quiet_add_out(titlify("sys_call_table (arm64)")) sys_call_table_addr = KernelAddressHeuristicFinder.get_sys_call_table_arm64() self.syscall_table_view("arm64", sys_call_table_addr, get_syscall_table("ARM64", "ARM")) self.quiet_add_out(titlify("compat_sys_call_table (arm32)")) sys_call_table_addr = KernelAddressHeuristicFinder.get_sys_call_table_arm64_compat() self.syscall_table_view("arm64_32", sys_call_table_addr, get_syscall_table("ARM", "32")) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): if not hasattr(self, "cached_table"): self.cached_table = {} self.out = [] self.dump_syscall_table() self.print_output(check_terminal_size=True) return class ExecAsm: """Execute embedded asm. e.g., ExecAsm(asm_op_list).exec_code(). WARNING: Disable `-enable-kvm` option for qemu-system; If set, this code will crash the guest OS.""" def __init__(self, target_codes, regs=None, step=None, use_bp=False, debug=False): self.regs = regs self.step = step or 1 # Step execution often fails due to an interrupt on ARM64 self.use_bp = use_bp # debug print enable self.debug = debug # output is always stdout self.stdout = 1 codes = [] if target_codes: # to stop another thread codes += [current_arch.infloop_insn] if current_arch.has_delay_slot: codes += [current_arch.nop_insn] codes += target_codes # list to bytes if Endian.is_big_endian(): self.code = b"".join(code[::-1] for code in codes) else: self.code = b"".join(codes) return def get_state(self): d = {} # pc # This value is used to point to the code location. It is not used to restore registers. d["pc"] = current_arch.pc if is_arm32() or is_arm32_cortex_m(): if current_arch.is_thumb(): d["pc"] -= 1 # code d["code"] = read_memory(d["pc"], len(self.code)) # reg d["reg"] = {} for reg in current_arch.all_registers: d["reg"][reg] = get_register(reg) return d def revert_state(self, d): # code write_memory(d["pc"], d["code"]) # reg for reg, v in d["reg"].items(): if get_register(reg) == v: continue if (is_hppa32() or is_hppa64()) and reg == "$pc": continue if is_sh4() and reg in ["$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7"]: reg = reg + "b0" # since r0-r7 cannot be changed directly, use bank 0 try: gdb.execute("set {:s} = {:#x}".format(reg, v), to_string=True) except gdb.error as e: if str(e).startswith("Cannot access memory at address"): pass else: info("set {:s} = {:#x} is failed".format(reg, v)) return def close_stdout(self): if self.debug: return self.stdout_bak = os.dup(self.stdout) f = open("/dev/null") os.dup2(f.fileno(), self.stdout) f.close() EventHooking.gef_on_stop_unhook(EventHandler.hook_stop_handler) return def revert_stdout(self): if self.debug: return EventHooking.gef_on_stop_hook(EventHandler.hook_stop_handler) os.dup2(self.stdout_bak, self.stdout) os.close(self.stdout_bak) return def modify_regs(self): if not self.regs: return for reg, v in self.regs.items(): if get_register(reg) == v: continue try: gdb.execute("set {:s} = {:#x}".format(reg, v), to_string=True) except gdb.error as e: if str(e).startswith("Cannot access memory at address"): pass else: info("set {:s} = {:#x} is failed".format(reg, v)) return def exec_code(self): # backup d = self.get_state() # modify code, regs self.modify_regs() write_memory(d["pc"], self.code) if self.debug: gdb.execute("context") # skip infloop if self.code: dst = d["pc"] + len(current_arch.infloop_insn) if current_arch.has_delay_slot: dst += len(current_arch.nop_insn) if is_hppa32() or is_hppa64(): gdb.execute("set $pcoqh = {:#x}".format(dst), to_string=True) dst2 = dst + len(current_arch.syscall_insn) gdb.execute("set $pcoqt = {:#x}".format(dst2), to_string=True) elif is_sparc32() or is_sparc32plus() or is_sparc64(): gdb.execute("set $pc = {:#x}".format(dst), to_string=True) dst2 = dst + len(current_arch.syscall_insn) gdb.execute("set $npc = {:#x}".format(dst2), to_string=True) else: gdb.execute("set $pc = {:#x}".format(dst), to_string=True) # exec self.close_stdout() if self.debug: gdb.execute("context") if self.use_bp: bp = None try: bp_addr = current_arch.pc for _ in range(self.step): bp_addr += get_insn(bp_addr).size bp = gdb.Breakpoint("*{:#x}".format(bp_addr)) gdb.execute("continue", to_string=True) except gdb.error: pass finally: if bp: bp.delete() else: try: gdb.execute("stepi {:d}".format(self.step), to_string=True) except gdb.MemoryError: pass if self.debug: gdb.execute("context") self.revert_stdout() # get result ret = self.get_state() # revert self.revert_state(d) return ret class ExecSyscall(ExecAsm): """Execute embedded asm for syscall. e.g., ExecSyscall(nr, args).exec_code(). WARNING: Disable `-enable-kvm` option for qemu-system; If set, this code will crash the guest OS.""" def __init__(self, nr, args, debug=False, use_bp=False): self.syscall_nr = nr self.syscall_args = args # Step execution often fails due to an interrupt on ARM64 self.use_bp = use_bp # debug print enable self.debug = debug # output is always stdout self.stdout = 1 if is_hppa32() or is_hppa64(): self.step = 3 # syscall, delay slot, trampoline else: self.step = 1 codes = [] # to stop another thread codes += [current_arch.infloop_insn] if current_arch.has_delay_slot: codes += [current_arch.nop_insn] # syscall opcodes syscall_insn = current_arch.syscall_insn if is_s390x() and nr <= 127: syscall_insn = syscall_insn[:-1] + bytes([nr]) codes += [syscall_insn] # Stepping through a syscall instruction may continue execution to the next instruction. # Depending on gdb version, this occurs even on architectures without delay slots, requiring a nop. codes += [current_arch.nop_insn] # list to bytes if Endian.is_big_endian(): self.code = b"".join(code[::-1] for code in codes) else: self.code = b"".join(codes) return def get_state(self): d = super().get_state() # mem if is_mips32(): d["mem"] = {} for offset in [0x10, 0x14, 0x18, 0x1c]: d["mem"][offset] = read_memory(current_arch.sp + offset, 4) if is_cris(): d["mem"] = {} for offset in [0x1c]: d["mem"][offset] = read_memory(current_arch.sp + offset, 4) return d def revert_state(self, d): super().revert_state(d) # mem if is_mips32(): for offset in [0x10, 0x14, 0x18, 0x1c]: if read_memory(current_arch.sp + offset, 4) == d["mem"][offset]: continue write_memory(current_arch.sp + offset, d["mem"][offset]) if is_cris(): for offset in [0x1c]: if read_memory(current_arch.sp + offset, 4) == d["mem"][offset]: continue write_memory(current_arch.sp + offset, d["mem"][offset]) return def modify_regs(self): # modify syscall args if is_mips32(): syscall_parameters = current_arch.syscall_parameters_o32 else: syscall_parameters = current_arch.syscall_parameters for reg, val in zip(syscall_parameters, self.syscall_args): if is_mips32() and "+" in reg: reg, off = reg.split("+") write_memory(get_register(reg) + int(off, 16), p32(val)) else: if is_sh4() and reg in ["$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7"]: reg = reg + "b0" # since r0-r7 cannot be changed directly, use bank 0 gdb.execute("set {:s} = {:#x}".format(reg, val), to_string=True) # modify syscall register if is_s390x(): if self.syscall_nr > 127: # embedded in instruction reg = current_arch.syscall_register[1] gdb.execute("set {:s} = {:#x}".format(reg, self.syscall_nr), to_string=True) else: reg = current_arch.syscall_register if is_sh4() and reg in ["$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7"]: reg = reg + "b0" # since r0-r7 cannot be changed directly, use bank 0 gdb.execute("set {:s} = {:#x}".format(reg, self.syscall_nr), to_string=True) return @register_command class TlsCommand(GenericCommand, BufferingOutput): """Display TLS base address. Requires glibc.""" _cmdline_ = "tls" _category_ = "02-b. Process Information - Base Address" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--all", action="store_true", help="show all TLS address.") parser.add_argument("-i", "--thread-id", type=AddressUtil.parse_address, help="show specific TLS address.") parser.add_argument("-s", "--symbol-hint", action="store_true", help="show hints if symbol is available (x64/x86 only).") parser.add_argument("-v", "--verbose", action="count", default=1, help="show more entries (+16).") parser.add_argument("-V", "--more-verbose", action="count", default=0, help="show more entries (+256).") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -vvv # repeat `-v` to display more lines", ] _example_ = "\n".join(_example_).format(_cmdline_) @staticmethod def get_direction(): if is_x86() or is_sparc64() or is_s390x(): direction = -1 else: direction = 1 return direction def get_specific_tls(self, thread_id): orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() threads = gdb.selected_inferior().threads() threads = [th for th in threads if th.num == thread_id] if len(threads) != 1: err("Could not find the target thread") return try: threads[0].switch() tls = current_arch.get_tls() except Exception: tls = None orig_thread.switch() # revert orig_frame.select() return tls def print_all_tls(self): orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() threads = gdb.selected_inferior().threads() threads = sorted(threads, key=lambda th: th.num) if not threads: err("No thread is detected") return for thread in threads: msg = "Thread Id:{:d}".format(thread.num) try: thread.switch() except gdb.error: msg += " - Failed to switch to this thread" continue tls = current_arch.get_tls() msg += " - {:#x}".format(tls) gef_print(msg) orig_thread.switch() # revert orig_frame.select() return def get_varnames(self): tp = GefUtil.cached_lookup_type("tcbhead_t") if tp is None: return "" aligned_members = [] for name, field in tp.items(): if field.bitpos % 8: continue if (field.bitpos // 8) % current_arch.ptrsize: continue aligned_members.append([(field.bitpos // 8) // current_arch.ptrsize, name]) args_string = " ".join(["-t {:d} {:s}".format(i, n) for i, n in aligned_members]) return args_string def dump_tls(self, tls): self.out.append("$tls = {:#x}".format(tls)) gdb.execute("p $tls = {:#x}".format(tls), to_string=True) n_entries = (16 * self.args.verbose) + (256 * self.args.more_verbose) self.out.append(titlify("TLS-{:#x}".format(current_arch.ptrsize * n_entries))) r = gdb.execute("dereference $tls-{:#x} {:d} --no-pager".format( current_arch.ptrsize * n_entries, n_entries, ), to_string=True) self.out.extend(r.rstrip().splitlines()) self.out.append(titlify("TLS")) if self.args.symbol_hint and is_x86(): args_string = self.get_varnames() r = gdb.execute("dereference $tls {:d} --no-pager {}".format(n_entries, args_string), to_string=True) else: r = gdb.execute("dereference $tls {:d} --no-pager".format(n_entries), to_string=True) self.out.extend(r.rstrip().splitlines()) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @require_arch_set def do_invoke(self, args): if not current_arch.tls_supported: warn("This command is not supported on this architecture") return if args.all: self.print_all_tls() return if args.thread_id: tls = self.get_specific_tls(args.thread_id) else: tls = current_arch.get_tls() if tls is None: err("Failed to get TLS address") return if not is_valid_addr(tls): err("Cannot access memory at address {:#x}".format(tls)) return self.out = [] self.dump_tls(tls) self.print_output(check_terminal_size=True) return @register_command class FsbaseCommand(GenericCommand): """Display fsbase address.""" _cmdline_ = "fsbase" _category_ = "02-b. Process Information - Base Address" _aliases_ = ["fs"] parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() _note_ = [ "This command overwrites original \"fs (=tui focus)\" command.", ] _note_ = "\n".join(_note_) @parse_args @only_if_gdb_running @only_if_specific_arch(arch=("x86_32", "x86_64")) @exclude_specific_gdb_mode(mode=("qiling", "kgdb")) def do_invoke(self, args): fsbase = current_arch.get_fs() if fsbase is not None: gef_print("$fs_base: {:#x}".format(fsbase)) return @register_command class GsbaseCommand(GenericCommand): """Display gsbase address.""" _cmdline_ = "gsbase" _category_ = "02-b. Process Information - Base Address" _aliases_ = ["gs"] parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qiling", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): gsbase = current_arch.get_gs() if gsbase is not None: gef_print("$gs_base: {:#x}".format(gsbase)) return @register_command class GdtInfoCommand(GenericCommand, BufferingOutput): """Print GDT/LDT entries. If user-land, show sample entries.""" _cmdline_ = "gdtinfo" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--only-gdt", action="store_true", help="show only GDT entries (qemu-system only).") parser.add_argument("--only-ldt", action="store_true", help="show only LDT entries (qemu-system only).") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") parser.add_argument("-v", "--verbose", action="store_true", help="also display bit information of gdt entries.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = [ "This command is intended to dump the GDTR and LDTR when working with qemu-system.", "When you're debugging a normal userland app you can't read the GDTR or LDTR,", "so this is just to show you an example of what information is stored there.", "However, the segment registers show the correct (real) values.", ] _note_ = "\n".join(_note_) # arch/x86/include/asm/segment.h SEGMENT_DESCRIPTION_64 = { 0: "NULL", 1: "KERNEL32_CS", 2: "KERNEL_CS", 3: "KERNEL_DS", 4: "DEFAULT_USER32_CS", 5: "DEFAULT_USER_DS", 6: "DEFAULT_USER_CS", 7: "???", 8: "TSS-part1", 9: "TSS-part2", 10: "LDT-part1", 11: "LDT-part2", 12: "TLS_#1", 13: "TLS_#2", 14: "TLS_#3", 15: "CPUNODE", } SEGMENT_DESCRIPTION_32 = { 0: "NULL", 1: "RESERVED", 2: "RESERVED", 3: "RESERVED", 4: "UNUSED", 5: "UNUSED", 6: "TLS_#1", 7: "TLS_#2", 8: "TLS_#3", 9: "RESERVED", 10: "RESERVED", 11: "RESERVED", 12: "KERNEL_CS", 13: "KERNEL_DS", 14: "DEFAULT_USER_CS", 15: "DEFAULT_USER_DS", 16: "TSS", 17: "LDT", 18: "PNPBIOS_CS32", 19: "PNPBIOS_CS16", 20: "PNPBIOS_DS", 21: "PNPBIOS_TS1", 22: "PNPBIOS_TS2", 23: "APMBIOS_BASE", 24: "APMBIOS", 25: "APMBIOS", 26: "ESPFIX_SS", 27: "PERCPU", 28: "STACK_CANARY", 29: "UNUSED", 30: "UNUSED", 31: "DOUBLEFAULT_TSS", } def print_seg_info(self): if not is_alive(): return self.out.append(titlify("Current register values")) for k in ["cs", "ds", "es", "fs", "gs", "ss"]: v = get_register(k) rpl = v & 0b11 ti = (v >> 2) & 0b1 index = (v >> 3) red_k = Color.colorify("{:4s}".format(k), "bold red") self.out.append("{:s}: {:#4x} (=rpl:{:d}, ti:{:d}, index:{:d})".format(red_k, v, rpl, ti, index)) self.out.append(" * rpl: Requested Privilege Level (0:Ring0, 3:Ring3)") self.out.append(" * ti: Table Indicator (0:GDT, 1:LDT)") self.out.append(" * index: Index of GDT/LDT") self.out.append(" * segment register value = (index << 3) | (ti << 2) | rpl") self.out.append(" * commonly used cs values:") self.out.append(" * x64 code: 0x33") self.out.append(" * x86 code (on x64): 0x23") self.out.append(" * x86 code (native): 0x73") return def entry_unpack(self, vals): if isinstance(vals, list): val = vals[0] # for 64bit SYSTEM segment else: val = vals # parse entry = {} entry["value"] = val entry["type_bytes"] = (val >> 40) & 0b1111 entry["p"] = (val >> 47) & 0b1 entry["dpl"] = (val >> 45) & 0b11 entry["s"] = (val >> 44) & 0b1 entry["s_s"] = ["SYSTEM", "CODE/DATA"][entry["s"]] if entry["s"] == 0: # SYSTEM segment entry["type_bytes_s"] = Color.boldify({ 0b0000: ["Reserved", "Reserved"], 0b0001: ["Available 16bit TSS", "Reserved"], 0b0010: ["LDT", "LDT"], 0b0011: ["Busy 16bit TSS", "Reserved"], 0b0100: ["16bit call gate", "Reserved"], 0b0101: ["16/32bit task gate", "Reserved"], 0b0110: ["16bit interrupt gate", "Reserved"], 0b0111: ["16bit trap gate", "Reserved"], 0b1000: ["Reserved", "Reserved"], 0b1001: ["Available 32bit TSS", "64bit TSS"], 0b1010: ["Reserved", "Reserved"], 0b1011: ["Busy 32bit TSS", "Busy 64bit TSS"], 0b1100: ["32bit call gate", "64bit call gate"], 0b1101: ["Reserved", "Reserved"], 0b1110: ["32bit interrupt gate", "64bit interrupt gate"], 0b1111: ["32bit trap gate", "64bit trap gate"], }[entry["type_bytes"]][is_x86_64()]) if entry["s"] == 0 and entry["type_bytes"] == 0b1100: # SYSTEM segment (call gate) entry["offseg0"] = val & 0xffff entry["segsel"] = (val >> 16) & 0xffff entry["offseg1"] = (val >> 48) & 0xffff entry["offseg"] = (entry["offseg1"] << 16) | entry["offseg0"] if isinstance(vals, list): # for 64bit SYSTEM segment (call gate) entry["value"] = vals[1] # overwrite entry["offseg2"] = vals[1] & 0xffff_ffff entry["offseg"] |= entry["offseg2"] << 32 else: # CODE/DATA segment or SYSTEM segment (not call gate) entry["g"] = (val >> 54) & 0x01 grsize = {0: 1, 1: 4096}[entry["g"]] entry["limit0"] = val & 0xffff entry["base0"] = (val >> 16) & 0xffff entry["base1"] = (val >> 32) & 0xff entry["limit1"] = (val >> 48) & 0x0f entry["base2"] = (val >> 56) & 0xff entry["limit"] = ((entry["limit1"] << 16) | entry["limit0"]) * grsize entry["base"] = (entry["base2"] << 24) | (entry["base1"] << 16) | entry["base0"] if isinstance(vals, list): # for 64bit SYSTEM segment (not call gate) entry["value"] = vals[1] # overwrite entry["base3"] = vals[1] & 0xffff_ffff entry["base"] |= entry["base3"] << 32 if entry["s"] == 1: # CODE/DATA segment entry["db"] = (val >> 53) & 0x01 entry["l"] = (val >> 52) & 0x01 dbl = (entry["db"] << 1) | entry["l"] entry["dbl"] = "{:d}".format(dbl) entry["dbl_s"] = ["16bit", "64bit", "32bit", "(N/A)"][dbl] entry["avl"] = (val >> 51) & 0x01 entry["e"] = (val >> 43) & 0x01 entry["dc"] = (val >> 42) & 0x01 entry["rw"] = (val >> 41) & 0x01 entry["ac"] = (val >> 40) & 0x01 if entry["e"] == 0: # DATA segment entry["e_s"] = Color.boldify("DATA") entry["rw_s"] = ["RO", "RW"][entry["rw"]] entry["dc_s"] = ["EXPAND-UP", "EXPAND-DOWN"][entry["dc"]] else: # CODE segment entry["e_s"] = Color.boldify("CODE") entry["rw_s"] = ["RO", "RX"][entry["rw"]] entry["dc_s"] = ["NON-CONFORMING", "CONFORMING"][entry["dc"]] entry["ac_s"] = ["NotAccessed", "Accessed"][entry["ac"]] Entry = collections.namedtuple("Entry", entry.keys()) return Entry(*entry.values()) def entry2str(self, value, value_only=False): if value_only: return "{:#018x}".format(value) if value == 0: # and not list return "{:#018x}".format(value) entry = self.entry_unpack(value) out = "" out += "{:#018x} ".format(entry.value) if entry.s == 0 and entry.type_bytes == 0b1100: # SYSTEM - call gate out += "{:#018x} ".format(entry.segsel) out += "{:#010x} ".format(entry.offseg) out += "{:15s}".format("") out += "{:<1d} ".format(entry.p) out += "{:<3d} ".format(entry.dpl) out += "{:<1d}{:11s} ".format(entry.s, "({:s})".format(entry.s_s)) out += "{:#06b}({:s})".format(entry.type_bytes, entry.type_bytes_s) else: out += "{:#018x} ".format(entry.base) out += "{:#010x} ".format(entry.limit) out += "{:<1d} ".format(entry.g) if entry.s == 0: # SYSTEM - Other out += "{:13s}".format("") else: # CODE/DATA out += "{:<1s}({:5s}) ".format(entry.dbl, entry.dbl_s) out += "{:<3d} ".format(entry.avl) out += "{:<1d} ".format(entry.p) out += "{:<3d} ".format(entry.dpl) out += "{:<1d}{:11s} ".format(entry.s, "({:s})".format(entry.s_s)) if entry.s == 0: # SYSTEM - Other out += "{:#06b}({:s})".format(entry.type_bytes, entry.type_bytes_s) else: # CODE/DATA type_bytes_s = [] type_bytes_s.append(entry.e_s) type_bytes_s.append(entry.dc_s) type_bytes_s.append(entry.rw_s) type_bytes_s.append(entry.ac_s) type_bytes_s = ",".join(type_bytes_s) out += "{:#06b}({:s})".format(entry.type_bytes, type_bytes_s) return out def get_segreg_list(self): regs = {} if is_alive(): for k in ["cs", "ds", "es", "fs", "gs", "ss"]: v = get_register(k) ti = (v >> 2) & 0b1 index = int(v >> 3) if v != 0 and ti == 0: regs[index] = regs.get(index, []) + [k] return regs def print_entries(self, entries, segm_desc=None, skip_null=False): regs = self.get_segreg_list() # print legend fmt = "{:2s} {:20s} {:18s} {:18s} {:10s} {:1s} {:8s} {:3s} {:1s} {:3s} {:12s} {:s}" legend = ["#", "SegmentName", "Value", "BASE", "LIMIT", "G", "D/B,L", "AVL", "P", "DPL", "S", "TYPE"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # print entry i = 0 concat_prev = False while i < len(entries): # check null entry if entries[i] == 0 and skip_null: if not concat_prev: i += 1 continue # segment name if segm_desc: segname = segm_desc.get(i, "Undefined") else: segname = "???" # parse and make string entry = self.entry_unpack(entries[i]) if concat_prev: # lower half of 64bit SYSTEM entries estr = self.entry2str([entries[i - 1], entries[i]]) concat_prev = False if not segname.endswith("-part2"): segname += "-part2" elif entry.s == 0 and entry.type_bytes != 0 and (is_x86_64() or is_emulated32()): # upper half of 64bit SYSTEM entries estr = self.entry2str(entries[i], value_only=True) concat_prev = True if not segname.endswith("-part1"): segname += "-part1" else: # CODE/DATA segment or 16/32bit SYSTEM entries estr = self.entry2str(entries[i]) concat_prev = False # extra info reglist = regs.get(i, []) if reglist: regstr = Color.colorify( " <- {:s}".format(" ,".join(reglist)), Config.get_gef_setting("theme.dereference_register_value"), ) else: regstr = "" # print self.out.append("{:<2d} {:20s} {:s} {:s}".format(i, segname, estr, regstr)) i += 1 return def print_gdt_example(self): # print title if is_x86_64() or is_emulated32(): self.out.append(titlify("GDT Entry (x64 sample)")) segm_desc = self.SEGMENT_DESCRIPTION_64 else: self.out.append(titlify("GDT Entry (x86 sample)")) segm_desc = self.SEGMENT_DESCRIPTION_32 # print legend self.info_add_out("*** This is an {:s} ***".format(Color.boldify("EXAMPLE"))) # print entry if is_x86_64() or is_emulated32(): entries = [ 0x0000_0000_0000_0000, 0x00cf_9b00_0000_ffff, 0x00af_9b00_0000_ffff, 0x00cf_9300_0000_ffff, 0x00cf_fb00_0000_ffff, 0x00cf_f300_0000_ffff, 0x00af_fb00_0000_ffff, 0x0000_0000_0000_0000, 0x0000_8b00_0000_206f, 0x0000_0000_ffff_fe00, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0040_f500_0000_0000, ] else: entries = [ 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x00cf_9a00_0000_ffff, 0x00cf_9300_0000_ffff, 0x00cf_fa00_0000_ffff, 0x00cf_f300_0000_ffff, 0xff00_8b80_4000_206b, 0x0000_0000_0000_0000, 0x0040_9a00_0000_ffff, 0x0000_9a00_0000_ffff, 0x0000_9200_0000_ffff, 0x0000_9200_0000_0000, 0x0000_9200_0000_0000, 0x0040_9a00_0000_ffff, 0x0000_9a00_0000_ffff, 0x0040_9200_0000_ffff, 0x00cf_9200_0000_ffff, 0x038f_9370_8000_ffff, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0xc400_8970_6000_206b, ] if is_x86_64(): segm_desc = self.SEGMENT_DESCRIPTION_64 else: segm_desc = self.SEGMENT_DESCRIPTION_32 self.print_entries(entries, segm_desc) return def print_gdt_real(self): # parse real value if is_qemu_system(): res = gdb.execute("monitor info registers", to_string=True) r = re.search(r"GDT\s*=\s*(\S+) (\S+)", res) elif is_vmware(): res = gdb.execute("monitor r gdtr", to_string=True) r = re.search(r"gdtr base=(\S+) limit=(\S+)", res) if not r: self.err_add_out("Could not find GDTR") return base = int(r.group(1), 16) limit = int(r.group(2), 16) # print title self.out.append(titlify("GDT Entry: base:{:#x} / limit:{:#x}".format(base, limit))) # check initialized or not if (base == 0x0 and limit == 0xffff) or limit == 0x0: self.err_add_out("GDT is uninitialized") return try: gdt_data = read_memory(base, limit + 1) except gdb.MemoryError: self.err_add_out("Memory read error") return entries = slice_unpack(gdt_data, 8) if is_x86_64(): segm_desc = self.SEGMENT_DESCRIPTION_64 else: segm_desc = self.SEGMENT_DESCRIPTION_32 self.print_entries(entries, segm_desc) return def print_ldt_real(self): # parse real value if is_qemu_system(): res = gdb.execute("monitor info registers", to_string=True) r = re.search(r"LDT=\S+ (\S+) (\S+)", res) elif is_vmware(): res = gdb.execute("monitor r ldtr", to_string=True) r = re.search(r"ldtr base=(\S+) limit=(\S+)", res) if not r: self.err_add_out("Could not find LDTR") return base = int(r.group(1), 16) limit = int(r.group(2), 16) # print title self.out.append(titlify("LDT Entry: base:{:#x} / limit:{:#x}".format(base, limit))) # check initialized or not if (base == 0x0 and limit == 0xffff_ffff) or limit == 0x0: self.err_add_out("LDT is uninitialized") return try: ldt_data = read_memory(base, limit + 1) except gdb.MemoryError: self.err_add_out("Memory read error") return entries = slice_unpack(ldt_data, 8) self.print_entries(entries, skip_null=True) return def print_gdt_entry_legend(self): self.out.append(titlify("legend (GDT/LDT entry for S=1)")) self.out.append(" <----- Access bytes----->") self.out.append(" <---Type bytes--->") self.out.append(" 31 23 22 21 20 19 15 14 12 11 10 9 8 7 0bit") self.out.append("-------------------------------------------------------------------------- 8byte") self.out.append("| | |D | |A | | | | | |D |R |A | |") self.out.append("| BASE2 31:24 |G |/ |L |V | LIMIT1 |P |DPL|S(1)|E | | | | BASE1 23:16 |") self.out.append("| | |B | |L | 19:16 | | | | |C |W |C | |") self.out.append("-------------------------------------------------------------------------- 4byte") self.out.append("| BASE0 15:0 | LIMIT0 15:0 |") self.out.append("-------------------------------------------------------------------------- 0byte") self.out.append(" * BASE : Start address") self.out.append(" * LIMIT : Segment size (4KB unit if G=1)") self.out.append(" * Flag bytes") self.out.append(" * G : Granularity flag (0:SegLimitAsByte, 1:SegLimitAs4KB)") self.out.append(" * D/B : Segment flag (0:16bitSeg, 1:32bitSeg)") self.out.append(" * L (if code seg) : 64-bit code segment flag (0:32bitSeg, 1:64bitSeg)") self.out.append(" * L (if data seg) : Reserved (0)") self.out.append(" * AVL : Used by system software") self.out.append(" * Access bytes") self.out.append(" * P : Segment present flag (0:SegmentNotInMemory, 1:SegmentInMemory)") self.out.append(" * DPL : Descriptor privilege level (0:Ring0, 3:Ring3)") self.out.append(" * S : Descriptor type flag (0:SystemSegment, 1:Code/DataSegment)") self.out.append(" * Type bytes (if S=1)") self.out.append(" * E : Executable bit (0:Unexecutable/DataSegment, 1:Executable/CodeSegment)") self.out.append(" * DC (if code seg) : Conforming bit (0:NoConforming, 1:Conforming)") self.out.append(" * DC (if data seg) : Direction bit (0:ExpandUp, 1:ExpandDown)") self.out.append(" * RW (if code seg) : Read/Exec bit (0:ExecOnly, 1:Read/Exec)") self.out.append(" * RW (if data seg) : Read/Write bit (0:ReadOnly, 1:Read/Write)") self.out.append(" * AC : Access bit (0:NotAccessed, 1:Accessed)") self.out.append(titlify("legend (GDT/LDT entry for S=0, not call gate)")) self.out.append(" <---Type bytes--->") self.out.append(" 31 23 22 19 15 14 12 11 7 0bit") self.out.append("-------------------------------------------------------------------------- 16byte") self.out.append("| ZERO1 (x64 only) |") self.out.append("-------------------------------------------------------------------------- 12byte") self.out.append("| BASE3 47:32 (x64 only) |") self.out.append("-------------------------------------------------------------------------- 8byte") self.out.append("| | | | | | | | | |") self.out.append("| BASE2 31:24 |G | ZERO0 | LIMIT1 |P |DPL|S(0)| type | BASE1 23:16 |") self.out.append("| | | | 19:16 | | | | | |") self.out.append("-------------------------------------------------------------------------- 4byte") self.out.append("| BASE0 15:0 | LIMIT0 15:0 |") self.out.append("-------------------------------------------------------------------------- 0byte") self.out.append(" * LIMIT (if TSS Entry) : __KERNEL_TSS_LIMIT") self.out.append(" * LIMIT (if LDT Entry) : (LDT entries * 8) - 1") self.out.append(" * Access bytes") self.out.append(" * Type bytes (if S=0) 16bit/32bit / 64bit") self.out.append(" * 0000 : Reserved / Reserved") self.out.append(" * 0001 : Available 16bit TSS / Reserved") self.out.append(" * 0010 : LDT / LDT") self.out.append(" * 0011 : Busy 16bit TSS / Reserved") self.out.append(" * 0100 : 16bit call gate / Reserved") self.out.append(" * 0101 : 16/32bit task gate / Reserved") self.out.append(" * 0110 : 16bit interrupt gate / Reserved") self.out.append(" * 0111 : 16bit trap gate / Reserved") self.out.append(" * 1000 : Reserved / Reserved") self.out.append(" * 1001 : Available 32bit TSS / 64bit TSS") self.out.append(" * 1010 : Reserved / Reserved") self.out.append(" * 1011 : Busy 32bit TSS / Busy 64bit TSS") self.out.append(" * 1100 : 32bit call gate / 64bit call gate") self.out.append(" * 1101 : Reserved / Reserved") self.out.append(" * 1110 : 32bit interrupt gate / 64bit interrupt gate") self.out.append(" * 1111 : 32bit trap gate / 64bit trap gate") self.out.append(titlify("legend (GDT/LDT entry for S=0, call gate)")) self.out.append(" <---Type bytes--->") self.out.append(" 31 19 15 14 12 11 7 4 0bit") self.out.append("-------------------------------------------------------------------------- 16byte") self.out.append("| ZERO (x64 only) |") self.out.append("-------------------------------------------------------------------------- 12byte") self.out.append("| OffsetInSegment2 63:32 (x64 only) |") self.out.append("-------------------------------------------------------------------------- 8byte") self.out.append("| | | | | | | |") self.out.append("| OffsetInSegment1 31:16 |P |DPL|S(0)| type |0 0 0 |Param |") self.out.append("| | | | | (1 1 0 0) | |Count |") self.out.append("-------------------------------------------------------------------------- 4byte") self.out.append("| SegmentSelector 15:0 | OffsetInSegment0 15:0 |") self.out.append("-------------------------------------------------------------------------- 0byte") return @parse_args @exclude_specific_gdb_mode(mode=("wine",)) @only_if_specific_arch(arch=("x86_32", "x86_64", "x86_16")) def do_invoke(self, args): self.out = [] if not args.quiet: self.print_seg_info() if is_qemu_system() or is_vmware(): if not args.only_ldt: self.print_gdt_real() if not args.only_gdt: self.print_ldt_real() else: self.print_gdt_example() if args.verbose: self.print_gdt_entry_legend() else: self.quiet_info_add_out("for flags description, use `-v`") self.print_output(check_terminal_size=True) return @register_command class IdtInfoCommand(GenericCommand, BufferingOutput): """Print IDT entries. If user-land, show sample entries.""" _cmdline_ = "idtinfo" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") parser.add_argument("-v", "--verbose", action="store_true", help="also display bit information of idt entries.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = [ "This command is intended to dump the IDTR when working with qemu-system.", "When you're debugging a normal userland app you can't read the IDTR,", "so this is just to show you an example of what information is stored there.", ] _note_ = "\n".join(_note_) # arch/x86/include/asm/trapnr.h INTERRUPT_DESCRIPTION = { 0: "#DE: Divide-by-zero", 1: "#DB: Debug", 2: "#NMI: Non-maskable Interrupt", 3: "#BP: Breakpoint", 4: "#OF: Overflow" , 5: "#BR: BOUND Range Exceeded", 6: "#UD: Invalid Opcode", 7: "#NM: Device Not Available", 8: "#DF: Double Fault", 9: "#OLD_MF: Coprocessor Segment Overrun", 10: "#TS: Invalid TSS", 11: "#NP: Segment Not Present", 12: "#SS: Stack Segment Fault", 13: "#GP: General Protection Fault", 14: "#PF: Page Fault", 15: "#SPRIOUS: Sprious Interrupt", 16: "#MF: x87 Floating-Point Exception", 17: "#AC: Alignment Check", 18: "#MC: Machine-Check", 19: "#XF: SIMD Floating-Point Exception", 20: "#VE: Virtualization Exception", 21: "#CP: Control Protection Exception", 29: "#VC: VMM Communication Exception", 32: "#IRET: IRET Exception", } @staticmethod def idt_unpack(val): idt = {} idt["value"] = val idt["offset"] = val & 0xffff idt["offset"] = idt["offset"] | ((val >> 32) & (0xffff_0000)) idt["offset"] = ((val >> 32) & (0xffff_ffff_0000_0000)) | idt["offset"] idt["segment"] = (val >> 16) & 0xffff idt["ist"] = (val >> 32) & 0b111 # codespell:ignore idt["gate_type"] = (val >> 40) & (0b1111) idt["dpl"] = (val >> 45) & (0b11) idt["present"] = (val >> 47) & (0b1) Idt = collections.namedtuple("Idt", idt.keys()) return Idt(*idt.values()) @staticmethod def idtval2str(value): val_width = current_arch.ptrsize * 4 + 2 ofs_width = current_arch.ptrsize * 2 + 2 idt = IdtInfoCommand.idt_unpack(value) if idt.present == 0: return "(none)" out = "" out += "{:#0{:d}x} ".format(idt.value, val_width) out += "{:#03x} ".format(idt.gate_type) out += "{:#03x} ".format(idt.ist) # codespell:ignore out += "{:#03x} ".format(idt.dpl) out += "{:#03x} ".format(idt.present) out += "{:#06x}:{:#0{:d}x}".format(idt.segment, idt.offset, ofs_width) return out @staticmethod def idtval2str_legend(): val_width = current_arch.ptrsize * 4 + 2 ofs_width = current_arch.ptrsize * 2 + 2 return "{:3s} {:36s} {:{:d}s} {:3s} {:3s} {:3s} {:3s} {:6s}:{:{:d}s}".format( "#", "name", "value", val_width, "typ", "ist", "dpl", "p", "segm", "offset", ofs_width, # codespell:ignore ) def print_idt_example(self): # print title if is_x86_64() or is_emulated32(): self.out.append(titlify("IDT Entry (x64 sample)")) else: self.out.append(titlify("IDT Entry (x86 sample)")) # print legend self.info_add_out("*** This is an {:s} ***".format(Color.boldify("EXAMPLE"))) self.out.append(GefUtil.make_legend(self.idtval2str_legend())) # print entry if is_x86_64() or is_emulated32(): entries = [ # idx, value [0, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0c30], [1, 0x00_0000_0000_ffff_ffff_8160_8e03_0010_0f00], [2, 0x00_0000_0000_ffff_ffff_8160_8e02_0010_12f0], [3, 0x00_0000_0000_ffff_ffff_8160_ee00_0010_0f60], [4, 0x00_0000_0000_ffff_ffff_8160_ee00_0010_0c60], [5, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0c90], [6, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0cc0], [7, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0cf0], [8, 0x00_0000_0000_ffff_ffff_8160_8e01_0010_0d20], [9, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0d50], [10, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0d80], [11, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0db0], [12, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0fb0], [13, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0fe0], [14, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_1010], [15, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0de0], [16, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0e10], [17, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0e40], [18, 0x00_0000_0000_ffff_ffff_8160_8e04_0010_1090], [19, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0e70], [20, 0x00_0000_0000_ffff_ffff_81c9_8e00_0010_f0b4], [21, 0x00_0000_0000_ffff_ffff_81c9_8e00_0010_f0bd], [29, 0x00_0000_0000_ffff_ffff_81c9_8e00_0010_f105], [32, 0x00_0000_0000_ffff_ffff_8160_8e00_0010_0b90], ] else: entries = [ # idx, value [0, 0x00_c378_8e00_0060_5974], [1, 0x00_c378_8e00_0060_5c4c], [2, 0x00_c378_8e00_0060_5c5c], [3, 0x00_c378_ee00_0060_5ef8], [4, 0x00_c378_ee00_0060_58f4], [5, 0x00_c378_8e00_0060_5904], [6, 0x00_c378_8e00_0060_5914], [7, 0x00_c378_8e00_0060_58e0], [8, 0x00_0000_8500_00f8_0000], [9, 0x00_c378_8e00_0060_5924], [10, 0x00_c378_8e00_0060_5934], [11, 0x00_c378_8e00_0060_5944], [12, 0x00_c378_8e00_0060_5954], [13, 0x00_c378_8e00_0060_5ffc], [14, 0x00_c378_8e00_0060_59a4], [15, 0x00_c378_8e00_0060_5994], [16, 0x00_c378_8e00_0060_58c0], [17, 0x00_c378_8e00_0060_5964], [18, 0x00_c378_8e00_0060_5984], [19, 0x00_c378_8e00_0060_58d0], [20, 0x00_c395_8e00_0060_30bc], [21, 0x00_c395_8e00_0060_30c5], [29, 0x00_c395_8e00_0060_310d], [32, 0x00_c378_8e00_0060_4b90], ] for i, value in entries: if value != 0: int_name = self.INTERRUPT_DESCRIPTION.get(i, "User defined Interrupt {:#x}".format(i)) self.out.append("{:<3d} {:36s} {:s}".format(i, int_name, self.idtval2str(value))) return def print_idt_real(self): # parse real value if is_qemu_system(): res = gdb.execute("monitor info registers", to_string=True) r = re.search(r"IDT\s*=\s*(\S+) (\S+)", res) elif is_vmware(): res = gdb.execute("monitor r idtr", to_string=True) r = re.search(r"idtr base=(\S+) limit=(\S+)", res) if not r: self.err_add_out("Could not find IDTR") return base = int(r.group(1), 16) limit = int(r.group(2), 16) # print title self.out.append(titlify("IDT Entry: base:{:#x} / limit:{:#x}".format(base, limit))) # print legend self.out.append(GefUtil.make_legend(self.idtval2str_legend())) # check initialized or not if (base == 0x0 and limit == 0xffff) or limit == 0x0: self.err_add_out("IDT is uninitialized") return try: idt_data = read_memory(base, min(limit + 1, current_arch.ptrsize * 2 * 256)) except gdb.MemoryError: self.err_add_out("Memory read error") return entries = slice_unpack(idt_data, current_arch.ptrsize * 2) # print entry for i, b in enumerate(entries): int_name = self.INTERRUPT_DESCRIPTION.get(i, "User defined Interrupt {:#x}".format(i)) valstr = self.idtval2str(b) sym = Symbol.get_symbol_string(self.idt_unpack(b).offset, nosymbol_string=" ") self.out.append("{:<3d} {:36s} {:s}{:s}".format(i, int_name, valstr, sym)) return def print_idt_entry_legend(self): self.out.append(titlify("legend (Normal IDT entry)")) self.out.append(" 31 15 14 13 12 8 3 0bit") self.out.append("------------------------------------------------------------------------") self.out.append("| RESERVED | 12byte") self.out.append("------------------------------------------------------------------------") self.out.append("| OFFSET2 63:32 | 8byte") self.out.append("------------------------------------------------------------------------") self.out.append("| OFFSET1 31:16 | P | DPL | 0 | Type | 00000 | IST | 4byte") # codespell:ignore self.out.append("------------------------------------------------------------------------") self.out.append("| Segment Selector | OFFSET0 15:0 | 0byte") self.out.append("------------------------------------------------------------------------") self.out.append(" * segment selector : Segment selector for destination code segment") self.out.append(" * offset : Offset to handler procedure entry point") self.out.append(" * ist : Interrupt stack table") # codespell:ignore self.out.append(" * type : One of following") self.out.append(" 0x5: Task gate") self.out.append(" 0xC: Call gate") self.out.append(" 0xE: 32/64-bit interrupt gate") self.out.append(" 0xF: 32/64-bit trap gate") self.out.append(" * dpl : Descriptor privilege level") self.out.append(" * p : Segment present flag") return @parse_args @exclude_specific_gdb_mode(mode=("wine",)) @only_if_specific_arch(arch=("x86_32", "x86_64", "x86_16")) def do_invoke(self, args): self.out = [] if is_qemu_system() or is_vmware(): self.print_idt_real() else: self.print_idt_example() if args.verbose: self.print_idt_entry_legend() else: self.quiet_info_add_out("for flags description, use `-v`") self.print_output(check_terminal_size=True) return @register_command class MemoryCompareCommand(GenericCommand, BufferingOutput): """Compare the memory contents of two locations.""" _cmdline_ = "memcmp" _category_ = "03-c. Memory - Compare" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys1", action="store_true", help="treat LOCATION1 as a physical address.") parser.add_argument("location1", metavar="LOCATION1", type=AddressUtil.parse_address, help="first address for comparison.") parser.add_argument("--phys2", action="store_true", help="treat LOCATION2 as a physical address.") parser.add_argument("location2", metavar="LOCATION2", type=AddressUtil.parse_address, help="second address for comparison.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for comparison.") parser.add_argument("-f", "--full", action="store_true", help="display the same line without omitting.") parser.add_argument("-t", "--telescope-like", action="store_true", help="compare the output like telescope.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def read_data(self, from1, from2, size): try: if self.args.phys1: from1data = read_physmem(from1, size) else: from1data = read_memory(from1, size) except (gdb.MemoryError, ValueError, OverflowError, MemoryError): err("Read error {:#x}".format(from1)) return None try: if self.args.phys2: from2data = read_physmem(from2, size) else: from2data = read_memory(from2, size) except (gdb.MemoryError, ValueError, OverflowError, MemoryError): err("Read error {:#x}".format(from2)) return None return from1data, from2data def memcmp_telescope_like(self, from1data, from2data): unpack = {2:u16, 4:u32, 8:u64}[current_arch.ptrsize] diff_found = False asterisk = False for pos in range(0, self.args.size, current_arch.ptrsize): f1_bin = from1data[pos : pos + current_arch.ptrsize] f2_bin = from2data[pos : pos + current_arch.ptrsize] if not self.args.full: if f1_bin == f2_bin: if asterisk is False: self.out.append("*") asterisk = True continue diff_found = True asterisk = False f1_hex = ProcessMap.lookup_address(unpack(f1_bin)) f2_hex = ProcessMap.lookup_address(unpack(f2_bin)) addr1 = ProcessMap.lookup_address(self.args.location1 + pos) addr2 = ProcessMap.lookup_address(self.args.location2 + pos) self.out.append("{!s}|{:+#07x}|{:+04d}: {:s} | {!s}|{:+#07x}|{:+04d}: {:s}".format( addr1, pos, pos // current_arch.ptrsize, f1_hex.long_fmt(), addr2, pos, pos // current_arch.ptrsize, f2_hex.long_fmt(), )) if diff_found is False: self.info_add_out("No difference") return def memcmp(self, from1data, from2data): diff_found = False asterisk = False hex_pad_len = { 1: 37, 2: 35, 3: 32, 4: 30, 5: 27, 6: 25, 7: 22, 8: 20, 9: 17, 10: 15, 11: 12, 12: 9, 13: 7, 14: 5, 15: 2, 16: 0, } for pos in range(0, self.args.size, 16): # determining continuity f1_bin = from1data[pos : pos + 16] f2_bin = from2data[pos : pos + 16] if not self.args.full: if f1_bin == f2_bin: if asterisk is False: self.out.append("*") asterisk = True continue diff_found = True asterisk = False # coloring f1_hex = [] f2_hex = [] f1_ascii = [] f2_ascii = [] for i in range(min(len(f1_bin), 16)): if f1_bin[i] == f2_bin[i]: color_func = lambda x: x else: color_func = Color.boldify f1_hex.append(color_func("{:02x}".format(f1_bin[i]))) f2_hex.append(color_func("{:02x}".format(f2_bin[i]))) f1_ascii.append(color_func(chr(f1_bin[i]) if 0x20 <= f1_bin[i] < 0x7f else ".")) f2_ascii.append(color_func(chr(f2_bin[i]) if 0x20 <= f2_bin[i] < 0x7f else ".")) # formatting # ["00", "00", "00" "00", ...] -> ["0000", "0000", ...] f1_hex2 = ["".join(x) for x in slicer(f1_hex, 2)] f2_hex2 = ["".join(x) for x in slicer(f2_hex, 2)] # padding # ["0000", "0000", ...] -> "0000 0000 ..." f1_hex_s = " ".join(f1_hex2) + " " * hex_pad_len[len(f1_hex)] f2_hex_s = " ".join(f2_hex2) + " " * hex_pad_len[len(f2_hex)] # [".", ".", ...] -> "................" f1_ascii_s = "".join(f1_ascii) + " " * (16 - len(f1_ascii)) f2_ascii_s = "".join(f2_ascii) + " " * (16 - len(f2_ascii)) # make line addr1 = ProcessMap.lookup_address(self.args.location1 + pos) addr2 = ProcessMap.lookup_address(self.args.location2 + pos) self.out.append("{!s}: {:s} |{:s}| {!s}: {:s} |{:s}|".format( addr1, f1_hex_s, f1_ascii_s, addr2, f2_hex_s, f2_ascii_s, )) if diff_found is False: self.info_add_out("No difference") return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys1 or args.phys2: if not is_qemu_system(): err("Unsupported `--phys` option in this gdb mode") return if args.size == 0: self.info_add_out("The size is zero, maybe wrong") ret = self.read_data(args.location1, args.location2, args.size) if ret is None: return self.out = [] from1data, from2data = ret if args.telescope_like: if current_arch is None: err("current_arch is None") return if args.size % current_arch.ptrsize != 0: err("The size must be aligned {:#x}".format(current_arch.ptrsize)) return self.memcmp_telescope_like(from1data, from2data) else: self.memcmp(from1data, from2data) self.print_output(check_terminal_size=True) return @register_command class MemorySetCommand(GenericCommand): """Set the value to the memory range.""" _cmdline_ = "memset" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat TO_ADDRESS as a physical address.") parser.add_argument("to_addr", metavar="TO_ADDRESS", type=AddressUtil.parse_address, help="destination of memset.") parser.add_argument("value", metavar="VALUE", type=AddressUtil.parse_address, help="the value to write.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for memset.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0xff 0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "If you want to specify a large value for `VALUE`, use the `patch string` command.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def memset(self, to_phys, to_addr, value, size): data = bytes([value]) * size try: PatchCommand.PatchInfo(to_addr, data, length=size, phys=to_phys).patch() except (gdb.MemoryError, ValueError, OverflowError): err("Write error {:#x}".format(to_addr)) return return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys: if not is_qemu_system(): err("Unsupported `--phys` option in this gdb mode") return if args.size == 0: info("The size is zero, maybe wrong") if args.value < 0 or 256 <= args.value: err("Wrong value (it must be 0x00-0xff)") return self.memset(args.phys, args.to_addr, args.value, args.size) return @register_command class MemoryCopyCommand(GenericCommand): """Copy the contents of one memory to another.""" _cmdline_ = "memcpy" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys1", action="store_true", help="treat TO_ADDRESS as a physical address.") parser.add_argument("to_addr", metavar="TO_ADDRESS", type=AddressUtil.parse_address, help="destination of memcpy.") parser.add_argument("--phys2", action="store_true", help="treat FROM_ADDRESS as a physical address.") parser.add_argument("from_addr", metavar="FROM_ADDRESS", type=AddressUtil.parse_address, help="source of memcpy.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for memcpy.") _syntax_ = parser.format_help() _note_ = [ "memcpy dst src 8", " <--size-->", " dst src", " Before: [ AAAAAAAA | BBBBBBBB | CCCCCCCC ]", " After : [ CCCCCCCC | BBBBBBBB | CCCCCCCC ]", "", "memswap dst src 8", " <--size-->", " dst src", " Before: [ AAAAAAAA | BBBBBBBB | CCCCCCCC ]", " After : [ CCCCCCCC | BBBBBBBB | AAAAAAAA ]", "", "meminsert dst src 16 8", " <-------size1-------> <--size2->", " dst src", " Before: [ AAAAAAAA | BBBBBBBB | CCCCCCCC ]", " After : [ CCCCCCCC | AAAAAAAA | BBBBBBBB ]", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return @staticmethod def get_data(from_phys, from_addr, size): try: if from_phys: data = read_physmem(from_addr, size) else: data = read_memory(from_addr, size) except (gdb.MemoryError, ValueError, OverflowError): err("Read error {:#x}".format(from_addr)) return None info("Read count: {:#x}".format(len(data))) return data def memcpy(self, to_phys, to_addr, from_phys, from_addr, size): data = MemoryCopyCommand.get_data(from_phys, from_addr, size) if data is None: return try: PatchCommand.PatchInfo(to_addr, data, length=size, phys=to_phys).patch() except (gdb.MemoryError, ValueError, OverflowError): err("Write error {:#x}".format(to_addr)) return return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys1 or args.phys2: if not is_qemu_system(): err("Unsupported `--phys` option in this gdb mode") return if args.size == 0: info("The size is zero, maybe wrong") self.memcpy(args.phys1, args.to_addr, args.phys2, args.from_addr, args.size) return @register_command class MemorySwapCommand(GenericCommand): """Swap the contents of one memory to another.""" _cmdline_ = "memswap" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys1", action="store_true", help="treat SWAP_ADDRESS1 as a physical address.") parser.add_argument("swap_addr1", metavar="SWAP_ADDRESS1", type=AddressUtil.parse_address, help="swap target address.") parser.add_argument("--phys2", action="store_true", help="treat SWAP_ADDRESS2 as a physical address.") parser.add_argument("swap_addr2", metavar="SWAP_ADDRESS2", type=AddressUtil.parse_address, help="another swap target address.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for memory swap.") _syntax_ = parser.format_help() _note_ = MemoryCopyCommand._note_ def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def memswap(self, phys1, addr1, phys2, addr2, size): data1 = MemoryCopyCommand.get_data(phys1, addr1, size) if data1 is None: return data2 = MemoryCopyCommand.get_data(phys2, addr2, size) if data2 is None: return tag = PatchCommand.PatchInfo.get_unique_tag() try: PatchCommand.PatchInfo(addr1, data2, length=size, phys=phys1, tag=tag).patch() except (gdb.MemoryError, ValueError, OverflowError): err("Write error {:#x}".format(addr1)) return try: PatchCommand.PatchInfo(addr2, data1, length=size, phys=phys2, tag=tag).patch() except (gdb.MemoryError, ValueError, OverflowError): err("Write error {:#x}".format(addr2)) return return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys1 or args.phys2: if not is_qemu_system(): err("Unsupported `--phys` option in this gdb mode") return if args.size == 0: info("The size is zero, maybe wrong") self.memswap(args.phys1, args.swap_addr1, args.phys2, args.swap_addr2, args.size) return @register_command class MemoryInsertCommand(GenericCommand): """Insert the contents of one memory to another.""" _cmdline_ = "meminsert" _category_ = "03-d. Memory - Patch" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys1", action="store_true", help="treat TO_ADDRESS as a physical address.") parser.add_argument("to_addr", metavar="TO_ADDRESS", type=AddressUtil.parse_address, help="destination of meminsert.") parser.add_argument("--phys2", action="store_true", help="treat FROM_ADDRESS as a physical address.") parser.add_argument("from_addr", metavar="FROM_ADDRESS", type=AddressUtil.parse_address, help="source of meminsert.") parser.add_argument("size1", metavar="SIZE1", type=AddressUtil.parse_address, help="the pushed back size for meminsert.") parser.add_argument("size2", metavar="SIZE2", type=AddressUtil.parse_address, help="the inserted(slided) size for meminsert.") _syntax_ = parser.format_help() _note_ = MemoryCopyCommand._note_ def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def meminsert(self, phys1, addr1, size1, phys2, addr2, size2): data1 = MemoryCopyCommand.get_data(phys1, addr1, size1) if data1 is None: return data2 = MemoryCopyCommand.get_data(phys2, addr2, size2) if data2 is None: return to_write_data = data2 + data1 try: PatchCommand.PatchInfo(addr1, to_write_data, phys=phys1).patch() except (gdb.MemoryError, ValueError, OverflowError): err("Write error {:#x}".format(addr1)) return return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys1 or args.phys2: if not is_qemu_system(): err("Unsupported `--phys` option in this gdb mode") return if args.size2 == 0: info("The size2 is zero, maybe wrong") self.meminsert(args.phys1, args.to_addr, args.size1, args.phys2, args.from_addr, args.size2) return class Hash: class SHA512TruncBase: block_size = 128 digest_size = None # SHA-512 round constants K = ( 0x428a_2f98_d728_ae22, 0x7137_4491_23ef_65cd, 0xb5c0_fbcf_ec4d_3b2f, 0xe9b5_dba5_8189_dbbc, 0x3956_c25b_f348_b538, 0x59f1_11f1_b605_d019, 0x923f_82a4_af19_4f9b, 0xab1c_5ed5_da6d_8118, 0xd807_aa98_a303_0242, 0x1283_5b01_4570_6fbe, 0x2431_85be_4ee4_b28c, 0x550c_7dc3_d5ff_b4e2, 0x72be_5d74_f27b_896f, 0x80de_b1fe_3b16_96b1, 0x9bdc_06a7_25c7_1235, 0xc19b_f174_cf69_2694, 0xe49b_69c1_9ef1_4ad2, 0xefbe_4786_384f_25e3, 0x0fc1_9dc6_8b8c_d5b5, 0x240c_a1cc_77ac_9c65, 0x2de9_2c6f_592b_0275, 0x4a74_84aa_6ea6_e483, 0x5cb0_a9dc_bd41_fbd4, 0x76f9_88da_8311_53b5, 0x983e_5152_ee66_dfab, 0xa831_c66d_2db4_3210, 0xb003_27c8_98fb_213f, 0xbf59_7fc7_beef_0ee4, 0xc6e0_0bf3_3da8_8fc2, 0xd5a7_9147_930a_a725, 0x06ca_6351_e003_826f, 0x1429_2967_0a0e_6e70, 0x27b7_0a85_46d2_2ffc, 0x2e1b_2138_5c26_c926, 0x4d2c_6dfc_5ac4_2aed, 0x5338_0d13_9d95_b3df, 0x650a_7354_8baf_63de, 0x766a_0abb_3c77_b2a8, 0x81c2_c92e_47ed_aee6, 0x9272_2c85_1482_353b, 0xa2bf_e8a1_4cf1_0364, 0xa81a_664b_bc42_3001, 0xc24b_8b70_d0f8_9791, 0xc76c_51a3_0654_be30, 0xd192_e819_d6ef_5218, 0xd699_0624_5565_a910, 0xf40e_3585_5771_202a, 0x106a_a070_32bb_d1b8, 0x19a4_c116_b8d2_d0c8, 0x1e37_6c08_5141_ab53, 0x2748_774c_df8e_eb99, 0x34b0_bcb5_e19b_48a8, 0x391c_0cb3_c5c9_5a63, 0x4ed8_aa4a_e341_8acb, 0x5b9c_ca4f_7763_e373, 0x682e_6ff3_d6b2_b8a3, 0x748f_82ee_5def_b2fc, 0x78a5_636f_4317_2f60, 0x84c8_7814_a1f0_ab72, 0x8cc7_0208_1a64_39ec, 0x90be_fffa_2363_1e28, 0xa450_6ceb_de82_bde9, 0xbef9_a3f7_b2c6_7915, 0xc671_78f2_e372_532b, 0xca27_3ece_ea26_619c, 0xd186_b8c7_21c0_c207, 0xeada_7dd6_cde0_eb1e, 0xf57d_4f7f_ee6e_d178, 0x06f0_67aa_7217_6fba, 0x0a63_7dc5_a2c8_98a6, 0x113f_9804_bef9_0dae, 0x1b71_0b35_131c_471b, 0x28db_77f5_2304_7d84, 0x32ca_ab7b_40c7_2493, 0x3c9e_be0a_15c9_bebc, 0x431d_67c4_9c10_0d4c, 0x4cc5_d4be_cb3e_42b6, 0x597f_299c_fc65_7e2a, 0x5fcb_6fab_3ad6_faec, 0x6c44_198c_4a47_5817, ) def __init__(self, data=b""): if self.digest_size is None: raise ValueError("digest_size must be set in subclass") self.buf = bytearray() self.msg_len = 0 # in bytes self.h0 = self.initial_state[0] self.h1 = self.initial_state[1] self.h2 = self.initial_state[2] self.h3 = self.initial_state[3] self.h4 = self.initial_state[4] self.h5 = self.initial_state[5] self.h6 = self.initial_state[6] self.h7 = self.initial_state[7] if data: self.update(data) return def copy(self): other = self.__class__() other.h0 = self.h0 other.h1 = self.h1 other.h2 = self.h2 other.h3 = self.h3 other.h4 = self.h4 other.h5 = self.h5 other.h6 = self.h6 other.h7 = self.h7 other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 128: block = bytes(self.buf[:128]) del self.buf[:128] self.compress(block) return self def digest(self): c = self.copy() c.finalize() full = struct.pack(">8Q", c.h0, c.h1, c.h2, c.h3, c.h4, c.h5, c.h6, c.h7) return full[: self.digest_size] def hexdigest(self): return self.digest().hex() def finalize(self): # Padding: append 0x80, then 0x00* until length ≡ 112 (mod 128), # then append message length in bits as 128-bit big-endian. bit_len = self.msg_len * 8 self.buf.append(0x80) while (len(self.buf) % 128) != 112: self.buf.append(0x00) self.buf.extend(struct.pack(">QQ", (bit_len >> 64) & 0xffff_ffff_ffff_ffff, bit_len & 0xffff_ffff_ffff_ffff)) while len(self.buf) >= 128: block = bytes(self.buf[:128]) del self.buf[:128] self.compress(block) return def rotr64(self, x, n): x &= 0xffff_ffff_ffff_ffff n &= 63 if n == 0: return x return ((x >> n) | (x << (64 - n))) & 0xffff_ffff_ffff_ffff def shr64(self, x, n): x &= 0xffff_ffff_ffff_ffff return (x >> n) & 0xffff_ffff_ffff_ffff def ch(self, x, y, z): return (x & y) ^ ((~x) & z) def maj(self, x, y, z): return (x & y) ^ (x & z) ^ (y & z) def big_sigma0(self, x): return self.rotr64(x, 28) ^ self.rotr64(x, 34) ^ self.rotr64(x, 39) def big_sigma1(self, x): return self.rotr64(x, 14) ^ self.rotr64(x, 18) ^ self.rotr64(x, 41) def small_sigma0(self, x): return self.rotr64(x, 1) ^ self.rotr64(x, 8) ^ self.shr64(x, 7) def small_sigma1(self, x): return self.rotr64(x, 19) ^ self.rotr64(x, 61) ^ self.shr64(x, 6) def compress(self, block): w = list(struct.unpack(">16Q", block)) for i in range(16, 80): v = (self.small_sigma1(w[i - 2]) + w[i - 7] + self.small_sigma0(w[i - 15]) + w[i - 16]) & 0xffff_ffff_ffff_ffff w.append(v) a = self.h0 b = self.h1 c = self.h2 d = self.h3 e = self.h4 f = self.h5 g = self.h6 h = self.h7 for i in range(80): t1 = (h + self.big_sigma1(e) + self.ch(e, f, g) + self.K[i] + w[i]) & 0xffff_ffff_ffff_ffff t2 = (self.big_sigma0(a) + self.maj(a, b, c)) & 0xffff_ffff_ffff_ffff h = g g = f f = e e = (d + t1) & 0xffff_ffff_ffff_ffff d = c c = b b = a a = (t1 + t2) & 0xffff_ffff_ffff_ffff self.h0 = (self.h0 + a) & 0xffff_ffff_ffff_ffff self.h1 = (self.h1 + b) & 0xffff_ffff_ffff_ffff self.h2 = (self.h2 + c) & 0xffff_ffff_ffff_ffff self.h3 = (self.h3 + d) & 0xffff_ffff_ffff_ffff self.h4 = (self.h4 + e) & 0xffff_ffff_ffff_ffff self.h5 = (self.h5 + f) & 0xffff_ffff_ffff_ffff self.h6 = (self.h6 + g) & 0xffff_ffff_ffff_ffff self.h7 = (self.h7 + h) & 0xffff_ffff_ffff_ffff return class SHA512_224(SHA512TruncBase): digest_size = 28 initial_state = ( 0x8c3d_37c8_1954_4da2, 0x73e1_9966_89dc_d4d6, 0x1dfa_b7ae_32ff_9c82, 0x679d_d514_582f_9fcf, 0x0f6d_2b69_7bd4_4da8, 0x77e3_6f73_04c4_8942, 0x3f9d_85a8_6a1d_36c8, 0x1112_e6ad_91d6_92a1, ) class SHA512_256(SHA512TruncBase): digest_size = 32 initial_state = ( 0x2231_2194_fc2b_f72c, 0x9f55_5fa3_c84c_64c2, 0x2393_b86b_6f53_b151, 0x9638_7719_5940_eabd, 0x9628_3ee2_a88e_ffe3, 0xbe5e_1e25_5386_3992, 0x2b01_99fc_2c85_b8aa, 0x0eb7_2ddc_81c5_2ca2, ) class KeccakBase: # Keccak-f[1600] parameters lanes = 25 rounds = 24 # Rho offsets (index = x + 5*y) rho = ( 0x00, 0x01, 0x3e, 0x1c, 0x1b, 0x24, 0x2c, 0x06, 0x37, 0x14, 0x03, 0x0a, 0x2b, 0x19, 0x27, 0x29, 0x2d, 0x0f, 0x15, 0x08, 0x12, 0x02, 0x3d, 0x38, 0x0e, ) # Round constants rc = ( 0x0000_0000_0000_0001, 0x0000_0000_0000_8082, 0x8000_0000_0000_808a, 0x8000_0000_8000_8000, 0x0000_0000_0000_808b, 0x0000_0000_8000_0001, 0x8000_0000_8000_8081, 0x8000_0000_0000_8009, 0x0000_0000_0000_008a, 0x0000_0000_0000_0088, 0x0000_0000_8000_8009, 0x0000_0000_8000_000a, 0x0000_0000_8000_808b, 0x8000_0000_0000_008b, 0x8000_0000_0000_8089, 0x8000_0000_0000_8003, 0x8000_0000_0000_8002, 0x8000_0000_0000_0080, 0x0000_0000_0000_800a, 0x8000_0000_8000_000a, 0x8000_0000_8000_8081, 0x8000_0000_0000_8080, 0x0000_0000_8000_0001, 0x8000_0000_8000_8008, ) def __init__(self, data=b""): self.block_size = self.rate_bytes self.state = [0] * 25 self.buf = bytearray() self.msg_len = 0 # in bytes if data: self.update(data) return def copy(self): other = self.__class__() other.state = list(self.state) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.rate_bytes: block = bytes(self.buf[:self.rate_bytes]) del self.buf[:self.rate_bytes] self.absorb(block) return self def digest(self): c = self.copy() c.finalize() out = c.squeeze(self.digest_size) return out def hexdigest(self): return self.digest().hex() def finalize(self): # Keccak padding: pad10*1 with domain separation 0x01 (Keccak, not SHA3) self.buf.append(0x01) while (len(self.buf) % self.rate_bytes) != (self.rate_bytes - 1): self.buf.append(0x00) self.buf.append(0x80) while len(self.buf) >= self.rate_bytes: block = bytes(self.buf[:self.rate_bytes]) del self.buf[:self.rate_bytes] self.absorb(block) return def absorb(self, block): # XOR block into the first rate_bytes of the state (little-endian lanes) lanes = self.rate_bytes // 8 for i in range(lanes): v = struct.unpack_from("> (64 - n))) & 0xffff_ffff_ffff_ffff for rnd in range(24): # Theta c = [0] * 5 for x in range(5): c[x] = a[x] ^ a[x + 5] ^ a[x + 10] ^ a[x + 15] ^ a[x + 20] d = [0] * 5 for x in range(5): d[x] = c[(x - 1) % 5] ^ rol64(c[(x + 1) % 5], 1) for y in range(5): for x in range(5): a[x + 5 * y] ^= d[x] # Rho + Pi b = [0] * 25 for y in range(5): for x in range(5): idx = x + 5 * y rot = self.rho[idx] v = rol64(a[idx], rot) x2 = y y2 = (2 * x + 3 * y) % 5 b[x2 + 5 * y2] = v # Chi for y in range(5): row = [b[x + 5 * y] for x in range(5)] for x in range(5): a[x + 5 * y] = row[x] ^ ((~row[(x + 1) % 5]) & row[(x + 2) % 5]) # Iota a[0] ^= self.rc[rnd] return class Keccak224(KeccakBase): rate_bytes = 144 digest_size = 28 class Keccak256(KeccakBase): rate_bytes = 136 digest_size = 32 class Keccak384(KeccakBase): rate_bytes = 104 digest_size = 48 class Keccak512(KeccakBase): rate_bytes = 72 digest_size = 64 class TurboShakeBase: # Keccak-p[1600,12] with little-endian 64-bit lanes rounds = 12 domain = 0x1f rho = ( 0x00, 0x01, 0x3e, 0x1c, 0x1b, 0x24, 0x2c, 0x06, 0x37, 0x14, 0x03, 0x0a, 0x2b, 0x19, 0x27, 0x29, 0x2d, 0x0f, 0x15, 0x08, 0x12, 0x02, 0x3d, 0x38, 0x0e, ) # Keccak-f[1600] round constants; Keccak-p[1600,12] uses the last 12 of these. rc_last12 = ( 0x0000_0000_8000_808b, 0x8000_0000_0000_008b, 0x8000_0000_0000_8089, 0x8000_0000_0000_8003, 0x8000_0000_0000_8002, 0x8000_0000_0000_0080, 0x0000_0000_0000_800a, 0x8000_0000_8000_000a, 0x8000_0000_8000_8081, 0x8000_0000_0000_8080, 0x0000_0000_8000_0001, 0x8000_0000_8000_8008, ) def __init__(self, data=b"", digest_bits=128): self.digest_size = digest_bits // 8 self.block_size = self.rate_bytes self.state = [0] * 25 self.buf = bytearray() self.msg_len = 0 lane_count = self.rate_bytes // 8 self.lane_unpack = struct.Struct("<" + ("Q" * lane_count)) self.lane_pack = struct.Struct("<" + ("Q" * lane_count)) if data: self.update(data) return def copy(self): other = self.__class__(digest_bits=self.digest_size * 8) other.state = list(self.state) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") mv = memoryview(data) self.msg_len += len(mv) # First, fill the partial block if any. if self.buf: need = self.rate_bytes - len(self.buf) if len(mv) < need: self.buf.extend(mv) return self self.buf.extend(mv[:need]) self.absorb(self.buf) self.buf.clear() mv = mv[need:] # Process full blocks directly from input without copying/deleting from the front. full_len = (len(mv) // self.rate_bytes) * self.rate_bytes for off in range(0, full_len, self.rate_bytes): self.absorb(mv[off:off + self.rate_bytes]) # Keep only the tail. if full_len != len(mv): self.buf.extend(mv[full_len:]) return self def digest(self): c = self.copy() c.finalize() out = c.squeeze(self.digest_size) return out def hexdigest(self): return self.digest().hex() def finalize(self): # update() invariant: self.buf length is always < rate_bytes. self.buf.append(self.domain) rem = len(self.buf) % self.rate_bytes if rem != 0: self.buf.extend(b"\x00" * (self.rate_bytes - rem)) self.buf[-1] ^= 0x80 # Exactly one block should exist here. self.absorb(self.buf) self.buf.clear() return def absorb(self, block): values = self.lane_unpack.unpack_from(block) state = self.state for i, v in enumerate(values): state[i] = (state[i] ^ v) & 0xffff_ffff_ffff_ffff self.keccak_p1600_12(state) return def squeeze(self, out_len): out = bytearray() while len(out) < out_len: out.extend(self.state_bytes(self.rate_bytes)) if len(out) < out_len: self.keccak_p1600_12(self.state) return bytes(out[:out_len]) def state_bytes(self, nbytes): lane_count = self.rate_bytes // 8 packed = self.lane_pack.pack(*[ self.state[i] & 0xffff_ffff_ffff_ffff for i in range(lane_count) ]) return packed[:nbytes] def keccak_p1600_12(self, a): def rol64(x, n): x &= 0xffff_ffff_ffff_ffff n &= 63 if n == 0: return x return ((x << n) | (x >> (64 - n))) & 0xffff_ffff_ffff_ffff for rnd in range(12): # Theta c = [0] * 5 for x in range(5): c[x] = a[x] ^ a[x + 5] ^ a[x + 10] ^ a[x + 15] ^ a[x + 20] d = [0] * 5 for x in range(5): d[x] = c[(x - 1) % 5] ^ rol64(c[(x + 1) % 5], 1) for y in range(5): for x in range(5): a[x + 5 * y] ^= d[x] # Rho + Pi b = [0] * 25 for y in range(5): for x in range(5): idx = x + 5 * y rot = self.rho[idx] v = rol64(a[idx], rot) x2 = y y2 = (2 * x + 3 * y) % 5 b[x2 + 5 * y2] = v # Chi for y in range(5): row = [b[x + 5 * y] for x in range(5)] for x in range(5): a[x + 5 * y] = row[x] ^ ((~row[(x + 1) % 5]) & row[(x + 2) % 5]) # Iota a[0] ^= self.rc_last12[rnd] return class TurboShake128(TurboShakeBase): rate_bytes = 168 # 1344 bits class TurboShake256(TurboShakeBase): rate_bytes = 136 # 1088 bits class KangarooTwelveBase: # Keccak-p[1600,12] parameters rho = ( 0x00, 0x01, 0x3e, 0x1c, 0x1b, 0x24, 0x2c, 0x06, 0x37, 0x14, 0x03, 0x0a, 0x2b, 0x19, 0x27, 0x29, 0x2d, 0x0f, 0x15, 0x08, 0x12, 0x02, 0x3d, 0x38, 0x0e, ) # Round constants rc_last12 = ( 0x0000_0000_8000_808b, 0x8000_0000_0000_008b, 0x8000_0000_0000_8089, 0x8000_0000_0000_8003, 0x8000_0000_0000_8002, 0x8000_0000_0000_0080, 0x0000_0000_0000_800a, 0x8000_0000_8000_000a, 0x8000_0000_8000_8081, 0x8000_0000_0000_8080, 0x0000_0000_8000_0001, 0x8000_0000_8000_8008, ) # K12 chunk size (bytes) chunk_size = 8192 def __init__(self, data=b"", custom=b"", digest_bits=128): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not isinstance(custom, (bytes, bytearray, memoryview)): raise TypeError("custom must be bytes-like") self.digest_size = digest_bits // 8 self.block_size = self.chunk_size self.buf = bytearray() self.msg_len = 0 # in bytes self.custom = custom if data: self.update(data) return def copy(self): other = self.__class__(b"", custom=self.custom, digest_bits=self.digest_size * 8) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) return self def digest(self): c = self.copy() c.finalize() return c.result def hexdigest(self): return self.digest().hex() def finalize(self): m = bytes(self.buf) self.result = self.k12(m, self.custom, self.digest_size) return def k12(self, m, custom, out_len): s = m + custom + self.length_encode(len(custom)) if len(s) <= self.chunk_size: out = self.turbo_shake(s, 7, out_len) return out chunks = [] for i in range(0, len(s), self.chunk_size): chunks.append(s[i:i + self.chunk_size]) n = len(chunks) final_node = bytearray() final_node.extend(chunks[0]) final_node.extend(b"\x03" + (b"\x00" * 7)) for i in range(1, n): cv = self.turbo_shake(chunks[i], 11, self.cv_len) final_node.extend(cv) final_node.extend(self.length_encode(n - 1)) final_node.extend(b"\xff\xff") out = self.turbo_shake(bytes(final_node), 6, out_len) return out def length_encode(self, x): if x < 0: raise ValueError("x must be non-negative") s = bytearray() while x > 0: s.insert(0, x & 0xff) x //= 256 s.append(len(s) & 0xff) return bytes(s) def turbo_shake(self, m, d, out_len): if not isinstance(m, (bytes, bytearray, memoryview)): raise TypeError("m must be bytes-like") if not (1 <= d <= 127): raise ValueError("domain byte d must be in [0x01..0x7f]") if out_len <= 0: raise ValueError("out_len must be positive") mp = bytearray(m) mp.append(d) rem = len(mp) % self.rate_bytes if rem != 0: mp.extend(b"\x00" * (self.rate_bytes - rem)) mp[-1] ^= 0x80 a = [0] * 25 lanes = self.rate_bytes // 8 for off in range(0, len(mp), self.rate_bytes): block = mp[off:off + self.rate_bytes] for i in range(lanes): v = struct.unpack_from("> (64 - n))) & 0xffff_ffff_ffff_ffff for rnd in range(12): # Theta c = [0] * 5 for x in range(5): c[x] = a[x] ^ a[x + 5] ^ a[x + 10] ^ a[x + 15] ^ a[x + 20] d = [0] * 5 for x in range(5): d[x] = c[(x - 1) % 5] ^ rol64(c[(x + 1) % 5], 1) for y in range(5): for x in range(5): a[x + 5 * y] ^= d[x] # Rho + Pi b = [0] * 25 for y in range(5): for x in range(5): idx = x + 5 * y rot = self.rho[idx] v = rol64(a[idx], rot) x2 = y y2 = (2 * x + 3 * y) % 5 b[x2 + 5 * y2] = v # Chi for y in range(5): row = [b[x + 5 * y] for x in range(5)] for x in range(5): a[x + 5 * y] = row[x] ^ ((~row[(x + 1) % 5]) & row[(x + 2) % 5]) # Iota a[0] ^= self.rc_last12[rnd] return class KangarooTwelve128(KangarooTwelveBase): rate_bytes = 168 # TurboSHAKE128 rate cv_len = 32 # KT128 chaining value length class KangarooTwelve256(KangarooTwelveBase): rate_bytes = 136 # TurboSHAKE256 rate cv_len = 64 # KT256 chaining value length class KMACBase: rho = ( 0x00, 0x01, 0x3e, 0x1c, 0x1b, 0x24, 0x2c, 0x06, 0x37, 0x14, 0x03, 0x0a, 0x2b, 0x19, 0x27, 0x29, 0x2d, 0x0f, 0x15, 0x08, 0x12, 0x02, 0x3d, 0x38, 0x0e, ) rc = ( 0x0000_0000_0000_0001, 0x0000_0000_0000_8082, 0x8000_0000_0000_808a, 0x8000_0000_8000_8000, 0x0000_0000_0000_808b, 0x0000_0000_8000_0001, 0x8000_0000_8000_8081, 0x8000_0000_0000_8009, 0x0000_0000_0000_008a, 0x0000_0000_0000_0088, 0x0000_0000_8000_8009, 0x0000_0000_8000_000a, 0x0000_0000_8000_808b, 0x8000_0000_0000_008b, 0x8000_0000_0000_8089, 0x8000_0000_0000_8003, 0x8000_0000_0000_8002, 0x8000_0000_0000_0080, 0x0000_0000_0000_800a, 0x8000_0000_8000_000a, 0x8000_0000_8000_8081, 0x8000_0000_0000_8080, 0x0000_0000_8000_0001, 0x8000_0000_8000_8008, ) def __init__(self, key=b"", data=b"", custom=b"", digest_bits=None): if not isinstance(key, (bytes, bytearray, memoryview)): raise TypeError("key must be bytes-like") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not isinstance(custom, (bytes, bytearray, memoryview)): raise TypeError("custom must be bytes-like") key = bytes(key) data = bytes(data) custom = bytes(custom) if digest_bits is not None: digest_size = digest_bits // 8 if digest_size < 1: raise ValueError("digest_size must be a positive integer") self.digest_size = digest_size self.key = key self.custom = custom self.buf = bytearray() self.msg_len = 0 # in bytes if data: self.update(data) return def copy(self): other = self.__class__(self.key, b"", self.custom, self.digest_size * 8) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) return self def finalize(self): return def digest(self): c = self.copy() c.finalize() return c.kmac(self.key, bytes(c.buf), c.digest_size * 8, c.custom) def hexdigest(self): return self.digest().hex() def left_encode(self, x): if not isinstance(x, int) or x < 0: raise ValueError("x must be a non-negative integer") n = 1 while (x >> (8 * n)) != 0: n += 1 out = bytes([n]) + x.to_bytes(n, "big") return out def right_encode(self, x): if not isinstance(x, int) or x < 0: raise ValueError("x must be a non-negative integer") n = 1 while (x >> (8 * n)) != 0: n += 1 out = x.to_bytes(n, "big") + bytes([n]) return out def encode_string(self, s): if not isinstance(s, (bytes, bytearray, memoryview)): raise TypeError("s must be bytes-like") s = bytes(s) out = self.left_encode(len(s) * 8) + s return out def bytepad(self, x, w): if not isinstance(x, (bytes, bytearray, memoryview)): raise TypeError("x must be bytes-like") if not isinstance(w, int) or w <= 0: raise ValueError("w must be a positive integer") x = bytes(x) z = self.left_encode(w) + x pad_len = (-len(z)) % w out = z + (b"\x00" * pad_len) return out def keccak_f1600(self, a): def rol64(x, n): x &= 0xffff_ffff_ffff_ffff n &= 63 if n == 0: return x return ((x << n) | (x >> (64 - n))) & 0xffff_ffff_ffff_ffff for rnd in range(24): # Theta c = [0] * 5 for x in range(5): c[x] = a[x] ^ a[x + 5] ^ a[x + 10] ^ a[x + 15] ^ a[x + 20] d = [0] * 5 for x in range(5): d[x] = c[(x - 1) % 5] ^ rol64(c[(x + 1) % 5], 1) for y in range(5): for x in range(5): a[x + 5 * y] ^= d[x] # Rho + Pi b = [0] * 25 for y in range(5): for x in range(5): idx = x + 5 * y rot = self.rho[idx] v = rol64(a[idx], rot) x2 = y y2 = (2 * x + 3 * y) % 5 b[x2 + 5 * y2] = v # Chi for y in range(5): row = [b[x + 5 * y] for x in range(5)] for x in range(5): a[x + 5 * y] = row[x] ^ ((~row[(x + 1) % 5]) & row[(x + 2) % 5]) # Iota a[0] ^= self.rc[rnd] return def sponge(self, data, outlen, rate, suffix): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not isinstance(outlen, int) or outlen < 0: raise ValueError("outlen must be a non-negative integer") if not isinstance(rate, int) or rate <= 0: raise ValueError("rate must be a positive integer") if not isinstance(suffix, int) or not (0 <= suffix <= 255): raise ValueError("suffix must be a byte") data = bytes(data) msg = bytearray(data) msg.append(suffix) while (len(msg) % rate) != (rate - 1): msg.append(0) msg.append(0x80) s = [0] * 25 rate_words = rate // 8 off = 0 while off < len(msg): block = msg[off:off + rate] for i in range(rate_words): lane = int.from_bytes(block[i * 8:(i + 1) * 8], "little") s[i] ^= lane self.keccak_f1600(s) off += rate out = bytearray() while len(out) < outlen: chunk = bytearray() for i in range(rate_words): chunk.extend(s[i].to_bytes(8, "little")) take = min(outlen - len(out), rate) out.extend(chunk[:take]) if len(out) < outlen: self.keccak_f1600(s) return bytes(out) def cshake(self, x, outlen_bytes, n, s): if not isinstance(x, (bytes, bytearray, memoryview)): raise TypeError("x must be bytes-like") if not isinstance(n, (bytes, bytearray, memoryview)): raise TypeError("n must be bytes-like") if not isinstance(s, (bytes, bytearray, memoryview)): raise TypeError("s must be bytes-like") x = bytes(x) n = bytes(n) s = bytes(s) if n == b"" and s == b"": return self.sponge(x, outlen_bytes, self.block_size, 31) prefix = self.bytepad(self.encode_string(n) + self.encode_string(s), self.block_size) return self.sponge(prefix + x, outlen_bytes, self.block_size, 4) def kmac(self, key, x, l_bits, custom): if not isinstance(key, (bytes, bytearray, memoryview)): raise TypeError("key must be bytes-like") if not isinstance(x, (bytes, bytearray, memoryview)): raise TypeError("x must be bytes-like") if not isinstance(custom, (bytes, bytearray, memoryview)): raise TypeError("custom must be bytes-like") if not isinstance(l_bits, int) or l_bits < 0: raise ValueError("l_bits must be a non-negative integer") if (l_bits % 8) != 0: raise ValueError("l_bits must be a multiple of 8") key = bytes(key) x = bytes(x) custom = bytes(custom) new_x = self.bytepad(self.encode_string(key), self.block_size) + x + self.right_encode(l_bits) outlen_bytes = l_bits // 8 return self.cshake(new_x, outlen_bytes, b"KMAC", custom) class KMAC128(KMACBase): block_size = 168 # rate for cshake_128/kmac_128 digest_size = 32 # 256 bits class KMAC256(KMACBase): block_size = 136 # rate for cshake_256/kmac_256 digest_size = 64 # 512 bits class TupleHashBase: rho = ( 0x00, 0x01, 0x3e, 0x1c, 0x1b, 0x24, 0x2c, 0x06, 0x37, 0x14, 0x03, 0x0a, 0x2b, 0x19, 0x27, 0x29, 0x2d, 0x0f, 0x15, 0x08, 0x12, 0x02, 0x3d, 0x38, 0x0e, ) rc = ( 0x0000_0000_0000_0001, 0x0000_0000_0000_8082, 0x8000_0000_0000_808a, 0x8000_0000_8000_8000, 0x0000_0000_0000_808b, 0x0000_0000_8000_0001, 0x8000_0000_8000_8081, 0x8000_0000_0000_8009, 0x0000_0000_0000_008a, 0x0000_0000_0000_0088, 0x0000_0000_8000_8009, 0x0000_0000_8000_000a, 0x0000_0000_8000_808b, 0x8000_0000_0000_008b, 0x8000_0000_0000_8089, 0x8000_0000_0000_8003, 0x8000_0000_0000_8002, 0x8000_0000_0000_0080, 0x0000_0000_0000_800a, 0x8000_0000_8000_000a, 0x8000_0000_8000_8081, 0x8000_0000_0000_8080, 0x0000_0000_8000_0001, 0x8000_0000_8000_8008, ) def __init__(self, data=b"", custom=b"", digest_bits=None): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not isinstance(custom, (bytes, bytearray, memoryview)): raise TypeError("custom must be bytes-like") self.suffix = 0x04 # cSHAKE domain separation suffix self.output_bits = digest_bits if digest_bits is not None: self.digest_size = digest_bits // 8 self.state = [0] * 25 # 25 lanes of 64-bit self.buf = bytearray() self.finalized = False self.custom = bytes(custom) # cSHAKE init: absorb bytepad(encode_string(N) || encode_string(S), rate) n = b"TupleHash" init = self.bytepad(self.encode_string(n) + self.encode_string(self.custom), self.rate) self.absorb(init) if data: self.update(bytes(data)) return def copy(self): other = self.__class__(b"", self.custom) other.rate = self.rate other.suffix = self.suffix other.output_bits = self.output_bits other.state = list(self.state) other.buf = bytearray(self.buf) other.finalized = self.finalized return other def update(self, data): if self.finalized: raise ValueError("cannot update after finalize") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) # TupleHash input is a tuple of strings: update(data) == append one element self.absorb(self.encode_string(data)) return self def digest(self): c = self.copy() c.finalize() out = c.squeeze(self.digest_size) return out def hexdigest(self): return self.digest().hex() def finalize(self): if self.finalized: return # Append right_encode(L) where L is output length in bits self.absorb(self.right_encode(self.output_bits)) # Apply cSHAKE padding and switch to squeezing self.pad_and_permute() self.finalized = True return def keccak_f1600(self, a): def rol64(x, n): x &= 0xffff_ffff_ffff_ffff n &= 63 if n == 0: return x return ((x << n) | (x >> (64 - n))) & 0xffff_ffff_ffff_ffff for rnd in range(24): # Theta c = [0] * 5 for x in range(5): c[x] = a[x] ^ a[x + 5] ^ a[x + 10] ^ a[x + 15] ^ a[x + 20] d = [0] * 5 for x in range(5): d[x] = c[(x - 1) % 5] ^ rol64(c[(x + 1) % 5], 1) for y in range(5): for x in range(5): a[x + 5 * y] ^= d[x] # Rho + Pi b = [0] * 25 for y in range(5): for x in range(5): idx = x + 5 * y rot = self.rho[idx] v = rol64(a[idx], rot) x2 = y y2 = (2 * x + 3 * y) % 5 b[x2 + 5 * y2] = v # Chi for y in range(5): row = [b[x + 5 * y] for x in range(5)] for x in range(5): a[x + 5 * y] = row[x] ^ ((~row[(x + 1) % 5]) & row[(x + 2) % 5]) # Iota a[0] ^= self.rc[rnd] return def absorb(self, data): if self.finalized: raise ValueError("cannot absorb after finalize") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.buf.extend(data) while len(self.buf) >= self.rate: block = bytes(self.buf[:self.rate]) del self.buf[:self.rate] self.xor_into_state(block) self.keccak_f1600(self.state) return def xor_into_state(self, block): # XOR block into state lanes (little-endian) lanes = self.rate // 8 for i in range(lanes): v = int.from_bytes(block[i * 8:(i + 1) * 8], "little") self.state[i] ^= v return def pad_and_permute(self): # pad10*1 with domain suffix (cSHAKE uses 0x04) self.buf.append(self.suffix) if len(self.buf) > self.rate: # should not happen, but keep strict raise ValueError("internal buffer overflow") if len(self.buf) == self.rate: # last byte already present; set MSB and absorb self.buf[self.rate - 1] |= 0x80 block = bytes(self.buf) self.buf.clear() self.xor_into_state(block) self.keccak_f1600(self.state) return while len(self.buf) < self.rate: self.buf.append(0x00) self.buf[self.rate - 1] |= 0x80 block = bytes(self.buf) self.buf.clear() self.xor_into_state(block) self.keccak_f1600(self.state) return def squeezeblock(self): # Extract rate bytes from state (little-endian) out = bytearray() lanes = self.rate // 8 for i in range(lanes): out.extend(self.state[i].to_bytes(8, "little")) return bytes(out) def squeeze(self, outlen): if not self.finalized: raise ValueError("must finalize before squeeze") if outlen < 0: raise ValueError("outlen must be non-negative") out = bytearray() while len(out) < outlen: block = self.squeezeblock() take = min(outlen - len(out), self.rate) out.extend(block[:take]) if len(out) < outlen: self.keccak_f1600(self.state) return bytes(out) def left_encode(self, x): if x < 0: raise ValueError("x must be non-negative") n = 1 t = x while t > 0xff: n += 1 t >>= 8 return bytes([n]) + x.to_bytes(n, "big") def right_encode(self, x): if x < 0: raise ValueError("x must be non-negative") n = 1 t = x while t > 0xff: n += 1 t >>= 8 return x.to_bytes(n, "big") + bytes([n]) def encode_string(self, s): if not isinstance(s, (bytes, bytearray, memoryview)): raise TypeError("s must be bytes-like") s = bytes(s) return self.left_encode(len(s) * 8) + s def bytepad(self, x, w): if w <= 0: raise ValueError("w must be positive") if not isinstance(x, (bytes, bytearray, memoryview)): raise TypeError("x must be bytes-like") x = bytes(x) z = bytearray() z.extend(self.left_encode(w)) z.extend(x) while (len(z) % w) != 0: z.append(0x00) return bytes(z) class TupleHash128(TupleHashBase): # TupleHash128 = cSHAKE128 with N="TupleHash" block_size = 168 # rate in bytes (Keccak[1600], r=1344) digest_size = 16 # 128 bits rate = 168 class TupleHash256(TupleHashBase): # TupleHash256 = cSHAKE256 with N="TupleHash" block_size = 136 # rate in bytes (Keccak[1600], r=1088) digest_size = 32 # 256 bits rate = 136 class TIGER: block_size = 64 sbox1 = ( 0x02aa_b17c_f7e9_0c5e, 0xac42_4b03_e243_a8ec, 0x72cd_5be3_0dd5_fcd3, 0x6d01_9b93_f6f9_7f3a, 0xcd99_78ff_d21f_9193, 0x7573_a1c9_7080_29e2, 0xb164_326b_922a_83c3, 0x4688_3eee_0491_5870, 0xeaac_e305_7103_ece6, 0xc541_69b8_08a3_535c, 0x4ce7_5491_8dde_c47c, 0x0aa2_f4df_dc0d_f40c, 0x10b7_6f18_a74d_befa, 0xc6cc_b623_5ad1_ab6a, 0x1372_6121_572f_e2ff, 0x1a48_8c6f_199d_921e, 0x4bc9_f9f4_da00_07ca, 0x26f5_e6f6_e852_41c7, 0x8590_79db_ea59_47b6, 0x4f18_85c5_c99e_8c92, 0xd78e_761e_a96f_864b, 0x8e36_428c_52b5_c17d, 0x69cf_6827_3730_63c1, 0xb607_c93d_9bb4_c56e, 0x7d82_0e76_0e76_b5ea, 0x645c_9cc6_f07f_dc42, 0xbf38_a078_2433_42e0, 0x5f6b_343c_9d2e_7d04, 0xf2c2_8aeb_600b_0ec6, 0x6c0e_d85f_7254_bcac, 0x7159_2281_a4db_4fe5, 0x1967_fa69_ce0f_ed9f, 0xfd52_93f8_b965_45db, 0xc879_e9d7_f2a7_600b, 0x8602_4892_0193_194e, 0xa4f9_533b_2d9c_c0b3, 0x9053_836c_1595_7613, 0xdb6d_cf8a_fc35_7bf1, 0x18be_ea7a_7a37_0f57, 0x0371_17ca_50b9_9066, 0x6ab3_0a97_7442_4a35, 0xf4e9_2f02_e325_249b, 0x7739_db07_061c_cae1, 0xd8f3_b49c_eca4_2a05, 0xbd56_be3f_5138_2f73, 0x45fa_ed58_43b0_bb28, 0x1c81_3d5c_11bf_1f83, 0x8af0_e4b6_d75f_a169, 0x33ee_18a4_87ad_9999, 0x3c26_e8ea_b1c9_4410, 0xb510_102b_c0a8_22f9, 0x141e_ef31_0ce6_123b, 0xfc65_b900_59dd_b154, 0xe015_8640_c5e0_e607, 0x884e_0798_26c3_a3cf, 0x930d_0d95_23c5_35fd, 0x3563_8d75_4e9a_2b00, 0x4085_fccf_4046_9dd5, 0xc4b1_7ad2_8be2_3a4c, 0xcab2_f0fc_6a3e_6a2e, 0x2860_971a_6b94_3fcd, 0x3dde_6ee2_12e3_0446, 0x6222_f32a_e017_65ae, 0x5d55_0bb5_4783_08fe, 0xa9ef_a98d_a0ed_a22a, 0xc351_a716_86c4_0da7, 0x1105_586d_9c86_7c84, 0xdcff_ee85_fda2_2853, 0xccfb_d026_2c5e_ef76, 0xbaf2_94cb_8990_d201, 0xe694_64f5_2afa_d975, 0x94b0_13af_df13_3e14, 0x06a7_d1a3_2823_c958, 0x6f95_fe51_30f6_1119, 0xd92a_b34e_462c_06c0, 0xed7b_de33_887c_71d2, 0x7974_6d6e_6518_393e, 0x5ba4_1938_5d71_3329, 0x7c1b_a6b9_48a9_7564, 0x3198_7c19_7bfd_ac67, 0xde6c_23c4_4b05_3d02, 0x581c_49fe_d002_d64d, 0xdd47_4d63_3826_1571, 0xaa45_46c3_e473_d062, 0x928f_ce34_9455_f860, 0x4816_1bba_caab_94d9, 0x6391_2430_770e_6f68, 0x6ec8_a5e6_02c6_641c, 0x8728_2515_337d_dd2b, 0x2cda_6b42_034b_701b, 0xb03d_37c1_81cb_096d, 0xe108_4382_66c7_1c6f, 0x2b31_80c7_eb51_b255, 0xdf92_b82f_96c0_8bbc, 0x5c68_c8c0_a632_f3ba, 0x5504_cc86_1c3d_0556, 0xabbf_a4e5_5fb2_6b8f, 0x4184_8b0a_b3ba_ceb4, 0xb334_a273_aa44_5d32, 0xbca6_96f0_a85a_d881, 0x24f6_ec65_b528_d56c, 0x0ce1_512e_90f4_524a, 0x4e9d_d79d_5506_d35a, 0x2589_05fa_c6ce_9779, 0x2019_295b_3e10_9b33, 0xf8a9_478b_73a0_54cc, 0x2924_f2f9_3441_7eb0, 0x3993_357d_536d_1bc4, 0x38a8_1ac2_1db6_ff8b, 0x47c4_fbf1_7d60_16bf, 0x1e0f_aadd_7667_e3f5, 0x7abc_ff62_938b_eb96, 0xa78d_ad94_8fc1_79c9, 0x8f1f_98b7_2911_e50d, 0x61e4_8eae_2712_1a91, 0x4d62_f7ad_3185_9808, 0xeceb_a345_ef5c_eaeb, 0xf5ce_b25e_bc96_84ce, 0xf633_e20c_b7f7_6221, 0xa32c_df06_ab82_93e4, 0x985a_202c_a5ee_2ca4, 0xcf0b_8447_cc8a_8fb1, 0x9f76_5244_9798_59a3, 0xa8d5_16b1_a124_0017, 0x0bd7_ba3e_bb5d_c726, 0xe54b_ca55_b86a_db39, 0x1d7a_3afd_6c47_8063, 0x519e_c608_e766_9edd, 0x0e57_15a2_d149_aa23, 0x177d_4571_848f_f194, 0xeeb5_5f32_4101_4c22, 0x0f5e_5ca1_3a6e_2ec2, 0x8029_927b_75f5_c361, 0xad13_9fab_c3d6_e436, 0x0d5d_f1a9_4ccf_402f, 0x3e8b_d948_bea5_dfc8, 0xa5a0_d357_bd3f_f77e, 0xa2d1_2e25_1f74_f645, 0x66fd_9e52_5e81_a082, 0x2e0c_90ce_7f68_7a49, 0xc2e8_bcbe_ba97_3bc5, 0x0000_01bc_e509_745f, 0x4237_77bb_e6da_b3d6, 0xd166_1c7e_aef0_6eb5, 0xa178_1f35_4daa_cfd8, 0x2d11_284a_2b16_affc, 0xf1fc_4f67_fa89_1d1f, 0x73ec_c25d_cb92_0ada, 0xae61_0c22_c2a1_2651, 0x96e0_a810_d356_b78a, 0x5a9a_381f_2fe7_870f, 0xd5ad_62ed_e94e_5530, 0xd225_e5e8_368d_1427, 0x6597_7b70_c7af_4631, 0x99f8_89b2_de39_d74f, 0x233f_30bf_54e1_d143, 0x9a96_75d3_d9a6_3c97, 0x5470_554f_f334_f9a8, 0x166a_cb74_4a4f_5688, 0x70c7_4caa_b2e4_aead, 0xf0d0_9164_6f29_4d12, 0x57b8_2a89_6840_31d1, 0xefd9_5a5a_61be_0b6b, 0x2fbd_12e9_69f2_f29a, 0x9bd3_7013_feff_9fe8, 0x3f9b_0404_d608_5a06, 0x4940_c1f3_166c_fe15, 0x0954_2c4d_cdf3_defb, 0xb4c5_2183_85cd_5ce3, 0xc935_b7dc_4462_a641, 0x3417_f8a6_8ed3_b63f, 0xb809_5929_5b21_5b40, 0xf99c_daef_3b8c_8572, 0x018c_0614_f8fc_b95d, 0x1b14_accd_1a3a_cdf3, 0x84d4_71f2_00bb_732d, 0xc1a3_110e_95e8_da16, 0x430a_7220_bf1a_82b8, 0xb77e_090d_39df_210e, 0x5ef4_bd9f_3cd0_5e9d, 0x9d4f_f6da_7e57_a444, 0xda1d_60e1_83d4_a5f8, 0xb287_c384_1799_8e47, 0xfe3e_dc12_1bb3_1886, 0xc7fe_3ccc_980c_cbef, 0xe46f_b590_189b_fd03, 0x3732_fd46_9a4c_57dc, 0x7ef7_00a0_7cf1_ad65, 0x59c6_4468_a31d_8859, 0x762f_b0b4_d45b_61f6, 0x155b_aed0_9904_7718, 0x6875_5e4c_3d50_baa6, 0xe921_4e7f_22d8_b4df, 0x2add_bf53_2eac_95f4, 0x32ae_3909_b4bd_0109, 0x834d_f537_b08e_3450, 0xfa20_9da8_4220_728d, 0x9e69_1d9b_9efe_23f7, 0x0446_d288_c4ae_8d7f, 0x7b4c_c524_e169_785b, 0x21d8_7f01_35ca_1385, 0xcebb_400f_137b_8aa5, 0x272e_2b66_5807_96be, 0x3612_2641_25c2_b0de, 0x0577_02bd_ad1e_fbb2, 0xd4ba_bb8e_acf8_4be9, 0x9158_3139_641b_c67b, 0x8bdc_2de0_8036_e024, 0x603c_8156_f49f_68ed, 0xf7d2_36f7_dbef_5111, 0x9727_c459_8ad2_1e80, 0xa08a_0896_670a_5fd7, 0xcb4a_8f43_09eb_a9cb, 0x81af_564b_0f70_36a1, 0xc0b9_9aa7_7819_9abd, 0x959f_1ec8_3fc8_e952, 0x8c50_5077_794a_81b9, 0x3aca_af8f_0563_38f0, 0x07b4_3f50_627a_6778, 0x4a44_ab49_f5ec_cc77, 0x3bc3_d6e4_b679_ee98, 0x9cc0_d4d1_cf14_108c, 0x4406_c00b_206b_c8a0, 0x82a1_8854_c8d7_2d89, 0x67e3_66b3_5c3c_432c, 0xb923_dd61_102b_37f2, 0x56ab_2779_d884_271d, 0xbe83_e1b0_ff15_25af, 0xfb7c_65d4_217e_49a9, 0x6bdb_e0e7_6d48_e7d4, 0x08df_8287_45d9_179e, 0x22ea_6a9a_dd53_bd34, 0xe36e_141c_5622_200a, 0x7f80_5d1b_8cb7_50ee, 0xafe5_c7a5_9f58_e837, 0xe27f_996a_4fb1_c23c, 0xd386_7dfb_0775_f0d0, 0xd0e6_73de_6e88_891a, 0x123a_eb9e_afb8_6c25, 0x30f1_d5d5_c145_b895, 0xbb43_4a2d_ee72_69e7, 0x78cb_67ec_f931_fa38, 0xf33b_0372_323b_bf9c, 0x52d6_6336_fb27_9c74, 0x505f_33ac_0afb_4eaa, 0xe8a5_cd99_a2cc_e187, 0x5349_7480_1e2d_30bb, 0x8d2d_5711_d587_6d90, 0x1f1a_4128_91bc_038e, 0xd6e2_e71d_82e5_6648, 0x7403_6c3a_4977_32b7, 0x89b6_7ed9_6361_f5ab, 0xffed_95d8_f1ea_02a2, 0xe72b_3bd6_1464_d43d, 0xa630_0f17_0bdc_4820, 0xebc1_8760_ed78_a77a, ) sbox2 = ( 0xe6a6_be5a_05a1_2138, 0xb5a1_22a5_b4f8_7c98, 0x563c_6089_140b_6990, 0x4c46_cb2e_391f_5dd5, 0xd932_addb_c9b7_9434, 0x08ea_70e4_2015_aff5, 0xd765_a667_3e47_8cf1, 0xc4fb_757e_ab27_8d99, 0xdf11_c686_2d6e_0692, 0xddeb_84f1_0d7f_3b16, 0x6f2e_f604_a665_ea04, 0x4a8e_0f0f_f0e0_dfb3, 0xa5ed_eef8_3dbc_ba51, 0xfc4f_0a2a_0ea4_371e, 0xe83e_1da8_5cb3_8429, 0xdc8f_f882_ba1b_1ce2, 0xcd45_505e_8353_e80d, 0x18d1_9a00_d4db_0717, 0x34a0_cfed_a5f3_8101, 0x0be7_7e51_8887_caf2, 0x1e34_1438_b3c4_5136, 0xe057_97f4_9089_ccf9, 0xffd2_3f9d_f259_1d14, 0x543d_da22_8595_c5cd, 0x661f_81fd_9905_2a33, 0x8736_e641_db0f_7b76, 0x1522_7725_418e_5307, 0xe25f_7f46_162e_b2fa, 0x48a8_b212_6c13_d9fe, 0xafdc_5417_92e7_6eea, 0x03d9_12bf_c6d1_898f, 0x31b1_aafa_1b83_f51b, 0xf1ac_2796_e42a_b7d9, 0x40a3_a7d7_fcd2_ebac, 0x1056_136d_0afb_bcc5, 0x7889_e1dd_9a6d_0c85, 0xd335_2578_2a79_74aa, 0xa7e2_5d09_078a_c09b, 0xbd41_38b3_eac6_edd0, 0x920a_bfbe_71eb_9e70, 0xa2a5_d0f5_4fc2_625c, 0xc054_e36b_0b12_90a3, 0xf6dd_59ff_62fe_932b, 0x3537_3545_11a8_ac7d, 0xca84_5e91_72fa_dcd4, 0x84f8_2b60_329d_20dc, 0x79c6_2ce1_cd67_2f18, 0x8b09_a2ad_d124_642c, 0xd0c1_e96a_19d9_e726, 0x5a78_6a9b_4ba9_500c, 0x0e02_0336_634c_43f3, 0xc17b_474a_eb66_d822, 0x6a73_1ae3_ec9b_aac2, 0x8226_667a_e084_0258, 0x67d4_5676_91ca_eca5, 0x1d94_155c_4875_adb5, 0x6d00_fd98_5b81_3fdf, 0x5128_6efc_b774_cd06, 0x5e88_3447_1fa7_44af, 0xf72c_a0ae_e761_ae2e, 0xbe40_e4cd_aee8_e09a, 0xe997_0bbb_5118_f665, 0x726e_4beb_33df_1964, 0x703b_0007_2919_9762, 0x4631_d816_f5ef_30a7, 0xb880_b5b5_1504_a6be, 0x6417_93c3_7ed8_4b6c, 0x7b21_ed77_f6e9_7d96, 0x7763_0631_2ef9_6b73, 0xae52_8948_e86f_f3f4, 0x53db_d7f2_86a3_f8f8, 0x16ca_dce7_4cfc_1063, 0x005c_19bd_fa52_c6dd, 0x6886_8f5d_64d4_6ad3, 0x3a9d_512c_cf1e_186a, 0x367e_62c2_3856_60ae, 0xe359_e7ea_77dc_b1d7, 0x526c_0773_749a_be6e, 0x735a_e5f9_d09f_734b, 0x493f_c7cc_8a55_8ba8, 0xb0b9_c153_3041_ab45, 0x3219_58ba_470a_59bd, 0x852d_b00b_5f46_c393, 0x9120_9b2b_d336_b0e5, 0x6e60_4f7d_659e_f19f, 0xb99a_8ae2_782c_cb24, 0xccf5_2ab6_c814_c4c7, 0x4727_d9af_be11_727b, 0x7e95_0d0c_0121_b34d, 0x756f_4356_70ad_471f, 0xf5ad_d442_615a_6849, 0x4e87_e099_80b9_957a, 0x2acf_a1df_50ae_e355, 0xd898_263a_fd2f_d556, 0xc8f4_924d_d80c_8fd6, 0xcf99_ca3d_754a_173a, 0xfe47_7bac_af91_bf3c, 0xed53_71f6_d690_c12d, 0x831a_5c28_5e68_7094, 0xc5d3_c90a_3708_a0a4, 0x0f7f_9037_17d0_6580, 0x19f9_bb13_b8fd_f27f, 0xb1bd_6f1b_4d50_2843, 0x1c76_1ba3_8fff_4012, 0x0d15_30c4_e2e2_1f3b, 0x8943_ce69_a737_2c8a, 0xe518_4e11_feb5_ce66, 0x618b_db80_bd73_6621, 0x7d29_bad6_8b57_4d0b, 0x81bb_613e_25e6_fe5b, 0x071c_9c10_bc07_913f, 0xc7be_eb79_09ac_2d97, 0xc3e5_8d35_3bc5_d757, 0xeb01_7892_f38f_61e8, 0xd4ef_fb9c_9b1c_c21a, 0x9972_7d26_f494_f7ab, 0xa3e0_63a2_956b_3e03, 0x9d4a_8b9a_4aa0_9c30, 0x3f6a_b7d5_0009_0fb4, 0x9cc0_f2a0_5726_8ac0, 0x3dee_9d2d_edbf_42d1, 0x330f_49c8_7960_a972, 0xc6b2_7202_8742_1b41, 0x0ac5_9ec0_7c00_369c, 0xef4e_ac49_cb35_3425, 0xf450_244e_ef01_29d8, 0x8acc_46e5_caf4_deb6, 0x2ffe_ab63_9892_63f7, 0x8f7c_b9fe_5d7a_4578, 0x5bd8_f764_4e63_4635, 0x427a_7315_bf2d_c900, 0x17d0_c4aa_2125_261c, 0x3992_486c_9351_8e50, 0xb4cb_fee0_a2d7_d4c3, 0x7c75_d620_2c5d_dd8d, 0xdbc2_95d8_e35b_6c61, 0x60b3_69d3_0203_2b19, 0xce42_685f_dce4_4132, 0x06f3_ddb9_ddf6_5610, 0x8ea4_d21d_b5e1_48f0, 0x20b0_fce6_2fcd_496f, 0x2c1b_9123_58b0_ee31, 0xb283_17b8_18f5_a308, 0xa89c_1e18_9ca6_d2cf, 0x0c6b_1857_6aaa_dbc8, 0xb65d_eaa9_1299_fae3, 0xfb2b_794b_7f10_27e7, 0x04e4_317f_443b_5beb, 0x4b85_2d32_5939_d0a6, 0xd5ae_6bee_fb20_7ffc, 0x3096_82b2_81c7_d374, 0xbae3_09a1_94c3_b475, 0x8cc3_f97b_13b4_9f05, 0x98a9_422f_f829_3967, 0x244b_16b0_1076_ff7c, 0xf8bf_571c_663d_67ee, 0x1f0d_6758_eee3_0da1, 0xc9b6_11d9_7ade_b9b7, 0xb7af_d588_7b6c_57a2, 0x6290_ae84_6b98_4fe1, 0x94df_4cde_acc1_a5fd, 0x058a_5bd1_c548_3aff, 0x6316_6cc1_42ba_3c37, 0x8db8_526e_b2f7_6f40, 0xe108_8003_6f0d_6d4e, 0x9e05_23c9_971d_311d, 0x45ec_2824_cc7c_d691, 0x575b_8359_e623_82c9, 0xfa9e_400d_c488_9995, 0xd182_3ecb_4572_1568, 0xdafd_983b_8206_082f, 0xaa7d_2908_2386_a8cb, 0x269f_cd44_03b8_7588, 0x1b91_f5f7_28bd_d1e0, 0xe466_9f39_0402_01f6, 0x7a1d_7c21_8cf0_4ade, 0x6562_3c29_d79c_e5ce, 0x2368_4490_96c0_0bb1, 0xab9b_f187_9da5_03ba, 0xbc23_ecb1_a458_058e, 0x9a58_df01_bb40_1ecc, 0xa070_e868_a85f_143d, 0x4ff1_8830_7df2_239e, 0x14d5_65b4_1a64_1183, 0xee13_3374_5270_1602, 0x950e_3dcf_3f28_5e09, 0x5993_0254_b9c8_0953, 0x3bf2_9940_8930_da6d, 0xa955_943f_5369_1387, 0xa15e_deca_a9cb_8784, 0x2914_2127_352b_e9a0, 0x76f0_371f_ff4e_7afb, 0x0239_f450_274f_2228, 0xbb07_3af0_1d5e_868b, 0xbfc8_0571_c10e_96c1, 0xd267_0885_6822_2e23, 0x9671_a3d4_8e80_b5b0, 0x55b5_d38a_e193_bb81, 0x693a_e2d0_a18b_04b8, 0x5c48_b4ec_add5_335f, 0xfd74_3b19_4916_a1ca, 0x2577_0181_34be_98c4, 0xe779_87e8_3c54_a4ad, 0x28e1_1014_da33_e1b9, 0x270c_c59e_226a_a213, 0x7149_5f75_6d1a_5f60, 0x9be8_53fb_60af_ef77, 0xadc7_86a7_f744_3dbf, 0x0904_4561_73b2_9a82, 0x58bc_7a66_c232_bd5e, 0xf306_558c_673a_c8b2, 0x41f6_39c6_b6c9_772a, 0x216d_efe9_9fda_35da, 0x1164_0cc7_1c7b_e615, 0x93c4_3694_565c_5527, 0xea03_8e62_4677_7839, 0xf9ab_f3ce_5a3e_2469, 0x741e_768d_0fd3_12d2, 0x0144_b883_ced6_52c6, 0xc20b_5a5b_a33f_8552, 0x1ae6_9633_c343_5a9d, 0x97a2_8ca4_088c_fdec, 0x8824_a43c_1e96_f420, 0x3761_2fa6_6eee_a746, 0x6b4c_b165_f9cf_0e5a, 0x43aa_1c06_a0ab_fb4a, 0x7f4d_c26f_f162_796b, 0x6cba_cc8e_54ed_9b0f, 0xa6b7_ffef_d2bb_253e, 0x2e25_bc95_b0a2_9d4f, 0x86d6_a58b_def1_388c, 0xded7_4ac5_76b6_f054, 0x8030_bdbc_2b45_805d, 0x3c81_af70_e94d_9289, 0x3eff_6dda_9e31_00db, 0xb38d_c39f_dfcc_8847, 0x1238_8552_8d17_b87e, 0xf2da_0ed2_40b1_b642, 0x44ce_fadc_d54b_f9a9, 0x1312_200e_433c_7ee6, 0x9ffc_c84f_3a78_c748, 0xf0cd_1f72_2485_76bb, 0xec69_7405_3638_cfe4, 0x2ba7_b67c_0cec_4e4c, 0xac2f_4df3_e5ce_32ed, 0xcb33_d143_26ea_4c11, 0xa4e9_044c_c77e_58bc, 0x5f51_3293_d934_fcef, 0x5dc9_6455_06e5_5444, 0x50de_418f_317d_e40a, 0x388c_b31a_69dd_e259, 0x2db4_a834_5582_0a86, 0x9010_a91e_8471_1ae9, 0x4df7_f0b7_b149_8371, 0xd62a_2eab_c097_7179, 0x22fa_c097_aa8d_5c0e, ) sbox3 = ( 0xf49f_cc2f_f1da_f39b, 0x487f_d5c6_6ff2_9281, 0xe8a3_0667_fcdc_a83f, 0x2c9b_4be3_d2fc_ce63, 0xda3f_f74b_93fb_bbc2, 0x2fa1_65d2_fe70_ba66, 0xa103_e279_970e_93d4, 0xbecd_ec77_b0e4_5e71, 0xcfb4_1e72_3985_e497, 0xb70a_aa02_5ef7_5017, 0xd423_09f0_3840_b8e0, 0x8efc_1ad0_3589_8579, 0x96c6_920b_e2b2_abc5, 0x66af_4163_375a_9172, 0x2174_abdc_ca71_27fb, 0xb33c_cea6_4a72_ff41, 0xf04a_4933_0830_66a5, 0x8d97_0acd_d728_9af5, 0x8f96_e8e0_31c8_c25e, 0xf3fe_c022_7687_5d47, 0xec7b_f310_0561_90dd, 0xf5ad_b0ae_bb0f_1491, 0x9b50_f885_0fd5_8892, 0x4975_4883_58b7_4de8, 0xa335_4ff6_9153_1c61, 0x0702_bbe4_81d2_c6ee, 0x89fb_2405_7ded_ed98, 0xac30_7513_8596_e902, 0x1d2d_3580_1727_72ed, 0xeb73_8fc2_8e6b_c30d, 0x5854_ef8f_6304_4326, 0x9e5c_5232_5add_3bbe, 0x90aa_53cf_325c_4623, 0xc1d2_4d51_349d_d067, 0x2051_cfee_a69e_a624, 0x1322_0f0a_862e_7e4f, 0xce39_3994_04e0_4864, 0xd9c4_2ca4_7086_fcb7, 0x685a_d223_8a03_e7cc, 0x0664_84b2_ab2f_f1db, 0xfe9d_5d70_efbf_79ec, 0x5b13_b9dd_9c48_1854, 0x15f0_d475_ed15_09ad, 0x0beb_cd06_0ec7_9851, 0xd58c_6791_183a_b7f8, 0xd118_7c50_52f3_eee4, 0xc95d_1192_e54e_82ff, 0x86ee_a14c_b9ac_6ca2, 0x3485_beb1_5367_7d5d, 0xdd19_1d78_1f8c_492a, 0xf608_66ba_a784_ebf9, 0x518f_643b_a2d0_8c74, 0x8852_e956_e108_7c22, 0xa768_cb8d_c410_ae8d, 0x3804_7726_bfec_8e1a, 0xa677_38b4_cd3b_45aa, 0xad16_691c_ec0d_de19, 0xc6d4_3193_8046_2e07, 0xc5a5_876d_0ba6_1938, 0x16b9_fa1f_a58f_d840, 0x188a_b117_3ca7_4f18, 0xabda_2f98_c99c_021f, 0x3e05_80ab_134a_e816, 0x5f3b_05b7_7364_5abb, 0x2501_a2be_5575_f2f6, 0x1b2f_7400_4e7e_8ba9, 0x1cd7_5803_71e8_d953, 0x7f6e_d895_6276_4e30, 0xb159_26ff_596f_003d, 0x9f65_293d_a8c5_d6b9, 0x6ece_f04d_d690_f84c, 0x4782_275f_ff33_af88, 0xe414_3308_3f82_0801, 0xfd0d_fe40_9a1a_f9b5, 0x4325_a334_2cdb_396b, 0x8ae7_7e62_b301_b252, 0xc36f_9e9f_6655_615a, 0x8545_5a2d_92d3_2c09, 0xf2c7_dea9_4947_7485, 0x63cf_b4c1_33a3_9eba, 0x83b0_40cc_6ebc_5462, 0x3b94_54c8_fdb3_26b0, 0x56f5_6a9e_87ff_d78c, 0x2dc2_940d_99f4_2bc6, 0x98f7_df09_6b09_6e2d, 0x19a6_e01e_3ad8_52bf, 0x42a9_9ccb_dbd4_b40b, 0xa599_98af_45e9_c559, 0x3662_95e8_07d9_3186, 0x6b48_181b_faa1_f773, 0x1fec_57e2_157a_0a1d, 0x4667_446a_f620_1ad5, 0xe615_ebca_cfb0_f075, 0xb8f3_1f4f_6829_0778, 0x2271_3ed6_ce22_d11e, 0x3057_c1a7_2ec3_c93b, 0xcb46_acc3_7c3f_1f2f, 0xdbb8_93fd_02aa_f50e, 0x331f_d92e_600b_9fcf, 0xa498_f961_48ea_3ad6, 0xa8d8_426e_8b6a_83ea, 0xa089_b274_b773_5cdc, 0x87f6_b373_1e52_4a11, 0x1188_08e5_cbc9_6749, 0x9906_e4c7_b19b_d394, 0xafed_7f7e_9b24_a20c, 0x6509_eade_eb36_44a7, 0x6c1e_f1d3_e8ef_0ede, 0xb9c9_7d43_e979_8fb4, 0xa2f2_d784_740c_28a3, 0x7b84_9647_6197_566f, 0x7a5b_e3e6_b65f_069d, 0xf963_30ed_78be_6f10, 0xeee6_0de7_7a07_6a15, 0x2b4b_ee4a_a08b_9bd0, 0x6a56_a63e_c7b8_894e, 0x0212_1359_ba34_fef4, 0x4cbf_99f8_2837_03fc, 0x3980_7135_0caf_30c8, 0xd0a7_7a89_f017_687a, 0xf1c1_a9eb_9e42_3569, 0x8c79_7628_2dee_8199, 0x5d17_37a5_dd1f_7abd, 0x4f53_433c_09a9_fa80, 0xfa8b_0c53_df7c_a1d9, 0x3fd9_dcbc_886c_cb77, 0xc040_917c_a91b_4720, 0x7dd0_0142_f9d1_dcdf, 0x8476_fc1d_4f38_7b58, 0x23f8_e7c5_f331_6503, 0x032a_2244_e7e3_7339, 0x5c87_a5d7_50f5_a74b, 0x082b_4cc4_3698_992e, 0xdf91_7bec_b858_f63c, 0x3270_b8fc_5bf8_6dda, 0x10ae_72bb_29b5_dd76, 0x576a_c94e_7700_362b, 0x1ad1_12da_c61e_fb8f, 0x691b_c30e_c5fa_a427, 0xff24_6311_cc32_7143, 0x3142_368e_30e5_3206, 0x7138_0e31_e02c_a396, 0x958d_5c96_0aad_76f1, 0xf8d6_f430_c16d_a536, 0xc8ff_d13f_1be7_e1d2, 0x7578_ae66_004d_dbe1, 0x0583_3f01_067b_e646, 0xbb34_b5ad_3bfe_586d, 0x095f_34c9_a12b_97f0, 0x247a_b645_25d6_0ca8, 0xdcdb_c6f3_0174_77d1, 0x4a2e_14d4_deca_d24d, 0xbdb5_e6d9_be0a_1eeb, 0x2a7e_70f7_7943_01ab, 0xdef4_2d8a_2705_40fd, 0x0107_8ec0_a34c_22c1, 0xe5de_511a_f4c1_6387, 0x7ebb_3a52_bd9a_330a, 0x7769_7857_aa7d_6435, 0x004e_8316_03ae_4c32, 0xe7a2_1020_ad78_e312, 0x9d41_a70c_6ab4_20f2, 0x28e0_6c18_ea11_41e6, 0xd2b2_8cbd_984f_6b28, 0x26b7_5f6c_446e_9d83, 0xba47_568c_4d41_8d7f, 0xd80b_adbf_e618_3d8e, 0x0e20_6d7f_5f16_6044, 0xe258_a439_11cb_ca3e, 0x723a_1746_b21d_c0bc, 0xc7ca_a854_f5d7_cdd3, 0x7cac_3288_3d26_1d9c, 0x7690_c264_23ba_942c, 0x17e5_5524_4780_42b8, 0xe0be_4776_56a2_389f, 0x4d28_9b5e_67ab_2da0, 0x4486_2b9c_8fbb_fd31, 0xb47c_c804_9d14_1365, 0x822c_1b36_2b91_c793, 0x4eb1_4655_fb13_dfd8, 0x1ecb_ba07_14e2_a97b, 0x6143_459d_5cde_5f14, 0x53a8_fbf1_d5f0_ac89, 0x97ea_04d8_1c5e_5b00, 0x6221_81a8_d4fd_b3f3, 0xe9bc_d341_572a_1208, 0x1411_2586_43cc_e58a, 0x9144_c5fe_a4c6_e0a4, 0x0d33_d065_65cf_620f, 0x54a4_8d48_9f21_9ca1, 0xc43e_5eac_6d63_c821, 0xa972_8b3a_7277_0daf, 0xd793_4e7b_20df_87ef, 0xe355_03b6_1a3e_86e5, 0xcae3_21fb_c819_d504, 0x129a_50b3_ac60_bfa6, 0xcd5e_68ea_7e9f_b6c3, 0xb01c_9019_9483_b1c7, 0x3de9_3cd5_c295_376c, 0xaed5_2edf_2ab9_ad13, 0x2e60_f512_c0a0_7884, 0xbc3d_86a3_e362_10c9, 0x3526_9d9b_1639_51ce, 0x0c7d_6e2a_d0cd_b5fa, 0x59e8_6297_d87f_5733, 0x298e_f221_898d_b0e7, 0x5500_0029_d1a5_aa7e, 0x8bc0_8ae1_b506_1b45, 0xc2c3_1c2b_6c92_703a, 0x94cc_596b_af25_ef42, 0x0a1d_73db_2254_0456, 0x04b6_a0f9_d9c4_179a, 0xeffd_afa2_ae3d_3c60, 0xf7c8_075b_b494_96c4, 0x9cc5_c714_1d1c_d4e3, 0x78bd_1638_218e_5534, 0xb2f1_1568_f850_246a, 0xedfa_bcfa_9502_bc29, 0x796c_e5f2_da23_051b, 0xaae1_28b0_dc93_537c, 0x3a49_3da0_ee4b_29ae, 0xb5df_6b2c_4168_95d7, 0xfcab_bd25_122d_7f37, 0x7081_0b58_105d_c4b1, 0xe10f_dd37_f788_2a90, 0x524d_cab5_518a_3f5c, 0x3c9e_8587_8451_255b, 0x4029_8281_19bd_34e2, 0x74a0_5b6f_5d3c_eccb, 0xb610_0215_42e1_3eca, 0x0ff9_79d1_2f59_e2ac, 0x6037_da27_e4f9_cc50, 0x5e92_975a_0df1_847d, 0xd66d_e190_d3e6_23fe, 0x5032_d6b8_7b56_8048, 0x9a36_b7ce_8235_216e, 0x8027_2a7a_24f6_4b4a, 0x93ef_ed8b_8c69_16f7, 0x37dd_bff4_4cce_1555, 0x4b95_db5d_4b99_bd25, 0x92d3_fda1_6981_2fc0, 0xfb1a_4a9a_9066_0bb6, 0x730c_1969_46a4_b9b2, 0x81e2_89aa_7f49_da68, 0x6466_9a0f_83b1_a05f, 0x27b3_ff7d_9644_f48b, 0xcc6b_615c_8db6_75b3, 0x674f_20b9_bceb_be95, 0x6f31_2382_7565_5982, 0x5ae4_8871_3e45_cf05, 0xbf61_9f99_54c2_1157, 0xeaba_c460_40a8_eae9, 0x454c_6fe9_f2c0_c1cd, 0x419c_f649_6412_691c, 0xd3dc_3bef_265b_0f70, 0x6d0e_60f5_c357_8a9e, ) sbox4 = ( 0x5b0e_6085_2632_3c55, 0x1a46_c1a9_fa1b_59f5, 0xa9e2_45a1_7c4c_8ffa, 0x65ca_5159_db29_55d7, 0x05db_0a76_ce35_afc2, 0x81ea_c77e_a911_3d45, 0x528e_f88a_b6ac_0a0d, 0xa09e_a253_597b_e3ff, 0x430d_dfb3_ac48_cd56, 0xc4b3_a67a_f45c_e46f, 0x4ece_cfd8_fbe2_d05e, 0x3ef5_6f10_b399_35f0, 0x0b22_d682_9cd6_19c6, 0x17fd_460a_74df_2069, 0x6cf8_cc8e_8510_ed40, 0xd6c8_24bf_3a6e_caa7, 0x6124_3d58_1a81_7049, 0x048b_acb6_bbc1_63a2, 0xd9a3_8ac2_7d44_cc32, 0x7fdd_ff5b_aaf4_10ab, 0xad6d_495a_a804_824b, 0xe1a6_a74f_2d8c_9f94, 0xd4f7_8512_35de_e8e3, 0xfd4b_7f88_6540_d893, 0x247c_2004_2aa4_bfda, 0x096e_a1c5_17d1_327c, 0xd569_66b4_361a_6685, 0x277d_a5c3_1221_057d, 0x94d5_9893_a43a_cff7, 0x64f0_c51c_cdc0_2281, 0x3d33_bcc4_ff61_89db, 0xe005_cb18_4ce6_6af1, 0xff5c_cd1d_1db9_9bea, 0xb0b8_54a7_fe42_980f, 0x7bd4_6a6a_718d_4b9f, 0xd10f_a8cc_22a5_fd8c, 0xd314_8495_2be4_bd31, 0xc7fa_975f_cb24_3847, 0x4886_ed1e_5846_c407, 0x28cd_db79_1eb7_0b04, 0xc2b0_0be2_f573_417f, 0x5c95_9045_2180_f877, 0x7a6b_ddff_f370_eb00, 0xce50_9e38_d6d9_d6a4, 0xebeb_0f00_647f_a702, 0x1dcc_06cf_7660_6f06, 0xe4d9_f28b_a286_ff0a, 0xd85a_305d_c918_c262, 0x475b_1d87_3222_5f54, 0x2d4f_b516_68cc_b5fe, 0xa679_b9d9_d72b_ba20, 0x5384_1c0d_912d_43a5, 0x3b7e_aa48_bf12_a4e8, 0x781e_0e47_f22f_1ddf, 0xeff2_0ce6_0ab5_0973, 0x20d2_61d1_9dff_b742, 0x16a1_2b03_062a_2e39, 0x1960_eb22_3965_0495, 0x251c_16fe_d50e_b8b8, 0x9ac0_c330_f826_016e, 0xed15_2665_953e_7671, 0x02d6_3194_a636_9570, 0x5074_f083_94b1_c987, 0x70ba_598c_90b2_5ce1, 0x794a_1581_0b97_42f6, 0x0d59_25e9_fcaf_8c6c, 0x3067_716c_d868_744e, 0x910a_b077_e8d7_731b, 0x6a61_bbdb_5ac4_2f61, 0x9351_3efb_f085_1567, 0xf494_724b_9e83_e9d5, 0xe887_e198_5c09_648d, 0x34b1_d3c6_7537_0cfd, 0xdc35_e433_bc0d_255d, 0xd0aa_b842_3413_1be0, 0x0804_2a50_b48b_7eaf, 0x9997_c4ee_44a3_ab35, 0x829a_7b49_2017_99d0, 0x263b_8307_b7c5_4441, 0x752f_95f4_fd6a_6ca6, 0x9272_1740_2c08_c6e5, 0x2a8a_b754_a795_d9ee, 0xa442_f755_2f72_943d, 0x2c31_334e_1978_1208, 0x4fa9_8d7c_eaee_6291, 0x55c3_862f_665d_b309, 0xbd06_1017_5d53_b1f3, 0x46fe_6cb8_4041_3f27, 0x3fe0_3792_df0c_fa59, 0xcfe7_0037_2eb8_5e8f, 0xa7be_29e7_adbc_e118, 0xe544_ee5c_de84_31dd, 0x8a78_1b1b_41f1_873e, 0xa5c9_4c78_a0d2_f0e7, 0x3941_2e28_77b6_0728, 0xa126_5ef3_afc9_a62c, 0xbcc2_770c_6a25_06c5, 0x3ab6_6dd5_dce1_ce12, 0xe654_99d0_4a67_5b37, 0x7d8f_5234_81bf_d216, 0x0f6f_64fc_ec15_f389, 0x74ef_be61_8b5b_13c8, 0xacdc_82b7_1427_3e1d, 0xdd40_bfe0_0319_9d17, 0x37e9_9257_e7e0_61f8, 0xfa52_6269_0477_5aaa, 0x8bbb_f63a_463d_56f9, 0xf001_3f15_43a2_6e64, 0xa830_7e9f_879e_c898, 0xcc4c_27a4_1501_77cc, 0x1b43_2f2c_ca1d_3348, 0xde1d_1f8f_9f6f_a013, 0x6066_02a0_47a7_ddd6, 0xd237_ab64_cc1c_b2c7, 0x9b93_8e72_25fc_d1d3, 0xec4e_0370_8e0f_f476, 0xfeb2_fbda_3d03_c12d, 0xae0b_ced2_ee43_889a, 0x22cb_8923_ebfb_4f43, 0x6936_0d01_3cf7_396d, 0x855e_3602_d2d4_e022, 0x0738_05ba_d01f_784c, 0x33e1_7a13_3852_f546, 0xdf48_7405_8ac7_b638, 0xba92_b29c_678a_a14a, 0x0ce8_9fc7_6cfa_adcd, 0x5f9d_4e09_0833_9e34, 0xf1af_e929_1f59_23b9, 0x6e34_80f6_0f4a_265f, 0xeebf_3a2a_b29b_841c, 0xe219_38a8_8f91_b4ad, 0x57df_eff8_45c6_d3c3, 0x2f00_6b0b_f62c_aaf2, 0x62f4_79ef_6f75_ee78, 0x11a5_5ad4_1c89_16a9, 0xf229_d290_84fe_d453, 0x42f1_c27b_16b0_00e6, 0x2b1f_7674_9823_c074, 0x4b76_eca3_c274_5360, 0x8c98_f463_b916_91bd, 0x14bc_c93c_f1ad_e66a, 0x8885_213e_6d45_8397, 0x8e17_7df0_274d_4711, 0xb49b_73b5_503f_2951, 0x1016_8168_c3f9_6b6b, 0x0e3d_963b_63ca_b0ae, 0x8dfc_4b56_55a1_db14, 0xf789_f135_6e14_de5c, 0x683e_68af_4e51_dac1, 0xc9a8_4f9d_8d4b_0fd9, 0x3691_e03f_52a0_f9d1, 0x5ed8_6e46_e187_8e80, 0x3c71_1a0e_99d0_7150, 0x5a08_65b2_0c4e_9310, 0x56fb_fc1f_e4f0_682e, 0xea8d_5de3_105e_df9b, 0x71ab_fdb1_2379_187a, 0x2eb9_9de1_bee7_7b9c, 0x21ec_c0ea_33cf_4523, 0x59a4_d752_1805_c7a1, 0x3896_f5eb_56ae_7c72, 0xaa63_8f3d_b18f_75dc, 0x9f39_358d_abe9_808e, 0xb7de_fa91_c00b_72ac, 0x6b55_41fd_6249_2d92, 0x6dc6_dee8_f92e_4d5b, 0x353f_57ab_c4be_ea7e, 0x7357_69d6_da56_90ce, 0x0a23_4aa6_4239_1484, 0xf6f9_5080_28f8_0d9d, 0xb8e3_19a2_7ab3_f215, 0x31ad_9c11_5134_1a4d, 0x773c_22a5_7bef_5805, 0x45c7_561a_0796_8633, 0xf913_da9e_249d_be36, 0xda65_2d9b_78a6_4c68, 0x4c27_a97f_3bc3_34ef, 0x7662_1220_e66b_17f4, 0x9677_4389_9acd_7d0b, 0xf3ee_5bca_e0ed_6782, 0x409f_7536_00c8_79fc, 0x06d0_9a39_b592_6db6, 0x6f83_aeb0_317a_c588, 0x01e6_ca4a_8638_1f21, 0x66ff_3462_d19f_3025, 0x7220_7c24_ddfd_3bfb, 0x4af6_b6d3_e2ec_e2eb, 0x9c99_4dbe_c7ea_08de, 0x49ac_e597_b09a_8bc4, 0xb38c_4766_cf07_97ba, 0x131b_9373_c57c_2a75, 0xb182_2cce_6193_1e58, 0x9d75_55b9_09ba_1c0c, 0x127f_afdd_937d_11d2, 0x29da_3bad_c66d_92e4, 0xa2c1_d571_54c2_ecbc, 0x58c5_134d_82f6_fe24, 0x1c3a_e351_5b62_274f, 0xe907_c82e_01cb_8126, 0xf8ed_0919_13e3_7fcb, 0x3249_d8f9_c800_46c9, 0x80cf_9bed_e388_fb63, 0x1881_539a_116c_f19e, 0x5103_f3f7_6bd5_2457, 0x15b7_e6f5_ae47_f7a8, 0xdbd7_c6de_d47e_9ccf, 0x44e5_5c41_0228_bb1a, 0xb647_d425_5edb_4e99, 0x5d11_882b_b8aa_fc30, 0xf509_8bbb_29d3_212a, 0x8fb5_ea14_e902_96b3, 0x677b_9421_57dd_025a, 0xfb58_e7c0_a390_acb5, 0x89d3_674c_83bd_4a01, 0x9e2d_a4df_4bf3_b93b, 0xfcc4_1e32_8cab_4829, 0x03f3_8c96_ba58_2c52, 0xcad1_bdbd_7fd8_5db2, 0xbbb4_42c1_6082_ae83, 0xb95f_e86b_a5da_9ab0, 0xb22e_0467_3771_a93f, 0x8453_58c9_4931_52d8, 0xbe2a_4886_97b4_541e, 0x95a2_dc2d_d38e_6966, 0xc02c_11ac_923c_852b, 0x2388_b199_0df2_a87b, 0x7c80_08fa_1b4f_37be, 0x1f70_d0c8_4d54_e503, 0x5490_adec_7ece_57d4, 0x002b_3c27_d906_3a3a, 0x7eae_a384_8030_a2bf, 0xc602_326d_ed20_03c0, 0x83a7_287d_69a9_4086, 0xc57a_5fcb_30f5_7a8a, 0xb568_44e4_79eb_e779, 0xa373_b40f_05dc_bce9, 0xd71a_786e_8857_0ee2, 0x879c_bacd_bde8_f6a0, 0x976a_d1bc_c164_a32f, 0xab21_e25e_9666_d78b, 0x9010_63aa_e5e5_c33c, 0x9818_b344_4869_8d90, 0xe364_87ae_3e1e_8abb, 0xafbd_f931_893b_dcb4, 0x6345_a0dc_5fbb_d519, 0x8628_fe26_9b94_65ca, 0x1e5d_0160_3f9c_51ec, 0x4de4_4006_a150_49b7, 0xbf6c_70e5_f776_cbb1, 0x4112_18f2_ef55_2bed, 0xcb0c_0708_705a_36a3, 0xe74d_1475_4f98_6044, 0xcd56_d943_0ea8_280e, 0xc125_91d7_535f_5065, 0xc832_23f1_720a_ef96, 0xc3a0_396f_7363_a51f, ) def __init__(self, data=b"", digest_bits=128, passes=3, version=1): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.a = 0x0123_4567_89ab_cdef self.b = 0xfedc_ba98_7654_3210 self.c = 0xf096_a5b4_c3b2_e187 self.digest_size = digest_bits // 8 self.passes = passes self.buf = bytearray() self.msg_len = 0 # in bytes self.version = version if data: self.update(bytes(data)) return def copy(self): other = self.__class__(digest_bits=self.digest_size * 8, passes=self.passes, version=self.version) other.a = self.a other.b = self.b other.c = self.c other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return self def digest(self): c = self.copy() c.finalize() out = struct.pack("<3Q", c.a, c.b, c.c) return out[: self.digest_size] def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 if self.version == 1: self.buf.append(0x01) else: self.buf.append(0x80) while (len(self.buf) % 64) != 56: self.buf.append(0x00) self.buf.extend(struct.pack("= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return def u64(self, x): return x & 0xffff_ffff_ffff_ffff def round(self, a, b, c, x, mul): s1 = self.__class__.sbox1 s2 = self.__class__.sbox2 s3 = self.__class__.sbox3 s4 = self.__class__.sbox4 c = self.u64(c ^ x) a = self.u64(a - (s1[(c >> 0) & 0xff] ^ s2[(c >> 16) & 0xff] ^ s3[(c >> 32) & 0xff] ^ s4[(c >> 48) & 0xff])) b = self.u64(b + (s4[(c >> 8) & 0xff] ^ s3[(c >> 24) & 0xff] ^ s2[(c >> 40) & 0xff] ^ s1[(c >> 56) & 0xff])) b = self.u64(b * mul) return a, b, c def key_schedule(self, x): x[0] = self.u64(x[0] - (x[7] ^ 0xa5a5_a5a5_a5a5_a5a5)) x[1] = self.u64(x[1] ^ x[0]) x[2] = self.u64(x[2] + x[1]) x[3] = self.u64(x[3] - (x[2] ^ (self.u64(~x[1]) << 19))) x[4] = self.u64(x[4] ^ x[3]) x[5] = self.u64(x[5] + x[4]) x[6] = self.u64(x[6] - (x[5] ^ (self.u64(~x[4]) >> 23))) x[7] = self.u64(x[7] ^ x[6]) x[0] = self.u64(x[0] + x[7]) x[1] = self.u64(x[1] - (x[0] ^ (self.u64(~x[7]) << 19))) x[2] = self.u64(x[2] ^ x[1]) x[3] = self.u64(x[3] + x[2]) x[4] = self.u64(x[4] - (x[3] ^ (self.u64(~x[2]) >> 23))) x[5] = self.u64(x[5] ^ x[4]) x[6] = self.u64(x[6] + x[5]) x[7] = self.u64(x[7] - (x[6] ^ 0x0123_4567_89ab_cdef)) return def do_pass(self, a, b, c, x, mul): a, b, c = self.round(a, b, c, x[0], mul) b, c, a = self.round(b, c, a, x[1], mul) c, a, b = self.round(c, a, b, x[2], mul) a, b, c = self.round(a, b, c, x[3], mul) b, c, a = self.round(b, c, a, x[4], mul) c, a, b = self.round(c, a, b, x[5], mul) a, b, c = self.round(a, b, c, x[6], mul) b, c, a = self.round(b, c, a, x[7], mul) return a, b, c def compress(self, block): if not isinstance(block, (bytes, bytearray, memoryview)) or len(block) != 64: raise ValueError("block must be 64 bytes") x = list(struct.unpack("<8Q", block)) aa = self.a bb = self.b cc = self.c # passes: 3 or 4 if self.passes == 3: # PASS(a,b,c,5); KS; PASS(c,a,b,7); KS; PASS(b,c,a,9) a, b, c = self.do_pass(self.a, self.b, self.c, x, 5) self.key_schedule(x) c, a, b = self.do_pass(c, a, b, x, 7) self.key_schedule(x) b, c, a = self.do_pass(b, c, a, x, 9) self.a, self.b, self.c = a, b, c elif self.passes == 4: a, b, c = self.do_pass(self.a, self.b, self.c, x, 5) self.key_schedule(x) c, a, b = self.do_pass(c, a, b, x, 7) self.key_schedule(x) b, c, a = self.do_pass(b, c, a, x, 9) self.key_schedule(x) b, c, a = self.do_pass(a, b, c, x, 9) self.a, self.b, self.c = a, b, c else: raise ValueError("passes must be 3 or 4") # feed-forward self.a = self.u64(self.a ^ aa) self.b = self.u64(self.b - bb) self.c = self.u64(self.c + cc) return class SHA0: block_size = 64 digest_size = 20 def __init__(self, data=b""): # Initial values are the same as SHA-1 self.h0 = 0x674_52301 self.h1 = 0xefc_dab89 self.h2 = 0x98b_adcfe self.h3 = 0x103_25476 self.h4 = 0xc3d_2e1f0 self.buf = bytearray() self.msg_len = 0 # in bytes if data: self.update(data) return def copy(self): other = self.__class__() other.h0 = self.h0 other.h1 = self.h1 other.h2 = self.h2 other.h3 = self.h3 other.h4 = self.h4 other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return struct.pack(">5I", c.h0, c.h1, c.h2, c.h3, c.h4) def hexdigest(self): return self.digest().hex() def finalize(self): # Padding: append 0x80, then 0x00* until length ≡ 56 (mod 64), # then append message length in bits as 64-bit big-endian. bit_len = self.msg_len * 8 self.buf.append(0x80) while (len(self.buf) % 64) != 56: self.buf.append(0x00) self.buf.extend(struct.pack(">Q", bit_len)) while len(self.buf) >= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return def compress(self, block): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff # Prepare message schedule W[0..79] W = list(struct.unpack(">16I", block)) for t in range(16, 80): # SHA-0: no rotation here (this is the key difference from SHA-1) W.append((W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]) & 0xffff_ffff) a = self.h0 b = self.h1 c = self.h2 d = self.h3 e = self.h4 for t in range(80): if 0 <= t <= 19: f = (b & c) | ((~b) & d) k = 0x5a82_7999 elif 20 <= t <= 39: f = b ^ c ^ d k = 0x6ed9_eba1 elif 40 <= t <= 59: f = (b & c) | (b & d) | (c & d) k = 0x8f1b_bcdc else: f = b ^ c ^ d k = 0xca62_c1d6 temp = (rol32(a, 5) + f + e + k + W[t]) & 0xffff_ffff e = d d = c c = rol32(b, 30) b = a a = temp self.h0 = (self.h0 + a) & 0xffff_ffff self.h1 = (self.h1 + b) & 0xffff_ffff self.h2 = (self.h2 + c) & 0xffff_ffff self.h3 = (self.h3 + d) & 0xffff_ffff self.h4 = (self.h4 + e) & 0xffff_ffff return class WhirlpoolBase: block_size = 64 digest_size = 64 def __init__(self, data=b""): self.ensure_tables() self.h = [0] * 8 self.buf = b"" self.count = 0 # total bytes processed if data != b"": self.update(data) return def gf_mul(self, a, b): # GF(2^8) with p(x)=x^8+x^4+x^3+x^2+1 (0x11d) a &= 0xff b &= 0xff res = 0 for _ in range(8): if b & 1: res ^= a b >>= 1 carry = a & 0x80 a = (a << 1) & 0xff if carry: # reduce by 0x11d; after shift xor with low 8 bits 0x1d a ^= 0x1d res &= 0xff return res def ensure_tables(self): if hasattr(self, "t_table") and hasattr(self, "rc_table"): return if not hasattr(self, "sbox") or not hasattr(self, "mds_row0"): raise ValueError("sbox/mds_row0 must be set in subclass") if len(self.sbox) != 256: raise ValueError("sbox must have length 256") if len(self.mds_row0) != 8: raise ValueError("mds_row0 must have length 8") # t[x] = pack8( mds_row0[i] * sbox[x] ) as u64 big-endian t = [0] * 256 for x in range(256): s = self.sbox[x] & 0xff w = 0 for i in range(8): w = (w << 8) | self.gf_mul(self.mds_row0[i], s) t[x] = w & 0xffff_ffff_ffff_ffff self.t_table = t # rc[r] = pack8( sbox[8*r : 8*r+8] ) as u64 big-endian rc = [0] * 10 for r in range(10): w = 0 base = 8 * r for i in range(8): w = (w << 8) | (self.sbox[base + i] & 0xff) rc[r] = w & 0xffff_ffff_ffff_ffff self.rc_table = rc return def copy(self): other = self.__class__() other.h = self.h[:] other.buf = self.buf other.count = self.count return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.count += len(data) self.buf += data while len(self.buf) >= 64: block = self.buf[:64] self.buf = self.buf[64:] self.process_block(block) return self def rho(self, a, n, c): def ror64(x, n): x &= 0xffff_ffff_ffff_ffff n &= 63 return ((x >> n) | (x << (64 - n))) & 0xffff_ffff_ffff_ffff n0 = n & 7 t = self.t_table b = t[(a[n0] >> 56) & 0xff] b ^= ror64(t[(a[(n0 + 7) & 7] >> 48) & 0xff], 8) b ^= ror64(t[(a[(n0 + 6) & 7] >> 40) & 0xff], 16) b ^= ror64(t[(a[(n0 + 5) & 7] >> 32) & 0xff], 24) b ^= ror64(t[(a[(n0 + 4) & 7] >> 24) & 0xff], 32) b ^= ror64(t[(a[(n0 + 3) & 7] >> 16) & 0xff], 40) b ^= ror64(t[(a[(n0 + 2) & 7] >> 8) & 0xff], 48) b ^= ror64(t[(a[(n0 + 1) & 7] >> 0) & 0xff], 56) return b ^ c def process_block(self, block): x = list(struct.unpack(">8Q", block)) state = [x[i] ^ self.h[i] for i in range(8)] k = self.h[:] rc = self.rc_table for r in range(10): l = [0] * 8 # noqa: E741 for i in range(8): l[i] = self.rho(k, i, 0) l[0] ^= rc[r] k = l l2 = [0] * 8 for i in range(8): l2[i] = self.rho(state, i, k[i]) state = l2 for i in range(8): self.h[i] ^= state[i] ^ x[i] return def finalize(self): bit_len = self.count * 8 if bit_len < 0 or bit_len >= (1 << 256): raise ValueError("message too long") buf = self.buf + b"\x80" pad_len = (32 - (len(buf) % 64)) % 64 buf += b"\x00" * pad_len buf += bit_len.to_bytes(32, "big") for off in range(0, len(buf), 64): self.process_block(buf[off:off + 64]) self.buf = b"" return def digest(self): tmp = self.copy() tmp.finalize() out = b"".join(struct.pack(">Q", w & 0xffff_ffff_ffff_ffff) for w in tmp.h) return out def hexdigest(self): out = self.digest().hex() return out class Whirlpool(WhirlpoolBase): # new sbox sbox = ( 0x18, 0x23, 0xc6, 0xe8, 0x87, 0xb8, 0x01, 0x4f, 0x36, 0xa6, 0xd2, 0xf5, 0x79, 0x6f, 0x91, 0x52, 0x60, 0xbc, 0x9b, 0x8e, 0xa3, 0x0c, 0x7b, 0x35, 0x1d, 0xe0, 0xd7, 0xc2, 0x2e, 0x4b, 0xfe, 0x57, 0x15, 0x77, 0x37, 0xe5, 0x9f, 0xf0, 0x4a, 0xda, 0x58, 0xc9, 0x29, 0x0a, 0xb1, 0xa0, 0x6b, 0x85, 0xbd, 0x5d, 0x10, 0xf4, 0xcb, 0x3e, 0x05, 0x67, 0xe4, 0x27, 0x41, 0x8b, 0xa7, 0x7d, 0x95, 0xd8, 0xfb, 0xee, 0x7c, 0x66, 0xdd, 0x17, 0x47, 0x9e, 0xca, 0x2d, 0xbf, 0x07, 0xad, 0x5a, 0x83, 0x33, 0x63, 0x02, 0xaa, 0x71, 0xc8, 0x19, 0x49, 0xd9, 0xf2, 0xe3, 0x5b, 0x88, 0x9a, 0x26, 0x32, 0xb0, 0xe9, 0x0f, 0xd5, 0x80, 0xbe, 0xcd, 0x34, 0x48, 0xff, 0x7a, 0x90, 0x5f, 0x20, 0x68, 0x1a, 0xae, 0xb4, 0x54, 0x93, 0x22, 0x64, 0xf1, 0x73, 0x12, 0x40, 0x08, 0xc3, 0xec, 0xdb, 0xa1, 0x8d, 0x3d, 0x97, 0x00, 0xcf, 0x2b, 0x76, 0x82, 0xd6, 0x1b, 0xb5, 0xaf, 0x6a, 0x50, 0x45, 0xf3, 0x30, 0xef, 0x3f, 0x55, 0xa2, 0xea, 0x65, 0xba, 0x2f, 0xc0, 0xde, 0x1c, 0xfd, 0x4d, 0x92, 0x75, 0x06, 0x8a, 0xb2, 0xe6, 0x0e, 0x1f, 0x62, 0xd4, 0xa8, 0x96, 0xf9, 0xc5, 0x25, 0x59, 0x84, 0x72, 0x39, 0x4c, 0x5e, 0x78, 0x38, 0x8c, 0xd1, 0xa5, 0xe2, 0x61, 0xb3, 0x21, 0x9c, 0x1e, 0x43, 0xc7, 0xfc, 0x04, 0x51, 0x99, 0x6d, 0x0d, 0xfa, 0xdf, 0x7e, 0x24, 0x3b, 0xab, 0xce, 0x11, 0x8f, 0x4e, 0xb7, 0xeb, 0x3c, 0x81, 0x94, 0xf7, 0xb9, 0x13, 0x2c, 0xd3, 0xe7, 0x6e, 0xc4, 0x03, 0x56, 0x44, 0x7f, 0xa9, 0x2a, 0xbb, 0xc1, 0x53, 0xdc, 0x0b, 0x9d, 0x6c, 0x31, 0x74, 0xf6, 0x46, 0xac, 0x89, 0x14, 0xe1, 0x16, 0x3a, 0x69, 0x09, 0x70, 0xb6, 0xd0, 0xed, 0xcc, 0x42, 0x98, 0xa4, 0x28, 0x5c, 0xf8, 0x86, ) # new mixrows mds_row0 = (0x01, 0x01, 0x04, 0x01, 0x08, 0x05, 0x02, 0x09) class WhirlpoolT(Whirlpool): # old mixrows mds_row0 = (0x01, 0x01, 0x03, 0x01, 0x05, 0x08, 0x09, 0x05) class Whirlpool0(WhirlpoolBase): # old sbox sbox = ( 0x68, 0xd0, 0xeb, 0x2b, 0x48, 0x9d, 0x6a, 0xe4, 0xe3, 0xa3, 0x56, 0x81, 0x7d, 0xf1, 0x85, 0x9e, 0x2c, 0x8e, 0x78, 0xca, 0x17, 0xa9, 0x61, 0xd5, 0x5d, 0x0b, 0x8c, 0x3c, 0x77, 0x51, 0x22, 0x42, 0x3f, 0x54, 0x41, 0x80, 0xcc, 0x86, 0xb3, 0x18, 0x2e, 0x57, 0x06, 0x62, 0xf4, 0x36, 0xd1, 0x6b, 0x1b, 0x65, 0x75, 0x10, 0xda, 0x49, 0x26, 0xf9, 0xcb, 0x66, 0xe7, 0xba, 0xae, 0x50, 0x52, 0xab, 0x05, 0xf0, 0x0d, 0x73, 0x3b, 0x04, 0x20, 0xfe, 0xdd, 0xf5, 0xb4, 0x5f, 0x0a, 0xb5, 0xc0, 0xa0, 0x71, 0xa5, 0x2d, 0x60, 0x72, 0x93, 0x39, 0x08, 0x83, 0x21, 0x5c, 0x87, 0xb1, 0xe0, 0x00, 0xc3, 0x12, 0x91, 0x8a, 0x02, 0x1c, 0xe6, 0x45, 0xc2, 0xc4, 0xfd, 0xbf, 0x44, 0xa1, 0x4c, 0x33, 0xc5, 0x84, 0x23, 0x7c, 0xb0, 0x25, 0x15, 0x35, 0x69, 0xff, 0x94, 0x4d, 0x70, 0xa2, 0xaf, 0xcd, 0xd6, 0x6c, 0xb7, 0xf8, 0x09, 0xf3, 0x67, 0xa4, 0xea, 0xec, 0xb6, 0xd4, 0xd2, 0x14, 0x1e, 0xe1, 0x24, 0x38, 0xc6, 0xdb, 0x4b, 0x7a, 0x3a, 0xde, 0x5e, 0xdf, 0x95, 0xfc, 0xaa, 0xd7, 0xce, 0x07, 0x0f, 0x3d, 0x58, 0x9a, 0x98, 0x9c, 0xf2, 0xa7, 0x11, 0x7e, 0x8b, 0x43, 0x03, 0xe2, 0xdc, 0xe5, 0xb2, 0x4e, 0xc7, 0x6d, 0xe9, 0x27, 0x40, 0xd8, 0x37, 0x92, 0x8f, 0x01, 0x1d, 0x53, 0x3e, 0x59, 0xc1, 0x4f, 0x32, 0x16, 0xfa, 0x74, 0xfb, 0x63, 0x9f, 0x34, 0x1a, 0x2a, 0x5a, 0x8d, 0xc9, 0xcf, 0xf6, 0x90, 0x28, 0x88, 0x9b, 0x31, 0x0e, 0xbd, 0x4a, 0xe8, 0x96, 0xa6, 0x0c, 0xc8, 0x79, 0xbc, 0xbe, 0xef, 0x6e, 0x46, 0x97, 0x5b, 0xed, 0x19, 0xd9, 0xac, 0x99, 0xa8, 0x29, 0x64, 0x1f, 0xad, 0x55, 0x13, 0xbb, 0xf7, 0x6f, 0xb9, 0x47, 0x2f, 0xee, 0xb8, 0x7b, 0x89, 0x30, 0xd3, 0x7f, 0x76, 0x82, ) # old mixrows row0 mds_row0 = (0x01, 0x01, 0x03, 0x01, 0x05, 0x08, 0x09, 0x05) class HASHxN: def __init__(self, name, N, data=b"", use_hex=False): self.name = name self.N = N self.hash = self.get_hash_func(name) self.digest_size = self.hash.digest_size self.use_hex = use_hex if data: self.update(data) return def get_hash_func(self, name): import hashlib return hashlib.new(name) def update(self, data): self.hash.update(data) return def digest(self): digest = self.hash.digest() for _ in range(self.N - 1): next_hash_obj = self.get_hash_func(self.name) if self.use_hex: next_hash_obj.update(digest.hex().encode()) else: next_hash_obj.update(digest) digest = next_hash_obj.digest() return digest def hexdigest(self): return self.digest().hex() class MD2: block_size = 16 digest_size = 16 def __init__(self, data=b""): self.buf = bytearray() self.msg_len = 0 # in bytes (kept for consistency) self.state = [0] * 48 self.checksum = [0] * 16 self.S = [ 0x29, 0x2e, 0x43, 0xc9, 0xa2, 0xd8, 0x7c, 0x01, 0x3d, 0x36, 0x54, 0xa1, 0xec, 0xf0, 0x06, 0x13, 0x62, 0xa7, 0x05, 0xf3, 0xc0, 0xc7, 0x73, 0x8c, 0x98, 0x93, 0x2b, 0xd9, 0xbc, 0x4c, 0x82, 0xca, 0x1e, 0x9b, 0x57, 0x3c, 0xfd, 0xd4, 0xe0, 0x16, 0x67, 0x42, 0x6f, 0x18, 0x8a, 0x17, 0xe5, 0x12, 0xbe, 0x4e, 0xc4, 0xd6, 0xda, 0x9e, 0xde, 0x49, 0xa0, 0xfb, 0xf5, 0x8e, 0xbb, 0x2f, 0xee, 0x7a, 0xa9, 0x68, 0x79, 0x91, 0x15, 0xb2, 0x07, 0x3f, 0x94, 0xc2, 0x10, 0x89, 0x0b, 0x22, 0x5f, 0x21, 0x80, 0x7f, 0x5d, 0x9a, 0x5a, 0x90, 0x32, 0x27, 0x35, 0x3e, 0xcc, 0xe7, 0xbf, 0xf7, 0x97, 0x03, 0xff, 0x19, 0x30, 0xb3, 0x48, 0xa5, 0xb5, 0xd1, 0xd7, 0x5e, 0x92, 0x2a, 0xac, 0x56, 0xaa, 0xc6, 0x4f, 0xb8, 0x38, 0xd2, 0x96, 0xa4, 0x7d, 0xb6, 0x76, 0xfc, 0x6b, 0xe2, 0x9c, 0x74, 0x04, 0xf1, 0x45, 0x9d, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5b, 0xcf, 0x65, 0xe6, 0x2d, 0xa8, 0x02, 0x1b, 0x60, 0x25, 0xad, 0xae, 0xb0, 0xb9, 0xf6, 0x1c, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7e, 0x0f, 0x55, 0x47, 0xa3, 0x23, 0xdd, 0x51, 0xaf, 0x3a, 0xc3, 0x5c, 0xf9, 0xce, 0xba, 0xc5, 0xea, 0x26, 0x2c, 0x53, 0x0d, 0x6e, 0x85, 0x28, 0x84, 0x09, 0xd3, 0xdf, 0xcd, 0xf4, 0x41, 0x81, 0x4d, 0x52, 0x6a, 0xdc, 0x37, 0xc8, 0x6c, 0xc1, 0xab, 0xfa, 0x24, 0xe1, 0x7b, 0x08, 0x0c, 0xbd, 0xb1, 0x4a, 0x78, 0x88, 0x95, 0x8b, 0xe3, 0x63, 0xe8, 0x6d, 0xe9, 0xcb, 0xd5, 0xfe, 0x3b, 0x00, 0x1d, 0x39, 0xf2, 0xef, 0xb7, 0x0e, 0x66, 0x58, 0xd0, 0xe4, 0xa6, 0x77, 0x72, 0xf8, 0xeb, 0x75, 0x4b, 0x0a, 0x31, 0x44, 0x50, 0xb4, 0x8f, 0xed, 0x1f, 0x1a, 0xdb, 0x99, 0x8d, 0x33, 0x9f, 0x11, 0x83, 0x14, ] if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.state = list(self.state) other.checksum = list(self.checksum) other.S = list(self.S) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 16: block = bytes(self.buf[:16]) del self.buf[:16] self.compress(block) return self def digest(self): c = self.copy() c.finalize() out = bytes(c.state[0:16]) return out def hexdigest(self): return self.digest().hex() def finalize(self): pad_len = 16 - (len(self.buf) % 16) pad = bytes([pad_len]) * pad_len padded = bytes(self.buf) + pad self.buf.clear() for i in range(0, len(padded), 16): self.compress(padded[i:i + 16]) checksum_block = bytes(self.checksum) self.compress(checksum_block) return def compress(self, block): m = list(block) for i in range(16): self.state[16 + i] = m[i] self.state[32 + i] = self.state[i] ^ m[i] t = 0 for i in range(18): for j in range(48): self.state[j] ^= self.S[t] t = self.state[j] & 0xff t = (t + i) & 0xff l = self.checksum[15] # noqa: E741 for i in range(16): c = m[i] self.checksum[i] ^= self.S[c ^ l] l = self.checksum[i] & 0xff # noqa: E741 return class MD4: block_size = 64 digest_size = 16 def __init__(self, data=b""): self.a = 0x6745_2301 self.b = 0xefcd_ab89 self.c = 0x98ba_dcfe self.d = 0x1032_5476 self.buf = bytearray() self.msg_len = 0 # bytes processed if data: self.update(data) return def copy(self): other = self.__class__() other.a, other.b, other.c, other.d = self.a, self.b, self.c, self.d other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def f(self, x, y, z): return (x & y) | (~x & z) def g(self, x, y, z): return (x & y) | (x & z) | (y & z) def h(self, x, y, z): return x ^ y ^ z def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return self def digest(self): c = self.copy() c.finalize() # MD4 output is little-endian A,B,C,D return struct.pack("<4I", c.a, c.b, c.c, c.d) def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 self.buf.append(0x80) while (len(self.buf) % 64) != 56: self.buf.append(0x00) self.buf.extend(struct.pack("= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return def compress(self, block): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff x = list(struct.unpack("<16I", block)) a, b, c, d = self.a, self.b, self.c, self.d m = 0xffff_ffff # round 1 a = rol32(a + self.f(b, c, d) + x[0], 3) d = rol32(d + self.f(a, b, c) + x[1], 7) c = rol32(c + self.f(d, a, b) + x[2], 11) b = rol32(b + self.f(c, d, a) + x[3], 19) a = rol32(a + self.f(b, c, d) + x[4], 3) d = rol32(d + self.f(a, b, c) + x[5], 7) c = rol32(c + self.f(d, a, b) + x[6], 11) b = rol32(b + self.f(c, d, a) + x[7], 19) a = rol32(a + self.f(b, c, d) + x[8], 3) d = rol32(d + self.f(a, b, c) + x[9], 7) c = rol32(c + self.f(d, a, b) + x[10], 11) b = rol32(b + self.f(c, d, a) + x[11], 19) a = rol32(a + self.f(b, c, d) + x[12], 3) d = rol32(d + self.f(a, b, c) + x[13], 7) c = rol32(c + self.f(d, a, b) + x[14], 11) b = rol32(b + self.f(c, d, a) + x[15], 19) # round 2 k2 = 0x5a82_7999 a = rol32(a + self.g(b, c, d) + x[0] + k2, 3) d = rol32(d + self.g(a, b, c) + x[4] + k2, 5) c = rol32(c + self.g(d, a, b) + x[8] + k2, 9) b = rol32(b + self.g(c, d, a) + x[12] + k2, 13) a = rol32(a + self.g(b, c, d) + x[1] + k2, 3) d = rol32(d + self.g(a, b, c) + x[5] + k2, 5) c = rol32(c + self.g(d, a, b) + x[9] + k2, 9) b = rol32(b + self.g(c, d, a) + x[13] + k2, 13) a = rol32(a + self.g(b, c, d) + x[2] + k2, 3) d = rol32(d + self.g(a, b, c) + x[6] + k2, 5) c = rol32(c + self.g(d, a, b) + x[10] + k2, 9) b = rol32(b + self.g(c, d, a) + x[14] + k2, 13) a = rol32(a + self.g(b, c, d) + x[3] + k2, 3) d = rol32(d + self.g(a, b, c) + x[7] + k2, 5) c = rol32(c + self.g(d, a, b) + x[11] + k2, 9) b = rol32(b + self.g(c, d, a) + x[15] + k2, 13) # round 3 k3 = 0x6ed9_eba1 a = rol32(a + self.h(b, c, d) + x[0] + k3, 3) d = rol32(d + self.h(a, b, c) + x[8] + k3, 9) c = rol32(c + self.h(d, a, b) + x[4] + k3, 11) b = rol32(b + self.h(c, d, a) + x[12] + k3, 15) a = rol32(a + self.h(b, c, d) + x[2] + k3, 3) d = rol32(d + self.h(a, b, c) + x[10] + k3, 9) c = rol32(c + self.h(d, a, b) + x[6] + k3, 11) b = rol32(b + self.h(c, d, a) + x[14] + k3, 15) a = rol32(a + self.h(b, c, d) + x[1] + k3, 3) d = rol32(d + self.h(a, b, c) + x[9] + k3, 9) c = rol32(c + self.h(d, a, b) + x[5] + k3, 11) b = rol32(b + self.h(c, d, a) + x[13] + k3, 15) a = rol32(a + self.h(b, c, d) + x[3] + k3, 3) d = rol32(d + self.h(a, b, c) + x[11] + k3, 9) c = rol32(c + self.h(d, a, b) + x[7] + k3, 11) b = rol32(b + self.h(c, d, a) + x[15] + k3, 15) self.a = (self.a + a) & m self.b = (self.b + b) & m self.c = (self.c + c) & m self.d = (self.d + d) & m return class NTLM: # NT hash: MD4(UTF-16LE(password)) digest_size = 32 def __init__(self, data=b""): self.md4 = Hash.MD4() if data: self.update(data) return def copy(self): other = self.__class__() other.md4 = self.md4.copy() return other def update(self, password, skip_utf16le=False): if isinstance(password, str): if skip_utf16le: data = password.encode() else: data = password.encode("utf-16le") elif isinstance(password, (bytes, bytearray, memoryview)): if skip_utf16le: data = bytes(password) else: data = String.bytes2str(password).encode("utf-16le") else: raise TypeError("password must be str or bytes-like") self.md4.update(data) return self def digest(self): return self.md4.digest() def hexdigest(self): return self.md4.hexdigest() class MD6: def __init__(self, d=256, key=b"", L=64, r=None): if not (1 <= int(d) <= 512): raise ValueError("d must be in [1, 512]") if not (0 <= len(key) <= 64): raise ValueError("key length must be in [0, 64] bytes") if int(L) < 0: raise ValueError("L must be non-negative") self.d = int(d) self.digest_size = self.d // 8 self.key = bytes(key) self.L = int(L) if r is None: base = 40 + ((self.d + 3) // 4) # 40 + ceil(d/4) if len(self.key) != 0: self.r = max(80, base) else: self.r = base else: if int(r) < 0: raise ValueError("r must be non-negative") self.r = int(r) self.buffer = b"" self.w = 64 self.n = 89 self.c = 16 self.b = 64 self.mask64 = 0xffff_ffff_ffff_ffff self.t0 = 0x11 self.t1 = 0x12 self.t2 = 0x15 self.t3 = 0x1f self.t4 = 0x43 self.rshift = ( 0x0a, 0x05, 0x0d, 0x0a, 0x0b, 0x0c, 0x02, 0x07, 0x0e, 0x0f, 0x07, 0x0d, 0x0b, 0x07, 0x06, 0x0c, ) self.lshift = ( 0x0b, 0x18, 0x09, 0x10, 0x0f, 0x09, 0x1b, 0x0f, 0x06, 0x02, 0x1d, 0x08, 0x0f, 0x05, 0x1f, 0x09, ) self.Q = [ 0x7311_c281_2425_cfa0, 0x6432_2864_34aa_c8e7, 0xb604_50e9_ef68_b7c1, 0xe8fb_2390_8d9f_06f1, 0xdd2e_76cb_a691_e5bf, 0x0cd0_d63b_2c30_bc41, 0x1f8c_cf68_2305_8f8a, 0x54e5_ed5b_88e3_775d, 0x4ad1_2aae_0a6d_6031, 0x3e7f_16bb_8822_2e0d, 0x8af8_671d_3fb5_0c2c, 0x995a_d117_8bd2_5c31, 0xc878_c1dd_04c4_b633, 0x3b72_066c_7a15_52ac, 0x0d6f_3522_631e_ffcb, ] self.S0 = 0x0123_4567_89ab_cdef self.Sstar = 0x7311_c281_2425_cfa0 return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buffer += bytes(data) return self def copy(self): other = self.__class__(d=self.d, key=self.key, L=self.L, r=self.r) other.buffer = self.buffer return other def digest(self): return self.hash(self.buffer) def hexdigest(self): return self.digest().hex() def rol64(self, x, n): n &= 63 x &= self.mask64 return ((x << n) & self.mask64) | (x >> (64 - n)) def pack_control(self, r, L, z, p, keylen, d): # Control word V layout (high-order 4 bits are zero): # [0:4][r:12][L:8][z:4][p:16][keylen:8][d:12] # r at bits 48..59, L at 40..47, z at 36..39, p at 20..35, keylen at 12..19, d at 0..11 r &= (1 << 12) - 1 L &= (1 << 8) - 1 z &= (1 << 4) - 1 p &= (1 << 16) - 1 keylen &= (1 << 8) - 1 d &= (1 << 12) - 1 return ((r << 48) | (L << 40) | (z << 36) | (p << 20) | (keylen << 12) | d) & self.mask64 def pack_node_id(self, ell, i): # Unique node ID U layout: # high-order byte is ell, remaining 7 bytes is index i return (((ell & 0xff) << 56) | (i & ((1 << 56) - 1))) & self.mask64 def bytes_to_words_be(self, data, count): out = [] need = count * 8 if len(data) < need: data = data + b"\x00" * (need - len(data)) for j in range(count): out.append(int.from_bytes(data[j * 8:(j + 1) * 8], "big")) return out def words_to_bytes_be(self, words): return b"".join((w & self.mask64).to_bytes(8, "big") for w in words) def make_key_words(self): kp = self.key + b"\x00" * (64 - len(self.key)) return self.bytes_to_words_be(kp, 8) def make_round_constants(self, r): # S'_0 = S0 # S'_{j+1} = (S'_j <<< 1) xor (S'_j & S*) # Use rotate-left by 1 (<<<). S = [] x = self.S0 & self.mask64 for _ in range(r): S.append(x) x = self.rol64(x, 1) ^ (x & self.Sstar) x &= self.mask64 return S def compress(self, N_words, r): # N_words length must be 89 (15 Q + 8 K + 1 U + 1 V + 64 B) if len(N_words) != self.n: raise ValueError("N_words must be length 89") t = 16 * r A = list(N_words) Sround = self.make_round_constants(r) for i in range(self.n, self.n + t): step = i - self.n # 0..t-1 ridx = step & 0x0f round_idx = step >> 4 x = Sround[round_idx] x ^= A[i - self.n] x ^= A[i - self.t0] x ^= (A[i - self.t1] & A[i - self.t2]) x ^= (A[i - self.t3] & A[i - self.t4]) x &= self.mask64 x ^= (x >> self.rshift[ridx]) x &= self.mask64 y = x ^ ((x << self.lshift[ridx]) & self.mask64) y &= self.mask64 A.append(y) # Output last 16 words return A[-self.c:] def par(self, M_bytes, ell): # PAR operator (Figure 2.5): input message at level ell-1 -> output message at level ell block_bytes = 512 # 64 words m_bits = len(M_bytes) * 8 j = (len(M_bytes) + block_bytes - 1) // block_bytes if j < 1: j = 1 total_bytes = j * block_bytes pad_bits = total_bytes * 8 - m_bits # 0..4096*? (here <= 4096-8) if pad_bits < 0: raise RuntimeError("internal padding error") Mp = M_bytes + b"\x00" * (total_bytes - len(M_bytes)) K_words = self.make_key_words() keylen = len(self.key) z_all = 1 if j == 1 else 0 out_chunks = [] for i in range(j): Bi = Mp[i * block_bytes:(i + 1) * block_bytes] B_words = self.bytes_to_words_be(Bi, 64) p = pad_bits if (i == j - 1) else 0 V = self.pack_control(self.r, self.L, z_all, p, keylen, self.d) U = self.pack_node_id(ell, i) N = self.Q + K_words + [U, V] + B_words Ci = self.compress(N, self.r) out_chunks.append(Ci) # Concatenate C0||C1||... as bytes out_words = [] for Ci in out_chunks: out_words.extend(Ci) return self.words_to_bytes_be(out_words) def seq(self, M_bytes): # SEQ operator (Figure 2.6): sequential MD-like; returns d-bit hash block_words = 48 block_bytes = block_words * 8 # 384 bytes m_bits = len(M_bytes) * 8 j = (len(M_bytes) + block_bytes - 1) // block_bytes if j < 1: j = 1 total_bytes = j * block_bytes pad_bits = total_bytes * 8 - m_bits # 0..3072 Mp = M_bytes + b"\x00" * (total_bytes - len(M_bytes)) K_words = self.make_key_words() keylen = len(self.key) C_prev = [0x00] * self.c # 16-word IV of zeros ell = self.L + 1 for i in range(j): Bi = Mp[i * block_bytes:(i + 1) * block_bytes] Bi_words = self.bytes_to_words_be(Bi, block_words) z = 1 if (i == j - 1) else 0 p = pad_bits if (i == j - 1) else 0 V = self.pack_control(self.r, self.L, z, p, keylen, self.d) U = self.pack_node_id(ell, i) # B is 64 words = C_{i-1} (16) || B_i (48) B_words = list(C_prev) + Bi_words N = self.Q + K_words + [U, V] + B_words C_prev = self.compress(N, self.r) # Return last d bits of C_{j-1} full = self.words_to_bytes_be(C_prev) # 128 bytes (1024 bits) return self.last_bits(full, self.d) def last_bits(self, data, d_bits): # Return the last d_bits of data (data is a big-endian bitstring). if d_bits == 0: return b"" total_bits = len(data) * 8 if d_bits > total_bits: raise ValueError("d_bits larger than available bits") # Fast path for byte-aligned if (d_bits & 7) == 0: return data[-(d_bits // 8):] # Bit-level extraction keep_bytes = (d_bits + 7) // 8 tail = data[-keep_bytes:] excess = (keep_bytes * 8) - d_bits # 1..7 # We need the lowest (8-excess) bits of the first byte first = tail[0] & ((1 << (8 - excess)) - 1) return bytes([first]) + tail[1:] def hash(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") M = bytes(data) # Main level-by-level loop (Figure 2.4): # ell starts at 0, then increments; if ell == L+1 -> SEQ on previous ell = 0 M_ell_minus_1 = M while True: ell += 1 if ell == self.L + 1: return self.seq(M_ell_minus_1) M_ell = self.par(M_ell_minus_1, ell) # If M_ell is exactly c words (16 words) long, return last d bits of M_ell if len(M_ell) == self.c * 8: return self.last_bits(M_ell, self.d) M_ell_minus_1 = M_ell raise class RIPEMDBase: block_size = 64 digest_size = None # bytes init_state_words = None # little-endian words rounds = None # 4 for 128/160/256, 5 for 320 # RIPEMD message word order and rotation amounts (base tables; 128/160/256 use first 64, 320 uses full 80) r = ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x07, 0x04, 0x0d, 0x01, 0x0a, 0x06, 0x0f, 0x03, 0x0c, 0x00, 0x09, 0x05, 0x02, 0x0e, 0x0b, 0x08, 0x03, 0x0a, 0x0e, 0x04, 0x09, 0x0f, 0x08, 0x01, 0x02, 0x07, 0x00, 0x06, 0x0d, 0x0b, 0x05, 0x0c, 0x01, 0x09, 0x0b, 0x0a, 0x00, 0x08, 0x0c, 0x04, 0x0d, 0x03, 0x07, 0x0f, 0x0e, 0x05, 0x06, 0x02, 0x04, 0x00, 0x05, 0x09, 0x07, 0x0c, 0x02, 0x0a, 0x0e, 0x01, 0x03, 0x08, 0x0b, 0x06, 0x0f, 0x0d, ) rp = ( 0x05, 0x0e, 0x07, 0x00, 0x09, 0x02, 0x0b, 0x04, 0x0d, 0x06, 0x0f, 0x08, 0x01, 0x0a, 0x03, 0x0c, 0x06, 0x0b, 0x03, 0x07, 0x00, 0x0d, 0x05, 0x0a, 0x0e, 0x0f, 0x08, 0x0c, 0x04, 0x09, 0x01, 0x02, 0x0f, 0x05, 0x01, 0x03, 0x07, 0x0e, 0x06, 0x09, 0x0b, 0x08, 0x0c, 0x02, 0x0a, 0x00, 0x04, 0x0d, 0x08, 0x06, 0x04, 0x01, 0x03, 0x0b, 0x0f, 0x00, 0x05, 0x0c, 0x02, 0x0d, 0x09, 0x07, 0x0a, 0x0e, 0x0c, 0x0f, 0x0a, 0x04, 0x01, 0x05, 0x08, 0x07, 0x06, 0x02, 0x0d, 0x0e, 0x00, 0x03, 0x09, 0x0b, ) s = ( 0x0b, 0x0e, 0x0f, 0x0c, 0x05, 0x08, 0x07, 0x09, 0x0b, 0x0d, 0x0e, 0x0f, 0x06, 0x07, 0x09, 0x08, 0x07, 0x06, 0x08, 0x0d, 0x0b, 0x09, 0x07, 0x0f, 0x07, 0x0c, 0x0f, 0x09, 0x0b, 0x07, 0x0d, 0x0c, 0x0b, 0x0d, 0x06, 0x07, 0x0e, 0x09, 0x0d, 0x0f, 0x0e, 0x08, 0x0d, 0x06, 0x05, 0x0c, 0x07, 0x05, 0x0b, 0x0c, 0x0e, 0x0f, 0x0e, 0x0f, 0x09, 0x08, 0x09, 0x0e, 0x05, 0x06, 0x08, 0x06, 0x05, 0x0c, 0x09, 0x0f, 0x05, 0x0b, 0x06, 0x08, 0x0d, 0x0c, 0x05, 0x0c, 0x0d, 0x0e, 0x0b, 0x08, 0x05, 0x06, ) sp = ( 0x08, 0x09, 0x09, 0x0b, 0x0d, 0x0f, 0x0f, 0x05, 0x07, 0x07, 0x08, 0x0b, 0x0e, 0x0e, 0x0c, 0x06, 0x09, 0x0d, 0x0f, 0x07, 0x0c, 0x08, 0x09, 0x0b, 0x07, 0x07, 0x0c, 0x07, 0x06, 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x0f, 0x0b, 0x08, 0x06, 0x06, 0x0e, 0x0c, 0x0d, 0x05, 0x0e, 0x0d, 0x0d, 0x07, 0x05, 0x0f, 0x05, 0x08, 0x0b, 0x0e, 0x0e, 0x06, 0x0e, 0x06, 0x09, 0x0c, 0x09, 0x0c, 0x05, 0x0f, 0x08, 0x08, 0x05, 0x0c, 0x09, 0x0c, 0x05, 0x0e, 0x06, 0x08, 0x0d, 0x06, 0x05, 0x0f, 0x0d, 0x0b, 0x0b, ) # constants for rounds (left line / right line) K4 = (0x0000_0000, 0x5a82_7999, 0x6ed9_eba1, 0x8f1b_bcdc) K4p = (0x50a2_8be6, 0x5c4d_d124, 0x6d70_3ef3, 0x0000_0000) K5 = (0x0000_0000, 0x5a82_7999, 0x6ed9_eba1, 0x8f1b_bcdc, 0xa953_fd4e) K5p = (0x50a2_8be6, 0x5c4d_d124, 0x6d70_3ef3, 0x7a6d_76e9, 0x0000_0000) def __init__(self, data=b""): self.h = self.init_state_words[:] self.buf = b"" self.total = 0 if data: self.update(data) return def f(self, j, x, y, z): # j: 0..4 (uses first 4 for 128/160/256) if j == 0: return x ^ y ^ z if j == 1: return (x & y) | ((~x) & z) if j == 2: return (x | (~y)) ^ z if j == 3: return (x & z) | (y & (~z)) return x ^ (y | (~z)) def fp(self, j, x, y, z): # parallel line order: for 4-round: I,H,G,F # for 5-round: J,I,H,G,F (i.e. f4,f3,f2,f1,f0) if self.rounds == 4: if j == 0: return (x & z) | (y & (~z)) # I if j == 1: return (x | (~y)) ^ z # H if j == 2: return (x & y) | ((~x) & z) # G return x ^ y ^ z # F else: # 0..4 maps to f4,f3,f2,f1,f0 if j == 0: return x ^ (y | (~z)) # J if j == 1: return (x & z) | (y & (~z)) # I if j == 2: return (x | (~y)) ^ z # H if j == 3: return (x & y) | ((~x) & z) # G return x ^ y ^ z # F def update(self, data): if not data: return self if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.total += len(data) self.buf += bytes(data) while len(self.buf) >= 64: block = self.buf[:64] self.buf = self.buf[64:] self.transform(block) return self def pad(self): bit_len = self.total * 8 pad = b"\x80" pad_len = (56 - (self.total + 1) % 64) % 64 pad += b"\x00" * pad_len pad += (bit_len & 0xffff_ffff_ffff_ffff).to_bytes(8, "little") return pad def digest(self): clone = self.__class__() clone.h = self.h[:] clone.buf = self.buf clone.total = self.total clone.update(clone.pad()) out = b"".join((w & 0xffff_ffff).to_bytes(4, "little") for w in clone.h) return out[: self.digest_size] def hexdigest(self): return self.digest().hex() class RIPEMD128(RIPEMDBase): digest_size = 16 init_state_words = ( 0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476, ) rounds = 4 def transform(self, block): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff X = [int.from_bytes(block[i:i + 4], "little") for i in range(0, 64, 4)] h0, h1, h2, h3 = self.h a, b, c, d = h0, h1, h2, h3 aa, bb, cc, dd = h0, h1, h2, h3 for j in range(64): rj = self.r[j] sj = self.s[j] round_idx = j // 16 t = (a + self.f(round_idx, b, c, d) + X[rj] + self.K4[round_idx]) & 0xffff_ffff t = rol32(t, sj) a, d, c, b = d, c, b, t rpj = self.rp[j] spj = self.sp[j] pround = j // 16 tp = (aa + self.fp(pround, bb, cc, dd) + X[rpj] + self.K4p[pround]) & 0xffff_ffff tp = rol32(tp, spj) aa, dd, cc, bb = dd, cc, bb, tp self.h = [ (h1 + c + dd) & 0xffff_ffff, (h2 + d + aa) & 0xffff_ffff, (h3 + a + bb) & 0xffff_ffff, (h0 + b + cc) & 0xffff_ffff, ] return class RIPEMD160(RIPEMDBase): digest_size = 20 init_state_words = ( 0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476, 0xc3d2_e1f0, ) rounds = 5 def transform(self, block): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff X = [int.from_bytes(block[i:i + 4], "little") for i in range(0, 64, 4)] h0, h1, h2, h3, h4 = self.h a, b, c, d, e = h0, h1, h2, h3, h4 aa, bb, cc, dd, ee = h0, h1, h2, h3, h4 for j in range(80): round_idx = j // 16 t = (a + self.f(round_idx, b, c, d) + X[self.r[j]] + self.K5[round_idx]) & 0xffff_ffff t = rol32(t, self.s[j]) t = (t + e) & 0xffff_ffff a, e, d, c, b = e, d, rol32(c, 0x0a), b, t # c <<< 10 tp = (aa + self.fp(round_idx, bb, cc, dd) + X[self.rp[j]] + self.K5p[round_idx]) & 0xffff_ffff tp = rol32(tp, self.sp[j]) tp = (tp + ee) & 0xffff_ffff aa, ee, dd, cc, bb = ee, dd, rol32(cc, 0x0a), bb, tp # cc <<< 10 self.h = [ (h1 + c + dd) & 0xffff_ffff, (h2 + d + ee) & 0xffff_ffff, (h3 + e + aa) & 0xffff_ffff, (h4 + a + bb) & 0xffff_ffff, (h0 + b + cc) & 0xffff_ffff, ] return class RIPEMD256(RIPEMDBase): digest_size = 32 init_state_words = ( 0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476, 0x7654_3210, 0xfedc_ba98, 0x89ab_cdef, 0x0123_4567, ) rounds = 4 def transform(self, block): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff X = [int.from_bytes(block[i:i + 4], "little") for i in range(0, 64, 4)] h0, h1, h2, h3, h4, h5, h6, h7 = self.h a, b, c, d = h0, h1, h2, h3 aa, bb, cc, dd = h4, h5, h6, h7 for j in range(64): round_idx = j // 16 t = (a + self.f(round_idx, b, c, d) + X[self.r[j]] + self.K4[round_idx]) & 0xffff_ffff t = rol32(t, self.s[j]) a, d, c, b = d, c, b, t tp = (aa + self.fp(round_idx, bb, cc, dd) + X[self.rp[j]] + self.K4p[round_idx]) & 0xffff_ffff tp = rol32(tp, self.sp[j]) aa, dd, cc, bb = dd, cc, bb, tp # swaps after each 16-step round (RIPEMD-256) if (j & 0x0f) == 0x0f: if round_idx == 0: a, aa = aa, a elif round_idx == 1: b, bb = bb, b elif round_idx == 2: c, cc = cc, c else: d, dd = dd, d self.h = [ (h0 + a) & 0xffff_ffff, (h1 + b) & 0xffff_ffff, (h2 + c) & 0xffff_ffff, (h3 + d) & 0xffff_ffff, (h4 + aa) & 0xffff_ffff, (h5 + bb) & 0xffff_ffff, (h6 + cc) & 0xffff_ffff, (h7 + dd) & 0xffff_ffff, ] return class RIPEMD320(RIPEMD256): digest_size = 40 init_state_words = ( 0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476, 0xc3d2_e1f0, 0x7654_3210, 0xfedc_ba98, 0x89ab_cdef, 0x0123_4567, 0x3c2d_1e0f, ) rounds = 5 def transform(self, block): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff X = [int.from_bytes(block[i:i + 4], "little") for i in range(0, 64, 4)] h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 = self.h a, b, c, d, e = h0, h1, h2, h3, h4 aa, bb, cc, dd, ee = h5, h6, h7, h8, h9 for j in range(80): round_idx = j // 16 t = (a + self.f(round_idx, b, c, d) + X[self.r[j]] + self.K5[round_idx]) & 0xffff_ffff t = rol32(t, self.s[j]) t = (t + e) & 0xffff_ffff a, e, d, c, b = e, d, rol32(c, 0x0a), b, t # rotate (c left by 10) tp = (aa + self.fp(round_idx, bb, cc, dd) + X[self.rp[j]] + self.K5p[round_idx]) & 0xffff_ffff tp = rol32(tp, self.sp[j]) tp = (tp + ee) & 0xffff_ffff aa, ee, dd, cc, bb = ee, dd, rol32(cc, 0x0a), bb, tp # swaps after each 16-step round (RIPEMD-320) if (j & 0x0f) == 0x0f: if round_idx == 0: b, bb = bb, b elif round_idx == 1: d, dd = dd, d elif round_idx == 2: a, aa = aa, a elif round_idx == 3: c, cc = cc, c else: e, ee = ee, e self.h = [ (h0 + a) & 0xffff_ffff, (h1 + b) & 0xffff_ffff, (h2 + c) & 0xffff_ffff, (h3 + d) & 0xffff_ffff, (h4 + e) & 0xffff_ffff, (h5 + aa) & 0xffff_ffff, (h6 + bb) & 0xffff_ffff, (h7 + cc) & 0xffff_ffff, (h8 + dd) & 0xffff_ffff, (h9 + ee) & 0xffff_ffff, ] return class BLAKEBase: sigma = ( (0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f), (0x0e, 0x0a, 0x04, 0x08, 0x09, 0x0f, 0x0d, 0x06, 0x01, 0x0c, 0x00, 0x02, 0x0b, 0x07, 0x05, 0x03), (0x0b, 0x08, 0x0c, 0x00, 0x05, 0x02, 0x0f, 0x0d, 0x0a, 0x0e, 0x03, 0x06, 0x07, 0x01, 0x09, 0x04), (0x07, 0x09, 0x03, 0x01, 0x0d, 0x0c, 0x0b, 0x0e, 0x02, 0x06, 0x05, 0x0a, 0x04, 0x00, 0x0f, 0x08), (0x09, 0x00, 0x05, 0x07, 0x02, 0x04, 0x0a, 0x0f, 0x0e, 0x01, 0x0b, 0x0c, 0x06, 0x08, 0x03, 0x0d), (0x02, 0x0c, 0x06, 0x0a, 0x00, 0x0b, 0x08, 0x03, 0x04, 0x0d, 0x07, 0x05, 0x0f, 0x0e, 0x01, 0x09), (0x0c, 0x05, 0x01, 0x0f, 0x0e, 0x0d, 0x04, 0x0a, 0x00, 0x07, 0x06, 0x03, 0x09, 0x02, 0x08, 0x0b), (0x0d, 0x0b, 0x07, 0x0e, 0x0c, 0x01, 0x03, 0x09, 0x05, 0x00, 0x0f, 0x04, 0x08, 0x06, 0x02, 0x0a), (0x06, 0x0f, 0x0e, 0x09, 0x0b, 0x03, 0x00, 0x08, 0x0c, 0x02, 0x0d, 0x07, 0x01, 0x04, 0x0a, 0x05), (0x0a, 0x02, 0x08, 0x04, 0x07, 0x06, 0x01, 0x05, 0x0f, 0x0b, 0x09, 0x0e, 0x03, 0x0c, 0x0d, 0x00), ) def __init__(self, data=b""): self.h = list(self.iv) self.buf = b"" self.total = 0 self.processed = 0 self.mask = (1 << self.word_bits) - 1 self.word_bytes = self.word_bits // 8 if self.word_bits == 32: self.rot = (16, 12, 8, 7) else: self.rot = (32, 25, 16, 11) if data: self.update(data) return def rol(self, x, n): x &= self.mask return ((x << n) | (x >> (self.word_bits - n))) & self.mask def ror(self, x, n): x &= self.mask return ((x >> n) | (x << (self.word_bits - n))) & self.mask def add(self, x, y): return (x + y) & self.mask def parse_block_words(self, block): return [ int.from_bytes(block[i:i + self.word_bytes], "big") for i in range(0, self.block_size, self.word_bytes) ] def split_counter(self, counter_bits): if self.word_bits == 32: t = counter_bits & 0xffff_ffff_ffff_ffff t0 = t & 0xffff_ffff t1 = (t >> 32) & 0xffff_ffff return t0, t1 t = counter_bits & ((1 << 128) - 1) t0 = t & 0xffff_ffff_ffff_ffff t1 = (t >> 64) & 0xffff_ffff_ffff_ffff return t0, t1 def g(self, v, a, b, c, d, m, r, i): s = self.sigma[r % 10] x = s[2 * i] y = s[2 * i + 1] cx = self.c[x] cy = self.c[y] r0, r1, r2, r3 = self.rot va = (v[a] + v[b] + (m[x] ^ cy)) & self.mask vd = self.ror(v[d] ^ va, r0) vc = (v[c] + vd) & self.mask vb = self.ror(v[b] ^ vc, r1) va = (va + vb + (m[y] ^ cx)) & self.mask vd = self.ror(vd ^ va, r2) vc = (vc + vd) & self.mask vb = self.ror(vb ^ vc, r3) v[a] = va v[b] = vb v[c] = vc v[d] = vd return def compress(self, block, counter_bits): m = self.parse_block_words(block) t0, t1 = self.split_counter(counter_bits) v = self.h[:] + [ self.c[0], self.c[1], self.c[2], self.c[3], t0 ^ self.c[4], t0 ^ self.c[5], t1 ^ self.c[6], t1 ^ self.c[7], ] for r in range(self.rounds): self.g(v, 0, 4, 8, 12, m, r, 0) self.g(v, 1, 5, 9, 13, m, r, 1) self.g(v, 2, 6, 10, 14, m, r, 2) self.g(v, 3, 7, 11, 15, m, r, 3) self.g(v, 0, 5, 10, 15, m, r, 4) self.g(v, 1, 6, 11, 12, m, r, 5) self.g(v, 2, 7, 8, 13, m, r, 6) self.g(v, 3, 4, 9, 14, m, r, 7) h0, h1, h2, h3, h4, h5, h6, h7 = self.h h0 = (h0 ^ v[0] ^ v[8]) & self.mask h1 = (h1 ^ v[1] ^ v[9]) & self.mask h2 = (h2 ^ v[2] ^ v[10]) & self.mask h3 = (h3 ^ v[3] ^ v[11]) & self.mask h4 = (h4 ^ v[4] ^ v[12]) & self.mask h5 = (h5 ^ v[5] ^ v[13]) & self.mask h6 = (h6 ^ v[6] ^ v[14]) & self.mask h7 = (h7 ^ v[7] ^ v[15]) & self.mask self.h = [h0, h1, h2, h3, h4, h5, h6, h7] return def update(self, data): if not data: return self if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") mv = memoryview(data) self.total += len(mv) if self.buf: need = self.block_size - len(self.buf) if len(mv) < need: self.buf += mv.tobytes() return self block = self.buf + mv[:need].tobytes() self.processed += self.block_size self.compress(block, self.processed * 8) self.buf = b"" mv = mv[need:] end = len(mv) - (len(mv) % self.block_size) for off in range(0, end, self.block_size): block = mv[off:off + self.block_size] self.processed += self.block_size self.compress(block, self.processed * 8) self.buf = mv[end:].tobytes() return self def make_padding(self): msg_bits = self.total * 8 buf_len = len(self.buf) pad_zero_len = ( self.block_size - ((buf_len + 1 + 1 + self.length_bytes) % self.block_size) ) % self.block_size pad = ( b"\x80" + (b"\x00" * pad_zero_len) + bytes([self.pad_delim]) + msg_bits.to_bytes(self.length_bytes, "big") ) return pad def finalize(self): msg_bits = self.total * 8 final_data = self.buf + self.make_padding() if len(final_data) % self.block_size != 0: raise ValueError("internal padding error") first_counter = msg_bits if (self.total % self.block_size) != 0 else 0 for off in range(0, len(final_data), self.block_size): block = final_data[off:off + self.block_size] counter_bits = first_counter if off == 0 else 0 self.compress(block, counter_bits) return def digest(self): clone = self.__class__() clone.h = self.h[:] clone.buf = self.buf clone.total = self.total clone.processed = self.processed clone.finalize() out = b"".join( (w & clone.mask).to_bytes(clone.word_bytes, "big") for w in clone.h ) return out[:self.digest_size] def hexdigest(self): return self.digest().hex() class BLAKE224(BLAKEBase): block_size = 64 digest_size = 28 word_bits = 32 rounds = 14 pad_delim = 0x00 length_bytes = 8 iv = ( 0xc105_9ed8, 0x367c_d507, 0x3070_dd17, 0xf70e_5939, 0xffc0_0b31, 0x6858_1511, 0x64f9_8fa7, 0xbefa_4fa4, ) c = ( 0x243f_6a88, 0x85a3_08d3, 0x1319_8a2e, 0x0370_7344, 0xa409_3822, 0x299f_31d0, 0x082e_fa98, 0xec4e_6c89, 0x4528_21e6, 0x38d0_1377, 0xbe54_66cf, 0x34e9_0c6c, 0xc0ac_29b7, 0xc97c_50dd, 0x3f84_d5b5, 0xb547_0917, ) class BLAKE256(BLAKEBase): block_size = 64 digest_size = 32 word_bits = 32 rounds = 14 pad_delim = 0x01 length_bytes = 8 iv = ( 0x6a09_e667, 0xbb67_ae85, 0x3c6e_f372, 0xa54f_f53a, 0x510e_527f, 0x9b05_688c, 0x1f83_d9ab, 0x5be0_cd19, ) c = ( 0x243f_6a88, 0x85a3_08d3, 0x1319_8a2e, 0x0370_7344, 0xa409_3822, 0x299f_31d0, 0x082e_fa98, 0xec4e_6c89, 0x4528_21e6, 0x38d0_1377, 0xbe54_66cf, 0x34e9_0c6c, 0xc0ac_29b7, 0xc97c_50dd, 0x3f84_d5b5, 0xb547_0917, ) class BLAKE384(BLAKEBase): block_size = 128 digest_size = 48 word_bits = 64 rounds = 16 pad_delim = 0x00 length_bytes = 16 iv = ( 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4, ) c = ( 0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344, 0xa409_3822_299f_31d0, 0x082e_fa98_ec4e_6c89, 0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c, 0xc0ac_29b7_c97c_50dd, 0x3f84_d5b5_b547_0917, 0x9216_d5d9_8979_fb1b, 0xd131_0ba6_98df_b5ac, 0x2ffd_72db_d01a_dfb7, 0xb8e1_afed_6a26_7e96, 0xba7c_9045_f12c_7f99, 0x24a1_9947_b391_6cf7, 0x0801_f2e2_858e_fc16, 0x6369_20d8_7157_4e69, ) class BLAKE512(BLAKEBase): block_size = 128 digest_size = 64 word_bits = 64 rounds = 16 pad_delim = 0x01 length_bytes = 16 iv = ( 0x6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1, 0x510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179, ) c = ( 0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344, 0xa409_3822_299f_31d0, 0x082e_fa98_ec4e_6c89, 0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c, 0xc0ac_29b7_c97c_50dd, 0x3f84_d5b5_b547_0917, 0x9216_d5d9_8979_fb1b, 0xd131_0ba6_98df_b5ac, 0x2ffd_72db_d01a_dfb7, 0xb8e1_afed_6a26_7e96, 0xba7c_9045_f12c_7f99, 0x24a1_9947_b391_6cf7, 0x0801_f2e2_858e_fc16, 0x6369_20d8_7157_4e69, ) class BLAKE2pBase: block_size = 0x0200 def __init__(self, data=b""): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf = bytearray() self.msg_len = 0 self.block_index = 0 self.finalized = False self.final_digest = None self.leaves = [self.create_leaf(i) for i in range(self.parallelism)] if data: self.update(data) return def create_leaf(self, node_offset): return self.blake2_func( digest_size=self.digest_size, fanout=self.parallelism, depth=2, leaf_size=0, node_offset=node_offset, node_depth=0, inner_size=self.inner_size, last_node=(node_offset == self.parallelism - 1), ) def create_root(self): return self.blake2_func( digest_size=self.digest_size, fanout=self.parallelism, depth=2, leaf_size=0, node_offset=0, node_depth=1, inner_size=self.inner_size, last_node=True, ) def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.block_index = self.block_index other.finalized = self.finalized other.final_digest = None if self.final_digest is None else bytes(self.final_digest) other.leaves = [h.copy() for h in self.leaves] return other def update(self, data): if self.finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) bs = self.leaf_block_size while len(self.buf) >= bs: block = bytes(self.buf[:bs]) del self.buf[:bs] lane = self.block_index % self.parallelism self.leaves[lane].update(block) self.block_index += 0x01 return self def finalize(self): if self.finalized: return if self.buf: lane = self.block_index % self.parallelism self.leaves[lane].update(bytes(self.buf)) self.buf.clear() root = self.create_root() for h in self.leaves: root.update(h.digest()) self.final_digest = root.digest() self.finalized = True return def digest(self): if self.finalized: return bytes(self.final_digest) c = self.copy() c.finalize() return bytes(c.final_digest) def hexdigest(self): return self.digest().hex() class BLAKE2sp(BLAKE2pBase): parallelism = 0x08 leaf_block_size = 0x40 digest_size = 0x20 inner_size = 0x20 blake2_func = hashlib.blake2s class BLAKE2bp(BLAKE2pBase): parallelism = 0x04 leaf_block_size = 0x80 digest_size = 0x40 inner_size = 0x40 blake2_func = hashlib.blake2b class BLAKE3: # BLAKE3 (default, unkeyed) / 256-bit output by default block_size = 64 digest_size = 32 block_len = 64 chunk_len = 1024 # flags chunk_start = 0x01 chunk_end = 0x02 parent = 0x04 root = 0x08 # IV == BLAKE2s IV (BLAKE3 also uses this) iv = ( 0x6a09_e667, 0xbb67_ae85, 0x3c6e_f372, 0xa54f_f53a, 0x510e_527f, 0x9b05_688c, 0x1f83_d9ab, 0x5be0_cd19, ) msg_perm = ( 0x02, 0x06, 0x03, 0x0a, 0x07, 0x00, 0x04, 0x0d, 0x01, 0x0b, 0x0c, 0x05, 0x09, 0x0e, 0x0f, 0x08, ) def __init__(self, data=b"", digest_bits=256): self.key_words = list(self.iv) # default unkeyed hash uses IV as key self.flags = 0 self.chunk_counter = 0 self.cv_stack = [] # list of chaining values (each is 8 words) self.cs_cv = self.key_words[:] self.cs_block = bytearray() self.cs_blocks_compressed = 0 # number of full 64-byte blocks already compressed in this chunk (0..15) self.digest_size = digest_bits // 8 # It can also be specified when issuing a digest if data: self.update(data) return def g(self, v, a, b, c, d, x, y): def rotr32(x, n): x &= 0xffff_ffff return ((x >> n) | (x << (32 - n))) & 0xffff_ffff v[a] = (v[a] + v[b] + x) & 0xffff_ffff v[d] = rotr32(v[d] ^ v[a], 16) v[c] = (v[c] + v[d]) & 0xffff_ffff v[b] = rotr32(v[b] ^ v[c], 12) v[a] = (v[a] + v[b] + y) & 0xffff_ffff v[d] = rotr32(v[d] ^ v[a], 8) v[c] = (v[c] + v[d]) & 0xffff_ffff v[b] = rotr32(v[b] ^ v[c], 7) return def compress(self, cv, block_words, counter, block_len, flags): # cv: 8 words, block_words: 16 words v = [0] * 16 v[0:8] = [w & 0xffff_ffff for w in cv] v[8:16] = self.iv[:] v[12] = counter & 0xffff_ffff v[13] = (counter >> 32) & 0xffff_ffff v[14] = block_len & 0xffff_ffff v[15] = flags & 0xffff_ffff m = [w & 0xffff_ffff for w in block_words] for _round in range(7): # columns self.g(v, 0, 4, 8, 12, m[0], m[1]) self.g(v, 1, 5, 9, 13, m[2], m[3]) self.g(v, 2, 6, 10, 14, m[4], m[5]) self.g(v, 3, 7, 11, 15, m[6], m[7]) # diagonals self.g(v, 0, 5, 10, 15, m[8], m[9]) self.g(v, 1, 6, 11, 12, m[10], m[11]) self.g(v, 2, 7, 8, 13, m[12], m[13]) self.g(v, 3, 4, 9, 14, m[14], m[15]) # permute message words for next round mp = [0] * 16 for i in range(16): mp[i] = m[self.msg_perm[i]] m = mp out = [0] * 16 for i in range(8): out[i] = v[i] ^ v[i + 8] for i in range(8): out[i + 8] = v[i + 8] ^ cv[i] return out def chunk_state_len(self): return self.cs_blocks_compressed * self.block_len + len(self.cs_block) def chunk_output(self): # returns an "output descriptor": (cv, block_words16, counter, block_len, flags) block_len = len(self.cs_block) block = bytes(self.cs_block) if block_len < self.block_len: block = block + b"\x00" * (self.block_len - block_len) block_words = [int.from_bytes(block[i:i + 4], "little") for i in range(0, 64, 4)] flags = self.flags | self.chunk_end if self.cs_blocks_compressed == 0: flags |= self.chunk_start return (self.cs_cv[:], block_words, self.chunk_counter, block_len, flags) def output_chaining_value(self, outdesc): cv, block_words, counter, block_len, flags = outdesc return self.compress(cv, block_words, counter, block_len, flags)[0:8] def parent_outdesc(self, left_cv, right_cv): block_words = [(w & 0xffff_ffff) for w in (left_cv + right_cv)] return (self.key_words[:], block_words, 0, self.block_len, self.flags | self.parent) def add_chunk_cv(self, chunk_cv): # stack merge by chunk index (standard Merkle stacking) i = self.chunk_counter # current chunk index (0-based) just completed cv = chunk_cv while (i & 1) == 1: left = self.cv_stack.pop() cv = self.output_chaining_value(self.parent_outdesc(left, cv)) i >>= 1 self.cv_stack.append(cv) return def update(self, data): if not data: return self if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") mv = memoryview(data) off = 0 n = len(mv) while off < n: # If the chunk is already full (1024 bytes) and we still have input left, # finalize the current chunk and move to the next one. if self.chunk_state_len() == self.chunk_len: # Reaching here is only valid when we have compressed 15 blocks and # have exactly one final full 64-byte block buffered. if not (self.cs_blocks_compressed == 15 and len(self.cs_block) == self.block_len): raise RuntimeError("invalid chunk state (chunk_len reached)") outdesc = self.chunk_output() chunk_cv = self.output_chaining_value(outdesc) self.add_chunk_cv(chunk_cv) self.chunk_counter += 1 self.cs_cv = self.key_words[:] self.cs_block = bytearray() self.cs_blocks_compressed = 0 continue # Consume as much as fits into the remaining space of the current chunk. # This must always make progress (take > 0). chunk_pos = self.chunk_state_len() want = self.chunk_len - chunk_pos take = want if want < (n - off) else (n - off) if take <= 0: raise RuntimeError("internal error: take <= 0") self.cs_block.extend(mv[off:off + take]) off += take # Compress full 64-byte blocks as long as we have them, but do not compress # the last block of the chunk here (so at most 15 blocks are compressed). while len(self.cs_block) >= self.block_len and self.cs_blocks_compressed < 15: block = bytes(self.cs_block[:self.block_len]) del self.cs_block[:self.block_len] block_words = [int.from_bytes(block[i:i + 4], "little") for i in range(0, 64, 4)] flags = self.flags if self.cs_blocks_compressed == 0: flags |= self.chunk_start out = self.compress(self.cs_cv, block_words, self.chunk_counter, self.block_len, flags) self.cs_cv = out[0:8] self.cs_blocks_compressed += 1 # If the chunk is exactly full (15 blocks compressed + 1 full block buffered) # and there is still input remaining, finalize now and advance to the next chunk. if self.cs_blocks_compressed == 15 and len(self.cs_block) == self.block_len and off < n: outdesc = self.chunk_output() chunk_cv = self.output_chaining_value(outdesc) self.add_chunk_cv(chunk_cv) self.chunk_counter += 1 self.cs_cv = self.key_words[:] self.cs_block = bytearray() self.cs_blocks_compressed = 0 return self def final_root_outdesc(self): # finalize current chunk, fold stack into parents, mark ROOT outdesc = self.chunk_output() cv = self.output_chaining_value(outdesc) # fold stack from right to left: parent(left_subtree, current) while self.cv_stack: left = self.cv_stack.pop() outdesc = self.parent_outdesc(left, cv) cv = self.output_chaining_value(outdesc) # mark root cv0, bw, ctr, bl, fl = outdesc outdesc = (cv0, bw, ctr, bl, fl | self.root) return outdesc def digest(self, length=None): if length is None: length = self.digest_size if not isinstance(length, int) or length < 0: raise ValueError("length must be a non-negative int") # clone state clone = self.__class__() clone.key_words = self.key_words[:] clone.flags = self.flags clone.chunk_counter = self.chunk_counter clone.cv_stack = [cv[:] for cv in self.cv_stack] clone.cs_cv = self.cs_cv[:] clone.cs_block = bytearray(self.cs_block) clone.cs_blocks_compressed = self.cs_blocks_compressed outdesc = clone.final_root_outdesc() cv, block_words, counter, block_len, flags = outdesc # XOF output: produce 64-byte output blocks indexed by output_counter out = bytearray() out_counter = 0 while len(out) < length: words16 = clone.compress(cv, block_words, out_counter, block_len, flags) block = b"".join((w & 0xffff_ffff).to_bytes(4, "little") for w in words16) out.extend(block) out_counter += 1 return bytes(out[:length]) def hexdigest(self, length=None): return self.digest(length).hex() class JHBase: block_size = 64 C_TEMPLATE = r""" #include static const uint8_t sbox0[16] = { 0x9, 0x0, 0x4, 0xb, 0xd, 0xc, 0x3, 0xf, 0x1, 0xa, 0x2, 0x6, 0x7, 0x5, 0x8, 0xe, }; static const uint8_t sbox1[16] = { 0x3, 0xc, 0x6, 0xd, 0x5, 0x7, 0x1, 0x9, 0xf, 0x2, 0x0, 0x4, 0xb, 0xa, 0xe, 0x8, }; static const uint8_t round_constant_init[64] = { 0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7, 0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8, 0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6, 0xe, 0xa, 0x9, 0x5, 0x7, 0xd, 0x3, 0xe, 0x3, 0xa, 0xd, 0xe, 0xc, 0x1, 0x7, 0x5, 0x1, 0x2, 0x7, 0x7, 0x5, 0x0, 0x9, 0x9, 0xd, 0xa, 0x2, 0xf, 0x5, 0x9, 0x0, 0xb, 0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa, }; static const uint8_t inv_perm[256] = { 0x00, 0x03, 0x04, 0x07, 0x08, 0x0b, 0x0c, 0x0f, 0x10, 0x13, 0x14, 0x17, 0x18, 0x1b, 0x1c, 0x1f, 0x20, 0x23, 0x24, 0x27, 0x28, 0x2b, 0x2c, 0x2f, 0x30, 0x33, 0x34, 0x37, 0x38, 0x3b, 0x3c, 0x3f, 0x40, 0x43, 0x44, 0x47, 0x48, 0x4b, 0x4c, 0x4f, 0x50, 0x53, 0x54, 0x57, 0x58, 0x5b, 0x5c, 0x5f, 0x60, 0x63, 0x64, 0x67, 0x68, 0x6b, 0x6c, 0x6f, 0x70, 0x73, 0x74, 0x77, 0x78, 0x7b, 0x7c, 0x7f, 0x80, 0x83, 0x84, 0x87, 0x88, 0x8b, 0x8c, 0x8f, 0x90, 0x93, 0x94, 0x97, 0x98, 0x9b, 0x9c, 0x9f, 0xa0, 0xa3, 0xa4, 0xa7, 0xa8, 0xab, 0xac, 0xaf, 0xb0, 0xb3, 0xb4, 0xb7, 0xb8, 0xbb, 0xbc, 0xbf, 0xc0, 0xc3, 0xc4, 0xc7, 0xc8, 0xcb, 0xcc, 0xcf, 0xd0, 0xd3, 0xd4, 0xd7, 0xd8, 0xdb, 0xdc, 0xdf, 0xe0, 0xe3, 0xe4, 0xe7, 0xe8, 0xeb, 0xec, 0xef, 0xf0, 0xf3, 0xf4, 0xf7, 0xf8, 0xfb, 0xfc, 0xff, 0x02, 0x01, 0x06, 0x05, 0x0a, 0x09, 0x0e, 0x0d, 0x12, 0x11, 0x16, 0x15, 0x1a, 0x19, 0x1e, 0x1d, 0x22, 0x21, 0x26, 0x25, 0x2a, 0x29, 0x2e, 0x2d, 0x32, 0x31, 0x36, 0x35, 0x3a, 0x39, 0x3e, 0x3d, 0x42, 0x41, 0x46, 0x45, 0x4a, 0x49, 0x4e, 0x4d, 0x52, 0x51, 0x56, 0x55, 0x5a, 0x59, 0x5e, 0x5d, 0x62, 0x61, 0x66, 0x65, 0x6a, 0x69, 0x6e, 0x6d, 0x72, 0x71, 0x76, 0x75, 0x7a, 0x79, 0x7e, 0x7d, 0x82, 0x81, 0x86, 0x85, 0x8a, 0x89, 0x8e, 0x8d, 0x92, 0x91, 0x96, 0x95, 0x9a, 0x99, 0x9e, 0x9d, 0xa2, 0xa1, 0xa6, 0xa5, 0xaa, 0xa9, 0xae, 0xad, 0xb2, 0xb1, 0xb6, 0xb5, 0xba, 0xb9, 0xbe, 0xbd, 0xc2, 0xc1, 0xc6, 0xc5, 0xca, 0xc9, 0xce, 0xcd, 0xd2, 0xd1, 0xd6, 0xd5, 0xda, 0xd9, 0xde, 0xdd, 0xe2, 0xe1, 0xe6, 0xe5, 0xea, 0xe9, 0xee, 0xed, 0xf2, 0xf1, 0xf6, 0xf5, 0xfa, 0xf9, 0xfe, 0xfd, }; static void l_transform_pair(uint8_t a, uint8_t b, uint8_t *ta, uint8_t *tb) { uint8_t tb_local = 0; uint8_t ta_local = 0; tb_local = (uint8_t)(b ^ ((((a << 1) ^ (a >> 3) ^ ((a >> 2) & 0x2)) & 0x0f))); ta_local = (uint8_t)(a ^ ((((tb_local << 1) ^ (tb_local >> 3) ^ ((tb_local >> 2) & 0x2)) & 0x0f))); *ta = (uint8_t)(ta_local & 0x0f); *tb = (uint8_t)(tb_local & 0x0f); } static void update_round_constant(uint8_t *rc) { uint8_t tem[64]; uint8_t new_rc[64]; int i = 0; for (i = 0; i < 64; i++) { tem[i] = sbox0[rc[i]]; } for (i = 0; i < 64; i += 2) { l_transform_pair(tem[i], tem[i + 1], &tem[i], &tem[i + 1]); } for (i = 0; i < 64; i += 4) { uint8_t t = tem[i + 2]; tem[i + 2] = tem[i + 3]; tem[i + 3] = t; } for (i = 0; i < 32; i++) { new_rc[i] = tem[i << 1]; new_rc[i + 32] = tem[(i << 1) + 1]; } for (i = 32; i < 64; i += 2) { uint8_t t = new_rc[i]; new_rc[i] = new_rc[i + 1]; new_rc[i + 1] = t; } for (i = 0; i < 64; i++) { rc[i] = new_rc[i]; } } static void e8_initialgroup(const uint8_t *h, uint8_t *a) { int i = 0; for (i = 0; i < 128; i++) { int sh = 7 - (i & 7); int b = i >> 3; int b2 = (i + 128) >> 3; a[i * 2] = (uint8_t)( (((h[b] >> sh) & 1) << 3) | (((h[b + 32] >> sh) & 1) << 2) | (((h[b + 64] >> sh) & 1) << 1) | ((h[b + 96] >> sh) & 1) ); a[(i * 2) + 1] = (uint8_t)( (((h[b2] >> sh) & 1) << 3) | (((h[b2 + 32] >> sh) & 1) << 2) | (((h[b2 + 64] >> sh) & 1) << 1) | ((h[b2 + 96] >> sh) & 1) ); } } static void e8_finaldegroup(uint8_t *h, const uint8_t *a) { int i = 0; for (i = 0; i < 128; i++) { h[i] = 0; } for (i = 0; i < 128; i++) { uint8_t v0 = a[i << 1]; uint8_t v1 = a[(i << 1) + 1]; int sh = 7 - (i & 7); int b = i >> 3; int b2 = (i + 128) >> 3; h[b] |= (uint8_t)(((v0 >> 3) & 1) << sh); h[b + 32] |= (uint8_t)(((v0 >> 2) & 1) << sh); h[b + 64] |= (uint8_t)(((v0 >> 1) & 1) << sh); h[b + 96] |= (uint8_t)((v0 & 1) << sh); h[b2] |= (uint8_t)(((v1 >> 3) & 1) << sh); h[b2 + 32] |= (uint8_t)(((v1 >> 2) & 1) << sh); h[b2 + 64] |= (uint8_t)(((v1 >> 1) & 1) << sh); h[b2 + 96] |= (uint8_t)((v1 & 1) << sh); } } static void e8_core(uint8_t *h) { uint8_t a[256]; uint8_t tem[256]; uint8_t new_a[256]; uint8_t rc[64]; int rnd = 0; int i = 0; e8_initialgroup(h, a); for (i = 0; i < 64; i++) { rc[i] = round_constant_init[i]; } for (rnd = 0; rnd < 42; rnd++) { for (i = 0; i < 256; i++) { int rc_bit = (rc[i >> 2] >> (3 - (i & 3))) & 1; tem[i] = rc_bit ? sbox1[a[i]] : sbox0[a[i]]; } for (i = 0; i < 256; i += 2) { l_transform_pair(tem[i], tem[i + 1], &tem[i], &tem[i + 1]); } for (i = 0; i < 256; i++) { new_a[i] = tem[inv_perm[i]]; } for (i = 0; i < 256; i++) { a[i] = new_a[i]; } update_round_constant(rc); } e8_finaldegroup(h, a); } void f8(uint8_t *h, const uint8_t *block) { int i = 0; for (i = 0; i < 64; i++) { h[i] ^= block[i]; } e8_core(h); for (i = 0; i < 64; i++) { h[i + 64] ^= block[i]; } } """ DEF_TEMPLATE = r""" void f8(uint8_t *h, const uint8_t *block); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.JHBase if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib")( ffi, lib, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return @classmethod def ensure_table(cls): if hasattr(Hash.JHBase, "SBOX_RND"): return L = [[0] * 16 for _ in range(16)] for a in range(16): for b in range(16): tb = b ^ (((a << 1) ^ (a >> 3) ^ (((a >> 2) & 0x2))) & 0xf) ta = a ^ (((tb << 1) ^ (tb >> 3) ^ (((tb >> 2) & 0x2))) & 0xf) L[a][b] = (ta, tb) Hash.JHBase.L_TRANSF = tuple(tuple(row) for row in L) P = [0] * 256 for i in range(256): swapped_i = i ^ 1 if (i & 2) else i if swapped_i % 2 == 0: k = swapped_i // 2 final_idx = k else: k = swapped_i // 2 final_idx = 128 + k if final_idx >= 128: final_idx ^= 1 P[i] = final_idx INV_P = [0] * 256 for i in range(256): INV_P[P[i]] = i Hash.JHBase.INV_P = tuple(INV_P) sbox0 = [ 0x9, 0x0, 0x4, 0xb, 0xd, 0xc, 0x3, 0xf, 0x1, 0xa, 0x2, 0x6, 0x7, 0x5, 0x8, 0xe, ] sbox1 = [ 0x3, 0xc, 0x6, 0xd, 0x5, 0x7, 0x1, 0x9, 0xf, 0x2, 0x0, 0x4, 0xb, 0xa, 0xe, 0x8, ] rc = [ 0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7, 0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8, 0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6, 0xe, 0xa, 0x9, 0x5, 0x7, 0xd, 0x3, 0xe, 0x3, 0xa, 0xd, 0xe, 0xc, 0x1, 0x7, 0x5, 0x1, 0x2, 0x7, 0x7, 0x5, 0x0, 0x9, 0x9, 0xd, 0xa, 0x2, 0xf, 0x5, 0x9, 0x0, 0xb, 0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa, ] SBOX_RND = [] for _rnd in range(42): sboxes = [] for i in range(256): rc_bit = (rc[i >> 2] >> (3 - (i & 3))) & 1 sboxes.append(tuple(sbox1) if rc_bit else tuple(sbox0)) SBOX_RND.append(tuple(sboxes)) tem = [0] * 64 for i in range(64): tem[i] = sbox0[rc[i]] for i in range(0, 64, 2): tem[i], tem[i + 1] = L[tem[i]][tem[i + 1]] for i in range(0, 64, 4): tem[i + 2], tem[i + 3] = tem[i + 3], tem[i + 2] new_rc = [0] * 64 for i in range(32): new_rc[i] = tem[i << 1] new_rc[i + 32] = tem[(i << 1) + 1] for i in range(32, 64, 2): new_rc[i], new_rc[i + 1] = new_rc[i + 1], new_rc[i] rc = new_rc Hash.JHBase.SBOX_RND = tuple(SBOX_RND) return def __init__(self, data=b""): if self.digest_size is None: raise ValueError("digest_size must be set in subclass") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.databitlen = 0 self.datasize_in_buffer = 0 # bits self.H = bytearray(128) self.A = [0] * 256 self.roundconstant = [0] * 64 self.buffer = bytearray(64) hashbitlen = self.digest_size * 8 self.H[0] = (hashbitlen >> 8) & 0xff self.H[1] = hashbitlen & 0xff self.ensure_table() self.init_cffi_backend() self.f8() if data: self.update(data) return def copy(self): other = self.__class__() other.databitlen = self.databitlen other.datasize_in_buffer = self.datasize_in_buffer other.H = bytearray(self.H) other.A = list(self.A) other.roundconstant = list(self.roundconstant) other.buffer = bytearray(self.buffer) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not data: return self self.databitlen += len(data) * 8 off = 0 while off < len(data): buf_bytes = self.datasize_in_buffer // 8 take = min(64 - buf_bytes, len(data) - off) self.buffer[buf_bytes:buf_bytes + take] = data[off:off + take] self.datasize_in_buffer += take * 8 off += take if self.datasize_in_buffer == 512: self.f8() self.datasize_in_buffer = 0 for i in range(64): self.buffer[i] = 0 return self def finalize(self): if (self.databitlen & 0x1_ff) == 0: for i in range(64): self.buffer[i] = 0 self.buffer[0] = 0x80 x = self.databitlen & 0xffff_ffff_ffff_ffff self.buffer[63] = x & 0xff self.buffer[62] = (x >> 8) & 0xff self.buffer[61] = (x >> 16) & 0xff self.buffer[60] = (x >> 24) & 0xff self.buffer[59] = (x >> 32) & 0xff self.buffer[58] = (x >> 40) & 0xff self.buffer[57] = (x >> 48) & 0xff self.buffer[56] = (x >> 56) & 0xff self.f8() self.datasize_in_buffer = 0 return if (self.datasize_in_buffer & 7) != 0: raise ValueError("internal error: datasize_in_buffer not multiple of 8") bytepos = ((self.databitlen & 0x1_ff) >> 3) for i in range(bytepos, 64): self.buffer[i] = 0 self.buffer[bytepos] |= 1 << (7 - (self.databitlen & 7)) self.f8() for i in range(64): self.buffer[i] = 0 x = self.databitlen & 0xffff_ffff_ffff_ffff self.buffer[63] = x & 0xff self.buffer[62] = (x >> 8) & 0xff self.buffer[61] = (x >> 16) & 0xff self.buffer[60] = (x >> 24) & 0xff self.buffer[59] = (x >> 32) & 0xff self.buffer[58] = (x >> 40) & 0xff self.buffer[57] = (x >> 48) & 0xff self.buffer[56] = (x >> 56) & 0xff self.f8() self.datasize_in_buffer = 0 return def digest(self): c = self.copy() c.finalize() return bytes(c.H[128 - self.digest_size:]) def hexdigest(self): return self.digest().hex() def f8(self): if self.USE_CFFI: h_buf = self.cffi.ffi.new("uint8_t[]", bytes(self.H)) block_buf = self.cffi.ffi.new("uint8_t[]", bytes(self.buffer)) self.cffi.lib.f8(h_buf, block_buf) self.H = bytearray(self.cffi.ffi.buffer(h_buf, 128)[:]) return def e8_initialgroup(): A = self.A H = self.H for i in range(128): sh = 7 - (i & 7) b = i >> 3 A[i * 2] = ((H[b] >> sh) & 1) << 3 | \ ((H[b + 32] >> sh) & 1) << 2 | \ ((H[b + 64] >> sh) & 1) << 1 | \ ((H[b + 96] >> sh) & 1) b2 = (i + 128) >> 3 A[i * 2 + 1] = ((H[b2] >> sh) & 1) << 3 | \ ((H[b2 + 32] >> sh) & 1) << 2 | \ ((H[b2 + 64] >> sh) & 1) << 1 | \ ((H[b2 + 96] >> sh) & 1) return A def e8_finaldegroup(): A = self.A H = bytearray(128) for i in range(128): v0 = A[i << 1] v1 = A[(i << 1) + 1] sh = 7 - (i & 7) b = i >> 3 H[b] |= ((v0 >> 3) & 1) << sh H[b + 32] |= ((v0 >> 2) & 1) << sh H[b + 64] |= ((v0 >> 1) & 1) << sh H[b + 96] |= (v0 & 1) << sh b2 = (i + 128) >> 3 H[b2] |= ((v1 >> 3) & 1) << sh H[b2 + 32] |= ((v1 >> 2) & 1) << sh H[b2 + 64] |= ((v1 >> 1) & 1) << sh H[b2 + 96] |= (v1 & 1) << sh self.H = H return def e8(): e8_initialgroup() A = self.A L = self.L_TRANSF INV = self.INV_P for rnd in range(42): sboxes = self.SBOX_RND[rnd] tem = [s[a] for s, a in zip(sboxes, A)] for i in range(0, 256, 2): tem[i], tem[i + 1] = L[tem[i]][tem[i + 1]] A = [tem[i] for i in INV] self.A = A e8_finaldegroup() return for i in range(64): self.H[i] ^= self.buffer[i] e8() for i in range(64): self.H[i + 64] ^= self.buffer[i] return class JH224(JHBase): digest_size = 28 class JH256(JHBase): digest_size = 32 class JH384(JHBase): digest_size = 48 class JH512(JHBase): digest_size = 64 class GroestlBase: sbox = ( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ) @classmethod def ensure_table(cls): if hasattr(Hash.GroestlBase, "mul2"): return def gf_mul(a, b): res = 0 for _ in range(8): if b & 1: res ^= a hi = a & 0x80 a = (a << 1) & 0xff if hi: a ^= 0x1b b >>= 1 return res m2, m3, m4, m5, m7 = [], [], [], [], [] for i in range(256): m2.append(gf_mul(i, 2)) m3.append(gf_mul(i, 3)) m4.append(gf_mul(i, 4)) m5.append(gf_mul(i, 5)) m7.append(gf_mul(i, 7)) Hash.GroestlBase.mul2 = tuple(m2) Hash.GroestlBase.mul3 = tuple(m3) Hash.GroestlBase.mul4 = tuple(m4) Hash.GroestlBase.mul5 = tuple(m5) Hash.GroestlBase.mul7 = tuple(m7) return def __init__(self, data=b""): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf = bytearray() self.msg_len = 0 self.ensure_table() self.h = self.init_state() if data: self.update(data) return def init_state(self): iv = bytearray(self.block_size) size = self.digest_size * 8 p = len(iv) - 1 while size > 0: iv[p] = size & 0xff size >>= 8 p -= 1 return self.bytes_to_state(bytes(iv)) def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.h = [row[:] for row in self.h] return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return c.output() def hexdigest(self): return self.digest().hex() def finalize(self): bs = self.block_size ml = self.msg_len zeros_pad = (-(ml + 1 + 8)) % bs total_blocks = (ml + 1 + zeros_pad + 8) // bs self.buf.append(0x80) self.buf.extend(b"\x00" * zeros_pad) self.buf.extend(struct.pack(">Q", total_blocks)) while len(self.buf) >= bs: block = bytes(self.buf[:bs]) del self.buf[:bs] self.compress(block) return def output(self): hp = self.permutation([row[:] for row in self.h], "P") res = self.xor_state(hp, self.h) out = self.state_to_bytes(res) return out[-self.digest_size:] def compress(self, block): m = self.bytes_to_state(block) pside = self.permutation(self.xor_state(self.h, m), "P") qside = self.permutation([row[:] for row in m], "Q") self.h = self.xor_state(self.xor_state(pside, qside), self.h) return def permutation(self, state, which): for rnd in range(self.rounds): self.add_round_constant(state, which, rnd) self.sub_bytes(state) self.shift_bytes(state, which) self.mix_bytes(state) return state def add_round_constant(self, state, which, rnd): cols = self.block_size // 8 if which == "P": for c in range(cols): state[0][c] ^= ((c << 4) ^ rnd) & 0xff else: for r in range(8): for c in range(cols): if r == 7: state[r][c] ^= (((0xff - ((c << 4) & 0xff)) & 0xff) ^ rnd) & 0xff else: state[r][c] ^= 0xff return def sub_bytes(self, state): s = self.sbox for r in range(8): state[r] = [s[x] for x in state[r]] return def shift_bytes(self, state, which): cols = self.block_size // 8 sigma = self.sigma_p if which == "P" else self.sigma_q for r in range(8): sh = sigma[r] % cols if sh: row = state[r] state[r] = row[sh:] + row[:sh] return def mix_bytes(self, state): cols = self.block_size // 8 m2, m3, m4, m5, m7 = self.mul2, self.mul3, self.mul4, self.mul5, self.mul7 for c in range(cols): s0, s1, s2, s3, s4, s5, s6, s7 = (state[r][c] for r in range(8)) state[0][c] = m2[s0] ^ m2[s1] ^ m3[s2] ^ m4[s3] ^ m5[s4] ^ m3[s5] ^ m5[s6] ^ m7[s7] state[1][c] = m7[s0] ^ m2[s1] ^ m2[s2] ^ m3[s3] ^ m4[s4] ^ m5[s5] ^ m3[s6] ^ m5[s7] state[2][c] = m5[s0] ^ m7[s1] ^ m2[s2] ^ m2[s3] ^ m3[s4] ^ m4[s5] ^ m5[s6] ^ m3[s7] state[3][c] = m3[s0] ^ m5[s1] ^ m7[s2] ^ m2[s3] ^ m2[s4] ^ m3[s5] ^ m4[s6] ^ m5[s7] state[4][c] = m5[s0] ^ m3[s1] ^ m5[s2] ^ m7[s3] ^ m2[s4] ^ m2[s5] ^ m3[s6] ^ m4[s7] state[5][c] = m4[s0] ^ m5[s1] ^ m3[s2] ^ m5[s3] ^ m7[s4] ^ m2[s5] ^ m2[s6] ^ m3[s7] state[6][c] = m3[s0] ^ m4[s1] ^ m5[s2] ^ m3[s3] ^ m5[s4] ^ m7[s5] ^ m2[s6] ^ m2[s7] state[7][c] = m2[s0] ^ m3[s1] ^ m4[s2] ^ m5[s3] ^ m3[s4] ^ m5[s5] ^ m7[s6] ^ m2[s7] return def bytes_to_state(self, block): cols = self.block_size // 8 return [[block[c * 8 + r] for c in range(cols)] for r in range(8)] def state_to_bytes(self, state): cols = self.block_size // 8 out = bytearray(self.block_size) for r in range(8): row = state[r] for c in range(cols): out[c * 8 + r] = row[c] return bytes(out) def xor_state(self, a, b): return [[x ^ y for x, y in zip(ra, rb)] for ra, rb in zip(a, b)] class Groestl224(GroestlBase): block_size = 64 digest_size = 28 rounds = 10 sigma_p = (0, 1, 2, 3, 4, 5, 6, 7) sigma_q = (1, 3, 5, 7, 0, 2, 4, 6) class Groestl256(GroestlBase): block_size = 64 digest_size = 32 rounds = 10 sigma_p = (0, 1, 2, 3, 4, 5, 6, 7) sigma_q = (1, 3, 5, 7, 0, 2, 4, 6) class Groestl384(GroestlBase): block_size = 128 digest_size = 48 rounds = 14 sigma_p = (0, 1, 2, 3, 4, 5, 6, 11) sigma_q = (1, 3, 5, 11, 0, 2, 4, 6) class Groestl512(GroestlBase): block_size = 128 digest_size = 64 rounds = 14 sigma_p = (0, 1, 2, 3, 4, 5, 6, 11) sigma_q = (1, 3, 5, 11, 0, 2, 4, 6) class SkeinBBase: type_cfg = 0x04 type_msg = 0x30 type_out = 0x3f flag_first = 0x4000_0000_0000_0000 flag_final = 0x8000_0000_0000_0000 c240 = 0x1bd1_1bda_a9fc_1a22 def __init__(self, data=b"", digest_bits=None): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if digest_bits is None: digest_bits = self.block_size * 8 if digest_bits not in (0x100, 0x200, 0x400): raise ValueError("digest_bits must be 256, 512, or 1024") self.digest_bits = int(digest_bits) self.digest_size = self.digest_bits // 8 self.buf = bytearray() self.msg_len = 0 self.pos = 0 self.is_first = True self.state = self.make_initial_state(self.digest_bits) if data: self.update(data) return def copy(self): other = self.__class__(b"", digest_bits=self.digest_bits) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.pos = self.pos other.is_first = self.is_first other.state = list(self.state) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not data: return self self.msg_len += len(data) self.buf.extend(data) while len(self.buf) > self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.pos += self.block_size self.state = self.ubi_compress_words(self.state, block, self.pos, self.type_msg, self.is_first, False) self.is_first = False return self def digest(self): c = self.copy() c.finalize() return c.output_digest() def hexdigest(self): return self.digest().hex() def finalize(self): nm = self.msg_len if nm == 0: block = b"\x00" * self.block_size self.state = self.ubi_compress_words(self.state, block, 0, self.type_msg, True, True) self.pos = 0 self.is_first = False self.buf = bytearray() return if len(self.buf) > self.block_size: raise ValueError("internal error: buffer larger than block size") block = bytes(self.buf) + b"\x00" * (self.block_size - len(self.buf)) self.state = self.ubi_compress_words(self.state, block, nm, self.type_msg, self.is_first, True) self.pos = nm self.is_first = False self.buf = bytearray() return def output_digest(self): out = bytearray() need = self.digest_size counter = 0 while len(out) < need: block = struct.pack("> (64 - n))) & 0xffff_ffff_ffff_ffff n = self.word_count nr = self.rounds k = list(key_words) parity = self.c240 for x in k: parity ^= x k.append(parity) t = [tweak0, tweak1, tweak0 ^ tweak1] v = pt_words[:] for d in range(nr): if (d & 0x03) == 0: sub = self.threefish_subkey(k, t, d >> 2) for i in range(n): v[i] = (v[i] + sub[i]) & 0xffff_ffff_ffff_ffff f = [0] * n rset = self.rotation[d & 0x07] for j in range(n // 2): x0 = v[2 * j] x1 = v[2 * j + 1] y0 = (x0 + x1) & 0xffff_ffff_ffff_ffff y1 = rol64(x1, rset[j]) ^ y0 f[2 * j] = y0 f[2 * j + 1] = y1 v = [f[self.perm[i]] for i in range(n)] sub = self.threefish_subkey(k, t, nr >> 2) for i in range(n): v[i] = (v[i] + sub[i]) & 0xffff_ffff_ffff_ffff return v def threefish_subkey(self, k_ext, t_ext, s): n = self.word_count sub = [0] * n for i in range(n): sub[i] = k_ext[(s + i) % (n + 1)] sub[n - 3] = (sub[n - 3] + t_ext[s % 3]) & 0xffff_ffff_ffff_ffff sub[n - 2] = (sub[n - 2] + t_ext[(s + 1) % 3]) & 0xffff_ffff_ffff_ffff sub[n - 1] = (sub[n - 1] + (s & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff return sub class Skein256(SkeinBBase): block_size = 0x20 word_count = 0x04 rounds = 0x48 rotation = ( (0x0e, 0x10), (0x34, 0x39), (0x17, 0x28), (0x05, 0x25), (0x19, 0x21), (0x2e, 0x0c), (0x3a, 0x16), (0x20, 0x20), ) perm = (0x00, 0x03, 0x02, 0x01) class Skein512(SkeinBBase): block_size = 0x40 word_count = 0x08 rounds = 0x48 rotation = ( (0x2e, 0x24, 0x13, 0x25), (0x21, 0x1b, 0x0e, 0x2a), (0x11, 0x31, 0x24, 0x27), (0x2c, 0x09, 0x36, 0x38), (0x27, 0x1e, 0x22, 0x18), (0x0d, 0x32, 0x0a, 0x11), (0x19, 0x1d, 0x27, 0x2b), (0x08, 0x23, 0x38, 0x16), ) perm = (0x02, 0x01, 0x04, 0x07, 0x06, 0x05, 0x00, 0x03) class Skein1024(SkeinBBase): block_size = 0x80 word_count = 0x10 rounds = 0x50 rotation = ( (0x18, 0x0d, 0x08, 0x2f, 0x08, 0x11, 0x16, 0x25), (0x26, 0x13, 0x0a, 0x37, 0x31, 0x12, 0x17, 0x34), (0x21, 0x04, 0x33, 0x0d, 0x22, 0x29, 0x3b, 0x11), (0x05, 0x14, 0x30, 0x29, 0x2f, 0x1c, 0x10, 0x19), (0x29, 0x09, 0x25, 0x1f, 0x0c, 0x2f, 0x2c, 0x1e), (0x10, 0x22, 0x38, 0x33, 0x04, 0x35, 0x2a, 0x29), (0x1f, 0x2c, 0x2f, 0x2e, 0x13, 0x2a, 0x2c, 0x19), (0x09, 0x30, 0x23, 0x34, 0x17, 0x1f, 0x25, 0x14), ) perm = (0x00, 0x09, 0x02, 0x0d, 0x06, 0x0b, 0x04, 0x0f, 0x0a, 0x07, 0x0c, 0x03, 0x0e, 0x05, 0x08, 0x01) class ShabalBBase: block_size = 64 digest_size = None shabal_block_words = 16 shabal_param_r = 12 shabal_param_p = 3 shabal_param_e = 3 o1 = 13 o2 = 9 o3 = 6 def __init__(self, data=b""): if self.digest_size is None: raise ValueError("digest_size must be set in derived class") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.a = [0] * self.shabal_param_r self.b = [0] * self.shabal_block_words self.c = [0] * self.shabal_block_words self.wlow = 0xffff_ffff self.whigh = 0xffff_ffff self.buf = bytearray() self.msg_len = 0 self.init_state() if data: self.update(data) return def copy(self): other = self.__class__() other.a = self.a[:] other.b = self.b[:] other.c = self.c[:] other.wlow = self.wlow other.whigh = self.whigh other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.process_block(block) return self def digest(self): c = self.copy() c.finalize() words = c.digest_size * 8 // 32 start = self.shabal_block_words - words out = bytearray() for i in range(start, self.shabal_block_words): out.extend(struct.pack("= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.process_block(block) self.buf.append(0x80) while len(self.buf) < self.block_size: self.buf.append(0x00) m = self.decode_block(bytes(self.buf[:self.block_size])) self.buf = bytearray() self.input_block_add(m) self.xor_counter() self.apply_perm(m) for _ in range(self.shabal_param_e): self.swap_bc() self.xor_counter() self.apply_perm(m) return def init_state(self): hashbitlen = self.digest_size * 8 m = [0] * self.shabal_block_words for j in range(0, 2 * self.shabal_block_words, self.shabal_block_words): for i in range(self.shabal_block_words): m[i] = hashbitlen + j + i self.input_block_add(m) self.xor_counter() self.apply_perm(m) self.input_block_sub(m) self.swap_bc() self.incr_counter() return def swap_bc(self): self.b, self.c = self.c, self.b return def decode_block(self, block): if len(block) != self.block_size: raise ValueError("block must be 64 bytes") return list(struct.unpack("<16I", block)) def input_block_add(self, m): for i in range(self.shabal_block_words): self.b[i] = (self.b[i] + m[i]) & 0xffff_ffff return def input_block_sub(self, m): for i in range(self.shabal_block_words): self.c[i] = (self.c[i] - m[i]) & 0xffff_ffff return def xor_counter(self): self.a[0] ^= self.wlow self.a[1] ^= self.whigh return def incr_counter(self): self.wlow = (self.wlow + 1) & 0xffff_ffff if self.wlow == 0: self.whigh = (self.whigh + 1) & 0xffff_ffff return def apply_perm(self, m): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff for i in range(self.shabal_block_words): t = self.b[i] self.b[i] = ((t << 17) | (t >> 15)) & 0xffff_ffff for j in range(self.shabal_param_p): for i in range(self.shabal_block_words): idx_a = (i + self.shabal_block_words * j) % self.shabal_param_r idx_am1 = (i + self.shabal_block_words * j + (self.shabal_param_r - 1)) % self.shabal_param_r xa = self.a[idx_a] xam1 = self.a[idx_am1] rot15 = ((xam1 << 15) | (xam1 >> 17)) & 0xffff_ffff v = (5 * rot15) & 0xffff_ffff u_in = xa ^ v ^ self.c[(8 + self.shabal_block_words - i) % self.shabal_block_words] u = (3 * u_in) & 0xffff_ffff x_new = u ^ self.b[(i + self.o1) % self.shabal_block_words] x_new ^= self.b[(i + self.o2) % self.shabal_block_words] & \ (~self.b[(i + self.o3) % self.shabal_block_words]) & 0xffff_ffff self.a[idx_a] = x_new ^ m[i] tb = self.b[i] self.b[i] = (rol32(tb, 1) ^ ((~self.a[idx_a]) & 0xffff_ffff)) & 0xffff_ffff for j in range(3 * self.shabal_param_r): idx_a = (3 * self.shabal_param_r - 1 - j) % self.shabal_param_r idx_c = (3 * self.shabal_param_r * self.shabal_block_words + 6 - j) % self.shabal_block_words self.a[idx_a] = (self.a[idx_a] + self.c[idx_c]) & 0xffff_ffff return def process_block(self, block): m = self.decode_block(block) self.input_block_add(m) self.xor_counter() self.apply_perm(m) self.input_block_sub(m) self.swap_bc() self.incr_counter() return class Shabal192(ShabalBBase): digest_size = 24 class Shabal224(ShabalBBase): digest_size = 28 class Shabal256(ShabalBBase): digest_size = 32 class Shabal384(ShabalBBase): digest_size = 48 class Shabal512(ShabalBBase): digest_size = 64 class XXHashBase: prime32_1 = 0x9e37_79b1 prime32_2 = 0x85eb_ca77 prime32_3 = 0xc2b2_ae3d prime32_4 = 0x27d4_eb2f prime32_5 = 0x1656_67b1 prime64_1 = 0x9e37_79b1_85eb_ca87 prime64_2 = 0xc2b2_ae3d_27d4_eb4f prime64_3 = 0x1656_67b1_9e37_79f9 prime64_4 = 0x85eb_ca77_c2b2_ae63 prime64_5 = 0x27d4_eb2f_1656_67c5 prime_mx1 = 0x1656_6791_9e37_79f9 prime_mx2 = 0x9fb2_1c65_1e98_df25 default_secret = bytes([ 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, ]) def __init__(self, seed=0): self.seed = seed & 0xffff_ffff_ffff_ffff self.buf = b"" self.total = 0 return def update(self, data): if not data: return self if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") b = bytes(data) self.buf += b self.total += len(b) return self def hexdigest(self): return self.digest().hex() def rol32(self, x, r): x &= 0xffff_ffff r &= 31 return ((x << r) | (x >> (32 - r))) & 0xffff_ffff def rol64(self, x, r): x &= 0xffff_ffff_ffff_ffff r &= 63 return ((x << r) | (x >> (64 - r))) & 0xffff_ffff_ffff_ffff def bswap32(self, x): x &= 0xffff_ffff return int.from_bytes(x.to_bytes(4, "little"), "big") def bswap64(self, x): x &= 0xffff_ffff_ffff_ffff return int.from_bytes(x.to_bytes(8, "little"), "big") def u32le(self, b, off): return int.from_bytes(b[off:off + 4], "little") def u64le(self, b, off): return int.from_bytes(b[off:off + 8], "little") def lower32(self, x): return x & 0xffff_ffff def higher32(self, x): return (x >> 32) & 0xffff_ffff def lower64(self, x): return x & 0xffff_ffff_ffff_ffff def higher64(self, x): return (x >> 64) & 0xffff_ffff_ffff_ffff def round32(self, acc, lane): acc = (acc + lane * self.prime32_2) & 0xffff_ffff acc = self.rol32(acc, 13) return (acc * self.prime32_1) & 0xffff_ffff def round64(self, acc, lane): acc = (acc + lane * self.prime64_2) & 0xffff_ffff_ffff_ffff acc = self.rol64(acc, 31) return (acc * self.prime64_1) & 0xffff_ffff_ffff_ffff class XXH32(XXHashBase): digest_size = 4 def avalanche32(self, h): h &= 0xffff_ffff h ^= (h >> 15) h = (h * self.prime32_2) & 0xffff_ffff h ^= (h >> 13) h = (h * self.prime32_3) & 0xffff_ffff h ^= (h >> 16) return h def digest(self): data = self.buf seed32 = self.seed & 0xffff_ffff n = len(data) p = 0 if n >= 16: acc1 = (seed32 + self.prime32_1 + self.prime32_2) & 0xffff_ffff acc2 = (seed32 + self.prime32_2) & 0xffff_ffff acc3 = (seed32 + 0) & 0xffff_ffff acc4 = (seed32 - self.prime32_1) & 0xffff_ffff limit = n - 16 while p <= limit: acc1 = self.round32(acc1, self.u32le(data, p)) p += 4 acc2 = self.round32(acc2, self.u32le(data, p)) p += 4 acc3 = self.round32(acc3, self.u32le(data, p)) p += 4 acc4 = self.round32(acc4, self.u32le(data, p)) p += 4 h32 = ( self.rol32(acc1, 1) + self.rol32(acc2, 7) + self.rol32(acc3, 12) + self.rol32(acc4, 18) ) & 0xffff_ffff else: h32 = (seed32 + self.prime32_5) & 0xffff_ffff h32 = (h32 + n) & 0xffff_ffff while p + 4 <= n: h32 = (h32 + self.u32le(data, p) * self.prime32_3) & 0xffff_ffff h32 = self.rol32(h32, 17) h32 = (h32 * self.prime32_4) & 0xffff_ffff p += 4 while p < n: h32 = (h32 + data[p] * self.prime32_5) & 0xffff_ffff h32 = self.rol32(h32, 11) h32 = (h32 * self.prime32_1) & 0xffff_ffff p += 1 return self.avalanche32(h32).to_bytes(4, "big") class XXH64(XXHashBase): digest_size = 8 def avalanche_xxh64(self, x): x &= 0xffff_ffff_ffff_ffff x ^= (x >> 33) x = (x * self.prime64_2) & 0xffff_ffff_ffff_ffff x ^= (x >> 29) x = (x * self.prime64_3) & 0xffff_ffff_ffff_ffff x ^= (x >> 32) return x def digest(self): data = self.buf seed = self.seed n = len(data) p = 0 if n >= 32: acc1 = (seed + self.prime64_1 + self.prime64_2) & 0xffff_ffff_ffff_ffff acc2 = (seed + self.prime64_2) & 0xffff_ffff_ffff_ffff acc3 = (seed + 0) & 0xffff_ffff_ffff_ffff acc4 = (seed - self.prime64_1) & 0xffff_ffff_ffff_ffff limit = n - 32 while p <= limit: acc1 = self.round64(acc1, self.u64le(data, p)) p += 8 acc2 = self.round64(acc2, self.u64le(data, p)) p += 8 acc3 = self.round64(acc3, self.u64le(data, p)) p += 8 acc4 = self.round64(acc4, self.u64le(data, p)) p += 8 h64 = ( self.rol64(acc1, 1) + self.rol64(acc2, 7) + self.rol64(acc3, 12) + self.rol64(acc4, 18) ) & 0xffff_ffff_ffff_ffff h64 ^= self.round64(0, acc1) h64 = (h64 * self.prime64_1 + self.prime64_4) & 0xffff_ffff_ffff_ffff h64 ^= self.round64(0, acc2) h64 = (h64 * self.prime64_1 + self.prime64_4) & 0xffff_ffff_ffff_ffff h64 ^= self.round64(0, acc3) h64 = (h64 * self.prime64_1 + self.prime64_4) & 0xffff_ffff_ffff_ffff h64 ^= self.round64(0, acc4) h64 = (h64 * self.prime64_1 + self.prime64_4) & 0xffff_ffff_ffff_ffff else: h64 = (seed + self.prime64_5) & 0xffff_ffff_ffff_ffff h64 = (h64 + n) & 0xffff_ffff_ffff_ffff while p + 8 <= n: h64 ^= self.round64(0, self.u64le(data, p)) h64 = (self.rol64(h64, 27) * self.prime64_1 + self.prime64_4) & 0xffff_ffff_ffff_ffff p += 8 if p + 4 <= n: h64 ^= (self.u32le(data, p) * self.prime64_1) & 0xffff_ffff_ffff_ffff h64 = (self.rol64(h64, 23) * self.prime64_2 + self.prime64_3) & 0xffff_ffff_ffff_ffff p += 4 while p < n: h64 ^= (data[p] * self.prime64_5) & 0xffff_ffff_ffff_ffff h64 = (self.rol64(h64, 11) * self.prime64_1) & 0xffff_ffff_ffff_ffff p += 1 return self.avalanche_xxh64(h64).to_bytes(8, "big") class XXH3Base(XXH64): def derive_secret(self, seed): seed &= 0xffff_ffff_ffff_ffff words = [self.u64le(self.default_secret, i * 8) for i in range(24)] for i in range(12): words[i * 2] = (words[i * 2] + seed) & 0xffff_ffff_ffff_ffff words[i * 2 + 1] = (words[i * 2 + 1] - seed) & 0xffff_ffff_ffff_ffff return b"".join(w.to_bytes(8, "little") for w in words) def secret_for_length(self, n): if n > 240 and self.seed != 0: return self.derive_secret(self.seed) return self.default_secret def mix_step(self, data16, secret, secret_offset, seed): dw0 = self.u64le(data16, 0) dw1 = self.u64le(data16, 8) sw0 = self.u64le(secret, secret_offset) sw1 = self.u64le(secret, secret_offset + 8) add = (sw0 + seed) & 0xffff_ffff_ffff_ffff sub = (sw1 - seed) & 0xffff_ffff_ffff_ffff mul = (dw0 ^ add) * (dw1 ^ sub) return self.lower64(mul) ^ self.higher64(mul) def accumulate_stripe(self, acc, stripe64bytes, secret, secret_offset): secret_words = [self.u64le(secret, secret_offset + i * 8) for i in range(8)] stripe_words = [self.u64le(stripe64bytes, i * 8) for i in range(8)] for i in range(8): value = stripe_words[i] ^ secret_words[i] acc[i ^ 1] = (acc[i ^ 1] + stripe_words[i]) & 0xffff_ffff_ffff_ffff prod = self.lower32(value) * self.higher32(value) acc[i] = (acc[i] + prod) & 0xffff_ffff_ffff_ffff return acc def scramble_acc(self, acc, secret): secret_len = len(secret) tail = secret[secret_len - 64:secret_len] secret_words = [self.u64le(tail, i * 8) for i in range(8)] for i in range(8): v = acc[i] v ^= (v >> 47) v ^= secret_words[i] acc[i] = (v * self.prime32_1) & 0xffff_ffff_ffff_ffff return acc def avalanche_xxh3(self, x): x &= 0xffff_ffff_ffff_ffff x ^= (x >> 37) x = (x * self.prime_mx1) & 0xffff_ffff_ffff_ffff x ^= (x >> 32) return x def final_merge(self, acc, secret, init_value, secret_offset): secret_words = [self.u64le(secret, secret_offset + i * 8) for i in range(8)] result = init_value & 0xffff_ffff_ffff_ffff for i in range(4): a = (acc[i * 2] ^ secret_words[i * 2]) & 0xffff_ffff_ffff_ffff b = (acc[i * 2 + 1] ^ secret_words[i * 2 + 1]) & 0xffff_ffff_ffff_ffff mul = a * b result = (result + (self.lower64(mul) ^ self.higher64(mul))) & 0xffff_ffff_ffff_ffff return self.avalanche_xxh3(result) class XXH3_64(XXH3Base): digest_size = 8 def digest(self): data = self.buf n = len(data) seed = self.seed secret = self.secret_for_length(n) if n == 0: sw0 = self.u64le(secret, 56) sw1 = self.u64le(secret, 64) value = seed ^ sw0 ^ sw1 return self.avalanche_xxh64(value).to_bytes(8, "big") if n <= 3: combined = data[n - 1] | (n << 8) | (data[0] << 16) | (data[n >> 1] << 24) s0 = self.u32le(secret, 0) s1 = self.u32le(secret, 4) value = (((s0 ^ s1) + seed) & 0xffff_ffff_ffff_ffff) ^ combined return self.avalanche_xxh64(value).to_bytes(8, "big") if n <= 8: input_first = self.u32le(data, 0) input_last = self.u32le(data, n - 4) modified_seed = seed ^ (self.bswap32(seed) << 32) sw0 = self.u64le(secret, 8) sw1 = self.u64le(secret, 16) combined = (input_last | (input_first << 32)) & 0xffff_ffff_ffff_ffff value = (((sw0 ^ sw1) - modified_seed) & 0xffff_ffff_ffff_ffff) ^ combined value ^= self.rol64(value, 49) ^ self.rol64(value, 24) value = (value * self.prime_mx2) & 0xffff_ffff_ffff_ffff value ^= ((value >> 35) + n) & 0xffff_ffff_ffff_ffff value = (value * self.prime_mx2) & 0xffff_ffff_ffff_ffff value ^= (value >> 28) return value.to_bytes(8, "big") if n <= 16: input_first = self.u64le(data, 0) input_last = self.u64le(data, n - 8) sw0 = self.u64le(secret, 24) sw1 = self.u64le(secret, 32) sw2 = self.u64le(secret, 40) sw3 = self.u64le(secret, 48) low = (((sw0 ^ sw1) + seed) & 0xffff_ffff_ffff_ffff) ^ input_first high = (((sw2 ^ sw3) - seed) & 0xffff_ffff_ffff_ffff) ^ input_last mul = low * high value = n + self.bswap64(low) + high + (self.lower64(mul) ^ self.higher64(mul)) return self.avalanche_xxh3(value).to_bytes(8, "big") if n <= 128: acc = (n * self.prime64_1) & 0xffff_ffff_ffff_ffff num_rounds = ((n - 1) >> 5) + 1 for i in range(num_rounds - 1, -1, -1): off_start = i * 16 off_end = n - i * 16 - 16 ms1 = self.mix_step(data[off_start:off_start + 16], secret, i * 32, seed) ms2 = self.mix_step(data[off_end:off_end + 16], secret, i * 32 + 16, seed) acc = (acc + ms1) & 0xffff_ffff_ffff_ffff acc = (acc + ms2) & 0xffff_ffff_ffff_ffff return self.avalanche_xxh3(acc).to_bytes(8, "big") if n <= 240: acc = (n * self.prime64_1) & 0xffff_ffff_ffff_ffff num_chunks = n >> 4 for i in range(8): ms = self.mix_step(data[i * 16:i * 16 + 16], secret, i * 16, seed) acc = (acc + ms) & 0xffff_ffff_ffff_ffff acc = self.avalanche_xxh3(acc) for i in range(8, num_chunks): ms = self.mix_step(data[i * 16:i * 16 + 16], secret, (i - 8) * 16 + 3, seed) acc = (acc + ms) & 0xffff_ffff_ffff_ffff ms = self.mix_step(data[n - 16:n], secret, 119, seed) return self.avalanche_xxh3(acc + ms).to_bytes(8, "big") # large secret_len = len(secret) stripes_per_block = (secret_len - 64) // 8 block_size = 64 * stripes_per_block acc = [ self.prime32_3, self.prime64_1, self.prime64_2, self.prime64_3, self.prime64_4, self.prime32_2, self.prime64_5, self.prime32_1, ] p = 0 while n - p > block_size: block = data[p:p + block_size] for stripe_index in range(stripes_per_block): stripe = block[stripe_index * 64:stripe_index * 64 + 64] acc = self.accumulate_stripe(acc, stripe, secret, stripe_index * 8) acc = self.scramble_acc(acc, secret) p += block_size # last round last = data[p:] last_len = len(last) n_full = (last_len - 1) // 64 for stripe_index in range(n_full): stripe = last[stripe_index * 64:stripe_index * 64 + 64] acc = self.accumulate_stripe(acc, stripe, secret, stripe_index * 8) last_stripe = data[n - 64:n] acc = self.accumulate_stripe(acc, last_stripe, secret, secret_len - 71) return self.final_merge(acc, secret, n * self.prime64_1, 11).to_bytes(8, "big") class XXH3_128(XXH3Base): digest_size = 16 def digest(self): data = self.buf n = len(data) seed = self.seed secret = self.secret_for_length(n) def pack128(low, high): return high.to_bytes(8, "big") + low.to_bytes(8, "big") if n == 0: sw0 = self.u64le(secret, 64) sw1 = self.u64le(secret, 72) sw2 = self.u64le(secret, 80) sw3 = self.u64le(secret, 88) low = self.avalanche_xxh64(seed ^ sw0 ^ sw1) high = self.avalanche_xxh64(seed ^ sw2 ^ sw3) return pack128(low, high) if n <= 3: combined = data[n - 1] | (n << 8) | (data[0] << 16) | (data[n >> 1] << 24) s0 = self.u32le(secret, 0) s1 = self.u32le(secret, 4) s2 = self.u32le(secret, 8) s3 = self.u32le(secret, 12) low = (((s0 ^ s1) + seed) & 0xffff_ffff_ffff_ffff) ^ combined rot = self.rol32(self.bswap32(combined), 13) high = (((s2 ^ s3) - seed) & 0xffff_ffff_ffff_ffff) ^ rot low = self.avalanche_xxh64(low) high = self.avalanche_xxh64(high) return pack128(low, high) if n <= 8: input_first = self.u32le(data, 0) input_last = self.u32le(data, n - 4) modified_seed = seed ^ (self.bswap32(seed) << 32) sw0 = self.u64le(secret, 16) sw1 = self.u64le(secret, 24) combined = (input_first | (input_last << 32)) & 0xffff_ffff_ffff_ffff value = (((sw0 ^ sw1) + modified_seed) & 0xffff_ffff_ffff_ffff) ^ combined mul = (value & 0xffff_ffff_ffff_ffff) * ((self.prime64_1 + (n << 2)) & 0xffff_ffff_ffff_ffff) high = self.higher64(mul) low = self.lower64(mul) high = (high + (low << 1)) & 0xffff_ffff_ffff_ffff low ^= (high >> 3) low ^= (low >> 35) low = (low * self.prime_mx2) & 0xffff_ffff_ffff_ffff low ^= (low >> 28) high = self.avalanche_xxh3(high) return pack128(low, high) if n <= 16: input_first = self.u64le(data, 0) input_last = self.u64le(data, n - 8) sw0 = self.u64le(secret, 32) sw1 = self.u64le(secret, 40) sw2 = self.u64le(secret, 48) sw3 = self.u64le(secret, 56) val1 = (((sw0 ^ sw1) - seed) & 0xffff_ffff_ffff_ffff) ^ input_first ^ input_last val2 = (((sw2 ^ sw3) + seed) & 0xffff_ffff_ffff_ffff) ^ input_last mul1 = (val1 & 0xffff_ffff_ffff_ffff) * self.prime64_1 low = (self.lower64(mul1) + ((n - 1) << 54)) & 0xffff_ffff_ffff_ffff high = ( self.higher64(mul1) + (self.higher32(val2) << 32) + self.lower32(val2) * self.prime32_2 ) & 0xffff_ffff_ffff_ffff low ^= self.bswap64(high) mul2 = (low & 0xffff_ffff_ffff_ffff) * self.prime64_2 low2 = self.lower64(mul2) high2 = (self.higher64(mul2) + high * self.prime64_2) & 0xffff_ffff_ffff_ffff low_out = self.avalanche_xxh3(low2) high_out = self.avalanche_xxh3(high2) return pack128(low_out, high_out) # medium if n <= 240: acc0 = (n * self.prime64_1) & 0xffff_ffff_ffff_ffff acc1 = 0 if n <= 128: num_rounds = ((n - 1) >> 5) + 1 for i in range(num_rounds - 1, -1, -1): off_start = i * 16 off_end = n - i * 16 - 16 d1 = data[off_start:off_start + 16] d2 = data[off_end:off_end + 16] acc0 = (acc0 + self.mix_step(d1, secret, i * 32, seed)) & 0xffff_ffff_ffff_ffff acc1 = (acc1 + self.mix_step(d2, secret, i * 32 + 16, seed)) & 0xffff_ffff_ffff_ffff dw1_0 = self.u64le(d1, 0) dw1_1 = self.u64le(d1, 8) dw2_0 = self.u64le(d2, 0) dw2_1 = self.u64le(d2, 8) acc0 ^= (dw2_0 + dw2_1) & 0xffff_ffff_ffff_ffff acc1 ^= (dw1_0 + dw1_1) & 0xffff_ffff_ffff_ffff else: num_chunks = n >> 5 for i in range(4): base = i * 32 d1 = data[base:base + 16] d2 = data[base + 16:base + 32] acc0 = (acc0 + self.mix_step(d1, secret, i * 32, seed)) & 0xffff_ffff_ffff_ffff acc1 = (acc1 + self.mix_step(d2, secret, i * 32 + 16, seed)) & 0xffff_ffff_ffff_ffff dw1_0 = self.u64le(d1, 0) dw1_1 = self.u64le(d1, 8) dw2_0 = self.u64le(d2, 0) dw2_1 = self.u64le(d2, 8) acc0 ^= (dw2_0 + dw2_1) & 0xffff_ffff_ffff_ffff acc1 ^= (dw1_0 + dw1_1) & 0xffff_ffff_ffff_ffff acc0 = self.avalanche_xxh3(acc0) acc1 = self.avalanche_xxh3(acc1) for i in range(4, num_chunks): base = i * 32 d1 = data[base:base + 16] d2 = data[base + 16:base + 32] off = (i - 4) * 32 + 3 acc0 = (acc0 + self.mix_step(d1, secret, off, seed)) & 0xffff_ffff_ffff_ffff acc1 = (acc1 + self.mix_step(d2, secret, off + 16, seed)) & 0xffff_ffff_ffff_ffff dw1_0 = self.u64le(d1, 0) dw1_1 = self.u64le(d1, 8) dw2_0 = self.u64le(d2, 0) dw2_1 = self.u64le(d2, 8) acc0 ^= (dw2_0 + dw2_1) & 0xffff_ffff_ffff_ffff acc1 ^= (dw1_0 + dw1_1) & 0xffff_ffff_ffff_ffff # last mixed: note different half-order and seed negation d1 = data[n - 16:n] d2 = data[n - 32:n - 16] neg_seed = (0 - seed) & 0xffff_ffff_ffff_ffff acc0 = (acc0 + self.mix_step(d1, secret, 103, neg_seed)) & 0xffff_ffff_ffff_ffff acc1 = (acc1 + self.mix_step(d2, secret, 103 + 16, neg_seed)) & 0xffff_ffff_ffff_ffff dw1_0 = self.u64le(d1, 0) dw1_1 = self.u64le(d1, 8) dw2_0 = self.u64le(d2, 0) dw2_1 = self.u64le(d2, 8) acc0 ^= (dw2_0 + dw2_1) & 0xffff_ffff_ffff_ffff acc1 ^= (dw1_0 + dw1_1) & 0xffff_ffff_ffff_ffff low = (acc0 + acc1) & 0xffff_ffff_ffff_ffff high = ( (acc0 * self.prime64_1) + \ (acc1 * self.prime64_4) + \ (((n - seed) & 0xffff_ffff_ffff_ffff) * self.prime64_2) ) & 0xffff_ffff_ffff_ffff low_out = self.avalanche_xxh3(low) high_out = (0 - self.avalanche_xxh3(high)) & 0xffff_ffff_ffff_ffff return pack128(low_out, high_out) # large secret_len = len(secret) stripes_per_block = (secret_len - 64) // 8 block_size = 64 * stripes_per_block acc = [ self.prime32_3, self.prime64_1, self.prime64_2, self.prime64_3, self.prime64_4, self.prime32_2, self.prime64_5, self.prime32_1, ] p = 0 while n - p > block_size: block = data[p:p + block_size] for stripe_index in range(stripes_per_block): stripe = block[stripe_index * 64:stripe_index * 64 + 64] acc = self.accumulate_stripe(acc, stripe, secret, stripe_index * 8) acc = self.scramble_acc(acc, secret) p += block_size last = data[p:] last_len = len(last) n_full = (last_len - 1) // 64 for stripe_index in range(n_full): stripe = last[stripe_index * 64:stripe_index * 64 + 64] acc = self.accumulate_stripe(acc, stripe, secret, stripe_index * 8) last_stripe = data[n - 64:n] acc = self.accumulate_stripe(acc, last_stripe, secret, secret_len - 71) low = self.final_merge(acc, secret, (n * self.prime64_1) & 0xffff_ffff_ffff_ffff, 11) high = self.final_merge(acc, secret, (~(n * self.prime64_2)) & 0xffff_ffff_ffff_ffff, secret_len - 75) return pack128(low, high) class FNVBase: bits = None fnv_prime = None offset_basis = None variant = None # "fnv0", "fnv0a", "fnv1", "fnv1a" def __init__(self, data=b"", basis=None, variant="fnv1"): if self.bits is None or self.fnv_prime is None or self.offset_basis is None: raise ValueError("FNVBase must be subclassed with constants") self.variant = variant self.mask = (1 << self.bits) - 1 if basis is None: if variant in ["fnv0", "fnv0a"]: self.h = 0 else: self.h = self.offset_basis & self.mask else: self.h = int(basis) & self.mask if data: self.update(data) return def update(self, data): if not data: return self if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") b = bytes(data) if self.variant in ["fnv0", "fnv1"]: for byte in b: self.h = (self.h * self.fnv_prime) & self.mask self.h ^= byte else: # fnv0a, fnv1a for byte in b: self.h ^= byte self.h = (self.h * self.fnv_prime) & self.mask return self def digest(self): out_len = self.bits // 8 return int(self.h & self.mask).to_bytes(out_len, "big") def hexdigest(self): return self.digest().hex() class FNV_32(FNVBase): bits = 32 digest_size = 4 fnv_prime = 0x0100_0193 offset_basis = 0x811c_9dc5 class FNV_64(FNVBase): bits = 64 digest_size = 8 fnv_prime = 0x0000_0100_0000_01b3 offset_basis = 0xcbf2_9ce4_8422_2325 class FNV_128(FNVBase): bits = 128 digest_size = 16 fnv_prime = 0x0000_0000_0100_0000_0000_0000_0000_013b offset_basis = 0x6c62_272e_07bb_0142_62b8_2175_6295_c58d class FNV_256(FNVBase): bits = 256 digest_size = 32 fnv_prime = 0x0000_0000_0000_0000_0000_0100_0000_0000_0000_0000_0000_0000_0000_0000_0000_0163 offset_basis = 0xdd26_8dbc_aac5_5036_2d98_c384_c4e5_76cc_c8b1_5368_47b6_bbb3_1023_b4c8_caee_0535 class FNV_512(FNVBase): bits = 512 digest_size = 64 fnv_prime = ( (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 3)) | \ (0x0000_0000_0100_0000_0000_0000_0000_0000 << (128 * 2)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 1)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0157 << (128 * 0)) ) offset_basis = ( (0xb86d_b0b1_171f_4416_dca1_e50f_3099_90ac << (128 * 3)) | \ (0xac87_d059_c900_0000_0000_0000_0000_0d21 << (128 * 2)) | \ (0xe948_f68a_34c1_92f6_2ea7_9bc9_42db_e7ce << (128 * 1)) | \ (0x1820_3641_5f56_e34b_ac98_2aac_4afe_9fd9 << (128 * 0)) ) class FNV_1024(FNVBase): bits = 1024 digest_size = 128 fnv_prime = ( (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 7)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 6)) | \ (0x0000_0000_0000_0000_0000_0100_0000_0000 << (128 * 5)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 4)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 3)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 2)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 1)) | \ (0x0000_0000_0000_0000_0000_0000_0000_018d << (128 * 0)) ) offset_basis = ( (0x0000_0000_0000_0000_005f_7a76_758e_cc4d << (128 * 7)) | \ (0x32e5_6d5a_5910_28b7_4b29_fc42_23fd_ada1 << (128 * 6)) | \ (0x6c3b_f34e_da36_74da_9a21_d900_0000_0000 << (128 * 5)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 4)) | \ (0x0000_0000_0000_0000_0000_0000_0000_0000 << (128 * 3)) | \ (0x0000_0000_0000_0000_0000_0000_0004_c6d7 << (128 * 2)) | \ (0xeb6e_7380_2734_510a_555f_256c_c005_ae55 << (128 * 1)) | \ (0x6bde_8cc9_c6a9_3b21_aff4_b16c_71ee_90b3 << (128 * 0)) ) class HAVAL: block_size = 128 # bytes (1024 bits) version = 0x01 # Table 1 word processing orders ord1 = ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ) ord2 = ( 0x05, 0x0e, 0x1a, 0x12, 0x0b, 0x1c, 0x07, 0x10, 0x00, 0x17, 0x14, 0x16, 0x01, 0x0a, 0x04, 0x08, 0x1e, 0x03, 0x15, 0x09, 0x11, 0x18, 0x1d, 0x06, 0x13, 0x0c, 0x0f, 0x0d, 0x02, 0x19, 0x1f, 0x1b, ) ord3 = ( 0x13, 0x09, 0x04, 0x14, 0x1c, 0x11, 0x08, 0x16, 0x1d, 0x0e, 0x19, 0x0c, 0x18, 0x1e, 0x10, 0x1a, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x12, 0x1b, 0x0d, 0x06, 0x15, 0x0a, 0x17, 0x0b, 0x05, 0x02, ) ord4 = ( 0x18, 0x04, 0x00, 0x0e, 0x02, 0x07, 0x1c, 0x17, 0x1a, 0x06, 0x1e, 0x14, 0x12, 0x19, 0x13, 0x03, 0x16, 0x0b, 0x1f, 0x15, 0x08, 0x1b, 0x0c, 0x09, 0x01, 0x1d, 0x05, 0x0f, 0x11, 0x0a, 0x10, 0x0d, ) ord5 = ( 0x1b, 0x03, 0x15, 0x1a, 0x11, 0x0b, 0x14, 0x1d, 0x13, 0x00, 0x0c, 0x07, 0x0d, 0x08, 0x1f, 0x0a, 0x05, 0x09, 0x0e, 0x1e, 0x12, 0x06, 0x1c, 0x18, 0x02, 0x17, 0x10, 0x16, 0x04, 0x01, 0x19, 0x0f, ) # Table 2 permutations phi (indices into coords=[x6,x5,x4,x3,x2,x1,x0]) phi_3_1 = (0x05, 0x06, 0x03, 0x01, 0x00, 0x04, 0x02) phi_3_2 = (0x02, 0x04, 0x05, 0x06, 0x01, 0x03, 0x00) phi_3_3 = (0x00, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06) phi_4_1 = (0x04, 0x00, 0x05, 0x02, 0x01, 0x03, 0x06) phi_4_2 = (0x03, 0x01, 0x04, 0x06, 0x05, 0x00, 0x02) phi_4_3 = (0x05, 0x02, 0x03, 0x00, 0x06, 0x04, 0x01) phi_4_4 = (0x00, 0x02, 0x06, 0x01, 0x04, 0x05, 0x03) phi_5_1 = (0x03, 0x02, 0x05, 0x06, 0x01, 0x04, 0x00) phi_5_2 = (0x00, 0x04, 0x05, 0x06, 0x03, 0x02, 0x01) phi_5_3 = (0x04, 0x00, 0x06, 0x02, 0x03, 0x05, 0x01) phi_5_4 = (0x05, 0x01, 0x03, 0x04, 0x06, 0x02, 0x00) phi_5_5 = (0x04, 0x01, 0x06, 0x00, 0x02, 0x03, 0x05) # Constants from pi (D0..D7, then K2, K3, K4, K5) const = ( 0x243f_6a88, 0x85a3_08d3, 0x1319_8a2e, 0x0370_7344, 0xa409_3822, 0x299f_31d0, 0x082e_fa98, 0xec4e_6c89, 0x4528_21e6, 0x38d0_1377, 0xbe54_66cf, 0x34e9_0c6c, 0xc0ac_29b7, 0xc97c_50dd, 0x3f84_d5b5, 0xb547_0917, 0x9216_d5d9, 0x8979_fb1b, 0xd131_0ba6, 0x98df_b5ac, 0x2ffd_72db, 0xd01a_dfb7, 0xb8e1_afed, 0x6a26_7e96, 0xba7c_9045, 0xf12c_7f99, 0x24a1_9947, 0xb391_6cf7, 0x0801_f2e2, 0x858e_fc16, 0x6369_20d8, 0x7157_4e69, 0xa458_fea3, 0xf493_3d7e, 0x0d95_748f, 0x728e_b658, 0x718b_cd58, 0x8215_4aee, 0x7b54_a41d, 0xc25a_59b5, 0x9c30_d539, 0x2af2_6013, 0xc5d1_b023, 0x2860_85f0, 0xca41_7918, 0xb8db_38ef, 0x8e79_dcb0, 0x603a_180e, 0x6c9e_0e8b, 0xb01e_8a3e, 0xd715_77c1, 0xbd31_4b27, 0x78af_2fda, 0x5560_5c60, 0xe655_25f3, 0xaa55_ab94, 0x5748_9862, 0x63e8_1440, 0x55ca_396a, 0x2aab_10b6, 0xb4cc_5c34, 0x1141_e8ce, 0xa154_86af, 0x7c72_e993, 0xb3ee_1411, 0x636f_bc2a, 0x2ba9_c55d, 0x7418_31f6, 0xce5c_3e16, 0x9b87_931e, 0xafd6_ba33, 0x6c24_cf5c, 0x7a32_5381, 0x2895_8677, 0x3b8f_4898, 0x6b4b_b9af, 0xc4bf_e81b, 0x6628_2193, 0x61d8_09cc, 0xfb21_a991, 0x487c_ac60, 0x5dec_8032, 0xef84_5d5d, 0xe985_75b1, 0xdc26_2302, 0xeb65_1b88, 0x2389_3e81, 0xd396_acc5, 0x0f6d_6ff3, 0x83f4_4239, 0x2e0b_4482, 0xa484_2004, 0x69c8_f04a, 0x9e1f_9b5e, 0x21c6_6842, 0xf6e9_6c9a, 0x670c_9c61, 0xabd3_88f0, 0x6a51_a0d2, 0xd854_2f68, 0x960f_a728, 0xab51_33a3, 0x6eef_0b6c, 0x137a_3be4, 0xba3b_f050, 0x7efb_2a98, 0xa1f1_651d, 0x39af_0176, 0x66ca_593e, 0x8243_0e88, 0x8cee_8619, 0x456f_9fb4, 0x7d84_a5c3, 0x3b8b_5ebe, 0xe06f_75d8, 0x85c1_2073, 0x401a_449f, 0x56c1_6aa6, 0x4ed3_aa62, 0x363f_7706, 0x1bfe_df72, 0x429b_023d, 0x37d0_d724, 0xd00a_1248, 0xdb0f_ead3, 0x49f1_c09b, 0x0753_72c9, 0x8099_1b7b, 0x25d4_79d8, 0xf6e8_def7, 0xe3fe_501a, 0xb679_4c3b, 0x976c_e0bd, 0x04c0_06ba, 0xc1a9_4fb6, 0x409f_60c4, ) def __init__(self, data=b"", digest_bits=256, passes=5): if digest_bits not in (128, 160, 192, 224, 256): raise ValueError("digest_bits must be one of 128,160,192,224,256") if passes not in (3, 4, 5): raise ValueError("passes must be one of 3,4,5") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.digest_bits = digest_bits self.passes = passes self.digest_size = digest_bits // 8 if len(self.const) != 136: raise ValueError("constant table size mismatch") self.iv = list(self.const[0:8]) self.k2 = list(self.const[8:40]) self.k3 = list(self.const[40:72]) self.k4 = list(self.const[72:104]) self.k5 = list(self.const[104:136]) self.h = self.iv[:] self.buf = bytearray() self.bitlen = 0 # in bits if data: self.update(data) return def copy(self): other = self.__class__(b"", self.digest_bits, self.passes) other.h = self.h[:] other.buf = bytearray(self.buf) other.bitlen = self.bitlen return other def tailor(self, state, out_bits): def split_lsb(word, lens_lsb): seg = [] w = word & 0xffff_ffff for l in lens_lsb: # noqa: E741 seg.append(w & ((1 << l) - 1)) w >>= l return seg def concat(parts): out = 0 for v, bl in parts: out = (out << bl) | (v & ((1 << bl) - 1)) return out d = [x & 0xffff_ffff for x in state] if out_bits == 256: return d[0:8] if out_bits == 224: seg7 = split_lsb(d[7], [4, 5, 4, 5, 4, 5, 5]) y0 = (d[0] + seg7[6]) & 0xffff_ffff y1 = (d[1] + seg7[5]) & 0xffff_ffff y2 = (d[2] + seg7[4]) & 0xffff_ffff y3 = (d[3] + seg7[3]) & 0xffff_ffff y4 = (d[4] + seg7[2]) & 0xffff_ffff y5 = (d[5] + seg7[1]) & 0xffff_ffff y6 = (d[6] + seg7[0]) & 0xffff_ffff return [y0, y1, y2, y3, y4, y5, y6] if out_bits == 192: seg7 = split_lsb(d[7], [5, 5, 6, 5, 5, 6]) seg6 = split_lsb(d[6], [5, 5, 6, 5, 5, 6]) y0 = (d[0] + concat([(seg7[0], 5), (seg6[5], 6)])) & 0xffff_ffff y1 = (d[1] + concat([(seg7[1], 5), (seg6[0], 5)])) & 0xffff_ffff y2 = (d[2] + concat([(seg7[2], 6), (seg6[1], 5)])) & 0xffff_ffff y3 = (d[3] + concat([(seg7[3], 5), (seg6[2], 6)])) & 0xffff_ffff y4 = (d[4] + concat([(seg7[4], 5), (seg6[3], 5)])) & 0xffff_ffff y5 = (d[5] + concat([(seg7[5], 6), (seg6[4], 5)])) & 0xffff_ffff return [y0, y1, y2, y3, y4, y5] if out_bits == 160: seg7 = split_lsb(d[7], [6, 6, 7, 6, 7]) seg6 = split_lsb(d[6], [6, 6, 7, 6, 7]) seg5 = split_lsb(d[5], [6, 6, 7, 6, 7]) y0 = (d[0] + concat([(seg7[0], 6), (seg6[4], 7), (seg5[3], 6)])) & 0xffff_ffff y1 = (d[1] + concat([(seg7[1], 6), (seg6[0], 6), (seg5[4], 7)])) & 0xffff_ffff y2 = (d[2] + concat([(seg7[2], 7), (seg6[1], 6), (seg5[0], 6)])) & 0xffff_ffff y3 = (d[3] + concat([(seg7[3], 6), (seg6[2], 7), (seg5[1], 6)])) & 0xffff_ffff y4 = (d[4] + concat([(seg7[4], 7), (seg6[3], 6), (seg5[2], 7)])) & 0xffff_ffff return [y0, y1, y2, y3, y4] if out_bits == 128: seg7 = split_lsb(d[7], [8, 8, 8, 8]) seg6 = split_lsb(d[6], [8, 8, 8, 8]) seg5 = split_lsb(d[5], [8, 8, 8, 8]) seg4 = split_lsb(d[4], [8, 8, 8, 8]) y0 = (d[0] + concat([(seg7[0], 8), (seg6[3], 8), (seg5[2], 8), (seg4[1], 8)])) & 0xffff_ffff y1 = (d[1] + concat([(seg7[1], 8), (seg6[0], 8), (seg5[3], 8), (seg4[2], 8)])) & 0xffff_ffff y2 = (d[2] + concat([(seg7[2], 8), (seg6[1], 8), (seg5[0], 8), (seg4[3], 8)])) & 0xffff_ffff y3 = (d[3] + concat([(seg7[3], 8), (seg6[2], 8), (seg5[1], 8), (seg4[0], 8)])) & 0xffff_ffff return [y0, y1, y2, y3] raise ValueError("invalid out_bits") def compress(self, block): def u32le(b, off): return int.from_bytes(b[off:off + 4], "little") def f1(x6, x5, x4, x3, x2, x1, x0): return (x1 & x4) ^ (x2 & x5) ^ (x3 & x6) ^ (x0 & x1) ^ x0 def f2(x6, x5, x4, x3, x2, x1, x0): return ( (x1 & x2 & x3) ^ (x2 & x4 & x5) ^ (x1 & x2) ^ (x1 & x4) ^ (x2 & x6) ^ (x3 & x5) ^ (x4 & x5) ^ (x0 & x2) ^ x0 ) def f3(x6, x5, x4, x3, x2, x1, x0): return (x1 & x2 & x3) ^ (x1 & x4) ^ (x2 & x5) ^ (x3 & x6) ^ (x0 & x3) ^ x0 def f4(x6, x5, x4, x3, x2, x1, x0): return ( (x1 & x2 & x3) ^ (x2 & x4 & x5) ^ (x3 & x4 & x6) ^ (x1 & x4) ^ (x2 & x6) ^ (x3 & x4) ^ (x3 & x5) ^ (x3 & x6) ^ (x4 & x5) ^ (x4 & x6) ^ (x0 & x4) ^ x0 ) def f5(x6, x5, x4, x3, x2, x1, x0): return (x1 & x4) ^ (x2 & x5) ^ (x3 & x6) ^ (x0 & x1 & x2 & x3) ^ (x0 & x5) ^ x0 def do_pass(func, phi, order, kconst, t, w): def ror32(x, r): x &= 0xffff_ffff r = r & 31 return ((x >> r) | ((x << (32 - r)) & 0xffff_ffff)) & 0xffff_ffff for i in range(32): coords = [t[6], t[5], t[4], t[3], t[2], t[1], t[0]] a0 = coords[phi[0]] a1 = coords[phi[1]] a2 = coords[phi[2]] a3 = coords[phi[3]] a4 = coords[phi[4]] a5 = coords[phi[5]] a6 = coords[phi[6]] p = func(a0, a1, a2, a3, a4, a5, a6) r = (ror32(p, 7) + ror32(t[7], 11) + w[order[i]] + kconst[i]) & 0xffff_ffff t = [r, t[0], t[1], t[2], t[3], t[4], t[5], t[6]] return t w = [u32le(block, 4 * i) for i in range(32)] t = self.h[:] zero32 = [0] * 32 if self.passes == 3: t = do_pass(f1, self.phi_3_1, self.ord1, zero32, t, w) t = do_pass(f2, self.phi_3_2, self.ord2, self.k2, t, w) t = do_pass(f3, self.phi_3_3, self.ord3, self.k3, t, w) elif self.passes == 4: t = do_pass(f1, self.phi_4_1, self.ord1, zero32, t, w) t = do_pass(f2, self.phi_4_2, self.ord2, self.k2, t, w) t = do_pass(f3, self.phi_4_3, self.ord3, self.k3, t, w) t = do_pass(f4, self.phi_4_4, self.ord4, self.k4, t, w) else: t = do_pass(f1, self.phi_5_1, self.ord1, zero32, t, w) t = do_pass(f2, self.phi_5_2, self.ord2, self.k2, t, w) t = do_pass(f3, self.phi_5_3, self.ord3, self.k3, t, w) t = do_pass(f4, self.phi_5_4, self.ord4, self.k4, t, w) t = do_pass(f5, self.phi_5_5, self.ord5, self.k5, t, w) for i in range(8): self.h[i] = (self.h[i] + t[i]) & 0xffff_ffff return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf.extend(data) self.bitlen += len(data) * 8 while len(self.buf) >= 128: block = bytes(self.buf[:128]) del self.buf[:128] self.compress(block) return self def finalize(self): # padding: 0x01, zeros, then 2 bytes (version/passes/outbits), then 8 bytes bitlen (LE) msg = bytes(self.buf) bitlen = self.bitlen pad = bytearray() pad.append(0x01) while ((len(msg) + len(pad)) % 128) != 118: pad.append(0x00) # tail (2 bytes) + bitlen (8 bytes, little-endian) b0 = (self.version & 0x07) | ((self.passes & 0x07) << 3) | ((self.digest_bits & 0x03) << 6) b1 = (self.digest_bits >> 2) & 0xff tail = bytes([b0, b1]) + bitlen.to_bytes(8, "little") self.update(bytes(pad) + tail) if len(self.buf) != 0: raise RuntimeError("internal error: buffer not empty after finalize") return def output(self): out_words = self.tailor(self.h, self.digest_bits) out = bytearray() for x in out_words: out.extend(int(x & 0xffff_ffff).to_bytes(4, "little")) return bytes(out[: self.digest_size]) def digest(self): c = self.copy() c.finalize() return c.output() def hexdigest(self): return self.digest().hex() class MurmurHashBase: block_size = None digest_size = None def __init__(self, data=b"", seed=0): if not isinstance(seed, int): raise TypeError("seed must be int") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.seed = seed self.buf = bytearray() self.msg_len = 0 self.finalized = False self.init_state() if data: self.update(data) return def copy(self): other = self.__class__(b"", self.seed) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.finalized = self.finalized other.set_state(self.get_state()) return other def update(self, data): if self.finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) return self def finalize(self): if self.finalized: return full = bytes(self.buf) self.process_all(full) self.finalize_state() self.buf.clear() self.finalized = True return def digest(self): c = self.copy() c.finalize() return c.pack_digest() def hexdigest(self): return self.digest().hex() def finalize_state(self): return def digest_normalize(self, digest): digest = bytes.fromhex(digest) return str(int.from_bytes(digest[::-1], "big")) class MurmurHash32Base(MurmurHashBase): block_size = 4 digest_size = 4 def init_state(self): self.seed &= 0xffff_ffff self.h = 0 return def get_state(self): return (self.h & 0xffff_ffff,) def set_state(self, state): self.h = state[0] & 0xffff_ffff return def pack_digest(self): return struct.pack("> 16) h &= 0xffff_ffff i += 4 tail = data[i:] tl = len(tail) if tl == 3: h = (h + (tail[2] << 16)) & 0xffff_ffff h = (h + (tail[1] << 8)) & 0xffff_ffff h = (h + tail[0]) & 0xffff_ffff h = (h * m) & 0xffff_ffff h ^= (h >> r) h &= 0xffff_ffff elif tl == 2: h = (h + (tail[1] << 8)) & 0xffff_ffff h = (h + tail[0]) & 0xffff_ffff h = (h * m) & 0xffff_ffff h ^= (h >> r) h &= 0xffff_ffff elif tl == 1: h = (h + tail[0]) & 0xffff_ffff h = (h * m) & 0xffff_ffff h ^= (h >> r) h &= 0xffff_ffff else: pass h = (h * m) & 0xffff_ffff h ^= (h >> 10) h = (h * m) & 0xffff_ffff h ^= (h >> 17) h &= 0xffff_ffff self.h = h return class MurmurHash2(MurmurHash32Base): def process_all(self, data): length = len(data) m = 0x5bd1_e995 r = 24 h = (self.seed ^ length) & 0xffff_ffff i = 0 while i + 4 <= length: k = struct.unpack_from("> r) k = (k * m) & 0xffff_ffff h = (h * m) & 0xffff_ffff h ^= k h &= 0xffff_ffff i += 4 tail = data[i:] tl = len(tail) if tl == 3: h ^= (tail[2] << 16) h ^= (tail[1] << 8) h ^= tail[0] h = (h * m) & 0xffff_ffff elif tl == 2: h ^= (tail[1] << 8) h ^= tail[0] h = (h * m) & 0xffff_ffff elif tl == 1: h ^= tail[0] h = (h * m) & 0xffff_ffff else: pass h ^= (h >> 13) h = (h * m) & 0xffff_ffff h ^= (h >> 15) h &= 0xffff_ffff self.h = h return class MurmurHash2A(MurmurHash32Base): def mmix(self, h, k): m = 0x5bd1_e995 r = 24 k = (k * m) & 0xffff_ffff k ^= (k >> r) k = (k * m) & 0xffff_ffff h = (h * m) & 0xffff_ffff h ^= k h &= 0xffff_ffff return h def process_all(self, data): length = len(data) m = 0x5bd1_e995 h = self.seed & 0xffff_ffff l = length & 0xffff_ffff # noqa: E741 i = 0 while i + 4 <= length: k = struct.unpack_from("> 13) h = (h * m) & 0xffff_ffff h ^= (h >> 15) h &= 0xffff_ffff self.h = h return class MurmurHash64Base(MurmurHashBase): block_size = 8 digest_size = 8 def init_state(self): self.seed &= 0xffff_ffff_ffff_ffff self.h = 0 return def get_state(self): return (self.h & 0xffff_ffff_ffff_ffff,) def set_state(self, state): self.h = state[0] & 0xffff_ffff_ffff_ffff return def pack_digest(self): return struct.pack("> r) k = (k * m) & 0xffff_ffff_ffff_ffff h ^= k h = (h * m) & 0xffff_ffff_ffff_ffff i += 8 tail = data[i:] tl = len(tail) if tl >= 7: h ^= (tail[6] << 48) if tl >= 6: h ^= (tail[5] << 40) if tl >= 5: h ^= (tail[4] << 32) if tl >= 4: h ^= (tail[3] << 24) if tl >= 3: h ^= (tail[2] << 16) if tl >= 2: h ^= (tail[1] << 8) if tl >= 1: h ^= (tail[0] << 0) h = (h * m) & 0xffff_ffff_ffff_ffff h ^= (h >> r) h = (h * m) & 0xffff_ffff_ffff_ffff h ^= (h >> r) h &= 0xffff_ffff_ffff_ffff self.h = h return class MurmurHash64B(MurmurHash64Base): block_size = 4 def process_all(self, data): length = len(data) m = 0x5bd1_e995 r = 24 seed_lo = self.seed & 0xffff_ffff seed_hi = (self.seed >> 32) & 0xffff_ffff h1 = (seed_lo ^ length) & 0xffff_ffff h2 = seed_hi & 0xffff_ffff i = 0 l = length # noqa: E741 while l >= 8: k1 = struct.unpack_from("> r) k1 = (k1 * m) & 0xffff_ffff h1 = (h1 * m) & 0xffff_ffff h1 ^= k1 h1 &= 0xffff_ffff l -= 4 # noqa: E741 k2 = struct.unpack_from("> r) k2 = (k2 * m) & 0xffff_ffff h2 = (h2 * m) & 0xffff_ffff h2 ^= k2 h2 &= 0xffff_ffff l -= 4 # noqa: E741 if l >= 4: k1 = struct.unpack_from("> r) k1 = (k1 * m) & 0xffff_ffff h1 = (h1 * m) & 0xffff_ffff h1 ^= k1 h1 &= 0xffff_ffff l -= 4 # noqa: E741 tail = data[i:] tl = len(tail) if tl == 3: h2 ^= tail[2] << 16 h2 ^= tail[1] << 8 h2 ^= tail[0] h2 = (h2 * m) & 0xffff_ffff elif tl == 2: h2 ^= tail[1] << 8 h2 ^= tail[0] h2 = (h2 * m) & 0xffff_ffff elif tl == 1: h2 ^= tail[0] h2 = (h2 * m) & 0xffff_ffff else: pass h1 ^= (h2 >> 18) h1 = (h1 * m) & 0xffff_ffff h2 ^= (h1 >> 22) h2 = (h2 * m) & 0xffff_ffff h1 ^= (h2 >> 17) h1 = (h1 * m) & 0xffff_ffff h2 ^= (h1 >> 19) h2 = (h2 * m) & 0xffff_ffff h = ((h1 & 0xffff_ffff) << 32) | (h2 & 0xffff_ffff) self.h = h & 0xffff_ffff_ffff_ffff return class MurmurHash3Base: def __init__(self, data=b"", seed=0): if not isinstance(seed, int): raise TypeError("seed must be int") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.seed = seed self.buf = bytearray() self.msg_len = 0 self.finalized = False self.init_state() if data: self.update(data) return def copy(self): other = self.__class__(b"", self.seed) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.finalized = self.finalized other.set_state(self.get_state()) return other def update(self, data): if self.finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) bs = self.block_size while len(self.buf) >= bs: block = bytes(self.buf[:bs]) del self.buf[:bs] self.process_block(block) return self def finalize(self): if self.finalized: return tail = bytes(self.buf) self.process_tail(tail) self.finalize_state() self.buf.clear() self.finalized = True return def digest(self): c = self.copy() c.finalize() return c.pack_digest() def hexdigest(self): return self.digest().hex() def rol32(self, x, r): x &= 0xffff_ffff return ((x << r) | (x >> (32 - r))) & 0xffff_ffff def rol64(self, x, r): x &= 0xffff_ffff_ffff_ffff return ((x << r) | (x >> (64 - r))) & 0xffff_ffff_ffff_ffff class MurmurHash3_x86_32(MurmurHash3Base): block_size = 4 digest_size = 4 def init_state(self): self.seed &= 0xffff_ffff self.h1 = self.seed return def get_state(self): return (self.h1 & 0xffff_ffff,) def set_state(self, state): self.h1 = state[0] & 0xffff_ffff return def mix_k1(self, k1): c1 = 0xcc9e_2d51 c2 = 0x1b87_3593 k1 &= 0xffff_ffff k1 = (k1 * c1) & 0xffff_ffff k1 = self.rol32(k1, 15) k1 = (k1 * c2) & 0xffff_ffff return k1 def process_block(self, block): k1 = struct.unpack("> 16) h = (h * 0x85eb_ca6b) & 0xffff_ffff h ^= (h >> 13) h = (h * 0xc2b2_ae35) & 0xffff_ffff h ^= (h >> 16) return h & 0xffff_ffff self.h1 ^= (self.msg_len & 0xffff_ffff) self.h1 = fmix32(self.h1) return def pack_digest(self): return struct.pack("= 15: k4 ^= tail[14] << 16 if tl >= 14: k4 ^= tail[13] << 8 if tl >= 13: k4 ^= tail[12] << 0 k4 = (k4 * c4) & 0xffff_ffff k4 = self.rol32(k4, 18) k4 = (k4 * c1) & 0xffff_ffff self.h4 ^= k4 if tl >= 12: k3 ^= tail[11] << 24 if tl >= 11: k3 ^= tail[10] << 16 if tl >= 10: k3 ^= tail[9] << 8 if tl >= 9: k3 ^= tail[8] << 0 k3 = (k3 * c3) & 0xffff_ffff k3 = self.rol32(k3, 17) k3 = (k3 * c4) & 0xffff_ffff self.h3 ^= k3 if tl >= 8: k2 ^= tail[7] << 24 if tl >= 7: k2 ^= tail[6] << 16 if tl >= 6: k2 ^= tail[5] << 8 if tl >= 5: k2 ^= tail[4] << 0 k2 = (k2 * c2) & 0xffff_ffff k2 = self.rol32(k2, 16) k2 = (k2 * c3) & 0xffff_ffff self.h2 ^= k2 if tl >= 4: k1 ^= tail[3] << 24 if tl >= 3: k1 ^= tail[2] << 16 if tl >= 2: k1 ^= tail[1] << 8 if tl >= 1: k1 ^= tail[0] << 0 k1 = (k1 * c1) & 0xffff_ffff k1 = self.rol32(k1, 15) k1 = (k1 * c2) & 0xffff_ffff self.h1 ^= k1 return def finalize_state(self): def fmix32(h): h &= 0xffff_ffff h ^= (h >> 16) h = (h * 0x85eb_ca6b) & 0xffff_ffff h ^= (h >> 13) h = (h * 0xc2b2_ae35) & 0xffff_ffff h ^= (h >> 16) return h & 0xffff_ffff length = self.msg_len & 0xffff_ffff self.h1 ^= length self.h2 ^= length self.h3 ^= length self.h4 ^= length self.h1 = (self.h1 + self.h2 + self.h3 + self.h4) & 0xffff_ffff self.h2 = (self.h2 + self.h1) & 0xffff_ffff self.h3 = (self.h3 + self.h1) & 0xffff_ffff self.h4 = (self.h4 + self.h1) & 0xffff_ffff self.h1 = fmix32(self.h1) self.h2 = fmix32(self.h2) self.h3 = fmix32(self.h3) self.h4 = fmix32(self.h4) self.h1 = (self.h1 + self.h2 + self.h3 + self.h4) & 0xffff_ffff self.h2 = (self.h2 + self.h1) & 0xffff_ffff self.h3 = (self.h3 + self.h1) & 0xffff_ffff self.h4 = (self.h4 + self.h1) & 0xffff_ffff return def pack_digest(self): return struct.pack("<4I", self.h1, self.h2, self.h3, self.h4) def digest_normalize(self, digest): digest = bytes.fromhex(digest) a, b, c, d = digest[:4], digest[4:8], digest[8:12], digest[12:] return " ".join(x[::-1].hex() for x in [a, b, c, d]) class MurmurHash3_x64_128(MurmurHash3Base): block_size = 16 digest_size = 16 word_bits = [64, 64] def init_state(self): self.seed &= 0xffff_ffff_ffff_ffff self.h1 = self.seed self.h2 = self.seed return def get_state(self): return ( self.h1 & 0xffff_ffff_ffff_ffff, self.h2 & 0xffff_ffff_ffff_ffff, ) def set_state(self, state): self.h1 = state[0] & 0xffff_ffff_ffff_ffff self.h2 = state[1] & 0xffff_ffff_ffff_ffff return def process_block(self, block): k1, k2 = struct.unpack("<2Q", block) c1 = 0x87c3_7b91_1142_53d5 c2 = 0x4cf5_ad43_2745_937f k1 = (k1 * c1) & 0xffff_ffff_ffff_ffff k1 = self.rol64(k1, 31) k1 = (k1 * c2) & 0xffff_ffff_ffff_ffff self.h1 ^= k1 self.h1 = self.rol64(self.h1, 27) self.h1 = (self.h1 + self.h2) & 0xffff_ffff_ffff_ffff self.h1 = (self.h1 * 5 + 0x52dc_e729) & 0xffff_ffff_ffff_ffff k2 = (k2 * c2) & 0xffff_ffff_ffff_ffff k2 = self.rol64(k2, 33) k2 = (k2 * c1) & 0xffff_ffff_ffff_ffff self.h2 ^= k2 self.h2 = self.rol64(self.h2, 31) self.h2 = (self.h2 + self.h1) & 0xffff_ffff_ffff_ffff self.h2 = (self.h2 * 5 + 0x3849_5ab5) & 0xffff_ffff_ffff_ffff return def process_tail(self, tail): tl = len(tail) k1 = 0 k2 = 0 c1 = 0x87c3_7b91_1142_53d5 c2 = 0x4cf5_ad43_2745_937f if tl >= 15: k2 ^= tail[14] << 48 if tl >= 14: k2 ^= tail[13] << 40 if tl >= 13: k2 ^= tail[12] << 32 if tl >= 12: k2 ^= tail[11] << 24 if tl >= 11: k2 ^= tail[10] << 16 if tl >= 10: k2 ^= tail[9] << 8 if tl >= 9: k2 ^= tail[8] << 0 if k2 != 0: k2 = (k2 * c2) & 0xffff_ffff_ffff_ffff k2 = self.rol64(k2, 33) k2 = (k2 * c1) & 0xffff_ffff_ffff_ffff self.h2 ^= k2 if tl >= 8: k1 ^= tail[7] << 56 if tl >= 7: k1 ^= tail[6] << 48 if tl >= 6: k1 ^= tail[5] << 40 if tl >= 5: k1 ^= tail[4] << 32 if tl >= 4: k1 ^= tail[3] << 24 if tl >= 3: k1 ^= tail[2] << 16 if tl >= 2: k1 ^= tail[1] << 8 if tl >= 1: k1 ^= tail[0] << 0 if k1 != 0: k1 = (k1 * c1) & 0xffff_ffff_ffff_ffff k1 = self.rol64(k1, 31) k1 = (k1 * c2) & 0xffff_ffff_ffff_ffff self.h1 ^= k1 return def finalize_state(self): def fmix64(k): k &= 0xffff_ffff_ffff_ffff k ^= (k >> 33) k = (k * 0xff51_afd7_ed55_8ccd) & 0xffff_ffff_ffff_ffff k ^= (k >> 33) k = (k * 0xc4ce_b9fe_1a85_ec53) & 0xffff_ffff_ffff_ffff k ^= (k >> 33) return k & 0xffff_ffff_ffff_ffff length = self.msg_len & 0xffff_ffff_ffff_ffff self.h1 ^= length self.h2 ^= length self.h1 = (self.h1 + self.h2) & 0xffff_ffff_ffff_ffff self.h2 = (self.h2 + self.h1) & 0xffff_ffff_ffff_ffff self.h1 = fmix64(self.h1) self.h2 = fmix64(self.h2) self.h1 = (self.h1 + self.h2) & 0xffff_ffff_ffff_ffff self.h2 = (self.h2 + self.h1) & 0xffff_ffff_ffff_ffff return def pack_digest(self): return struct.pack("<2Q", self.h1, self.h2) def digest_normalize(self, digest): digest = bytes.fromhex(digest) a, b = digest[:8], digest[8:] return " ".join(x[::-1].hex() for x in [a, b]) class FORK256: block_size = 64 # 512 bits digest_size = 32 # 256 bits def __init__(self, data=b""): # IV0 (same as SHA-256 IV) self.h0 = 0x6a09_e667 self.h1 = 0xbb67_ae85 self.h2 = 0x3c6e_f372 self.h3 = 0xa54f_f53a self.h4 = 0x510e_527f self.h5 = 0x9b05_688c self.h6 = 0x1f83_d9ab self.h7 = 0x5be0_cd19 self.buf = bytearray() self.msg_len = 0 # in bytes if data: self.update(data) return def copy(self): other = self.__class__() other.h0 = self.h0 other.h1 = self.h1 other.h2 = self.h2 other.h3 = self.h3 other.h4 = self.h4 other.h5 = self.h5 other.h6 = self.h6 other.h7 = self.h7 other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return struct.pack(">8I", c.h0, c.h1, c.h2, c.h3, c.h4, c.h5, c.h6, c.h7) def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 self.buf.append(0x80) while (len(self.buf) % 64) != 56: self.buf.append(0x00) self.buf.extend(struct.pack(">Q", bit_len)) while len(self.buf) >= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return def step(self, v, m0, m1, alpha, beta): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff def add32(a, b): return (a + b) & 0xffff_ffff def add32_3(a, b, c): return (a + b + c) & 0xffff_ffff def f(x): # f(x) = x + (x<<<7 XOR x<<<22) return add32(x, rol32(x, 7) ^ rol32(x, 22)) def g(x): # g(x) = x XOR (x<<<13 + x<<<27) return (x ^ add32(rol32(x, 13), rol32(x, 27))) & 0xffff_ffff av, bv, cv, dv, ev, fv, gv, hv = v ae = add32(av, m0) ee = add32(ev, m1) ae_alpha = add32(ae, alpha) ee_beta = add32(ee, beta) a1 = add32(hv, rol32(g(ee), 21)) ^ rol32(f(ee_beta), 17) b1 = add32_3(av, m0, alpha) c1 = add32(bv, f(ae)) ^ g(ae_alpha) d1 = add32(cv, rol32(f(ae), 5)) ^ rol32(g(ae_alpha), 9) e1 = add32(dv, rol32(f(ae), 17)) ^ rol32(g(ae_alpha), 21) f1 = add32_3(ev, m1, beta) g1 = add32(fv, g(ee)) ^ f(ee_beta) h1 = add32(gv, rol32(g(ee), 9)) ^ rol32(f(ee_beta), 5) return [ a1 & 0xffff_ffff, b1 & 0xffff_ffff, c1 & 0xffff_ffff, d1 & 0xffff_ffff, e1 & 0xffff_ffff, f1 & 0xffff_ffff, g1 & 0xffff_ffff, h1 & 0xffff_ffff, ] def compress(self, block): def add_words(x, y): return [(x[i] + y[i]) & 0xffff_ffff for i in range(8)] def xor_words(x, y): return [(x[i] ^ y[i]) & 0xffff_ffff for i in range(8)] m = list(struct.unpack(">16I", block)) sigma = [ [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F], [0x0E, 0x0F, 0x0B, 0x09, 0x08, 0x0A, 0x03, 0x04, 0x02, 0x0D, 0x00, 0x05, 0x06, 0x07, 0x0C, 0x01], [0x07, 0x06, 0x0A, 0x0E, 0x0D, 0x02, 0x09, 0x0C, 0x0B, 0x04, 0x0F, 0x08, 0x05, 0x00, 0x01, 0x03], [0x05, 0x0C, 0x01, 0x08, 0x0F, 0x00, 0x0D, 0x0B, 0x03, 0x0A, 0x09, 0x02, 0x07, 0x0E, 0x04, 0x06], ] delta = [ 0x428a_2f98, 0x7137_4491, 0xb5c0_fbcf, 0xe9b5_dba5, 0x3956_c25b, 0x59f1_11f1, 0x923f_82a4, 0xab1c_5ed5, 0xd807_aa98, 0x1283_5b01, 0x2431_85be, 0x550c_7dc3, 0x72be_5d74, 0x80de_b1fe, 0x9bdc_06a7, 0xc19b_f174, ] # alpha/beta table per (branch, step) ab = [ # branch 1 [(delta[0], delta[1]), (delta[2], delta[3]), (delta[4], delta[5]), (delta[6], delta[7]), (delta[8], delta[9]), (delta[10], delta[11]), (delta[12], delta[13]), (delta[14], delta[15])], # branch 2 [(delta[15], delta[14]), (delta[13], delta[12]), (delta[11], delta[10]), (delta[9], delta[8]), (delta[7], delta[6]), (delta[5], delta[4]), (delta[3], delta[2]), (delta[1], delta[0])], # branch 3 [(delta[1], delta[0]), (delta[3], delta[2]), (delta[5], delta[4]), (delta[7], delta[6]), (delta[9], delta[8]), (delta[11], delta[10]), (delta[13], delta[12]), (delta[15], delta[14])], # branch 4 [(delta[14], delta[15]), (delta[12], delta[13]), (delta[10], delta[11]), (delta[8], delta[9]), (delta[6], delta[7]), (delta[4], delta[5]), (delta[2], delta[3]), (delta[0], delta[1])], ] cv = [self.h0, self.h1, self.h2, self.h3, self.h4, self.h5, self.h6, self.h7] outs = [] for j in range(4): v = list(cv) perm = sigma[j] for k in range(8): m0 = m[perm[2 * k]] m1 = m[perm[2 * k + 1]] alpha, beta = ab[j][k] v = self.step(v, m0, m1, alpha, beta) outs.append(v) t12 = add_words(outs[0], outs[1]) t34 = add_words(outs[2], outs[3]) mixed = xor_words(t12, t34) new_cv = add_words(cv, mixed) self.h0, self.h1, self.h2, self.h3, self.h4, self.h5, self.h6, self.h7 = new_cv return class BELT: block_size = 32 digest_size = 32 h5 = ( 0x0000_1620, 0x0000_1280, 0x0000_1740, 0x0000_1900, 0x0000_0140, 0x0000_0100, 0x0000_1ea0, 0x0000_0760, 0x0000_06c0, 0x0000_0da0, 0x0000_0000, 0x0000_11c0, 0x0000_0b00, 0x0000_0940, 0x0000_0ba0, 0x0000_1c80, 0x0000_10a0, 0x0000_0080, 0x0000_1f40, 0x0000_13a0, 0x0000_0360, 0x0000_16c0, 0x0000_18e0, 0x0000_1580, 0x0000_04a0, 0x0000_05c0, 0x0000_0e40, 0x0000_1840, 0x0000_0040, 0x0000_1fa0, 0x0000_19c0, 0x0000_01a0, 0x0000_0b60, 0x0000_1c60, 0x0000_1ac0, 0x0000_0240, 0x0000_02e0, 0x0000_1720, 0x0000_0c20, 0x0000_1020, 0x0000_1fc0, 0x0000_0ce0, 0x0000_10c0, 0x0000_15a0, 0x0000_0e20, 0x0000_0d60, 0x0000_1120, 0x0000_0160, 0x0000_0b80, 0x0000_1600, 0x0000_1800, 0x0000_1fe0, 0x0000_0660, 0x0000_1860, 0x0000_0ac0, 0x0000_1700, 0x0000_06a0, 0x0000_1880, 0x0000_00a0, 0x0000_15c0, 0x0000_1b00, 0x0000_1c00, 0x0000_0fe0, 0x0000_1320, 0x0000_1c20, 0x0000_0560, 0x0000_1b80, 0x0000_0340, 0x0000_1c40, 0x0000_1040, 0x0000_0ae0, 0x0000_1d80, 0x0000_0e00, 0x0000_07e0, 0x0000_1980, 0x0000_1e00, 0x0000_12a0, 0x0000_1dc0, 0x0000_11a0, 0x0000_1e20, 0x0000_1820, 0x0000_1560, 0x0000_0ec0, 0x0000_0700, 0x0000_13e0, 0x0000_1cc0, 0x0000_0f00, 0x0000_1940, 0x0000_1ee0, 0x0000_18c0, 0x0000_1f00, 0x0000_0c00, 0x0000_1aa0, 0x0000_1760, 0x0000_1380, 0x0000_09e0, 0x0000_1e60, 0x0000_0780, 0x0000_0ca0, 0x0000_0f60, 0x0000_0c60, 0x0000_0f80, 0x0000_0600, 0x0000_0d40, 0x0000_1ba0, 0x0000_09c0, 0x0000_14e0, 0x0000_0f20, 0x0000_13c0, 0x0000_1640, 0x0000_07a0, 0x0000_0620, 0x0000_07c0, 0x0000_1300, 0x0000_16a0, 0x0000_0dc0, 0x0000_04e0, 0x0000_1a60, 0x0000_1780, 0x0000_19e0, 0x0000_0b20, 0x0000_03c0, 0x0000_0300, 0x0000_03e0, 0x0000_0980, 0x0000_0b40, 0x0000_16e0, 0x0000_1260, 0x0000_1d20, 0x0000_1bc0, 0x0000_1ce0, 0x0000_0580, 0x0000_11e0, 0x0000_0180, 0x0000_01e0, 0x0000_14c0, 0x0000_05a0, 0x0000_1b60, 0x0000_0920, 0x0000_1e80, 0x0000_0de0, 0x0000_0e60, 0x0000_12c0, 0x0000_08e0, 0x0000_00c0, 0x0000_00e0, 0x0000_0a60, 0x0000_02c0, 0x0000_1da0, 0x0000_0480, 0x0000_0f40, 0x0000_06e0, 0x0000_0720, 0x0000_1960, 0x0000_1460, 0x0000_1060, 0x0000_0060, 0x0000_1520, 0x0000_1160, 0x0000_1ec0, 0x0000_1240, 0x0000_17a0, 0x0000_1360, 0x0000_0380, 0x0000_1ca0, 0x0000_1a20, 0x0000_0820, 0x0000_0020, 0x0000_0a80, 0x0000_08a0, 0x0000_1f60, 0x0000_1920, 0x0000_0bc0, 0x0000_09a0, 0x0000_01c0, 0x0000_1e40, 0x0000_0d00, 0x0000_0400, 0x0000_1000, 0x0000_1540, 0x0000_0440, 0x0000_0fa0, 0x0000_0c80, 0x0000_05e0, 0x0000_04c0, 0x0000_10e0, 0x0000_1f20, 0x0000_0680, 0x0000_1200, 0x0000_0800, 0x0000_0aa0, 0x0000_0220, 0x0000_17c0, 0x0000_0640, 0x0000_12e0, 0x0000_0260, 0x0000_0860, 0x0000_1f80, 0x0000_1340, 0x0000_0900, 0x0000_1400, 0x0000_0540, 0x0000_1100, 0x0000_0be0, 0x0000_0320, 0x0000_0960, 0x0000_0120, 0x0000_1420, 0x0000_0fc0, 0x0000_19a0, 0x0000_1480, 0x0000_1a00, 0x0000_02a0, 0x0000_0880, 0x0000_15e0, 0x0000_1180, 0x0000_14a0, 0x0000_1080, 0x0000_0a00, 0x0000_17e0, 0x0000_0cc0, 0x0000_1a40, 0x0000_1d00, 0x0000_1140, 0x0000_1440, 0x0000_1ae0, 0x0000_08c0, 0x0000_0a40, 0x0000_0840, 0x0000_1500, 0x0000_1be0, 0x0000_1660, 0x0000_0d20, 0x0000_0e80, 0x0000_18a0, 0x0000_0a20, 0x0000_1d60, 0x0000_0460, 0x0000_0520, 0x0000_0420, 0x0000_1a80, 0x0000_1de0, 0x0000_1b20, 0x0000_1680, 0x0000_0740, 0x0000_0c40, 0x0000_0500, 0x0000_0ea0, 0x0000_1220, 0x0000_0280, 0x0000_0200, 0x0000_1d40, 0x0000_0ee0, 0x0000_0d80, 0x0000_1b40, 0x0000_03a0 ) def __init__(self, data=b""): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.r = 0 self.s = [0, 0, 0, 0] self.h = [ 0xc8ba_94b1, 0x3bf5_080a, 0x8e00_6d36, 0xe45d_4a58, 0x9dfa_0485, 0xacc7_b61b, 0xc272_2e25, 0x0dce_fd02, ] self.buf = bytearray() self.done = False self.out = None if data: self.update(data) return def copy(self): other = self.__class__() other.r = self.r other.s = list(self.s) other.h = list(self.h) other.buf = bytearray(self.buf) other.done = self.done other.out = None if self.out is None else list(self.out) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if self.done: raise ValueError("hash context is finalized") self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.r += 1 self.compress_block(block) return self def digest(self): c = self.copy() c.finalize() out = bytearray() for w in c.out: out.extend(w.to_bytes(4, "little")) return bytes(out) def hexdigest(self): return self.digest().hex() def finalize(self): def encode_r(r_bits): r = int(r_bits) return [ (r & 0xffff_ffff), ((r >> 32) & 0xffff_ffff), ((r >> 64) & 0xffff_ffff), ((r >> 96) & 0xffff_ffff), ] if self.done: return pos = len(self.buf) if pos != 0: block = bytes(self.buf) + b"\x00" * (self.block_size - pos) self.buf.clear() self.compress_block(block) bit_len = (self.r * self.block_size + pos) * 8 r_words = encode_r(bit_len) _, y = self.belt_compress(r_words, self.s, self.h) self.out = y self.done = True return def compress_block(self, block): def u32le(data, i): return int.from_bytes(data[4 * i : 4 * i + 4], "little") x1 = [ u32le(block, 0), u32le(block, 1), u32le(block, 2), u32le(block, 3), ] x2 = [ u32le(block, 4), u32le(block, 5), u32le(block, 6), u32le(block, 7), ] t, h = self.belt_compress(x1, x2, self.h) self.h = h self.s = [(self.s[i] ^ t[i]) & 0xffff_ffff for i in range(4)] return def belt_block_raw(self, x, key): def key_idx(key, i, delta): return key[(7 * i - delta - 1) % 8] & 0xffff_ffff def h13_entry(i): return (h5[i] << 8) & 0xffff_ffff def h21_entry(i): return (h5[i] << 16) & 0xffff_ffff def h29_entry(i): v = h5[i] & 0xffff_ffff return ((v >> 8) | ((v & 0xff) << 24)) & 0xffff_ffff def g5(u): return ( h29_entry((u >> 24) & 0xff) ^ h21_entry((u >> 16) & 0xff) ^ h13_entry((u >> 8) & 0xff) ^ h5[u & 0xff] ) & 0xffff_ffff def g13(u): return ( h5[(u >> 24) & 0xff] ^ h29_entry((u >> 16) & 0xff) ^ h21_entry((u >> 8) & 0xff) ^ h13_entry(u & 0xff) ) & 0xffff_ffff def g21(u): return ( h13_entry((u >> 24) & 0xff) ^ h5[(u >> 16) & 0xff] ^ h29_entry((u >> 8) & 0xff) ^ h21_entry(u & 0xff) ) & 0xffff_ffff h5 = self.h5 a = x[0] & 0xffff_ffff b = x[1] & 0xffff_ffff c = x[2] & 0xffff_ffff d = x[3] & 0xffff_ffff for i in range(1, 9): b ^= g5(a + key_idx(key, i, 6)) c ^= g21(d + key_idx(key, i, 5)) a = (a - g13(b + key_idx(key, i, 4))) & 0xffff_ffff e = (g21(b + c + key_idx(key, i, 3)) ^ i) & 0xffff_ffff b = (b + e) & 0xffff_ffff c = (c - e) & 0xffff_ffff d = (d + g13(c + key_idx(key, i, 2))) & 0xffff_ffff b ^= g21(a + key_idx(key, i, 1)) c ^= g5(d + key_idx(key, i, 0)) a, b, c, d = b, d, a, c return [b & 0xffff_ffff, d & 0xffff_ffff, a & 0xffff_ffff, c & 0xffff_ffff] def belt_compress(self, x1, x2, x34): def xor4(a, b): return [ (a[0] ^ b[0]) & 0xffff_ffff, (a[1] ^ b[1]) & 0xffff_ffff, (a[2] ^ b[2]) & 0xffff_ffff, (a[3] ^ b[3]) & 0xffff_ffff, ] def concat4(a, b): return [a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]] x3 = [x34[0], x34[1], x34[2], x34[3]] x4 = [x34[4], x34[5], x34[6], x34[7]] t1 = self.belt_block_raw(xor4(x3, x4), concat4(x1, x2)) s = xor4(xor4(t1, x3), x4) t2 = self.belt_block_raw(x1, concat4(s, x4)) y1 = xor4(t2, x1) s_inv = [(~v) & 0xffff_ffff for v in s] t3 = self.belt_block_raw(x2, concat4(s_inv, x3)) y2 = xor4(t3, x2) return (s, concat4(y1, y2)) class CityFarmBase: # CityHash/FarmHash 64-bit constants k0 = 0xc3a5_c85c_97cb_3127 k1 = 0xb492_b66f_be98_f273 k2 = 0x9ae1_6a3b_2f90_404f # Murmur3 32-bit constants c1 = 0xcc9e_2d51 c2 = 0x1b87_3593 def __init__(self, data=b"", seed=0): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.data = data self.seed = seed return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.data += data return def ror32(self, x, r): x &= 0xffff_ffff r &= 31 if r == 0: return x return ((x >> r) | (x << (32 - r))) & 0xffff_ffff def ror64(self, x, r): x &= 0xffff_ffff_ffff_ffff r &= 63 if r == 0: return x return ((x >> r) | (x << (64 - r))) & 0xffff_ffff_ffff_ffff def u32le(self, data, off): return int.from_bytes(data[off:off + 4], "little", signed=False) def u64le(self, data, off): return int.from_bytes(data[off:off + 8], "little", signed=False) def bswap32(self, x): return ( ((x & 0x0000_00ff) << 24) | ((x & 0x0000_ff00) << 8) | ((x & 0x00ff_0000) >> 8) | ((x & 0xff00_0000) >> 24) ) def bswap64(self, x): return ( ((x & 0x0000_0000_0000_00ff) << 56) | ((x & 0x0000_0000_0000_ff00) << 40) | ((x & 0x0000_0000_00ff_0000) << 24) | ((x & 0x0000_0000_ff00_0000) << 8) | ((x & 0x0000_00ff_0000_0000) >> 8) | ((x & 0x0000_ff00_0000_0000) >> 24) | ((x & 0x00ff_0000_0000_0000) >> 40) | ((x & 0xff00_0000_0000_0000) >> 56) ) class CityHash32(CityFarmBase): digest_size = 4 def hexdigest(self): value = self.cityhash32(self.data) return f"{value:08x}" def cityhash32_fmix(self, h): h &= 0xffff_ffff h ^= (h >> 16) h = (h * 0x85eb_ca6b) & 0xffff_ffff h ^= (h >> 13) h = (h * 0xc2b2_ae35) & 0xffff_ffff h ^= (h >> 16) return h & 0xffff_ffff def cityhash32_mur(self, a, h): a &= 0xffff_ffff h &= 0xffff_ffff a = (a * self.c1) & 0xffff_ffff a = self.ror32(a, 17) a = (a * self.c2) & 0xffff_ffff h ^= a h = self.ror32(h, 19) h = (h * 5 + 0xe654_6b64) & 0xffff_ffff return h & 0xffff_ffff def cityhash32_len0to4(self, data, seed): def signed_byte(b): if b >= 128: return b - 256 return b b = seed & 0xffff_ffff c = 9 n = len(data) for i in range(n): v = signed_byte(data[i]) & 0xffff_ffff b = (b * self.c1 + v) & 0xffff_ffff c ^= b return self.cityhash32_fmix( self.cityhash32_mur( b, self.cityhash32_mur(n, c) ) ) def cityhash32_len5to12(self, data, seed): seed &= 0xffff_ffff n = len(data) a = n & 0xffff_ffff b = (a * 5) & 0xffff_ffff c = 9 d = (b + seed) & 0xffff_ffff a = (a + self.u32le(data, 0)) & 0xffff_ffff b = (b + self.u32le(data, n - 4)) & 0xffff_ffff c = (c + self.u32le(data, (n >> 1) & 4)) & 0xffff_ffff return self.cityhash32_fmix( self.cityhash32_mur( c, self.cityhash32_mur( b, self.cityhash32_mur(a, d) ) ) ^ seed ) def cityhash32_len13to24(self, data): n = len(data) a = self.u32le(data, (n >> 1) - 4) b = self.u32le(data, 4) c = self.u32le(data, n - 8) d = self.u32le(data, n >> 1) e = self.u32le(data, 0) f = self.u32le(data, n - 4) h = n & 0xffff_ffff return self.cityhash32_fmix( self.cityhash32_mur( f, self.cityhash32_mur( e, self.cityhash32_mur( d, self.cityhash32_mur( c, self.cityhash32_mur( b, self.cityhash32_mur(a, h) ) ) ) ) ) ) def cityhash32(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") n = len(data) if n <= 24: if n <= 12: if n <= 4: return self.cityhash32_len0to4(data, 0) return self.cityhash32_len5to12(data, 0) return self.cityhash32_len13to24(data) h = n & 0xffff_ffff g = (self.c1 * h) & 0xffff_ffff f = g a0 = (self.ror32(self.u32le(data, n - 4) * self.c1, 17) * self.c2) & 0xffff_ffff a1 = (self.ror32(self.u32le(data, n - 8) * self.c1, 17) * self.c2) & 0xffff_ffff a2 = (self.ror32(self.u32le(data, n - 16) * self.c1, 17) * self.c2) & 0xffff_ffff a3 = (self.ror32(self.u32le(data, n - 12) * self.c1, 17) * self.c2) & 0xffff_ffff a4 = (self.ror32(self.u32le(data, n - 20) * self.c1, 17) * self.c2) & 0xffff_ffff h ^= a0 h = self.ror32(h, 19) h = (h * 5 + 0xe654_6b64) & 0xffff_ffff h ^= a2 h = self.ror32(h, 19) h = (h * 5 + 0xe654_6b64) & 0xffff_ffff g ^= a1 g = self.ror32(g, 19) g = (g * 5 + 0xe654_6b64) & 0xffff_ffff g ^= a3 g = self.ror32(g, 19) g = (g * 5 + 0xe654_6b64) & 0xffff_ffff f = (f + a4) & 0xffff_ffff f = self.ror32(f, 19) f = (f * 5 + 0xe654_6b64) & 0xffff_ffff iters = (n - 1) // 20 off = 0 while iters != 0: a0 = (self.ror32(self.u32le(data, off) * self.c1, 17) * self.c2) & 0xffff_ffff a1 = self.u32le(data, off + 4) a2 = (self.ror32(self.u32le(data, off + 8) * self.c1, 17) * self.c2) & 0xffff_ffff a3 = (self.ror32(self.u32le(data, off + 12) * self.c1, 17) * self.c2) & 0xffff_ffff a4 = self.u32le(data, off + 16) h ^= a0 h = self.ror32(h, 18) h = (h * 5 + 0xe654_6b64) & 0xffff_ffff f = (f + a1) & 0xffff_ffff f = self.ror32(f, 19) f = (f * self.c1) & 0xffff_ffff g = (g + a2) & 0xffff_ffff g = self.ror32(g, 18) g = (g * 5 + 0xe654_6b64) & 0xffff_ffff h ^= (a3 + a1) & 0xffff_ffff h = self.ror32(h, 19) h = (h * 5 + 0xe654_6b64) & 0xffff_ffff g ^= a4 g = (self.bswap32(g) * 5) & 0xffff_ffff h = (h + a4 * 5) & 0xffff_ffff h = self.bswap32(h) f = (f + a0) & 0xffff_ffff f, h, g = g, f, h off += 20 iters -= 1 g = (self.ror32(g, 11) * self.c1) & 0xffff_ffff g = (self.ror32(g, 17) * self.c1) & 0xffff_ffff f = (self.ror32(f, 11) * self.c1) & 0xffff_ffff f = (self.ror32(f, 17) * self.c1) & 0xffff_ffff h = self.ror32(h + g, 19) h = (h * 5 + 0xe654_6b64) & 0xffff_ffff h = (self.ror32(h, 17) * self.c1) & 0xffff_ffff h = self.ror32(h + f, 19) h = (h * 5 + 0xe654_6b64) & 0xffff_ffff h = (self.ror32(h, 17) * self.c1) & 0xffff_ffff return h & 0xffff_ffff class CityHash64(CityFarmBase): digest_size = 8 def hexdigest(self): if self.seed: value = self.cityhash64_with_seed(self.data, self.seed) else: value = self.cityhash64(self.data) return f"{value:016x}" def shift_mix(self, x): x &= 0xffff_ffff_ffff_ffff return x ^ (x >> 47) def cityhash64_len16_mul(self, u, v, mul): u &= 0xffff_ffff_ffff_ffff v &= 0xffff_ffff_ffff_ffff mul &= 0xffff_ffff_ffff_ffff a = ((u ^ v) * mul) & 0xffff_ffff_ffff_ffff a ^= (a >> 47) b = ((v ^ a) * mul) & 0xffff_ffff_ffff_ffff b ^= (b >> 47) b = (b * mul) & 0xffff_ffff_ffff_ffff return b & 0xffff_ffff_ffff_ffff def cityhash64_len16(self, u, v): return self.cityhash64_len16_mul(u, v, 0x9ddf_ea08_eb38_2d69) def cityhash64_len0to16(self, data): n = len(data) if n >= 8: mul = (self.k2 + n * 2) & 0xffff_ffff_ffff_ffff a = (self.u64le(data, 0) + self.k2) & 0xffff_ffff_ffff_ffff b = self.u64le(data, n - 8) c = (self.ror64(b, 37) * mul + a) & 0xffff_ffff_ffff_ffff d = ((self.ror64(a, 25) + b) * mul) & 0xffff_ffff_ffff_ffff return self.cityhash64_len16_mul(c, d, mul) if n >= 4: mul = (self.k2 + n * 2) & 0xffff_ffff_ffff_ffff a = self.u32le(data, 0) return self.cityhash64_len16_mul(n + (a << 3), self.u32le(data, n - 4), mul) if n > 0: a = data[0] b = data[n >> 1] c = data[n - 1] y = a + (b << 8) z = n + (c << 2) return (self.shift_mix((y * self.k2) ^ (z * self.k0)) * self.k2) & 0xffff_ffff_ffff_ffff return self.k2 & 0xffff_ffff_ffff_ffff def cityhash64_len17to32(self, data): n = len(data) mul = (self.k2 + n * 2) & 0xffff_ffff_ffff_ffff a = (self.u64le(data, 0) * self.k1) & 0xffff_ffff_ffff_ffff b = self.u64le(data, 8) c = (self.u64le(data, n - 8) * mul) & 0xffff_ffff_ffff_ffff d = (self.u64le(data, n - 16) * self.k2) & 0xffff_ffff_ffff_ffff return self.cityhash64_len16_mul( self.ror64(a + b, 43) + self.ror64(c, 30) + d, a + self.ror64(b + self.k2, 18) + c, mul, ) def cityhash64_len33to64(self, data): n = len(data) mul = (self.k2 + n * 2) & 0xffff_ffff_ffff_ffff a = (self.u64le(data, 0) * self.k2) & 0xffff_ffff_ffff_ffff b = self.u64le(data, 8) c = self.u64le(data, n - 24) d = self.u64le(data, n - 32) e = (self.u64le(data, 16) * self.k2) & 0xffff_ffff_ffff_ffff f = (self.u64le(data, 24) * 9) & 0xffff_ffff_ffff_ffff g = self.u64le(data, n - 8) h = (self.u64le(data, n - 16) * mul) & 0xffff_ffff_ffff_ffff u = (self.ror64(a + g, 43) + (self.ror64(b, 30) + c) * 9) & 0xffff_ffff_ffff_ffff v = (((a + g) ^ d) + f + 1) & 0xffff_ffff_ffff_ffff w = (self.bswap64((u + v) * mul) + h) & 0xffff_ffff_ffff_ffff x = (self.ror64(e + f, 42) + c) & 0xffff_ffff_ffff_ffff y = ((self.bswap64((v + w) * mul) + g) * mul) & 0xffff_ffff_ffff_ffff z = (e + f + c) & 0xffff_ffff_ffff_ffff a2 = (self.bswap64((x + z) * mul + y) + b) & 0xffff_ffff_ffff_ffff b2 = (self.shift_mix((z + a2) * mul + d + h) * mul) & 0xffff_ffff_ffff_ffff return (b2 + x) & 0xffff_ffff_ffff_ffff def cityhash64(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") n = len(data) if n <= 32: if n <= 16: return self.cityhash64_len0to16(data) return self.cityhash64_len17to32(data) if n <= 64: return self.cityhash64_len33to64(data) x = self.u64le(data, n - 40) y = (self.u64le(data, n - 16) + self.u64le(data, n - 56)) & 0xffff_ffff_ffff_ffff z = self.cityhash64_len16(self.u64le(data, n - 48) + n, self.u64le(data, n - 24)) v = self.cityhash64_weak_hash_len32_with_seeds(data, n - 64, n, z) w = self.cityhash64_weak_hash_len32_with_seeds(data, n - 32, y + self.k1, x) x = (x * self.k1 + self.u64le(data, 0)) & 0xffff_ffff_ffff_ffff length = (n - 1) & ~63 off = 0 while length != 0: x = (self.ror64(x + y + v[0] + self.u64le(data, off + 8), 37) * self.k1) & 0xffff_ffff_ffff_ffff y = (self.ror64(y + v[1] + self.u64le(data, off + 48), 42) * self.k1) & 0xffff_ffff_ffff_ffff x ^= w[1] y = (y + v[0] + self.u64le(data, off + 40)) & 0xffff_ffff_ffff_ffff z = (self.ror64(z + w[0], 33) * self.k1) & 0xffff_ffff_ffff_ffff v = self.cityhash64_weak_hash_len32_with_seeds( data, off, v[1] * self.k1, x + w[0], ) w = self.cityhash64_weak_hash_len32_with_seeds( data, off + 32, z + w[1], y + self.u64le(data, off + 16), ) x, z = z, x off += 64 length -= 64 return self.cityhash64_len16( self.cityhash64_len16(v[0], w[0]) + self.shift_mix(y) * self.k1 + z, self.cityhash64_len16(v[1], w[1]) + x, ) def cityhash64_with_seeds(self, data, seed0, seed1): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") seed0 &= 0xffff_ffff_ffff_ffff seed1 &= 0xffff_ffff_ffff_ffff return self.cityhash64_len16(self.cityhash64(data) - seed0, seed1) def cityhash64_with_seed(self, data, seed): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") seed &= 0xffff_ffff_ffff_ffff return self.cityhash64_with_seeds(data, self.k2, seed) def cityhash64_weak_hash_len32_with_seeds(self, data, off, a, b): w = self.u64le(data, off) x = self.u64le(data, off + 8) y = self.u64le(data, off + 16) z = self.u64le(data, off + 24) a = (a + w) & 0xffff_ffff_ffff_ffff b = self.ror64(b + a + z, 21) c = a a = (a + x + y) & 0xffff_ffff_ffff_ffff b = (b + self.ror64(a, 44)) & 0xffff_ffff_ffff_ffff return ((a + z) & 0xffff_ffff_ffff_ffff, (b + c) & 0xffff_ffff_ffff_ffff) class CityHash128(CityHash64): digest_size = 16 def hexdigest(self): if self.seed: if isinstance(self.seed, tuple) and len(self.seed) == 2: seed_lo, seed_hi = self.seed else: seed_lo, seed_hi = self.seed, self.seed lo, hi = self.cityhash128_with_seed(self.data, seed_lo, seed_hi) else: lo, hi = self.cityhash128(self.data) return f"{lo & 0xffff_ffff_ffff_ffff:016x}{hi & 0xffff_ffff_ffff_ffff:016x}" def cityhash128_murmur(self, data, seed_lo, seed_hi): a = seed_lo & 0xffff_ffff_ffff_ffff b = seed_hi & 0xffff_ffff_ffff_ffff c = 0 d = 0 n = len(data) if n <= 16: a = (self.shift_mix(a * self.k1) * self.k1) & 0xffff_ffff_ffff_ffff c = (b * self.k1 + self.cityhash64_len0to16(data)) & 0xffff_ffff_ffff_ffff if n >= 8: d = self.shift_mix(a + self.u64le(data, 0)) & 0xffff_ffff_ffff_ffff else: d = self.shift_mix(a + c) & 0xffff_ffff_ffff_ffff else: c = self.cityhash64_len16(self.u64le(data, n - 8) + self.k1, a) d = self.cityhash64_len16(b + n, c + self.u64le(data, n - 16)) a = (a + d) & 0xffff_ffff_ffff_ffff off = 0 left = n while left > 16: a ^= (self.shift_mix(self.u64le(data, off) * self.k1) * self.k1) & 0xffff_ffff_ffff_ffff a = (a * self.k1) & 0xffff_ffff_ffff_ffff b ^= a c ^= (self.shift_mix(self.u64le(data, off + 8) * self.k1) * self.k1) & 0xffff_ffff_ffff_ffff c = (c * self.k1) & 0xffff_ffff_ffff_ffff d ^= c off += 16 left -= 16 a = self.cityhash64_len16(a, c) b = self.cityhash64_len16(d, b) return (a ^ b, self.cityhash64_len16(b, a)) def cityhash128_with_seed(self, data, seed_lo, seed_hi): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") n = len(data) if n < 128: return self.cityhash128_murmur(data, seed_lo, seed_hi) x = seed_lo & 0xffff_ffff_ffff_ffff y = seed_hi & 0xffff_ffff_ffff_ffff z = (n * self.k1) & 0xffff_ffff_ffff_ffff v0 = (self.ror64(y ^ self.k1, 49) * self.k1 + self.u64le(data, 0)) & 0xffff_ffff_ffff_ffff v1 = (self.ror64(v0, 42) * self.k1 + self.u64le(data, 8)) & 0xffff_ffff_ffff_ffff w0 = (self.ror64(y + z, 35) * self.k1 + x) & 0xffff_ffff_ffff_ffff w1 = (self.ror64(x + self.u64le(data, 88), 53) * self.k1) & 0xffff_ffff_ffff_ffff off = 0 length = n while length >= 128: x = (self.ror64(x + y + v0 + self.u64le(data, off + 8), 37) * self.k1) & 0xffff_ffff_ffff_ffff y = (self.ror64(y + v1 + self.u64le(data, off + 48), 42) * self.k1) & 0xffff_ffff_ffff_ffff x ^= w1 y = (y + v0 + self.u64le(data, off + 40)) & 0xffff_ffff_ffff_ffff z = (self.ror64(z + w0, 33) * self.k1) & 0xffff_ffff_ffff_ffff v0, v1 = self.cityhash64_weak_hash_len32_with_seeds( data, off, v1 * self.k1, x + w0, ) w0, w1 = self.cityhash64_weak_hash_len32_with_seeds( data, off + 32, z + w1, y + self.u64le(data, off + 16), ) x, z = z, x off += 64 x = (self.ror64(x + y + v0 + self.u64le(data, off + 8), 37) * self.k1) & 0xffff_ffff_ffff_ffff y = (self.ror64(y + v1 + self.u64le(data, off + 48), 42) * self.k1) & 0xffff_ffff_ffff_ffff x ^= w1 y = (y + v0 + self.u64le(data, off + 40)) & 0xffff_ffff_ffff_ffff z = (self.ror64(z + w0, 33) * self.k1) & 0xffff_ffff_ffff_ffff v0, v1 = self.cityhash64_weak_hash_len32_with_seeds( data, off, v1 * self.k1, x + w0, ) w0, w1 = self.cityhash64_weak_hash_len32_with_seeds( data, off + 32, z + w1, y + self.u64le(data, off + 16), ) x, z = z, x off += 64 length -= 128 x = (x + self.ror64(v0 + z, 49) * self.k0) & 0xffff_ffff_ffff_ffff y = ((y * self.k0) + self.ror64(w1, 37)) & 0xffff_ffff_ffff_ffff z = ((z * self.k0) + self.ror64(w0, 27)) & 0xffff_ffff_ffff_ffff w0 = (w0 * 9) & 0xffff_ffff_ffff_ffff v0 = (v0 * self.k0) & 0xffff_ffff_ffff_ffff tail_done = 0 while tail_done < length: tail_done += 32 y = (self.ror64(x + y, 42) * self.k0 + v1) & 0xffff_ffff_ffff_ffff w0 = (w0 + self.u64le(data, off + length - tail_done + 16)) & 0xffff_ffff_ffff_ffff x = (x * self.k0 + w0) & 0xffff_ffff_ffff_ffff z = (z + w1 + self.u64le(data, off + length - tail_done)) & 0xffff_ffff_ffff_ffff w1 = (w1 + v0) & 0xffff_ffff_ffff_ffff v0, v1 = self.cityhash64_weak_hash_len32_with_seeds(data, off + length - tail_done, v0 + z, v1) v0 = (v0 * self.k0) & 0xffff_ffff_ffff_ffff x = self.cityhash64_len16(x, v0) y = self.cityhash64_len16(y + z, w0) lo = (self.cityhash64_len16(x + v1, w1) + y) & 0xffff_ffff_ffff_ffff hi = self.cityhash64_len16(x + w1, y + v1) return (lo, hi) def cityhash128(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") n = len(data) if n >= 16: seed_lo = self.u64le(data, 0) seed_hi = (self.u64le(data, 8) + self.k0) & 0xffff_ffff_ffff_ffff return self.cityhash128_with_seed(data[16:], seed_lo, seed_hi) return self.cityhash128_with_seed(data, self.k0, self.k1) class FarmHash32(CityHash32): digest_size = 4 def __init__(self, data=b"", seed=0): super().__init__(data=data, seed=seed) return def hexdigest(self): if self.seed: value = self.farmhash32_with_seed(self.data, self.seed) else: value = self.farmhash32(self.data) return f"{value:08x}" def farmhash32_len13to24(self, data, seed): n = len(data) seed &= 0xffff_ffff a = self.u32le(data, (n >> 1) - 4) b = self.u32le(data, 4) c = self.u32le(data, n - 8) d = self.u32le(data, n >> 1) e = self.u32le(data, 0) f = self.u32le(data, n - 4) h = (d * self.c1 + n + seed) & 0xffff_ffff a = (self.ror32(a, 12) + f) & 0xffff_ffff h = (self.cityhash32_mur(c, h) + a) & 0xffff_ffff a = (self.ror32(a, 3) + c) & 0xffff_ffff h = (self.cityhash32_mur(e, h) + a) & 0xffff_ffff a = (self.ror32((a + f) & 0xffff_ffff, 12) + d) & 0xffff_ffff h = (self.cityhash32_mur(b ^ seed, h) + a) & 0xffff_ffff return self.cityhash32_fmix(h) def farmhash32(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") n = len(data) if n <= 24: if n >= 13: return self.farmhash32_len13to24(data, 0) if n >= 5: return self.cityhash32_len5to12(data, 0) return self.cityhash32_len0to4(data, 0) h = n & 0xffff_ffff g = (self.c1 * n) & 0xffff_ffff f = g a0 = (self.ror32(self.u32le(data, n - 4) * self.c1, 17) * self.c2) & 0xffff_ffff a1 = (self.ror32(self.u32le(data, n - 8) * self.c1, 17) * self.c2) & 0xffff_ffff a2 = (self.ror32(self.u32le(data, n - 16) * self.c1, 17) * self.c2) & 0xffff_ffff a3 = (self.ror32(self.u32le(data, n - 12) * self.c1, 17) * self.c2) & 0xffff_ffff a4 = (self.ror32(self.u32le(data, n - 20) * self.c1, 17) * self.c2) & 0xffff_ffff h ^= a0 h = (self.ror32(h, 19) * 5 + 0xe654_6b64) & 0xffff_ffff h ^= a2 h = (self.ror32(h, 19) * 5 + 0xe654_6b64) & 0xffff_ffff g ^= a1 g = (self.ror32(g, 19) * 5 + 0xe654_6b64) & 0xffff_ffff g ^= a3 g = (self.ror32(g, 19) * 5 + 0xe654_6b64) & 0xffff_ffff f = (f + a4) & 0xffff_ffff f = (self.ror32(f, 19) + 113) & 0xffff_ffff iters = (n - 1) // 20 off = 0 while iters != 0: a = self.u32le(data, off) b = self.u32le(data, off + 4) c = self.u32le(data, off + 8) d = self.u32le(data, off + 12) e = self.u32le(data, off + 16) h = (h + a) & 0xffff_ffff g = (g + b) & 0xffff_ffff f = (f + c) & 0xffff_ffff h = (self.cityhash32_mur(d, h) + e) & 0xffff_ffff g = (self.cityhash32_mur(c, g) + a) & 0xffff_ffff f = (self.cityhash32_mur(b + e * self.c1, f) + d) & 0xffff_ffff f = (f + g) & 0xffff_ffff g = (g + f) & 0xffff_ffff off += 20 iters -= 1 g = (self.ror32(g, 11) * self.c1) & 0xffff_ffff g = (self.ror32(g, 17) * self.c1) & 0xffff_ffff f = (self.ror32(f, 11) * self.c1) & 0xffff_ffff f = (self.ror32(f, 17) * self.c1) & 0xffff_ffff h = (self.ror32(h + g, 19) * 5 + 0xe654_6b64) & 0xffff_ffff h = (self.ror32(h, 17) * self.c1) & 0xffff_ffff h = (self.ror32(h + f, 19) * 5 + 0xe654_6b64) & 0xffff_ffff h = (self.ror32(h, 17) * self.c1) & 0xffff_ffff return h & 0xffff_ffff def farmhash32_with_seed(self, data, seed): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") seed &= 0xffff_ffff n = len(data) if n <= 24: if n >= 13: return self.farmhash32_len13to24(data, seed * self.c1) if n >= 5: return self.cityhash32_len5to12(data, seed) return self.cityhash32_len0to4(data, seed) h = self.farmhash32_len13to24(data[:24], seed ^ n) return self.cityhash32_mur(self.farmhash32(data[24:]) + seed, h) class FarmHash64(CityHash64): digest_size = 8 def __init__(self, data=b"", seed=0): super().__init__(data=data, seed=seed) return def hexdigest(self): if self.seed: value = self.farmhash64_with_seed(self.data, self.seed) else: value = self.farmhash64(self.data) return f"{value:016x}" def farmhash64_len33to64(self, data): n = len(data) mul = (self.k2 + n * 2) & 0xffff_ffff_ffff_ffff a = (self.u64le(data, 0) * self.k2) & 0xffff_ffff_ffff_ffff b = self.u64le(data, 8) c = (self.u64le(data, n - 8) * mul) & 0xffff_ffff_ffff_ffff d = (self.u64le(data, n - 16) * self.k2) & 0xffff_ffff_ffff_ffff y = (self.ror64(a + b, 43) + self.ror64(c, 30) + d) & 0xffff_ffff_ffff_ffff z = self.cityhash64_len16_mul(y, a + self.ror64(b + self.k2, 18) + c, mul) e = (self.u64le(data, 16) * mul) & 0xffff_ffff_ffff_ffff f = self.u64le(data, 24) g = ((y + self.u64le(data, n - 32)) * mul) & 0xffff_ffff_ffff_ffff h = ((z + self.u64le(data, n - 24)) * mul) & 0xffff_ffff_ffff_ffff return self.cityhash64_len16_mul( self.ror64(e + f, 43) + self.ror64(g, 30) + h, e + self.ror64(f + a, 18) + g, mul, ) def farmhash64(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") n = len(data) seed = 81 if n <= 32: if n <= 16: return self.cityhash64_len0to16(data) return self.cityhash64_len17to32(data) if n <= 64: return self.farmhash64_len33to64(data) x = seed & 0xffff_ffff_ffff_ffff y = (seed * self.k1 + 113) & 0xffff_ffff_ffff_ffff z = (self.shift_mix(y * self.k2 + 113) * self.k2) & 0xffff_ffff_ffff_ffff v = [0, 0] w = [0, 0] x = (x * self.k2 + self.u64le(data, 0)) & 0xffff_ffff_ffff_ffff end = ((n - 1) // 64) * 64 off = 0 while off != end: x = (self.ror64(x + y + v[0] + self.u64le(data, off + 8), 37) * self.k1) & 0xffff_ffff_ffff_ffff y = (self.ror64(y + v[1] + self.u64le(data, off + 48), 42) * self.k1) & 0xffff_ffff_ffff_ffff x ^= w[1] y = (y + v[0] + self.u64le(data, off + 40)) & 0xffff_ffff_ffff_ffff z = (self.ror64(z + w[0], 33) * self.k1) & 0xffff_ffff_ffff_ffff v = self.cityhash64_weak_hash_len32_with_seeds( data, off, v[1] * self.k1, x + w[0], ) w = self.cityhash64_weak_hash_len32_with_seeds( data, off + 32, z + w[1], y + self.u64le(data, off + 16), ) x, z = z, x off += 64 mul = (self.k1 + ((z & 0xff) << 1)) & 0xffff_ffff_ffff_ffff off = n - 64 w = ((w[0] + ((n - 1) & 63)) & 0xffff_ffff_ffff_ffff, w[1]) v = ((v[0] + w[0]) & 0xffff_ffff_ffff_ffff, v[1]) w = ((w[0] + v[0]) & 0xffff_ffff_ffff_ffff, w[1]) x = (self.ror64(x + y + v[0] + self.u64le(data, off + 8), 37) * mul) & 0xffff_ffff_ffff_ffff y = (self.ror64(y + v[1] + self.u64le(data, off + 48), 42) * mul) & 0xffff_ffff_ffff_ffff x ^= (w[1] * 9) & 0xffff_ffff_ffff_ffff y = (y + (v[0] * 9) + self.u64le(data, off + 40)) & 0xffff_ffff_ffff_ffff z = (self.ror64(z + w[0], 33) * mul) & 0xffff_ffff_ffff_ffff v = self.cityhash64_weak_hash_len32_with_seeds( data, off, v[1] * mul, x + w[0], ) w = self.cityhash64_weak_hash_len32_with_seeds( data, off + 32, z + w[1], y + self.u64le(data, off + 16), ) x, z = z, x return self.cityhash64_len16_mul( self.cityhash64_len16_mul(v[0], w[0], mul) + self.shift_mix(y) * self.k0 + z, self.cityhash64_len16_mul(v[1], w[1], mul) + x, mul, ) def farmhash64_with_seeds(self, data, seed0, seed1): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") seed0 &= 0xffff_ffff_ffff_ffff seed1 &= 0xffff_ffff_ffff_ffff return self.cityhash64_len16(self.farmhash64(data) - seed0, seed1) def farmhash64_with_seed(self, data, seed): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") seed &= 0xffff_ffff_ffff_ffff return self.farmhash64_with_seeds(data, self.k2, seed) class FarmHash128(CityHash128): digest_size = 16 def hexdigest(self): if self.seed: if isinstance(self.seed, tuple) and len(self.seed) == 2: seed_lo, seed_hi = self.seed else: seed_lo, seed_hi = self.seed, self.seed lo, hi = self.cityhash128_with_seed(self.data, seed_lo, seed_hi) else: lo, hi = self.cityhash128(self.data) return f"{hi:016x}{lo:016x}" class SipHashBase: block_size = 8 key_size = 16 iv0 = 0x736f_6d65_7073_6575 iv1 = 0x646f_7261_6e64_6f6d iv2 = 0x6c79_6765_6e65_7261 iv3 = 0x7465_6462_7974_6573 def __init__(self, key=b"\0" * 16, data=b""): if not isinstance(key, (bytes, bytearray, memoryview)): raise TypeError("key must be bytes-like") if len(key) != self.key_size: raise ValueError("key must be 16 bytes") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.key = key self.buf = bytearray() self.msg_len = 0 k0 = struct.unpack_from("= self.block_size: block = bytes(self.buf[:8]) del self.buf[:8] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return c.result def hexdigest(self): return self.digest().hex() def finalize(self): b = ((self.msg_len & 0xff) << 56) & 0xffff_ffff_ffff_ffff left = len(self.buf) for i in range(left): b |= (self.buf[i] & 0xff) << (8 * i) b &= 0xffff_ffff_ffff_ffff self.v3 ^= b for _ in range(self.c_rounds): self.sipround() self.v0 ^= b if self.digest_size == 16: self.v2 ^= 0xee else: self.v2 ^= 0xff for _ in range(self.d_rounds): self.sipround() out0 = (self.v0 ^ self.v1 ^ self.v2 ^ self.v3) & 0xffff_ffff_ffff_ffff if self.digest_size == 8: self.result = struct.pack("> (64 - n))) & 0xffff_ffff_ffff_ffff self.v0 = (self.v0 + self.v1) & 0xffff_ffff_ffff_ffff self.v1 = rol64(self.v1, 13) self.v1 ^= self.v0 self.v0 = rol64(self.v0, 32) self.v2 = (self.v2 + self.v3) & 0xffff_ffff_ffff_ffff self.v3 = rol64(self.v3, 16) self.v3 ^= self.v2 self.v0 = (self.v0 + self.v3) & 0xffff_ffff_ffff_ffff self.v3 = rol64(self.v3, 21) self.v3 ^= self.v0 self.v2 = (self.v2 + self.v1) & 0xffff_ffff_ffff_ffff self.v1 = rol64(self.v1, 17) self.v1 ^= self.v2 self.v2 = rol64(self.v2, 32) return class SipHash64_2_4(SipHashBase): digest_size = 8 c_rounds = 2 d_rounds = 4 class SipHash64_1_3(SipHashBase): digest_size = 8 c_rounds = 1 d_rounds = 3 class SipHash64_4_8(SipHashBase): digest_size = 8 c_rounds = 4 d_rounds = 8 class SipHash128_2_4(SipHashBase): digest_size = 16 c_rounds = 2 d_rounds = 4 class SipHash128_4_8(SipHashBase): digest_size = 16 c_rounds = 4 d_rounds = 8 class HalfSipHashBase: block_size = 4 key_size = 8 iv2 = 0x6c79_6765 iv3 = 0x7465_6462 def __init__(self, key=b"\0" * 8, data=b""): if not isinstance(key, (bytes, bytearray, memoryview)): raise TypeError("key must be bytes-like") if len(key) != self.key_size: raise ValueError("key must be 8 bytes") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.key = key self.buf = bytearray() self.msg_len = 0 self.v0 = 0 self.v1 = 0 self.v2 = self.iv2 self.v3 = self.iv3 k0 = struct.unpack_from("= self.block_size: block = bytes(self.buf[:4]) del self.buf[:4] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return c.result def hexdigest(self): return self.digest().hex() def finalize(self): b = ((self.msg_len & 0xff_ff_ff_ff) << 24) & 0xffff_ffff left = len(self.buf) if left >= 1: b |= (self.buf[0] & 0xff) << 0 if left >= 2: b |= (self.buf[1] & 0xff) << 8 if left >= 3: b |= (self.buf[2] & 0xff) << 16 b &= 0xffff_ffff self.v3 ^= b for _ in range(self.c_rounds): self.sipround() self.v0 ^= b if self.digest_size == 8: self.v2 ^= 0xee else: self.v2 ^= 0xff for _ in range(self.d_rounds): self.sipround() out0 = (self.v1 ^ self.v3) & 0xffff_ffff if self.digest_size == 4: self.result = struct.pack("> (32 - n))) & 0xffff_ffff self.v0 = (self.v0 + self.v1) & 0xffff_ffff self.v1 = rol32(self.v1, 5) self.v1 ^= self.v0 self.v0 = rol32(self.v0, 16) self.v2 = (self.v2 + self.v3) & 0xffff_ffff self.v3 = rol32(self.v3, 8) self.v3 ^= self.v2 self.v0 = (self.v0 + self.v3) & 0xffff_ffff self.v3 = rol32(self.v3, 7) self.v3 ^= self.v0 self.v2 = (self.v2 + self.v1) & 0xffff_ffff self.v1 = rol32(self.v1, 13) self.v1 ^= self.v2 self.v2 = rol32(self.v2, 16) return class HalfSipHash32_2_4(HalfSipHashBase): digest_size = 4 c_rounds = 2 d_rounds = 4 class HalfSipHash64_2_4(HalfSipHashBase): digest_size = 8 c_rounds = 2 d_rounds = 4 class SpookyHashBase: block_size = 96 digest_size = 16 sc_const = 0xdead_beef_dead_beef sc_num_vars = 12 sc_block_size = sc_num_vars * 8 sc_buf_size = 2 * sc_block_size def __init__(self, data=b"", seed=0): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.seed = seed & 0xffff_ffff_ffff_ffff self.buf = bytearray() self.msg_len = 0 self.hash1 = 0 self.hash2 = 0 if data: self.update(data) return def copy(self): other = self.__class__(seed=self.seed) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.hash1 = self.hash1 other.hash2 = self.hash2 return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) return self def digest(self): c = self.copy() c.finalize() return c.format_digest(c.hash1, c.hash2) def hexdigest(self): return self.digest().hex() def finalize(self): h1, h2 = self.hash128(bytes(self.buf), self.seed, self.seed) self.hash1 = h1 self.hash2 = h2 return def rol64(self, x, k): x &= 0xffff_ffff_ffff_ffff return ((x << k) | (x >> (64 - k))) & 0xffff_ffff_ffff_ffff def mix(self, data, s): rc = ( 11, 32, 43, 31, 17, 28, 39, 57, 55, 54, 22, 46, ) for i in range(12): s[i] = (s[i] + data[i]) & 0xffff_ffff_ffff_ffff s[(i + 2) % 12] ^= s[(i + 10) % 12] s[(i + 11) % 12] ^= s[i] s[i] = self.rol64(s[i], rc[i]) s[(i + 11) % 12] = (s[(i + 11) % 12] + s[(i + 1) % 12]) & 0xffff_ffff_ffff_ffff return s def end_partial(self, h): rc = ( 44, 15, 34, 21, 38, 33, 10, 13, 38, 53, 42, 54, ) for i in range(12): h[(i + 11) % 12] = (h[(i + 11) % 12] + h[(i + 1) % 12]) & 0xffff_ffff_ffff_ffff h[(i + 2) % 12] ^= h[(i + 11) % 12] h[(i + 1) % 12] = self.rol64(h[i], rc[i]) return h def end(self, h): h = self.end_partial(h) h = self.end_partial(h) h = self.end_partial(h) return h def end_data(self, data, h): for i in range(12): h[i] = (h[i] + data[i]) & 0xffff_ffff_ffff_ffff return self.end(h) def short_mix(self, h): rc = ( 50, 52, 30, 41, 54, 48, 38, 37, 62, 34, 5, 36, ) for i in range(12): h[(i + 2) % 4] = self.rol64(h[(i + 2) % 4], rc[i]) h[(i + 2) % 4] = (h[(i + 2) % 4] + h[(i + 3) % 4]) & 0xffff_ffff_ffff_ffff h[i % 4] ^= h[(i + 2) % 4] return h def short_end(self, h): rc = ( 15, 52, 26, 51, 28, 9, 47, 54, 32, 25, 63, ) for i in range(11): h[(i + 3) % 4] ^= h[(i + 2) % 4] h[(i + 2) % 4] = self.rol64(h[(i + 2) % 4], rc[i]) h[(i + 3) % 4] = (h[(i + 3) % 4] + h[(i + 2) % 4]) & 0xffff_ffff_ffff_ffff return h def short(self, message, hash1, hash2): def u64le(data, offset): return int.from_bytes(data[offset:offset + 8], "little") def u32le(data, offset): return int.from_bytes(data[offset:offset + 4], "little") length = len(message) remainder = length % 32 a = hash1 & 0xffff_ffff_ffff_ffff b = hash2 & 0xffff_ffff_ffff_ffff c = self.sc_const d = self.sc_const pos = 0 if length > 15: end = (length // 32) * 32 while pos < end: c = (c + u64le(message, pos)) & 0xffff_ffff_ffff_ffff d = (d + u64le(message, pos + 8)) & 0xffff_ffff_ffff_ffff a, b, c, d = self.short_mix([a, b, c, d]) a = (a + u64le(message, pos + 16)) & 0xffff_ffff_ffff_ffff b = (b + u64le(message, pos + 24)) & 0xffff_ffff_ffff_ffff pos += 32 if remainder >= 16: c = (c + u64le(message, pos)) & 0xffff_ffff_ffff_ffff d = (d + u64le(message, pos + 8)) & 0xffff_ffff_ffff_ffff a, b, c, d = self.short_mix(a, b, c, d) pos += 16 remainder -= 16 tail = message[pos:pos + remainder] d = (d + (((length & 0xffff_ffff_ffff_ffff) << 56) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff if remainder == 15: d = (d + (tail[14] << 48)) & 0xffff_ffff_ffff_ffff if remainder >= 14: d = (d + (tail[13] << 40)) & 0xffff_ffff_ffff_ffff if remainder >= 13: d = (d + (tail[12] << 32)) & 0xffff_ffff_ffff_ffff if remainder >= 12: d = (d + u32le(tail + b"\x00" * 4, 8)) & 0xffff_ffff_ffff_ffff c = (c + u64le(tail + b"\x00" * 8, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 11: d = (d + (tail[10] << 16)) & 0xffff_ffff_ffff_ffff d = (d + (tail[9] << 8)) & 0xffff_ffff_ffff_ffff d = (d + tail[8]) & 0xffff_ffff_ffff_ffff c = (c + u64le(tail + b"\x00" * 8, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 10: d = (d + (tail[9] << 8)) & 0xffff_ffff_ffff_ffff d = (d + tail[8]) & 0xffff_ffff_ffff_ffff c = (c + u64le(tail + b"\x00" * 8, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 9: d = (d + tail[8]) & 0xffff_ffff_ffff_ffff c = (c + u64le(tail + b"\x00" * 8, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 8: c = (c + u64le(tail, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 7: c = (c + (tail[6] << 48)) & 0xffff_ffff_ffff_ffff c = (c + (tail[5] << 40)) & 0xffff_ffff_ffff_ffff c = (c + (tail[4] << 32)) & 0xffff_ffff_ffff_ffff c = (c + u32le(tail + b"\x00" * 4, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 6: c = (c + (tail[5] << 40)) & 0xffff_ffff_ffff_ffff c = (c + (tail[4] << 32)) & 0xffff_ffff_ffff_ffff c = (c + u32le(tail + b"\x00" * 4, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 5: c = (c + (tail[4] << 32)) & 0xffff_ffff_ffff_ffff c = (c + u32le(tail + b"\x00" * 4, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 4: c = (c + u32le(tail, 0)) & 0xffff_ffff_ffff_ffff elif remainder == 3: c = (c + (tail[2] << 16)) & 0xffff_ffff_ffff_ffff c = (c + (tail[1] << 8)) & 0xffff_ffff_ffff_ffff c = (c + tail[0]) & 0xffff_ffff_ffff_ffff elif remainder == 2: c = (c + (tail[1] << 8)) & 0xffff_ffff_ffff_ffff c = (c + tail[0]) & 0xffff_ffff_ffff_ffff elif remainder == 1: c = (c + tail[0]) & 0xffff_ffff_ffff_ffff elif remainder == 0: c = (c + self.sc_const) & 0xffff_ffff_ffff_ffff d = (d + self.sc_const) & 0xffff_ffff_ffff_ffff a, b, c, d = self.short_end([a, b, c, d]) return a, b def hash128(self, message, seed1, seed2): length = len(message) if length < self.sc_buf_size: h1, h2 = self.short(message, seed1, seed2) return h1, h2 h0 = h3 = h6 = h9 = seed1 & 0xffff_ffff_ffff_ffff h1 = h4 = h7 = h10 = seed2 & 0xffff_ffff_ffff_ffff h2 = h5 = h8 = h11 = self.sc_const nblocks = length // self.sc_block_size pos = 0 for _ in range(nblocks): block = struct.unpack_from("<12Q", message, pos) state = [h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11] state = self.mix(block, state) h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11 = state pos += self.sc_block_size remainder = length - pos tail = message[pos:] buf = bytearray(self.sc_block_size) buf[:remainder] = tail buf[-1] = remainder & 0xff block = struct.unpack_from("<12Q", bytes(buf), 0) state = self.end_data(block, [h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11]) return state[0], state[1] def format_digest(self, hash1, hash2): return struct.pack("<2Q", hash1 & 0xffff_ffff_ffff_ffff, hash2 & 0xffff_ffff_ffff_ffff) class SpookyHash32(SpookyHashBase): digest_size = 4 def format_digest(self, hash1, hash2): return struct.pack(" end: fill = end off = self.bytes % 32 self.input[off:off + fill] = data[ptr:ptr + fill] ptr += fill self.bytes += fill if (self.bytes % 32) != 0: return self self.process_block(self.input, 0) self.bytes += (end - ptr) while ptr <= (end - 32): self.process_block(data, ptr) ptr += 32 if ptr < end: rem = end - ptr self.input[0:rem] = data[ptr:end] return self def digest(self): c = self.copy() c.finalize() return c.result def hexdigest(self): return self.digest().hex() def ror64(self, x, r): x &= 0xffff_ffff_ffff_ffff return ((x >> r) | ((x << (64 - r)) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff def u64le(self, data, offset): return int.from_bytes(data[offset:offset + 8], "little") def u32le(self, data, offset): return int.from_bytes(data[offset:offset + 4], "little") def u16le(self, data, offset): return int.from_bytes(data[offset:offset + 2], "little") def u8le(self, data, offset): return data[offset] def process_block(self, data, offset): v0, v1, v2, v3 = self.state v0 = (v0 + self.u64le(data, offset + 0) * self.k0) & 0xffff_ffff_ffff_ffff v0 = (self.ror64(v0, 29) + v2) & 0xffff_ffff_ffff_ffff v1 = (v1 + self.u64le(data, offset + 8) * self.k1) & 0xffff_ffff_ffff_ffff v1 = (self.ror64(v1, 29) + v3) & 0xffff_ffff_ffff_ffff v2 = (v2 + self.u64le(data, offset + 16) * self.k2) & 0xffff_ffff_ffff_ffff v2 = (self.ror64(v2, 29) + v0) & 0xffff_ffff_ffff_ffff v3 = (v3 + self.u64le(data, offset + 24) * self.k3) & 0xffff_ffff_ffff_ffff v3 = (self.ror64(v3, 29) + v1) & 0xffff_ffff_ffff_ffff self.state = [v0, v1, v2, v3] return class MetroHash64(MetroHashBase): digest_size = 8 k0 = 0xd6d0_18f5 k1 = 0xa2aa_033b k2 = 0x6299_2fc1 k3 = 0x30bc_5b29 def initialize(self, seed): self.vseed = ((seed + self.k2) * self.k0) & 0xffff_ffff_ffff_ffff self.state = [self.vseed, self.vseed, self.vseed, self.vseed] self.bytes = 0 return def finalize(self): v0, v1, v2, v3 = self.state if self.bytes >= 32: v = (((v0 + v3) * self.k0) + v1) & 0xffff_ffff_ffff_ffff v2 ^= (self.ror64(v, 37) * self.k1) & 0xffff_ffff_ffff_ffff v = (((v1 + v2) * self.k1) + v0) & 0xffff_ffff_ffff_ffff v3 ^= (self.ror64(v, 37) * self.k0) & 0xffff_ffff_ffff_ffff v = (((v0 + v2) * self.k0) + v3) & 0xffff_ffff_ffff_ffff v0 ^= (self.ror64(v, 37) * self.k1) & 0xffff_ffff_ffff_ffff v = (((v1 + v3) * self.k1) + v2) & 0xffff_ffff_ffff_ffff v1 ^= (self.ror64(v, 37) * self.k0) & 0xffff_ffff_ffff_ffff v0 = (self.vseed + (v0 ^ v1)) & 0xffff_ffff_ffff_ffff ptr = 0 end = self.bytes % 32 if (end - ptr) >= 16: t1 = (v0 + self.u64le(self.input, ptr) * self.k2) & 0xffff_ffff_ffff_ffff ptr += 8 t1 = (self.ror64(t1, 29) * self.k3) & 0xffff_ffff_ffff_ffff t2 = (v0 + self.u64le(self.input, ptr) * self.k2) & 0xffff_ffff_ffff_ffff ptr += 8 t2 = (self.ror64(t2, 29) * self.k3) & 0xffff_ffff_ffff_ffff t = (t1 * self.k0) & 0xffff_ffff_ffff_ffff t1 ^= (self.ror64(t, 21) + t2) & 0xffff_ffff_ffff_ffff t = (t2 * self.k3) & 0xffff_ffff_ffff_ffff t2 ^= (self.ror64(t, 21) + t1) & 0xffff_ffff_ffff_ffff v0 = (v0 + t2) & 0xffff_ffff_ffff_ffff v1 = t1 v2 = t2 if (end - ptr) >= 8: v0 = (v0 + self.u64le(self.input, ptr) * self.k3) & 0xffff_ffff_ffff_ffff ptr += 8 v0 ^= (self.ror64(v0, 55) * self.k1) & 0xffff_ffff_ffff_ffff if (end - ptr) >= 4: v0 = (v0 + self.u32le(self.input, ptr) * self.k3) & 0xffff_ffff_ffff_ffff ptr += 4 v0 ^= (self.ror64(v0, 26) * self.k1) & 0xffff_ffff_ffff_ffff if (end - ptr) >= 2: v0 = (v0 + self.u16le(self.input, ptr) * self.k3) & 0xffff_ffff_ffff_ffff ptr += 2 v0 ^= (self.ror64(v0, 48) * self.k1) & 0xffff_ffff_ffff_ffff if (end - ptr) >= 1: v0 = (v0 + self.u8le(self.input, ptr) * self.k3) & 0xffff_ffff_ffff_ffff v0 ^= (self.ror64(v0, 37) * self.k1) & 0xffff_ffff_ffff_ffff v0 ^= self.ror64(v0, 28) v0 = (v0 * self.k0) & 0xffff_ffff_ffff_ffff v0 ^= self.ror64(v0, 29) self.state = [v0, v1, v2, v3] self.bytes = 0 self.result = v0.to_bytes(8, "little") return class MetroHash128(MetroHashBase): digest_size = 16 k0 = 0xc83a_91e1 k1 = 0x8648_dbdb k2 = 0x7bde_c03b k3 = 0x2f58_70a5 def initialize(self, seed): self.state[0] = ((seed - self.k0) * self.k3) & 0xffff_ffff_ffff_ffff self.state[1] = ((seed + self.k1) * self.k2) & 0xffff_ffff_ffff_ffff self.state[2] = ((seed + self.k0) * self.k2) & 0xffff_ffff_ffff_ffff self.state[3] = ((seed - self.k1) * self.k3) & 0xffff_ffff_ffff_ffff self.bytes = 0 return def finalize(self): v0, v1, v2, v3 = self.state if self.bytes >= 32: v = (((v0 + v3) * self.k0) + v1) & 0xffff_ffff_ffff_ffff v2 ^= (self.ror64(v, 21) * self.k1) & 0xffff_ffff_ffff_ffff v = (((v1 + v2) * self.k1) + v0) & 0xffff_ffff_ffff_ffff v3 ^= (self.ror64(v, 21) * self.k0) & 0xffff_ffff_ffff_ffff v = (((v0 + v2) * self.k0) + v3) & 0xffff_ffff_ffff_ffff v0 ^= (self.ror64(v, 21) * self.k1) & 0xffff_ffff_ffff_ffff v = (((v1 + v3) * self.k1) + v2) & 0xffff_ffff_ffff_ffff v1 ^= (self.ror64(v, 21) * self.k0) & 0xffff_ffff_ffff_ffff ptr = 0 end = self.bytes % 32 if (end - ptr) >= 16: v0 = (v0 + self.u64le(self.input, ptr) * self.k2) & 0xffff_ffff_ffff_ffff ptr += 8 v0 = (self.ror64(v0, 33) * self.k3) & 0xffff_ffff_ffff_ffff v1 = (v1 + self.u64le(self.input, ptr) * self.k2) & 0xffff_ffff_ffff_ffff ptr += 8 v1 = (self.ror64(v1, 33) * self.k3) & 0xffff_ffff_ffff_ffff v = ((v0 * self.k2) + v1) & 0xffff_ffff_ffff_ffff v0 ^= (self.ror64(v, 45) * self.k1) & 0xffff_ffff_ffff_ffff v = ((v1 * self.k3) + v0) & 0xffff_ffff_ffff_ffff v1 ^= (self.ror64(v, 45) * self.k0) & 0xffff_ffff_ffff_ffff if (end - ptr) >= 8: v0 = (v0 + self.u64le(self.input, ptr) * self.k2) & 0xffff_ffff_ffff_ffff ptr += 8 v0 = (self.ror64(v0, 33) * self.k3) & 0xffff_ffff_ffff_ffff v = ((v0 * self.k2) + v1) & 0xffff_ffff_ffff_ffff v0 ^= (self.ror64(v, 27) * self.k1) & 0xffff_ffff_ffff_ffff if (end - ptr) >= 4: v1 = (v1 + self.u32le(self.input, ptr) * self.k2) & 0xffff_ffff_ffff_ffff ptr += 4 v1 = (self.ror64(v1, 33) * self.k3) & 0xffff_ffff_ffff_ffff v = ((v1 * self.k3) + v0) & 0xffff_ffff_ffff_ffff v1 ^= (self.ror64(v, 46) * self.k0) & 0xffff_ffff_ffff_ffff if (end - ptr) >= 2: v0 = (v0 + self.u16le(self.input, ptr) * self.k2) & 0xffff_ffff_ffff_ffff ptr += 2 v0 = (self.ror64(v0, 33) * self.k3) & 0xffff_ffff_ffff_ffff v = ((v0 * self.k2) + v1) & 0xffff_ffff_ffff_ffff v0 ^= (self.ror64(v, 22) * self.k1) & 0xffff_ffff_ffff_ffff if (end - ptr) >= 1: v1 = (v1 + self.u8le(self.input, ptr) * self.k2) & 0xffff_ffff_ffff_ffff v1 = (self.ror64(v1, 33) * self.k3) & 0xffff_ffff_ffff_ffff v = ((v1 * self.k3) + v0) & 0xffff_ffff_ffff_ffff v1 ^= (self.ror64(v, 58) * self.k0) & 0xffff_ffff_ffff_ffff v = ((v0 * self.k0) + v1) & 0xffff_ffff_ffff_ffff v0 = (v0 + self.ror64(v, 13)) & 0xffff_ffff_ffff_ffff v = ((v1 * self.k1) + v0) & 0xffff_ffff_ffff_ffff v1 = (v1 + self.ror64(v, 37)) & 0xffff_ffff_ffff_ffff v = ((v0 * self.k2) + v1) & 0xffff_ffff_ffff_ffff v0 = (v0 + self.ror64(v, 13)) & 0xffff_ffff_ffff_ffff v = ((v1 * self.k3) + v0) & 0xffff_ffff_ffff_ffff v1 = (v1 + self.ror64(v, 37)) & 0xffff_ffff_ffff_ffff self.state = [v0, v1, v2, v3] self.bytes = 0 self.result = v0.to_bytes(8, "little") + v1.to_bytes(8, "little") return class HighwayHashBase: block_size = 32 digest_size = 0 output_words = 0 final_rounds = 0 def __init__(self, data=b"", key=None): self.key = self.normalize_key(key) self.mul0 = [0, 0, 0, 0] self.mul1 = [0, 0, 0, 0] self.v0 = [0, 0, 0, 0] self.v1 = [0, 0, 0, 0] self.buf = bytearray() self.finalized = False self.result_words = None self.reset(self.key) if data: self.update(data) return def normalize_key(self, key): if key is None: return (0, 0, 0, 0) if isinstance(key, (bytes, bytearray, memoryview)): key_bytes = bytes(key) if len(key_bytes) != 32: raise ValueError("key must be 32 bytes") w0 = int.from_bytes(key_bytes[0:8], "little") w1 = int.from_bytes(key_bytes[8:16], "little") w2 = int.from_bytes(key_bytes[16:24], "little") w3 = int.from_bytes(key_bytes[24:32], "little") return (w0, w1, w2, w3) values = list(key) if len(values) != 4: raise ValueError("key must be 4x uint64 or 32 bytes") return ( int(values[0]) & 0xffff_ffff_ffff_ffff, int(values[1]) & 0xffff_ffff_ffff_ffff, int(values[2]) & 0xffff_ffff_ffff_ffff, int(values[3]) & 0xffff_ffff_ffff_ffff, ) def reset(self, key=None): if key is not None: self.key = self.normalize_key(key) self.mul0 = [ 0xdbe6_d5d5_fe4c_ce2f, 0xa409_3822_299f_31d0, 0x1319_8a2e_0370_7344, 0x243f_6a88_85a3_08d3, ] self.mul1 = [ 0x3bd3_9e10_cb0e_f593, 0xc0ac_f169_b5f1_8a8c, 0xbe54_66cf_34e9_0c6c, 0x4528_21e6_38d0_1377, ] self.v0 = [ (self.mul0[0] ^ self.key[0]) & 0xffff_ffff_ffff_ffff, (self.mul0[1] ^ self.key[1]) & 0xffff_ffff_ffff_ffff, (self.mul0[2] ^ self.key[2]) & 0xffff_ffff_ffff_ffff, (self.mul0[3] ^ self.key[3]) & 0xffff_ffff_ffff_ffff, ] self.v1 = [ (self.mul1[0] ^ self.swap32(self.key[0])) & 0xffff_ffff_ffff_ffff, (self.mul1[1] ^ self.swap32(self.key[1])) & 0xffff_ffff_ffff_ffff, (self.mul1[2] ^ self.swap32(self.key[2])) & 0xffff_ffff_ffff_ffff, (self.mul1[3] ^ self.swap32(self.key[3])) & 0xffff_ffff_ffff_ffff, ] self.buf = bytearray() self.finalized = False self.result_words = None return def copy(self): other = self.__class__(b"", self.key) other.mul0 = list(self.mul0) other.mul1 = list(self.mul1) other.v0 = list(self.v0) other.v1 = list(self.v1) other.buf = bytearray(self.buf) other.finalized = self.finalized other.result_words = None if self.result_words is None else list(self.result_words) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if self.finalized: raise ValueError("hash object already finalized") data_bytes = bytes(data) if not data_bytes: return self if self.buf: need = self.block_size - len(self.buf) if len(data_bytes) < need: self.buf.extend(data_bytes) return self self.buf.extend(data_bytes[:need]) self.update_packet(bytes(self.buf)) self.buf.clear() data_bytes = data_bytes[need:] i = 0 n = len(data_bytes) while i + 32 <= n: self.update_packet(data_bytes[i : i + 32]) i += 32 if i != n: self.buf.extend(data_bytes[i:]) return self def digest(self): c = self.copy() c.finalize() out = bytearray() for w in c.result_words: out.extend(c.bswap64(w).to_bytes(8, "big")) return bytes(out) def hexdigest(self): def bswap64(x): x &= 0xffff_ffff_ffff_ffff return int.from_bytes(x.to_bytes(8, "little"), "big") c = self.copy() c.finalize() parts = [] for w in c.result_words: parts.append(format(bswap64(w), "016x")) return "".join(parts) def finalize(self): if self.finalized: return if self.buf: self.update_remainder(bytes(self.buf)) self.buf.clear() self.final_permutes(self.final_rounds) self.result_words = self.finalize_words() self.finalized = True return def swap32(self, x): x &= 0xffff_ffff_ffff_ffff return ((x >> 32) | ((x << 32) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff def zipper_merge_and_add(self, v1, v0, add1, idx1, add0, idx0): def byte_of(value, index): return (value >> (index * 8)) & 0xff term0 = ( (byte_of(v0, 3) << 0) # out[0] | (byte_of(v1, 4) << 8) # out[1] | (byte_of(v0, 2) << 16) # out[2] | (byte_of(v0, 5) << 24) # out[3] | (byte_of(v1, 6) << 32) # out[4] | (byte_of(v0, 1) << 40) # out[5] | (byte_of(v1, 7) << 48) # out[6] | (byte_of(v0, 0) << 56) # out[7] ) term1 = ( (byte_of(v1, 3) << 0) # out[0] | (byte_of(v0, 4) << 8) # out[1] | (byte_of(v1, 2) << 16) # out[2] | (byte_of(v1, 5) << 24) # out[3] | (byte_of(v1, 1) << 32) # out[4] | (byte_of(v0, 6) << 40) # out[5] | (byte_of(v1, 0) << 48) # out[6] | (byte_of(v0, 7) << 56) # out[7] ) add0[idx0] = (add0[idx0] + term0) & 0xffff_ffff_ffff_ffff add1[idx1] = (add1[idx1] + term1) & 0xffff_ffff_ffff_ffff return def update_lanes(self, lanes): for i in range(4): self.v1[i] = (self.v1[i] + self.mul0[i] + lanes[i]) & 0xffff_ffff_ffff_ffff v = (self.v1[i] & 0xffff_ffff) * (self.v0[i] >> 32) self.mul0[i] = (self.mul0[i] ^ v) & 0xffff_ffff_ffff_ffff self.v0[i] = (self.v0[i] + self.mul1[i]) & 0xffff_ffff_ffff_ffff v = (self.v0[i] & 0xffff_ffff) * (self.v1[i] >> 32) self.mul1[i] = (self.mul1[i] ^ v) & 0xffff_ffff_ffff_ffff self.zipper_merge_and_add(self.v1[1], self.v1[0], self.v0, 1, self.v0, 0) self.zipper_merge_and_add(self.v1[3], self.v1[2], self.v0, 3, self.v0, 2) self.zipper_merge_and_add(self.v0[1], self.v0[0], self.v1, 1, self.v1, 0) self.zipper_merge_and_add(self.v0[3], self.v0[2], self.v1, 3, self.v1, 2) return def update_packet(self, packet): lanes = [ int.from_bytes(packet[0:8], "little"), int.from_bytes(packet[8:16], "little"), int.from_bytes(packet[16:24], "little"), int.from_bytes(packet[24:32], "little"), ] self.update_lanes(lanes) return def update_remainder(self, bytes_data): def rotate32_by(count, lanes): out = [0, 0, 0, 0] for i in range(4): x = lanes[i] & 0xffff_ffff_ffff_ffff half0 = x & 0xffff_ffff half1 = (x >> 32) & 0xffff_ffff r0 = ((half0 << count) | (half0 >> (32 - count))) & 0xffff_ffff r1 = ((half1 << count) | (half1 >> (32 - count))) & 0xffff_ffff out[i] = (r0 | (r1 << 32)) & 0xffff_ffff_ffff_ffff return out size_mod32 = len(bytes_data) size_mod4 = size_mod32 & 3 packet = bytearray(32) for i in range(4): self.v0[i] = (self.v0[i] + ((size_mod32 << 32) + size_mod32)) & 0xffff_ffff_ffff_ffff self.v1 = rotate32_by(size_mod32, self.v1) upto = size_mod32 & ~3 packet[:upto] = bytes_data[:upto] if size_mod32 & 16: tail = bytes_data[size_mod32 - 4 : size_mod32] packet[28:32] = tail else: if size_mod4: remainder = bytes_data[upto:] packet[16] = remainder[0] packet[17] = remainder[size_mod4 >> 1] packet[18] = remainder[size_mod4 - 1] self.update_packet(bytes(packet)) return def final_permutes(self, rounds): v0 = self.v0 for _ in range(rounds): v = [self.swap32(v0[2]), self.swap32(v0[3]), self.swap32(v0[0]), self.swap32(v0[1])] self.update_lanes(v) return def finalize_words(self): def modular_reduction(a3, a2, a1, a0): a3 &= 0x3fff_ffff_ffff_ffff a3_1l = (a3 << 1) & 0xffff_ffff_ffff_ffff a3_2l = (a3 << 2) & 0xffff_ffff_ffff_ffff a2_1l = (a2 << 1) & 0xffff_ffff_ffff_ffff a2_2l = (a2 << 2) & 0xffff_ffff_ffff_ffff a2_63r = a2 >> 63 a2_62r = a2 >> 62 m1 = a1 ^ (a3_1l | a2_63r) ^ (a3_2l | a2_62r) m0 = a0 ^ a2_1l ^ a2_2l return (m0, m1) if self.output_words == 1: w0 = (self.v0[0] + self.v1[0] + self.mul0[0] + self.mul1[0]) & 0xffff_ffff_ffff_ffff return [w0] if self.output_words == 2: w0 = (self.v0[0] + self.mul0[0] + self.v1[2] + self.mul1[2]) & 0xffff_ffff_ffff_ffff w1 = (self.v0[1] + self.mul0[1] + self.v1[3] + self.mul1[3]) & 0xffff_ffff_ffff_ffff return [w0, w1] if self.output_words == 4: a3 = (self.v1[1] + self.mul1[1]) & 0xffff_ffff_ffff_ffff a2 = (self.v1[0] + self.mul1[0]) & 0xffff_ffff_ffff_ffff a1 = (self.v0[1] + self.mul0[1]) & 0xffff_ffff_ffff_ffff a0 = (self.v0[0] + self.mul0[0]) & 0xffff_ffff_ffff_ffff h0, h1 = modular_reduction(a3, a2, a1, a0) b3 = (self.v1[3] + self.mul1[3]) & 0xffff_ffff_ffff_ffff b2 = (self.v1[2] + self.mul1[2]) & 0xffff_ffff_ffff_ffff b1 = (self.v0[3] + self.mul0[3]) & 0xffff_ffff_ffff_ffff b0 = (self.v0[2] + self.mul0[2]) & 0xffff_ffff_ffff_ffff h2, h3 = modular_reduction(b3, b2, b1, b0) return [h0, h1, h2, h3] raise ValueError("invalid output_words") class HighwayHash64(HighwayHashBase): digest_size = 8 output_words = 1 final_rounds = 4 class HighwayHash128(HighwayHashBase): digest_size = 16 output_words = 2 final_rounds = 6 class HighwayHash256(HighwayHashBase): digest_size = 32 output_words = 4 final_rounds = 10 class GOST: # codespell:ignore block_size = 32 digest_size = 32 sbox = None sbox = ( [0x04, 0x0a, 0x09, 0x02, 0x0d, 0x08, 0x00, 0x0e, 0x06, 0x0b, 0x01, 0x0c, 0x07, 0x0f, 0x05, 0x03], [0x0e, 0x0b, 0x04, 0x0c, 0x06, 0x0d, 0x0f, 0x0a, 0x02, 0x03, 0x08, 0x01, 0x00, 0x07, 0x05, 0x09], [0x05, 0x08, 0x01, 0x0d, 0x0a, 0x03, 0x04, 0x02, 0x0e, 0x0f, 0x0c, 0x07, 0x06, 0x00, 0x09, 0x0b], [0x07, 0x0d, 0x0a, 0x01, 0x00, 0x08, 0x09, 0x0f, 0x0e, 0x04, 0x06, 0x0c, 0x0b, 0x02, 0x05, 0x03], [0x06, 0x0c, 0x07, 0x01, 0x05, 0x0f, 0x0d, 0x08, 0x04, 0x0a, 0x09, 0x0e, 0x00, 0x03, 0x0b, 0x02], [0x04, 0x0b, 0x0a, 0x00, 0x07, 0x02, 0x01, 0x0d, 0x03, 0x06, 0x08, 0x05, 0x09, 0x0c, 0x0f, 0x0e], [0x0d, 0x0b, 0x04, 0x01, 0x03, 0x0f, 0x05, 0x09, 0x00, 0x0a, 0x0e, 0x07, 0x06, 0x08, 0x02, 0x0c], [0x01, 0x0f, 0x0d, 0x00, 0x05, 0x07, 0x0a, 0x04, 0x09, 0x02, 0x03, 0x0e, 0x06, 0x0b, 0x08, 0x0c], ) def __init__(self, data=b""): if self.sbox is None: raise ValueError("sbox must be defined in subclass") self.hash = [0] * 8 self.sum = [0] * 8 self.message = bytearray(32) self.msg_length = 0 self.gost_sbox = [[0] * 256 for _ in range(4)] self.init_table() if data: self.update(data) return def copy(self): other = self.__class__() other.hash = list(self.hash) other.sum = list(self.sum) other.message = bytearray(self.message) other.msg_length = self.msg_length other.gost_sbox = self.gost_sbox return other def rol32(self, x, n): x &= 0xffff_ffff n &= 31 return ((x << n) | (x >> (32 - n))) & 0xffff_ffff def init_table(self): sbox = self.sbox for a in range(16): ax = (sbox[1][a] << 15) & 0xffff_ffff bx = (sbox[3][a] << 23) & 0xffff_ffff cx = self.rol32(sbox[5][a], 31) dx = (sbox[7][a] << 7) & 0xffff_ffff for b in range(16): i = a * 16 + b self.gost_sbox[0][i] = (ax | (sbox[0][b] << 11)) & 0xffff_ffff self.gost_sbox[1][i] = (bx | (sbox[2][b] << 19)) & 0xffff_ffff self.gost_sbox[2][i] = (cx | (sbox[4][b] << 27)) & 0xffff_ffff self.gost_sbox[3][i] = (dx | (sbox[6][b] << 3)) & 0xffff_ffff return def encrypt_round(self, key1, key2, r, l): # noqa: E741 tmp = (key1 + r) & 0xffff_ffff l ^= ( # noqa: E741 self.gost_sbox[0][tmp & 0xff] ^ self.gost_sbox[1][(tmp >> 8) & 0xff] ^ self.gost_sbox[2][(tmp >> 16) & 0xff] ^ self.gost_sbox[3][(tmp >> 24) & 0xff] ) l &= 0xffff_ffff # noqa: E741 tmp = (key2 + l) & 0xffff_ffff r ^= ( self.gost_sbox[0][tmp & 0xff] ^ self.gost_sbox[1][(tmp >> 8) & 0xff] ^ self.gost_sbox[2][(tmp >> 16) & 0xff] ^ self.gost_sbox[3][(tmp >> 24) & 0xff] ) r &= 0xffff_ffff return r, l def encrypt(self, ii, key, varhash): r = varhash[ii] & 0xffff_ffff l = varhash[ii + 1] & 0xffff_ffff # noqa: E741 r, l = self.encrypt_round(key[0], key[1], r, l) # noqa: E741 r, l = self.encrypt_round(key[2], key[3], r, l) # noqa: E741 r, l = self.encrypt_round(key[4], key[5], r, l) # noqa: E741 r, l = self.encrypt_round(key[6], key[7], r, l) # noqa: E741 r, l = self.encrypt_round(key[0], key[1], r, l) # noqa: E741 r, l = self.encrypt_round(key[2], key[3], r, l) # noqa: E741 r, l = self.encrypt_round(key[4], key[5], r, l) # noqa: E741 r, l = self.encrypt_round(key[6], key[7], r, l) # noqa: E741 r, l = self.encrypt_round(key[0], key[1], r, l) # noqa: E741 r, l = self.encrypt_round(key[2], key[3], r, l) # noqa: E741 r, l = self.encrypt_round(key[4], key[5], r, l) # noqa: E741 r, l = self.encrypt_round(key[6], key[7], r, l) # noqa: E741 r, l = self.encrypt_round(key[7], key[6], r, l) # noqa: E741 r, l = self.encrypt_round(key[5], key[4], r, l) # noqa: E741 r, l = self.encrypt_round(key[3], key[2], r, l) # noqa: E741 r, l = self.encrypt_round(key[1], key[0], r, l) # noqa: E741 return [l & 0xffff_ffff, r & 0xffff_ffff] def block_compress(self, block): def get_byte32(value, index): return (value >> (index * 8)) & 0xff def pack_byte_column(words, byte_index): return ( (get_byte32(words[0], byte_index) << 0) | (get_byte32(words[1], byte_index) << 8) | (get_byte32(words[2], byte_index) << 16) | (get_byte32(words[3], byte_index) << 24) ) def lo16(value): return value & 0x0000_ffff def hi16(value): return value & 0xffff_0000 def shl16(value): return (value << 16) & 0xffff_ffff def shr16(value): return value >> 16 key = [0] * 8 u = [0] * 8 v = [0] * 8 w = [0] * 8 s = [0] * 8 for i in range(8): u[i] = self.hash[i] v[i] = block[i] w[i] = u[i] ^ v[i] i = 0 while True: even_words = w[0::2] odd_words = w[1::2] key[0] = pack_byte_column(even_words, 0) key[1] = pack_byte_column(even_words, 1) key[2] = pack_byte_column(even_words, 2) key[3] = pack_byte_column(even_words, 3) key[4] = pack_byte_column(odd_words, 0) key[5] = pack_byte_column(odd_words, 1) key[6] = pack_byte_column(odd_words, 2) key[7] = pack_byte_column(odd_words, 3) res = self.encrypt(i, key, self.hash) s[i] = res[0] s[i + 1] = res[1] if i == 0: w[0] = u[2] ^ v[4] w[1] = u[3] ^ v[5] w[2] = u[4] ^ v[6] w[3] = u[5] ^ v[7] v[0] ^= v[2] v[1] ^= v[3] w[4] = u[6] ^ v[0] w[5] = u[7] ^ v[1] u[0] ^= u[2] u[1] ^= u[3] v[2] ^= v[4] v[3] ^= v[5] w[6] = u[0] ^ v[2] w[7] = u[1] ^ v[3] elif (i & 2) != 0: if i == 6: break u[2] ^= u[4] ^ 0x0000_00ff u[3] ^= u[5] ^ 0xff00_ffff u[4] ^= 0xff00_ff00 u[5] ^= 0xff00_ff00 u[6] ^= 0x00ff_00ff u[7] ^= 0x00ff_00ff u[0] ^= 0x00ff_ff00 u[1] ^= 0xff00_00ff w[0] = u[4] ^ v[0] w[2] = u[6] ^ v[2] v[4] ^= v[6] w[4] = u[0] ^ v[4] v[6] ^= v[0] w[6] = u[2] ^ v[6] w[1] = u[5] ^ v[1] w[3] = u[7] ^ v[3] v[5] ^= v[7] w[5] = u[1] ^ v[5] v[7] ^= v[1] w[7] = u[3] ^ v[7] else: w[0] = u[6] ^ v[4] w[1] = u[7] ^ v[5] w[2] = u[0] ^ v[6] w[3] = u[1] ^ v[7] v[0] ^= v[2] v[1] ^= v[3] w[4] = u[2] ^ v[0] w[5] = u[3] ^ v[1] u[4] ^= u[6] u[5] ^= u[7] v[2] ^= v[4] v[3] ^= v[5] w[6] = u[4] ^ v[2] w[7] = u[5] ^ v[3] for j in range(8): u[j] &= 0xffff_ffff v[j] &= 0xffff_ffff w[j] &= 0xffff_ffff i += 2 u = [0] * 8 u[0] = block[0] ^ s[6] u[1] = block[1] ^ s[7] u[2] = ( block[2] ^ shl16(s[0]) ^ shr16(s[0]) ^ lo16(s[0]) ^ lo16(s[1]) ^ shr16(s[1]) ^ shl16(s[2]) ^ s[6] ^ shl16(s[6]) ^ hi16(s[7]) ^ shr16(s[7]) ) u[3] = ( block[3] ^ lo16(s[0]) ^ shl16(s[0]) ^ lo16(s[1]) ^ shl16(s[1]) ^ shr16(s[1]) ^ shl16(s[2]) ^ shr16(s[2]) ^ shl16(s[3]) ^ s[6] ^ shl16(s[6]) ^ shr16(s[6]) ^ lo16(s[7]) ^ shl16(s[7]) ^ shr16(s[7]) ) u[4] = ( block[4] ^ hi16(s[0]) ^ shl16(s[0]) ^ shr16(s[0]) ^ hi16(s[1]) ^ shr16(s[1]) ^ shl16(s[2]) ^ shr16(s[2]) ^ shl16(s[3]) ^ shr16(s[3]) ^ shl16(s[4]) ^ shl16(s[6]) ^ shr16(s[6]) ^ lo16(s[7]) ^ shl16(s[7]) ^ shr16(s[7]) ) u[5] = ( block[5] ^ shl16(s[0]) ^ shr16(s[0]) ^ hi16(s[0]) ^ lo16(s[1]) ^ s[2] ^ shr16(s[2]) ^ shl16(s[3]) ^ shr16(s[3]) ^ shl16(s[4]) ^ shr16(s[4]) ^ shl16(s[5]) ^ shl16(s[6]) ^ shr16(s[6]) ^ hi16(s[7]) ^ shl16(s[7]) ^ shr16(s[7]) ) u[6] = ( block[6] ^ s[0] ^ shr16(s[1]) ^ shl16(s[2]) ^ s[3] ^ shr16(s[3]) ^ shl16(s[4]) ^ shr16(s[4]) ^ shl16(s[5]) ^ shr16(s[5]) ^ s[6] ^ shl16(s[6]) ^ shr16(s[6]) ^ shl16(s[7]) ) u[7] = ( block[7] ^ hi16(s[0]) ^ shl16(s[0]) ^ lo16(s[1]) ^ shl16(s[1]) ^ shr16(s[2]) ^ shl16(s[3]) ^ s[4] ^ shr16(s[4]) ^ shl16(s[5]) ^ shr16(s[5]) ^ shr16(s[6]) ^ lo16(s[7]) ^ shl16(s[7]) ^ shr16(s[7]) ) h = self.hash v = [ h[0] ^ shl16(u[1]) ^ shr16(u[0]), h[1] ^ shl16(u[2]) ^ shr16(u[1]), h[2] ^ shl16(u[3]) ^ shr16(u[2]), h[3] ^ shl16(u[4]) ^ shr16(u[3]), h[4] ^ shl16(u[5]) ^ shr16(u[4]), h[5] ^ shl16(u[6]) ^ shr16(u[5]), h[6] ^ shl16(u[7]) ^ shr16(u[6]), h[7] ^ hi16(u[0]) ^ shl16(u[0]) ^ hi16(u[1]) ^ shl16(u[1]) ^ shl16(u[6]) ^ hi16(u[7]) ^ shr16(u[7]), ] self.hash = [ ( shl16(v[0]) ^ shr16(v[0]) ^ hi16(v[0]) ^ shr16(v[1]) ^ hi16(v[1]) ^ shl16(v[2]) ^ shr16(v[3]) ^ shl16(v[4]) ^ shr16(v[5]) ^ v[5] ^ shr16(v[6]) ^ shl16(v[7]) ^ shr16(v[7]) ^ lo16(v[7]) ), ( shl16(v[0]) ^ shr16(v[0]) ^ hi16(v[0]) ^ lo16(v[1]) ^ shr16(v[2]) ^ v[2] ^ shl16(v[3]) ^ shr16(v[4]) ^ shl16(v[5]) ^ shl16(v[6]) ^ v[6] ^ shr16(v[7]) ^ hi16(v[7]) ), ( shl16(v[0]) ^ lo16(v[0]) ^ shl16(v[1]) ^ shr16(v[1]) ^ hi16(v[1]) ^ shl16(v[2]) ^ shr16(v[3]) ^ v[3] ^ shl16(v[4]) ^ shr16(v[5]) ^ shr16(v[6]) ^ v[6] ^ shl16(v[7]) ^ shr16(v[7]) ^ lo16(v[7]) ), ( shl16(v[0]) ^ shr16(v[0]) ^ hi16(v[0]) ^ shr16(v[1]) ^ hi16(v[1]) ^ shl16(v[2]) ^ shr16(v[2]) ^ v[2] ^ shl16(v[3]) ^ shr16(v[4]) ^ v[4] ^ shl16(v[5]) ^ shl16(v[6]) ^ shr16(v[7]) ^ lo16(v[7]) ), ( shr16(v[0]) ^ shl16(v[1]) ^ v[1] ^ shr16(v[2]) ^ v[2] ^ shl16(v[3]) ^ shr16(v[3]) ^ v[3] ^ shl16(v[4]) ^ shr16(v[5]) ^ v[5] ^ shl16(v[6]) ^ shr16(v[6]) ^ shl16(v[7]) ), ( shl16(v[0]) ^ hi16(v[0]) ^ shl16(v[1]) ^ shr16(v[1]) ^ hi16(v[1]) ^ shl16(v[2]) ^ v[2] ^ shr16(v[3]) ^ v[3] ^ shl16(v[4]) ^ shr16(v[4]) ^ v[4] ^ shl16(v[5]) ^ shl16(v[6]) ^ shr16(v[6]) ^ v[6] ^ shl16(v[7]) ^ shr16(v[7]) ^ hi16(v[7]) ), ( v[0] ^ shr16(v[2]) ^ v[2] ^ shl16(v[3]) ^ v[3] ^ shr16(v[4]) ^ v[4] ^ shl16(v[5]) ^ shr16(v[5]) ^ v[5] ^ shl16(v[6]) ^ shr16(v[6]) ^ v[6] ^ shl16(v[7]) ^ v[7] ), ( shr16(v[0]) ^ v[0] ^ shl16(v[1]) ^ shr16(v[1]) ^ shl16(v[2]) ^ shr16(v[3]) ^ v[3] ^ shl16(v[4]) ^ v[4] ^ shr16(v[5]) ^ v[5] ^ shl16(v[6]) ^ shr16(v[6]) ^ shl16(v[7]) ^ v[7] ), ] return def compute_sum_and_hash(self, block): carry = 0 for i in range(8): hb = (self.sum[i] >> 24) & 0xff self.sum[i] = (self.sum[i] & 0x00ff_ffff) + (block[i] & 0x00ff_ffff) + carry hb = hb + ((block[i] >> 24) & 0xff) + ((self.sum[i] >> 24) & 0xff) self.sum[i] = (self.sum[i] & 0x00ff_ffff) | ((hb & 0xff) << 24) carry = 1 if (hb & 0x100) != 0 else 0 self.sum[i] &= 0xffff_ffff self.block_compress(block) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") size = len(data) index = self.msg_length & 31 self.msg_length += size p = 0 if index != 0: left = 32 - index if size < left: self.message[index : index + size] = data[:size] return self self.message[index : index + left] = data[:left] block = [0] * 8 for i in range(8): j = 4 * i block[i] = ( self.message[j] | (self.message[j + 1] << 8) | (self.message[j + 2] << 16) | (self.message[j + 3] << 24) ) & 0xffff_ffff self.compute_sum_and_hash(block) p += left size -= left while size >= 32: self.message[:] = data[p : p + 32] block = [0] * 8 for i in range(8): j = 4 * i block[i] = ( self.message[j] | (self.message[j + 1] << 8) | (self.message[j + 2] << 16) | (self.message[j + 3] << 24) ) & 0xffff_ffff self.compute_sum_and_hash(block) p += 32 size -= 32 if size != 0: self.message[:size] = data[p : p + size] return self def finalize(self): index = self.msg_length & 31 if index > 0: for i in range(32 - index): self.message[index + i] = 0 block = [0] * 8 for i in range(8): j = 4 * i block[i] = ( self.message[j] | (self.message[j + 1] << 8) | (self.message[j + 2] << 16) | (self.message[j + 3] << 24) ) & 0xffff_ffff self.compute_sum_and_hash(block) block = [0] * 8 block[0] = (self.msg_length << 3) & 0xffff_ffff block[1] = (self.msg_length >> 29) & 0xffff_ffff for i in range(2, 8): block[i] = 0 self.block_compress(block) self.block_compress(self.sum) return def digest(self): c = self.copy() c.finalize() out = bytearray() for w in c.hash: out.extend(struct.pack("> 62)) + i) & 0xffff_ffff_ffff_ffff self.index = self.n return def twist(self): for i in range(self.n): x = (self.mt[i] & self.upper_mask) + (self.mt[(i + 1) % self.n] & self.lower_mask) x_a = x >> 1 if x & 1: x_a ^= self.matrix_a self.mt[i] = (self.mt[(i + self.m) % self.n] ^ x_a) & 0xffff_ffff_ffff_ffff self.index = 0 return def next_u64(self): if self.index >= self.n: self.twist() y = self.mt[self.index] self.index += 1 y ^= (y >> 29) & 0x5555_5555_5555_5555 y ^= (y << 17) & 0x71d6_7fff_eda6_0000 y ^= (y << 37) & 0xfff7_eee0_0000_0000 y ^= (y >> 43) return y & 0xffff_ffff_ffff_ffff class HalfTimeHashBase: out_width = 2 in_width = 3 dimension = 6 encoded_dimension = 7 fanout = 8 max_stack_size = 9 digest_size = 8 block_size = 1 lanes = 1 def __init__(self, data=b""): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf.extend(data) self.msg_len += len(data) return self def digest(self): c = self.copy() c.finalize() return c.digest_bytes def hexdigest(self): return self.digest().hex() def finalize(self): data = bytes(self.buf) value = self.hash_uint64(data) self.digest_bytes = struct.pack(">Q", value) return def entropy_words_needed_for_hasher(self, length): def floor_log(a, b): if a <= 1: return 0 h = 0 while b >= a: b //= a h += 1 return h b = self.lanes h = floor_log(self.fanout, length // (b * self.dimension * self.in_width)) words = (self.encoded_dimension * self.in_width) \ + (self.fanout - 1) * self.out_width * h \ + b * self.fanout * self.out_width * h \ + b * self.dimension * self.in_width \ + self.out_width - 1 return words def hash_uint64(self, data): def tabulate_bytes(value, entropy, base): result = 0 for i in range(8): index = (value >> (8 * i)) & 0xff result ^= entropy[base + i * 256 + index] return result & 0xffff_ffff_ffff_ffff def ensure_entropy_words(needed): entropy_mt = Hash.Mt19937_64() entropy_words = [] while len(entropy_words) < needed: entropy_words.append(entropy_mt.next_u64()) return entropy_words length = len(data) table_max = 24 * 256 entropy_for_hasher_offset = self.out_width * 256 hasher_words = self.entropy_words_needed_for_hasher(length) needed = max(table_max, entropy_for_hasher_offset + hasher_words + (self.out_width + 2) * self.lanes + 32) entropy = ensure_entropy_words(needed) output0, output1 = self.hasher(entropy, entropy_for_hasher_offset, data, length) result = tabulate_bytes(length, entropy, 0) result ^= tabulate_bytes(output0, entropy, 8 * 256) result ^= tabulate_bytes(output1, entropy, 16 * 256) return result def load_one(self, word): w = word & 0xffff_ffff_ffff_ffff return [w] * self.lanes def load_block_words(self, entropy, offset): return [(entropy[offset + i] & 0xffff_ffff_ffff_ffff) for i in range(self.lanes)] def load_block_bytes(self, data, offset_bytes): out = [] for i in range(self.lanes): chunk = data[offset_bytes + 8 * i: offset_bytes + 8 * (i + 1)] if len(chunk) < 8: chunk = chunk + b"\x00" * (8 - len(chunk)) out.append(int.from_bytes(chunk, "little") & 0xffff_ffff_ffff_ffff) return out def plus(self, a, b): return [((x + y) & 0xffff_ffff_ffff_ffff) for x, y in zip(a, b)] def minus(self, a, b): return [((x - y) & 0xffff_ffff_ffff_ffff) for x, y in zip(a, b)] def xor(self, a, b): return [((x ^ y) & 0xffff_ffff_ffff_ffff) for x, y in zip(a, b)] def left_shift(self, a, s): return [((x << s) & 0xffff_ffff_ffff_ffff) for x in a] def right_shift32(self, a): return [((x >> 32) & 0xffff_ffff_ffff_ffff) for x in a] def plus32(self, a, b): out = [] for x, y in zip(a, b): lo = (x & 0xffff_ffff) + (y & 0xffff_ffff) hi = ((x >> 32) & 0xffff_ffff) + ((y >> 32) & 0xffff_ffff) lo &= 0xffff_ffff hi &= 0xffff_ffff out.append((lo | (hi << 32)) & 0xffff_ffff_ffff_ffff) return out def times(self, a, b): out = [] for x, y in zip(a, b): out.append(((x & 0xffff_ffff) * (y & 0xffff_ffff)) & 0xffff_ffff_ffff_ffff) return out def negate(self, a): return [(-x) & 0xffff_ffff_ffff_ffff for x in a] def sum_block(self, a): s = 0 for x in a: s = (s + x) & 0xffff_ffff_ffff_ffff return s def multiply_add(self, summand, factor1, factor2): return self.plus(summand, self.times(factor1, factor2)) def mix(self, accum, input_block, entropy_block): out = self.plus32(entropy_block, input_block) twin = self.right_shift32(out) out = self.multiply_add(accum, out, twin) return out def mix_one(self, accum, input_block, entropy_word): return self.mix(accum, input_block, self.load_one(entropy_word)) def mix_none(self, input_block, entropy_word): out = self.plus32(self.load_one(entropy_word), input_block) twin = self.right_shift32(out) out = self.times(out, twin) return out def simpler_times(self, k, x): if k == -1: return self.negate(x) if k == 0: return [0] * self.lanes if k == 1: return x if k == 2: return self.left_shift(x, 1) if k == 3: return self.plus(x, self.left_shift(x, 1)) if k == 4: return self.left_shift(x, 2) if k == 5: return self.plus(x, self.left_shift(x, 2)) if k == 7: return self.minus(self.left_shift(x, 3), x) if k == 8: return self.left_shift(x, 3) if k == 9: return self.plus(x, self.left_shift(x, 3)) raise ValueError("unsupported multiplier") def dot2(self, sinks, x, c0, c1): sinks[0] = self.plus(sinks[0], self.simpler_times(c0, x)) sinks[1] = self.plus(sinks[1], self.simpler_times(c1, x)) return sinks def combine2(self, input_blocks): sinks = [input_blocks[0], input_blocks[1]] self.dot2(sinks, input_blocks[2], 1, 1) self.dot2(sinks, input_blocks[3], 1, 2) self.dot2(sinks, input_blocks[4], 2, 1) self.dot2(sinks, input_blocks[5], 1, 4) self.dot2(sinks, input_blocks[6], 4, 1) return sinks def encode2(self, io): for i in range(self.in_width): acc = io[0][i] for j in range(1, self.dimension): acc = self.xor(acc, io[j][i]) io[self.dimension][i] = acc return io def parse_entropy_matrix(self, entropy, offset): m = [] idx = offset for _i in range(self.encoded_dimension): row = [] for _j in range(self.in_width): row.append(entropy[idx] & 0xffff_ffff_ffff_ffff) idx += 1 m.append(row) return m def hash_matrix(self, input_matrix, entropy_matrix): out = [None] * self.encoded_dimension for i in range(self.encoded_dimension): out[i] = self.mix_none(input_matrix[i][0], entropy_matrix[i][0]) for j in range(1, self.in_width): for i in range(self.encoded_dimension): out[i] = self.mix_one(out[i], input_matrix[i][j], entropy_matrix[i][j]) return out def ehc_base_layer(self, entropy, entropy_offset, data, data_offset_bytes): block_size = 8 * self.lanes io = [[None] * self.in_width for _ in range(self.encoded_dimension)] zero = [0] * self.lanes for i in range(self.dimension): for j in range(self.in_width): io[i][j] = self.load_block_bytes(data, data_offset_bytes + (i * self.in_width + j) * block_size) for i in range(self.dimension, self.encoded_dimension): for j in range(self.in_width): io[i][j] = zero self.encode2(io) entropy_matrix = self.parse_entropy_matrix(entropy, entropy_offset) tmp = self.hash_matrix(io, entropy_matrix) out_blocks = self.combine2(tmp) return out_blocks def ehc_upper_layer(self, entropy, entropy_offset, input_blocks): output = [None] * self.out_width for i in range(self.out_width): acc = input_blocks[0][i] for j in range(1, self.fanout): acc = self.mix_one(acc, input_blocks[j][i], entropy[entropy_offset + (self.fanout - 1) * i + (j - 1)]) output[i] = acc return output def dfs_tree_hash(self, entropy, entropy_offset, data, block_group_length, stack, stack_lengths): group_bytes = self.dimension * self.in_width * 8 * self.lanes for k in range(block_group_length): i = 0 while stack_lengths[i] == self.fanout: i += 1 for j in range(i - 1, -1, -1): upper_entropy_offset = entropy_offset + (self.encoded_dimension * self.in_width) + \ (self.fanout - 1) * self.out_width * j stack[j + 1][stack_lengths[j + 1]] = self.ehc_upper_layer(entropy, upper_entropy_offset, stack[j]) stack_lengths[j] = 0 stack_lengths[j + 1] += 1 stack[0][stack_lengths[0]] = self.ehc_base_layer(entropy, entropy_offset, data, k * group_bytes) stack_lengths[0] += 1 return def dfs_greedy_finalizer(self, entropy, entropy_offset, stack, stack_lengths, tail_bytes): block_words = self.lanes block_size = 8 * self.lanes accum = [[0] * self.lanes for _ in range(self.out_width)] idx = entropy_offset j = 0 while j < len(stack_lengths) and stack_lengths[j] > 0: for k in range(stack_lengths[j]): x_out = stack[j][k] for i in range(self.out_width): seed_block = self.load_block_words(entropy, idx) accum[i] = self.mix(accum[i], x_out[i], seed_block) idx += block_words j += 1 i = 0 while i + block_size <= len(tail_bytes): x_block = self.load_block_bytes(tail_bytes, i) base = idx for o in range(self.out_width): seed_block = self.load_block_words(entropy, base + o * block_words) accum[o] = self.mix(accum[o], x_block, seed_block) idx += block_words i += block_size x_block = self.load_block_bytes(tail_bytes, i) base = idx for o in range(self.out_width): seed_block = self.load_block_words(entropy, base + o * block_words) accum[o] = self.mix(accum[o], x_block, seed_block) idx += block_words output0 = self.sum_block(accum[0]) output1 = self.sum_block(accum[1]) return output0, output1 def hasher(self, entropy, entropy_offset, data, length): group_bytes = self.dimension * self.in_width * 8 * self.lanes wide_length = length // group_bytes stack = [ [ [ [0] * self.lanes for _ in range(self.out_width)] for _ in range(self.fanout) ] for _ in range(self.max_stack_size) ] stack_lengths = [0] * self.max_stack_size self.dfs_tree_hash(entropy, entropy_offset, data, wide_length, stack, stack_lengths) greedy_entropy_offset = entropy_offset + (self.encoded_dimension * self.in_width) + \ self.out_width * (self.fanout - 1) * self.max_stack_size used = wide_length * group_bytes tail = data[used:] return self.dfs_greedy_finalizer(entropy, greedy_entropy_offset, stack, stack_lengths, tail) class HalfTimeHash64(HalfTimeHashBase): lanes = 1 class HalfTimeHash128(HalfTimeHashBase): lanes = 2 class HalfTimeHash256(HalfTimeHashBase): lanes = 4 class HalfTimeHash512(HalfTimeHashBase): lanes = 8 class WYHashBase: block_size = 64 digest_size = 0 mask = 0 default_seed = 0 def __init__(self, data=b"", seed=None): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if seed is None: self.seed = self.default_seed & self.mask else: if not isinstance(seed, int): raise TypeError("seed must be int") self.seed = seed & self.mask self.buf = bytearray() self.msg_len = 0 self.result = 0 if data: self.update(data) return def copy(self): other = self.__class__(seed=self.seed) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.result = self.result return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) return self def sum(self): c = self.copy() c.finalize() return c.result def digest(self): h = self.sum() return int(h).to_bytes(self.digest_size, byteorder="big", signed=False) def hexdigest(self): width = self.digest_size * 2 return format(self.sum(), "0{}x".format(width)) def finalize(self): data = bytes(self.buf) self.result = self.hash(data) & self.mask return def u32le(self, data, off): return int.from_bytes(data[off:off + 4], byteorder="little", signed=False) def u64le(self, data, off): return int.from_bytes(data[off:off + 8], byteorder="little", signed=False) class WYHash32(WYHashBase): digest_size = 4 mask = 0xffff_ffff default_seed = 0 prime1 = 0x53c5_ca59 prime2 = 0x7474_3c1b def mix32(self, a, b): a &= self.mask b &= self.mask c = (a ^ self.prime1) * (b ^ self.prime2) lo = c & self.mask hi = (c >> 32) & self.mask return lo, hi def r24(self, data, off, k): return (((data[off] & 0xff) << 16) | ((data[off + (k >> 1)] & 0xff) << 8) | (data[off + k - 1] & 0xff)) & self.mask def hash(self, data): length = len(data) p = 0 i = length seed = self.seed & self.mask see1 = length & self.mask seed ^= (length >> 32) & self.mask seed, see1 = self.mix32(seed, see1) while i > 8: seed ^= self.u32le(data, p) see1 ^= self.u32le(data, p + 4) seed, see1 = self.mix32(seed, see1) p += 8 i -= 8 if i >= 4: seed ^= self.u32le(data, p) see1 ^= self.u32le(data, p + i - 4) elif i: seed ^= self.r24(data, p, i) seed, see1 = self.mix32(seed, see1) seed, see1 = self.mix32(seed, see1) return seed ^ see1 class WYHash64(WYHashBase): digest_size = 8 mask = 0xffff_ffff_ffff_ffff secret = ( 0xa076_1d64_78bd_642f, 0xe703_7ed1_a0b4_28db, 0x8ebc_6af0_9c88_c6e3, 0x5899_65cc_7537_4cc3, ) default_seed = secret[0] def mix64(self, a, b): a &= self.mask b &= self.mask c = a * b lo = c & self.mask hi = (c >> 64) & self.mask return lo ^ hi def r3(self, data, off, k): return ((data[off] << 16) | (data[off + (k >> 1)] << 8) | data[off + k - 1]) & self.mask def hash(self, data): length = len(data) p = 0 seed = self.seed a = 0 b = 0 if length <= 16: if length >= 4: a = ( (self.u32le(data, p) << 32) | self.u32le(data, p + ((length >> 3) << 2)) ) & self.mask b = ( (self.u32le(data, p + length - 4) << 32) | self.u32le(data, p + length - 4 - ((length >> 3) << 2)) ) & self.mask elif length > 0: a = self.r3(data, p, length) b = 0 else: a = 0 b = 0 else: i = length if i > 48: see1 = see2 = seed while i > 48: seed = self.mix64(self.u64le(data, p) ^ self.secret[1], self.u64le(data, p + 8) ^ seed) see1 = self.mix64(self.u64le(data, p + 16) ^ self.secret[2], self.u64le(data, p + 24) ^ see1) see2 = self.mix64(self.u64le(data, p + 32) ^ self.secret[3], self.u64le(data, p + 40) ^ see2) p += 48 i -= 48 seed = seed ^ see1 ^ see2 while i > 16: seed = self.mix64(self.u64le(data, p) ^ self.secret[1], self.u64le(data, p + 8) ^ seed) p += 16 i -= 16 a = self.u64le(data, p + i - 16) b = self.u64le(data, p + i - 8) return self.mix64(self.secret[1] ^ (length & self.mask), self.mix64(a ^ self.secret[1], b ^ seed)) class HAS160: block_size = 64 # 512 bits digest_size = 20 # 160 bits ROT_A = (5, 11, 7, 15, 6, 13, 8, 14, 7, 12, 9, 11, 8, 15, 6, 12, 9, 14, 5, 13) ROT_B = (10, 17, 25, 30) K = (0x0000_0000, 0x5a82_7999, 0x6ed9_eba1, 0x8f1b_bcdc) ORDER = ( (18, 0, 1, 2, 3, 19, 4, 5, 6, 7, 16, 8, 9, 10, 11, 17, 12, 13, 14, 15), (18, 3, 6, 9, 12, 19, 15, 2, 5, 8, 16, 11, 14, 1, 4, 17, 7, 10, 13, 0), (18, 12, 5, 14, 7, 19, 0, 9, 2, 11, 16, 4, 13, 6, 15, 17, 8, 1, 10, 3), (18, 7, 2, 13, 8, 19, 3, 14, 9, 4, 16, 15, 10, 5, 0, 17, 11, 6, 1, 12), ) def __init__(self, data=b""): self.h0 = 0x6745_2301 self.h1 = 0xefcd_ab89 self.h2 = 0x98ba_dcfe self.h3 = 0x1032_5476 self.h4 = 0xc3d2_e1f0 self.buf = bytearray() self.msg_len = 0 # in bytes if data: self.update(data) return def copy(self): other = self.__class__() other.h0 = self.h0 other.h1 = self.h1 other.h2 = self.h2 other.h3 = self.h3 other.h4 = self.h4 other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return struct.pack("<5I", c.h0, c.h1, c.h2, c.h3, c.h4) def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 self.buf.append(0x80) while (len(self.buf) % 64) != 56: self.buf.append(0x00) self.buf.extend(struct.pack("= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.compress(block) return def compress(self, block): def rol32(x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff def f0(x, y, z): return ((x & y) | ((~x) & z)) & 0xffff_ffff def f1(x, y, z): return (x ^ y ^ z) & 0xffff_ffff def f2(x, y, z): return (y ^ (x | (~z))) & 0xffff_ffff a = self.h0 b = self.h1 c = self.h2 d = self.h3 e = self.h4 x = list(struct.unpack("<16I", block)) for r in range(4): if r == 0: x16 = x[0] ^ x[1] ^ x[2] ^ x[3] x17 = x[4] ^ x[5] ^ x[6] ^ x[7] x18 = x[8] ^ x[9] ^ x[10] ^ x[11] x19 = x[12] ^ x[13] ^ x[14] ^ x[15] f = f0 elif r == 1: x16 = x[3] ^ x[6] ^ x[9] ^ x[12] x17 = x[15] ^ x[2] ^ x[5] ^ x[8] x18 = x[11] ^ x[14] ^ x[1] ^ x[4] x19 = x[7] ^ x[10] ^ x[13] ^ x[0] f = f1 elif r == 2: x16 = x[12] ^ x[5] ^ x[14] ^ x[7] x17 = x[0] ^ x[9] ^ x[2] ^ x[11] x18 = x[4] ^ x[13] ^ x[6] ^ x[15] x19 = x[8] ^ x[1] ^ x[10] ^ x[3] f = f2 else: x16 = x[7] ^ x[2] ^ x[13] ^ x[8] x17 = x[3] ^ x[14] ^ x[9] ^ x[4] x18 = x[15] ^ x[10] ^ x[5] ^ x[0] x19 = x[11] ^ x[6] ^ x[1] ^ x[12] f = f1 words = x + [x16, x17, x18, x19] k = self.K[r] rb = self.ROT_B[r] order = self.ORDER[r] for t in range(20): w = words[order[t]] temp = (rol32(a, self.ROT_A[t]) + f(b, c, d) + e + w + k) & 0xffff_ffff e = d d = c c = rol32(b, rb) b = a a = temp self.h0 = (self.h0 + a) & 0xffff_ffff self.h1 = (self.h1 + b) & 0xffff_ffff self.h2 = (self.h2 + c) & 0xffff_ffff self.h3 = (self.h3 + d) & 0xffff_ffff self.h4 = (self.h4 + e) & 0xffff_ffff return class SnefruBase: block_size = 64 security_level = 8 standard_sboxes = ( ( 0x64f9_001b, 0xfedd_cdf6, 0x7c8f_f1e2, 0x11d7_1514, 0x8b8c_18d3, 0xdddf_881e, 0x6eab_5056, 0x88ce_d8e1, 0x4914_8959, 0x69c5_6fd5, 0xb799_4f03, 0x0fbc_ee3e, 0x3c26_4940, 0x2155_7e58, 0xe14b_3fc2, 0x2e5c_f591, 0xdcef_f8ce, 0x092a_1648, 0xbe81_2936, 0xff7b_0c6a, 0xd525_1037, 0xafa4_48f1, 0x7daf_c95a, 0x1ea6_9c3f, 0xa417_abe7, 0x5890_e423, 0xb0cb_70c0, 0xc850_25f7, 0x244d_97e3, 0x1ff3_595f, 0xc4ec_6396, 0x5918_1e17, 0xe635_b477, 0x354e_7dbf, 0x796f_7753, 0x66eb_52cc, 0x77c3_f995, 0x32e3_a927, 0x80cc_aed6, 0x4e2b_e89d, 0x375b_bd28, 0xad1a_3d05, 0x2b1b_42b3, 0x16c4_4c71, 0x4d54_bfa8, 0xe57d_dc7a, 0xec6d_8144, 0x5a71_046b, 0xd822_9650, 0x87fc_8f24, 0xcbc6_0e09, 0xb639_0366, 0xd9f7_6092, 0xd393_a70b, 0x1d31_a08a, 0x9cd9_71c9, 0x5c1e_f445, 0x86fa_b694, 0xfdb4_4165, 0x8eaa_fcbe, 0x4bca_c6eb, 0xfb7a_94e5, 0x5789_d04e, 0xfa13_cf35, 0x236b_8da9, 0x4133_f000, 0x6224_261c, 0xf412_f23b, 0xe75e_56a4, 0x3002_2116, 0xbaf1_7f1f, 0xd098_72f9, 0xc1a3_699c, 0xf1e8_02aa, 0x0dd1_45dc, 0x4fdc_e093, 0x8d84_12f0, 0x6cd0_f376, 0x3de6_b73d, 0x84ba_737f, 0xb43a_30f2, 0x4456_9f69, 0x00e4_eaca, 0xb58d_e3b0, 0x9591_13c8, 0xd62e_fee9, 0x9086_1f83, 0xced6_9874, 0x2f79_3cee, 0xe857_1c30, 0x4836_65d1, 0xab07_b031, 0x914c_844f, 0x15bf_3be8, 0x2c3f_2a9a, 0x9eb9_5fd4, 0x92e7_472d, 0x2297_cc5b, 0xee5f_2782, 0x5377_b562, 0xdb8e_bbcf, 0xf961_dedd, 0xc59b_5c60, 0x1bd3_910d, 0x26d2_06ad, 0xb285_14d8, 0x5ecf_6b52, 0x7fea_78bb, 0x5048_79ac, 0xed34_a884, 0x36e5_1d3c, 0x1753_741d, 0x8c47_caed, 0x9d0a_40ef, 0x3145_e221, 0xda27_eb70, 0xdf73_0ba3, 0x183c_8789, 0x739a_c0a6, 0x9a58_dfc6, 0x54b1_34c1, 0xac3e_242e, 0xcc49_3902, 0x7b2d_da99, 0x8f15_bc01, 0x29fd_38c7, 0x27d5_318f, 0x604a_aff5, 0xf29c_6818, 0xc38a_a2ec, 0x1019_d4c3, 0xa8fb_936e, 0x20ed_7b39, 0x0b68_6119, 0x89a0_906f, 0x1cc7_829e, 0x9952_ef4b, 0x850e_9e8c, 0xcd06_3a90, 0x6700_2f8e, 0xcfac_8cb7, 0xeaa2_4b11, 0x988b_4e6c, 0x46f0_66df, 0xca7e_ec08, 0xc7bb_a664, 0x831d_17bd, 0x63f5_75e6, 0x9764_350e, 0x4787_0d42, 0x026c_a4a2, 0x8167_d587, 0x61b6_adab, 0xaa65_64d2, 0x70da_237b, 0x25e1_c74a, 0xa1c9_01a0, 0x0eb0_a5da, 0x7670_f741, 0x51c0_5aea, 0x933d_fa32, 0x0759_ff1a, 0x5601_0ab8, 0x5fde_cb78, 0x3f32_edf8, 0xaebe_dbb9, 0x39f8_326d, 0xd208_58c5, 0x9b63_8be4, 0xa572_c80a, 0x28e0_a19f, 0x4320_99fc, 0x3a37_c3cd, 0xbf95_c585, 0xb392_c12a, 0x6aa7_07d7, 0x52f6_6a61, 0x12d4_83b1, 0x9643_5b5e, 0x3e75_802b, 0x3ba5_2b33, 0xa99f_51a5, 0xbda1_e157, 0x78c2_e70c, 0xfcae_7ce0, 0xd160_2267, 0x2aff_ac4d, 0x4a51_0947, 0x0ab2_b83a, 0x7a04_e579, 0x340d_fd80, 0xb916_e922, 0xe29d_5e9b, 0xf562_4af4, 0x4ca9_d9af, 0x6bbd_2cfe, 0xe3b7_f620, 0xc274_6e07, 0x5b42_b9b6, 0xa069_19bc, 0xf0f2_c40f, 0x7221_7ab5, 0x14c1_9df3, 0xf380_2dae, 0xe094_beb4, 0xa210_1aff, 0x0529_575d, 0x55cd_b27c, 0xa33b_ddb2, 0x6528_b37d, 0x740c_05db, 0xe96a_62c4, 0x4078_2846, 0x6d30_d706, 0xbbf4_8e2c, 0xbce2_d3de, 0x049e_37fa, 0x01b5_e634, 0x2d88_6d8d, 0x7e5a_2e7e, 0xd741_2013, 0x06e9_0f97, 0xe45d_3eba, 0xb8ad_3386, 0x1305_1b25, 0x0c03_5354, 0x71c8_9b75, 0xc638_fbd0, 0x197f_11a1, 0xef0f_08fb, 0xf844_8651, 0x3840_9563, 0x452f_4443, 0x5d46_4d55, 0x03d8_764c, 0xb1b8_d638, 0xa70b_ba2f, 0x94b3_d210, 0xeb66_92a7, 0xd409_c2d9, 0x6883_8526, 0xa6db_8a15, 0x751f_6c98, 0xde76_9a88, 0xc9ee_4668, 0x1a82_a373, 0x0896_aa49, 0x4223_3681, 0xf62c_55cb, 0x9f1c_5404, 0xf74f_b15c, 0xc06e_4312, 0x6ffe_5d72, 0x8aa8_678b, 0x337c_d129, 0x8211_cefd, ), ( 0x074a_1d09, 0x52a1_0e5a, 0x9275_a3f8, 0x4b82_506c, 0x37df_7e1b, 0x4c78_b3c5, 0xcefa_b1da, 0xf472_267e, 0xb630_45f6, 0xd66a_1fc0, 0x4002_98e3, 0x27e6_0c94, 0x87d2_f1b8, 0xdf9e_56cc, 0x45cd_1803, 0x1d35_e098, 0xcce7_c736, 0x0348_3bf1, 0x1f73_07d7, 0xc6e8_f948, 0xe613_c111, 0x3955_c6ff, 0x1170_ed7c, 0x8e95_da41, 0x99c3_1bf4, 0xa4da_8021, 0x7b5f_94fb, 0xdd0d_a51f, 0x6562_aa77, 0x556b_cb23, 0xdb1b_acc6, 0x7980_40b9, 0xbfe5_378f, 0x731d_55e6, 0xdaa5_bfee, 0x389b_bc60, 0x1b33_fba4, 0x9c56_7204, 0x36c2_6c68, 0x77ee_9d69, 0x8aeb_3e88, 0x2d50_b5ce, 0x9579_e790, 0x42b1_3cfc, 0x33fb_d32b, 0xee05_03a7, 0xb586_2824, 0x15e4_1ead, 0xc841_2ef7, 0x9d44_1275, 0x2fce_c582, 0x5ff4_83b7, 0x8f39_31df, 0x2e5d_2a7b, 0x4946_7bf9, 0x0653_dea9, 0x2684_ce35, 0x7e65_5e5c, 0xf127_71d8, 0xbb15_cc67, 0xab09_7ca1, 0x983d_cf52, 0x10dd_f026, 0x2126_7f57, 0x2c58_f6b4, 0x3104_3265, 0x0bab_8c01, 0xd549_2099, 0xacaa_e619, 0x944c_e54a, 0xf2d1_3d39, 0xadd3_fc32, 0xcda0_8a40, 0xe2b0_d451, 0x9efe_08ae, 0xb9d5_0fd2, 0xea5c_d7fd, 0xc9a7_49dd, 0x13ea_2253, 0x832d_ebaa, 0x24be_640f, 0xe03e_926a, 0x29e0_1cde, 0x8bf5_9f18, 0x0f9d_00b6, 0xe123_8b46, 0x1e7d_8e34, 0x9361_9adb, 0x76b3_2f9f, 0xbd97_2cec, 0xe31f_a976, 0xa68f_bb10, 0xfb3b_a49d, 0x8587_c41d, 0xa5ad_d1d0, 0xf3cf_84bf, 0xd4e1_1150, 0xd9ff_a6bc, 0xc3f6_018c, 0xaef1_0572, 0x74a6_4b2f, 0xe7dc_9559, 0x2aae_35d5, 0x5b6f_587f, 0xa9e3_53fe, 0xca4f_b674, 0x04ba_24a8, 0xe5c6_875f, 0xdcbc_6266, 0x6bc5_c03f, 0x661e_ef02, 0xed74_0bab, 0x058e_34e4, 0xb7e9_46cf, 0x8869_8125, 0x72ec_48ed, 0xb110_73a3, 0xa134_85eb, 0xa2a2_429c, 0xfa40_7547, 0x50b7_6713, 0x5418_c37d, 0x9619_2da5, 0x170b_b04b, 0x518a_021e, 0xb0ac_13d1, 0x0963_fa2a, 0x4a6e_10e1, 0x5847_2bdc, 0xf7f8_d962, 0x9791_39ea, 0x8d85_6538, 0xc099_7042, 0x4832_4d7a, 0x4476_23cb, 0x8cbb_e364, 0x6e0c_6b0e, 0xd36d_63b0, 0x3f24_4c84, 0x3542_c971, 0x2b22_8dc1, 0xcb03_25bb, 0xf8c0_d6e9, 0xde11_066b, 0xa864_9327, 0xfc31_f83e, 0x7dd8_0406, 0xf916_dd61, 0xd89f_79d3, 0x6151_44c2, 0xebb4_5d31, 0x2800_2958, 0x5689_0a37, 0xf05b_3808, 0x123a_e844, 0x8683_9e16, 0x914b_0d83, 0xc506_b43c, 0xcf3c_ba5e, 0x7c60_f5c9, 0x22de_b2a0, 0x5d9c_2715, 0xc77b_a0ef, 0x4f45_360b, 0xc101_7d8b, 0xe45a_dc29, 0xa759_909b, 0x412c_d293, 0xd7d7_96b1, 0x00c8_ff30, 0x23a3_4a80, 0x4ec1_5c91, 0x714e_78b5, 0x47b9_e42e, 0x78f3_ea4d, 0x7f07_8f5b, 0x346c_593a, 0xa3a8_7a1a, 0x9bcb_fe12, 0x3d43_9963, 0xb2ef_6d8e, 0xb8d4_6028, 0x6c2f_d5ca, 0x6267_5256, 0x01f2_a2f3, 0xbc96_ae0a, 0x709a_8920, 0xb414_6e87, 0x6308_b9e2, 0x64bd_a7ba, 0xafed_6892, 0x6037_f2a2, 0xf529_69e0, 0x0adb_43a6, 0x8281_1400, 0x90d0_bdf0, 0x19c9_549e, 0x203f_6a73, 0x1acc_af4f, 0x8971_4e6d, 0x164d_4705, 0x6766_5f07, 0xec20_6170, 0x0c21_82b2, 0xa02b_9c81, 0x5328_9722, 0xf6a9_7686, 0x140e_4179, 0x9f77_8849, 0x9a88_e15d, 0x25ca_db54, 0xd157_f36f, 0x32a4_21c3, 0xb368_e98a, 0x5a92_cd0d, 0x757a_a8d4, 0xc20a_c278, 0x08b5_51c7, 0x8494_91e8, 0x4dc7_5ad6, 0x697c_33be, 0xbaf0_ca33, 0x4612_5b4e, 0x59d6_77b3, 0x30d9_c8f2, 0xd0af_860c, 0x1c7f_d0fa, 0xfe0f_f72c, 0x5c8d_6f43, 0x57fd_ec3b, 0x6ab6_ad97, 0xd22a_df89, 0x1817_1785, 0x02bf_e22d, 0x6db8_0917, 0x80b2_16af, 0xe85e_4f9a, 0x7a1c_306e, 0x6fc4_9bf5, 0x3af7_a11c, 0x81e2_15e7, 0x6836_3fcd, 0x3e93_57c8, 0xef52_fd55, 0x3b8b_ab4c, 0x3c8c_f495, 0xbefc_eebd, 0xfd25_b714, 0xc498_d83d, 0x0d2e_1a8d, 0xe9f9_66ac, 0x0e38_7445, 0x4354_19e5, 0x5e7e_bec4, 0xaa90_b8d9, 0xff1a_3a96, ), ( 0x4a8f_e4e3, 0xf27d_99cd, 0xd04a_40ca, 0xcb5f_f194, 0x3668_275a, 0xff48_16be, 0xa78b_394c, 0x4c6b_e9db, 0x4eec_38d2, 0x4296_ec80, 0xcdce_96f8, 0x888c_2f38, 0xe755_08f5, 0x7b91_6414, 0x060a_a14a, 0xa214_f327, 0xbe60_8daf, 0x1ebb_dec2, 0x61f9_8ce9, 0xe921_56fe, 0x4f22_d7a3, 0x3f76_a8d9, 0x559a_4b33, 0x38ad_2959, 0xf3f1_7e9e, 0x85e1_ba91, 0xe5eb_a6fb, 0x73dc_d48c, 0xf5c3_ff78, 0x481b_6058, 0x8a32_97f7, 0x8f1f_3bf4, 0x9378_5ab2, 0x477a_4a5b, 0x6334_eb5d, 0x6d25_1b2e, 0x74a9_102d, 0x07e3_8ffa, 0x915c_9c62, 0xccc2_75ea, 0x6be2_73ec, 0x3ebd_dd70, 0xd895_796c, 0xdc54_a91b, 0xc9af_df81, 0x2363_3f73, 0x2751_19b4, 0xb19f_6b67, 0x5075_6e22, 0x2bb1_52e2, 0x76ea_46a2, 0xa353_e232, 0x2f59_6ad6, 0x0b1e_db0b, 0x02d3_d9a4, 0x78b4_7843, 0x6489_3e90, 0x40f0_caad, 0xf68d_3ad7, 0x46fd_1707, 0x1c9c_67ef, 0xb5e0_86de, 0x96ee_6ca6, 0x9aa3_4774, 0x1ba4_f48a, 0x8d01_abfd, 0x183e_e1f6, 0x5ff8_aa7a, 0x17e4_faae, 0x3039_83b0, 0x6c08_668b, 0xd4ac_4382, 0xe6c5_849f, 0x92fe_fb53, 0xc1ca_c4ce, 0x4350_1388, 0x4411_18cf, 0xec4f_b308, 0x53a0_8e86, 0x9e0f_e0c5, 0xf91c_1525, 0xac45_be05, 0xd798_7cb5, 0x49ba_1487, 0x5793_8940, 0xd587_7648, 0xa958_727f, 0x58df_e3c3, 0xf436_cf77, 0x399e_4d11, 0xf0a5_bfa9, 0xef61_a33b, 0xa64c_ac60, 0x04a8_d0ba, 0x030d_d572, 0xb83d_320f, 0xcab2_3045, 0xe366_f2f0, 0x815d_008d, 0xc897_a43a, 0x1d35_2df3, 0xb9cc_571d, 0x8bf3_8744, 0x7220_9092, 0xeba1_24eb, 0xfb99_ce5e, 0x3bb9_4293, 0x28da_549c, 0xaab8_a228, 0xa419_7785, 0x33c7_0296, 0x25f6_259b, 0x5c85_da21, 0xdf15_bdee, 0x15b7_c7e8, 0xe2ab_ef75, 0xfcc1_9bc1, 0x417f_f868, 0x1488_4434, 0x6282_5179, 0xc6d5_c11c, 0x0e47_05dc, 0x2270_0de0, 0xd3d2_af18, 0x9be8_22a0, 0x35b6_69f1, 0xc42b_b55c, 0x0a80_1252, 0x115b_f0fc, 0x3cd7_d856, 0xb43f_5f9d, 0xc230_6516, 0xa123_1c47, 0xf149_207e, 0x5209_a795, 0x34b3_ccd8, 0x67ae_fe54, 0x2c83_924e, 0x6662_cbac, 0x5eed_d161, 0x84e6_81aa, 0x5d57_d26b, 0xfa46_5cc4, 0x7e3a_c3a8, 0xbf7c_0cc6, 0xe18a_9aa1, 0xc32f_0a6f, 0xb22c_c00d, 0x3d28_0369, 0x994e_554f, 0x68f4_80d3, 0xadcf_f5e6, 0x3a8e_b265, 0x8326_9831, 0xbd56_8a09, 0x4bc8_ae6a, 0x69f5_6d2b, 0x0f17_eac8, 0x772e_b6c7, 0x9f41_343c, 0xab1d_0742, 0x826a_6f50, 0xfea2_097c, 0x1912_c283, 0xce18_5899, 0xe444_4839, 0x2d86_35d5, 0x65d0_b1ff, 0x865a_7f17, 0x326d_9fb1, 0x59e5_2820, 0x0090_ade1, 0x753c_7149, 0x9ddd_8b98, 0xa5a6_91da, 0x0d03_82bb, 0x8904_c930, 0x086c_b000, 0x6e69_d3bd, 0x24d4_e7a7, 0x0524_4fd0, 0x101a_5e0c, 0x6a94_7dcb, 0xe840_f77b, 0x7d0c_5003, 0x7c37_0f1f, 0x8052_45ed, 0xe05e_3d3f, 0x7906_880e, 0xbabf_cd35, 0x1a7e_c697, 0x8c05_2324, 0x0c6e_c8df, 0xd129_a589, 0xc7a7_5b02, 0x12d8_1de7, 0xd9be_2a66, 0x1f42_63ab, 0xde73_fdb6, 0x2a00_680a, 0x5664_9e36, 0x3133_ed55, 0x90fa_0bf2, 0x2910_a02a, 0x949d_9d46, 0xa0d1_dcdd, 0xcfc9_b7d4, 0xd267_7be5, 0x95cb_36b3, 0x13cd_9410, 0xdbf7_3313, 0xb7c6_e8c0, 0xf781_414b, 0x510b_016d, 0xb0de_1157, 0xd6b0_f62c, 0xbb07_4ecc, 0x7f13_95b7, 0xee79_2cf9, 0xea6f_d63e, 0x5bd6_938e, 0xaf02_fc64, 0xdab5_7ab8, 0x8edb_3784, 0x8716_318f, 0x164d_1a01, 0x26f2_6141, 0xb372_e6b9, 0xf8fc_2b06, 0x7ac0_0e04, 0x3727_b89a, 0x97e9_bca5, 0x9c2a_742f, 0xbc3b_1f7d, 0x7165_b471, 0x609b_4c29, 0x2092_5351, 0x5ae7_2112, 0x454b_e5d1, 0xc0ff_b95f, 0xdd0e_f919, 0x6f2d_70c9, 0x0974_c5bf, 0x98aa_6263, 0x01d9_1e4d, 0x2184_bb6e, 0x70c4_3c1e, 0x4d43_5915, 0xae7b_8523, 0xb6fb_06bc, 0x5431_ee76, 0xfdbc_5d26, 0xed77_493d, 0xc571_2ee4, 0xa838_0437, 0x2eef_261a, ), ( 0x5a79_392b, 0xb8af_32c2, 0x41f7_720a, 0x833a_61ec, 0x13df_edac, 0xc499_0bc4, 0xdc0f_54bc, 0xfedd_5e88, 0x80da_1881, 0x4dea_1afd, 0xfd40_2cc6, 0xae67_cc7a, 0xc523_8525, 0x8ea0_1254, 0xb56b_9bd5, 0x862f_bd6d, 0xac85_75d3, 0x6fba_3714, 0xda7e_bf46, 0x59cd_5238, 0x8ac9_dbfe, 0x3537_29fc, 0xe497_d7f2, 0xc3ab_84e0, 0xf05a_114b, 0x7b88_7a75, 0xedc6_03dd, 0x5e6f_e680, 0x2c84_b399, 0x884e_b1da, 0x1cb8_c8bf, 0xaa51_098a, 0xc862_231c, 0x8bac_2221, 0x21b3_87e5, 0x208a_430d, 0x2a3f_0f8b, 0xa5ff_9cd2, 0x6012_a2ea, 0x147a_9ee7, 0xf62a_501d, 0xb4b2_e51a, 0x3ef3_484c, 0xc025_3c59, 0x2b82_b536, 0x0aa9_696b, 0xbe0c_109b, 0xc70b_7929, 0xce3e_8a19, 0x2f66_950e, 0x459f_1c2c, 0xe68f_b93d, 0xa3c3_ff3e, 0x62b4_5c62, 0x3009_91cb, 0x0191_4c57, 0x7f7b_c06a, 0x1828_31f5, 0xe7b7_4bca, 0xfa50_f6d0, 0x523c_aa61, 0xe3a7_cf05, 0xe9e4_1311, 0x280a_21d1, 0x6a42_97e1, 0xf24d_c67e, 0xfc31_89e6, 0xb72b_f34f, 0x4b1e_67af, 0x5434_02ce, 0x79a5_9867, 0x0648_e02a, 0x00a3_ac17, 0xc620_8d35, 0x6e7f_5f76, 0xa45b_b4be, 0xf168_fa63, 0x3f41_25f3, 0xf311_406f, 0x0270_6565, 0xbfe5_8022, 0x0cfc_fdd9, 0x0735_a7f7, 0x8f04_9092, 0xd98e_dc27, 0xf5c5_d55c, 0xe0f2_01db, 0x0dca_fc9a, 0x7727_fb79, 0xaf43_abf4, 0x26e9_38c1, 0x401b_26a6, 0x9007_20fa, 0x2752_d97b, 0xcff1_d1b3, 0xa9d9_e424, 0x42db_99ab, 0x6cf8_be5f, 0xe82c_ebe3, 0x3afb_733b, 0x6b73_4eb6, 0x1036_414a, 0x975f_667c, 0x049d_6377, 0xba58_7c60, 0xb1d1_0483, 0xde1a_efcc, 0x1129_d055, 0x7205_1e91, 0x6946_d623, 0xf9e8_6ea7, 0x4876_8c00, 0xb016_6c93, 0x9956_bbf0, 0x1f1f_6d84, 0xfb15_e18e, 0x033b_495d, 0x56e3_362e, 0x4f44_c53c, 0x747c_ba51, 0x89d3_7872, 0x5d9c_331b, 0xd2ef_9fa8, 0x2549_17f8, 0x1b10_6f47, 0x37d7_5553, 0xb3f0_53b0, 0x7dcc_d8ef, 0xd30e_b802, 0x5889_f42d, 0x6102_06d7, 0x1a7d_34a1, 0x92d8_7dd8, 0xe5f4_a315, 0xd1cf_0e71, 0xb22d_fe45, 0xb901_e8eb, 0x0fc0_ce5e, 0x2efa_60c9, 0x2de7_4290, 0x36d0_c906, 0x381c_70e4, 0x4c6d_a5b5, 0x3d81_a682, 0x7e38_1f34, 0x396c_4f52, 0x95ad_5901, 0x1db5_0c5a, 0x2998_2e9e, 0x1557_689f, 0x3471_ee42, 0xd7e2_f7c0, 0x8795_a1e2, 0xbc32_4d8d, 0xe224_c3c8, 0x1283_7e39, 0xcdee_3d74, 0x7ad2_143f, 0x0e13_d40c, 0x78bd_4a68, 0xa2eb_194d, 0xdb94_51f9, 0x859b_71dc, 0x5c4f_5b89, 0xca14_a8a4, 0xef92_f003, 0x1674_1d98, 0x33aa_4444, 0x9e96_7fbb, 0x092e_3020, 0xd86a_35b8, 0x8cc1_7b10, 0xe1bf_08ae, 0x5569_3fc5, 0x7680_ad13, 0x1e65_46e8, 0x23b6_e7b9, 0xee77_a4b2, 0x08ed_0533, 0x44fd_2895, 0xb639_3b69, 0x05d6_cacf, 0x9819_b209, 0xecbb_b72f, 0x9a75_779c, 0xeaec_0749, 0x94a6_5aee, 0xbdf5_2dc3, 0xd6a2_5d04, 0x8200_8e4e, 0xa6de_160f, 0x9b03_6afb, 0x228b_3a66, 0x5fb1_0a70, 0xcc33_8b58, 0x5378_a9df, 0xc908_bca9, 0x4959_e25b, 0x4690_9a97, 0x66ae_8f6e, 0xdd06_83e9, 0x65f9_94b4, 0x6426_cda5, 0xc24b_8840, 0x3253_9da0, 0x6317_5650, 0xd0c8_15ff, 0x50cb_c41e, 0xf7c7_74a3, 0x31b0_c231, 0x8d0d_8116, 0x24be_f16c, 0xd555_d256, 0xdf47_ea8c, 0x6d21_eccd, 0xa887_a012, 0x8454_2aed, 0xa7b9_c1bd, 0x914c_1bb1, 0xa0d5_b67d, 0x438c_e937, 0x7030_f873, 0x71f6_b0c7, 0x5745_76ba, 0xf8bc_4541, 0x9c61_d348, 0x1960_579d, 0x17c4_daad, 0x96a4_cb0b, 0xc193_f2f6, 0x756e_afa2, 0x7c1d_2f94, 0xf4fe_2b43, 0xcb86_e33a, 0xebd4_c728, 0x9d18_ae64, 0x9fe1_3e30, 0x3ce0_f5de, 0xaba1_f985, 0xaddc_2718, 0x68ce_6278, 0xd45e_241f, 0xa15c_82b7, 0x3b22_93d4, 0x739e_dd32, 0x674a_6bf1, 0x5b5d_587f, 0x4772_deaa, 0x4a63_968f, 0x0be6_8686, 0x513d_6426, 0x939a_4787, 0xbba8_9296, 0x4ec2_0007, 0x818d_0d08, 0xff64_dfd6, ), ( 0xcb22_97cb, 0xdb48_a144, 0xa16c_be4b, 0xbbea_1d6c, 0x5af6_b6b7, 0x8a81_10b6, 0xf923_6ef9, 0xc98f_83e6, 0x0f9c_65b8, 0x252d_4a89, 0xa497_f068, 0xa5d7_ed2d, 0x94c2_2845, 0x9da1_c8c4, 0xe27c_2e2e, 0x6e8b_a2b4, 0xc3dd_17fb, 0x498c_d482, 0x0dfe_6a9f, 0xb070_5829, 0x9a1e_6dc1, 0xf829_717c, 0x07bb_8e3a, 0xda3c_0b02, 0x1af8_2fc7, 0x73b7_0955, 0x7a04_379c, 0x5ee2_0a28, 0x8371_2ae5, 0xf4c4_7c6d, 0xdf72_ba56, 0xd794_858d, 0x8c0c_f709, 0x18f0_f390, 0xb6c6_9b35, 0xbf2f_01db, 0x2fa7_4dca, 0xd0cd_9127, 0xbde6_6cec, 0x3dee_bd46, 0x57c8_8fc3, 0xcee1_406f, 0x0066_385a, 0xf3c3_444f, 0x3a79_d5d5, 0x7575_1eb9, 0x3e7f_8185, 0x521c_2605, 0xe1aa_ab6e, 0x38eb_b80f, 0xbee7_e904, 0x61cb_9647, 0xea54_904e, 0x05ae_00e4, 0x2d7a_c65f, 0x0877_51a1, 0xdcd8_2915, 0x0921_ee16, 0xdd86_d33b, 0xd6bd_491a, 0x40fb_adf0, 0x4232_cbd2, 0x3380_8d10, 0x3909_8c42, 0x193f_3199, 0x0bc1_e47a, 0x4a82_b149, 0x02b6_5a8a, 0x104c_dc8e, 0x24a8_f52c, 0x685c_6077, 0xc79f_95c9, 0x1d11_fe50, 0xc08d_afcd, 0x7b1a_9a03, 0x1c1f_11d8, 0x8425_0e7f, 0x979d_b248, 0xebdc_0501, 0xb955_3395, 0xe3c0_5ea8, 0xb1e5_1c4c, 0x13b0_e681, 0x3b40_7766, 0x36db_3087, 0xee17_c9fc, 0x6c53_ecf2, 0xadcc_c58f, 0xc427_660b, 0xefd5_867d, 0x9b6d_54a5, 0x6ff1_aeff, 0x8e78_7952, 0x9e2b_ffe0, 0x8761_d034, 0xe00b_dbad, 0xae99_a8d3, 0xcc03_f6e2, 0xfd0e_d807, 0x0e50_8ae3, 0xb741_82ab, 0x4349_245d, 0xd120_a465, 0xb246_a641, 0xaf3b_7ab0, 0x2a64_88bb, 0x4b3a_0d1f, 0xe7c7_e58c, 0x3faf_f2eb, 0x9044_5ffd, 0xcf38_c393, 0x995d_07e7, 0xf24f_1b36, 0x356f_6891, 0x6d6e_bcbe, 0x8da9_e262, 0x50fd_520e, 0x5bca_9e1e, 0x3747_2cf3, 0x6907_5057, 0x7ec5_fded, 0x0cab_892a, 0xfb24_12ba, 0x1728_debf, 0xa000_a988, 0xd843_ce79, 0x042e_20dd, 0x4fe8_f853, 0x5665_9c3c, 0x2739_d119, 0xa78a_6120, 0x8096_0375, 0x7042_0611, 0x85e0_9f78, 0xabd1_7e96, 0x1b51_3eaf, 0x1e01_eb63, 0x26ad_2133, 0xa890_c094, 0x7613_cf60, 0x817e_781b, 0xa391_13d7, 0xe957_fa58, 0x4131_b99e, 0x28b1_efda, 0x66ac_fba7, 0xff68_944a, 0x77a4_4fd1, 0x7f33_1522, 0x59ff_b3fa, 0xa6df_935b, 0xfa12_d9df, 0xc6bf_6f3f, 0x8952_0cf6, 0x659e_dd6a, 0x544d_a739, 0x8b05_2538, 0x7c30_ea21, 0xc234_5525, 0x1592_7fb2, 0x144a_436b, 0xba10_7b8b, 0x1219_ac97, 0x0673_0432, 0x3183_1ab3, 0xc55a_5c24, 0xaa0f_cd3e, 0xe560_6be8, 0x5c88_f19b, 0x4c08_41ee, 0x1fe3_7267, 0x11f9_c4f4, 0x9f1b_9dae, 0x864e_76d0, 0xe637_c731, 0xd97d_23a6, 0x32f5_3d5c, 0xb816_1980, 0x93fa_0f84, 0xcaef_0870, 0x8874_487e, 0x98f2_cc73, 0x645f_b5c6, 0xcd85_3659, 0x2062_470d, 0x16ed_e8e9, 0x6b06_dab5, 0x78b4_3900, 0xfc95_b786, 0x5d8e_7de1, 0x465b_5954, 0xfe7b_a014, 0xf7d2_3f7b, 0x92bc_8b18, 0x0359_3592, 0x55ce_f4f7, 0x74b2_7317, 0x79de_1fc2, 0xc8a0_bfbd, 0x2293_98cc, 0x62a6_02ce, 0xbcb9_4661, 0x5336_d206, 0xd2a3_75fe, 0x6a6a_b483, 0x4702_a5a4, 0xa2e9_d73d, 0x23a2_e0f1, 0x9189_140a, 0x581d_18dc, 0xb39a_922b, 0x8235_6212, 0xd5f4_32a9, 0xd356_c2a3, 0x5f76_5b4d, 0x450a_fcc8, 0x4415_e137, 0xe8ec_dfbc, 0xed0d_e3ea, 0x60d4_2b13, 0xf13d_f971, 0x71fc_5da2, 0xc145_5340, 0xf087_742f, 0xf55e_5751, 0x67b3_c1f8, 0xac6b_8774, 0x7dcf_aaac, 0x9598_3bc0, 0x489b_b0b1, 0x2c18_4223, 0x964b_6726, 0x2bd3_271c, 0x7226_6472, 0xded6_4530, 0x0a2a_a343, 0xd4f7_16a0, 0xb4da_d6d9, 0x2184_345e, 0x512c_990c, 0x29d9_2d08, 0x2ebe_709a, 0x0114_4c69, 0x3458_4b9d, 0xe463_4ed6, 0xecc9_63cf, 0x3c69_84aa, 0x4ed0_56ef, 0x9ca5_6976, 0x8f3e_80d4, 0xb5ba_e7c5, 0x30b5_caf5, 0x63f3_3a64, 0xa9e4_bbde, 0xf6b8_2298, 0x4d67_3c1d, ), ( 0x4b4f_1121, 0xba18_3081, 0xc784_f41f, 0xd17d_0bac, 0x083d_2267, 0x37b1_361e, 0x3581_ad05, 0xfda2_f6bc, 0x1e89_2cdd, 0xb56d_3c3a, 0x3214_0e46, 0x138d_8aab, 0xe147_73d4, 0x5b0e_71df, 0x5d1f_e055, 0x3fb9_91d3, 0xf1f4_6c71, 0xa325_988c, 0x10f6_6e80, 0xb100_6348, 0x726a_9f60, 0x3b67_f8ba, 0x4e11_4ef4, 0x05c5_2115, 0x4c5c_a11c, 0x99e1_efd8, 0x471b_83b3, 0xcbf7_e524, 0x43ad_82f5, 0x690c_a93b, 0xfaa6_1bb2, 0x12a8_32b5, 0xb734_f943, 0xbd22_aea7, 0x88fe_c626, 0x5e80_c3e7, 0xbe3e_af5e, 0x4461_7652, 0xa572_4475, 0xbb3b_9695, 0x7f3f_ee8f, 0x964e_7deb, 0x518c_052d, 0x2a0b_bc2b, 0xc217_5f5c, 0x9a7b_3889, 0xa70d_8d0c, 0xeacc_dd29, 0xcccd_6658, 0x34bb_25e6, 0xb839_1090, 0xf651_356f, 0x5298_7c9e, 0x0c16_c1cd, 0x8e37_2d3c, 0x2fc6_ebbd, 0x6e5d_a3e3, 0xb0e2_7239, 0x5f68_5738, 0x4541_1786, 0x067f_65f8, 0x6177_8b40, 0x81ab_2e65, 0x14c8_f0f9, 0xa6b7_b4ce, 0x4036_eaec, 0xbf62_b00a, 0xecfd_5e02, 0x0454_49a6, 0xb20a_fd28, 0x2166_d273, 0x0d13_a863, 0x8950_8756, 0xd51a_7530, 0x2d65_3f7a, 0x3cdb_dbc3, 0x80c9_df4f, 0x3d58_12d9, 0x53fb_b1f3, 0xc0f1_85c0, 0x7a3c_3d7e, 0x6864_6410, 0x8576_07a0, 0x1d12_622e, 0x97f3_3466, 0xdb4c_9917, 0x6469_607c, 0x566e_043d, 0x79ef_1edb, 0x2c05_898d, 0xc957_8e25, 0xcd38_0101, 0x46e0_4377, 0x7d1c_c7a9, 0x6552_b837, 0x2019_2608, 0xb975_00c5, 0xed29_6b44, 0x3686_48b4, 0x6299_5cd5, 0x8273_1400, 0xf9ae_bd8b, 0x3844_c0c7, 0x7c2d_e794, 0x33a1_a770, 0x8ae5_28c2, 0x5a2b_e812, 0x1f8f_4a07, 0x2b5e_d7ca, 0x937e_b564, 0x6fda_7e11, 0xe49b_5d6c, 0xb4b3_244e, 0x18aa_53a4, 0x3a06_1334, 0x4d60_67a3, 0x83ba_5868, 0x9bdf_4dfe, 0x7449_f261, 0x709f_8450, 0xcad1_33cb, 0xde94_1c3f, 0xf52a_e484, 0x781d_77ed, 0x7e43_95f0, 0xae10_3b59, 0x9223_31bb, 0x42ce_50c8, 0xe6f0_8153, 0xe7d9_41d0, 0x5028_ed6b, 0xb3d2_c49b, 0xad4d_9c3e, 0xd201_fb6e, 0xa45b_d5be, 0xffcb_7f4b, 0x579d_7806, 0xf821_bb5b, 0x59d5_92ad, 0xd0be_0c31, 0xd4e3_b676, 0x0107_165a, 0x0fe9_39d2, 0x49bc_aafd, 0x55ff_cfe5, 0x2ec1_f783, 0xf39a_09a5, 0x3eb4_2772, 0x19b5_5a5d, 0x024a_0679, 0x8c83_b3f7, 0x8642_ba1d, 0xacac_d9ea, 0x87d3_52c4, 0x6093_1f45, 0xa05f_97d7, 0x1cec_d42c, 0xe2fc_c87b, 0xb60f_94e2, 0x67a3_4b0b, 0xfcdd_40c9, 0x0b15_0a27, 0xd3ee_9e04, 0x582e_29e9, 0x4ac2_2b41, 0x6ac4_e1b8, 0xbcca_a51a, 0x237a_f30e, 0xebc3_b709, 0xc4a5_9d19, 0x284b_c98a, 0xe9d4_1a93, 0x6bfa_2018, 0x73b2_d651, 0x11f9_a2fa, 0xce09_bff1, 0x41a4_70aa, 0x2588_8f22, 0x77e7_54e8, 0xf733_0d8e, 0x158e_ab16, 0xc5d6_8842, 0xc685_a6f6, 0xe5b8_2fde, 0x09ea_3a96, 0x6dde_1536, 0x4fa9_19da, 0x26c0_be9f, 0x9eed_6f69, 0xf055_55f2, 0xe06f_c285, 0x9cd7_6d23, 0xaf45_2a92, 0xefc7_4cb7, 0x9d6b_4732, 0x8be4_08ee, 0x2240_1d0d, 0xee6c_459d, 0x7587_cb82, 0xe874_6862, 0x5cbd_de87, 0x9879_4278, 0x31af_b94d, 0xc11e_0f2f, 0x30e8_fc2a, 0xcf32_61ef, 0x1a30_23e1, 0xaa2f_86cf, 0xf202_e24a, 0x8d08_dcff, 0x7648_37c6, 0xa263_74cc, 0x9f7c_3e88, 0x949c_c57d, 0xdd26_a07f, 0xc39e_fab0, 0xc8f8_79a1, 0xdce6_7bb9, 0xf4b0_a435, 0x912c_9ae0, 0xd856_03e4, 0x953a_9bbf, 0xfb82_90d6, 0x0aeb_cd5f, 0x1620_6a9a, 0x6c78_7a14, 0xd9a0_f16a, 0x29bf_4f74, 0x8f8b_ce91, 0x0e5a_9354, 0xab03_8cb1, 0x1b8a_d11b, 0xe327_ff49, 0x0053_da20, 0x90cf_51dc, 0xda92_fe6d, 0x0390_ca47, 0xa895_8097, 0xa9dc_5baf, 0x3931_e3c1, 0x8404_46b6, 0x63d0_69fb, 0xd746_0299, 0x7124_ecd1, 0x0791_e613, 0x4859_18fc, 0xd635_d04c, 0xdf96_ac33, 0x66f2_d303, 0x2470_56ae, 0xa1a7_b2a8, 0x27d8_cc9c, 0x17b6_e998, 0x7bf5_590f, 0xfe97_f557, 0x5471_d8a2, ), ( 0x83a3_27a1, 0x9f37_9f51, 0x40a7_d007, 0x1130_7423, 0x2245_87c1, 0xac27_d63b, 0x3b7e_64ea, 0x2e1c_bfa6, 0x0999_6000, 0x03bc_0e2c, 0xd4c4_478a, 0x4542_e0ab, 0xfeda_26d4, 0xc1d1_0fcb, 0x8252_f596, 0x4494_eb5c, 0xa362_f314, 0xf5ba_81fd, 0x75c3_a376, 0x4ca2_14ca, 0xe164_dedd, 0x5088_fa97, 0x4b09_30e0, 0x2fcf_b7e8, 0x33a6_f4b2, 0xc7e9_4211, 0x2d66_c774, 0x43be_8bae, 0xc663_d445, 0x908e_b130, 0xf4e3_be15, 0x63b9_d566, 0x5293_96b5, 0x1e1b_e743, 0x4d5f_f63f, 0x985e_4a83, 0x71ab_9df7, 0xc516_c6f5, 0x85c1_9ab4, 0x1f4d_aee4, 0xf297_3431, 0xb713_dc5e, 0x3f2e_159a, 0xc824_da16, 0x06bf_376a, 0xb2fe_23ec, 0xe39b_1c22, 0xf1ee_cb5f, 0x08e8_2d52, 0x5656_86c2, 0xab0a_ea93, 0xfd47_219f, 0xebdb_abd7, 0x2404_a185, 0x8c73_12b9, 0xa8f2_d828, 0x0c89_02da, 0x65b4_2b63, 0xc0bb_ef62, 0x4e3e_4cef, 0x788f_8018, 0xee1e_bab7, 0x9392_8f9d, 0x683d_2903, 0xd3b6_0689, 0xafcb_0ddc, 0x88a4_c47a, 0xf6dd_9c3d, 0x7ea5_fca0, 0x8a6d_7244, 0xbe11_f120, 0x04ff_91b8, 0x8d2d_c8c0, 0x27f9_7fdb, 0x7f9e_1f47, 0x1734_f0c7, 0x26f3_ed8e, 0x0df8_f2bf, 0xb083_3d9e, 0xe420_a4e5, 0xa423_cae6, 0x9561_6772, 0x9ae6_c049, 0x0759_41f2, 0xd8e1_2812, 0x000f_6f4f, 0x3c0d_6b05, 0x6cef_921c, 0xb82b_c264, 0x396c_b008, 0x5d60_8a6f, 0x6d77_82c8, 0x1865_50aa, 0x6b6f_ec09, 0x28e7_0b13, 0x57ce_5688, 0xecd3_af84, 0x2333_5a95, 0x91f4_0cd2, 0x7b6a_3b26, 0xbd32_b3b6, 0x3754_a6fb, 0x8ed0_88f0, 0xf867_e87c, 0x2085_1746, 0x6410_f9c6, 0x3538_0442, 0xc2ca_10a7, 0x1ade_a27f, 0x76bd_dd79, 0x9274_2cf4, 0x0e98_f7ee, 0x164e_931d, 0xb9c8_35b3, 0x6906_0a99, 0xb44c_531e, 0xfa7b_66fe, 0xc98a_5b53, 0x7d95_aae9, 0x302f_467b, 0x74b8_11de, 0xf386_6abd, 0xb5b3_d32d, 0xfc31_57a4, 0xd251_fe19, 0x0b5d_8eac, 0xda71_ffd5, 0x47ea_05a3, 0x05c6_a9e1, 0xca0e_e958, 0x9939_034d, 0x25dc_5edf, 0x7908_3cb1, 0x8676_8450, 0xcf75_7d6d, 0x5972_b6bc, 0xa78d_59c9, 0xc4ad_8d41, 0x2a36_2ad3, 0xd117_9991, 0x6014_07ff, 0xdcf5_0917, 0x5870_69d0, 0xe082_1ed6, 0xdbb5_9427, 0x7391_1a4b, 0x7c90_4fc3, 0x844a_fb92, 0x6f8c_955d, 0xe8c0_c5bb, 0xb67a_b987, 0xa529_d96c, 0xf91f_7181, 0x618b_1b06, 0xe718_bb0c, 0x8bd7_615b, 0xd5a9_3a59, 0x54ae_f81b, 0x7721_36e3, 0xce44_fd9c, 0x10cd_a57e, 0x87d6_6e0b, 0x3d79_8967, 0x1b2c_1804, 0x3edf_bd68, 0x15f6_e62b, 0xef68_b854, 0x3896_db35, 0x12b7_b5e2, 0xcb48_9029, 0x9e4f_98a5, 0x62eb_77a8, 0x217c_24a2, 0x9641_52f6, 0x49b2_080a, 0x53d2_3ee7, 0x48fb_6d69, 0x1903_d190, 0x9449_e494, 0xbf6e_7886, 0xfb35_6cfa, 0x3a26_1365, 0x424b_c1eb, 0xa119_2570, 0x019c_a782, 0x9d3f_7e0e, 0x9c12_7575, 0xedf0_2039, 0xad57_bcce, 0x5c15_3277, 0x81a8_4540, 0xbcaa_7356, 0xccd5_9b60, 0xa62a_629b, 0xa25c_cd10, 0x2b5b_65cf, 0x1c53_5832, 0x55fd_4e3a, 0x31d9_790d, 0xf06b_c37d, 0x4afc_1d71, 0xaeed_5533, 0xba46_1634, 0xbb69_4b78, 0x5f3a_5c73, 0x6a3c_764a, 0x8fb0_cca9, 0xf725_684c, 0x4fe5_382f, 0x1d01_63af, 0x5aa0_7a8f, 0xe205_a8ed, 0xc30b_ad38, 0xff22_cf1f, 0x7243_2e2e, 0x32c2_518b, 0x3487_ce4e, 0x7ae0_ac02, 0x709f_a098, 0x0a3b_395a, 0x5b40_43f8, 0xa9e4_8c36, 0x149a_8521, 0xd07d_ee6b, 0x46ac_d2f3, 0x8958_dffc, 0xb3a1_223c, 0xb11d_31c4, 0xcd7f_4d3e, 0x0f28_e3ad, 0xe5b1_00be, 0xaac5_4824, 0xe9c9_d7ba, 0x9bd4_7001, 0x80f1_49b0, 0x6602_2f0f, 0x020c_4048, 0x6efa_192a, 0x6707_3f8d, 0x13ec_7bf9, 0x3655_011a, 0xe6af_e157, 0xd984_5f6e, 0xdecc_4425, 0x511a_e2cc, 0xdf81_b4d8, 0xd780_9e55, 0xd6d8_83d9, 0x2cc7_978c, 0x5e78_7cc5, 0xdd00_33d1, 0xa050_c937, 0x97f7_5dcd, 0x299d_e580, 0x41e2_b261, 0xea5a_54f1, ), ( 0x7e67_2590, 0xbea5_13bb, 0x2c90_6fe6, 0x8602_9c2b, 0x55dc_4f74, 0x0553_398e, 0x63e0_9647, 0xcafd_0bab, 0x264c_37df, 0x8272_210f, 0x67af_a669, 0x12d9_8a5f, 0x8cab_23c4, 0x75c6_8bd1, 0xc337_0470, 0x33f3_7f4e, 0x2839_92ff, 0xe73a_3a67, 0x1032_f283, 0xf5ad_9fc2, 0x963f_0c5d, 0x664f_bc45, 0x202b_a41c, 0xc7c0_2d80, 0x5473_1e84, 0x8a10_85f5, 0x601d_80fb, 0x2f96_8e55, 0x35e9_6812, 0xe45a_8f78, 0xbd7d_e662, 0x3b6e_6ead, 0x8097_c5ef, 0x070b_6781, 0xb1e5_08f3, 0x24e4_fae3, 0xb81a_7805, 0xec0f_c918, 0x43c8_774b, 0x9b25_12a9, 0x2b05_ad04, 0x32c2_536f, 0xedf2_36e0, 0x8bc4_b0cf, 0xbace_b837, 0x4535_b289, 0x0d0e_94c3, 0xa5a3_71d0, 0xad69_5a58, 0x39e3_437d, 0x9186_bffc, 0x2103_8c3b, 0x0aa9_dff9, 0x5d1f_06ce, 0x62de_f8a4, 0xf740_a2b4, 0xa257_5868, 0x6826_83c1, 0xdbb3_0fac, 0x61fe_1928, 0x468a_6511, 0xc61c_d5f4, 0xe54d_9800, 0x6b98_d7f7, 0x8418_b6a5, 0x5f09_a5d2, 0x90b4_e80b, 0x49b2_c852, 0x69f1_1c77, 0x1741_2b7e, 0x7f6f_c0ed, 0x5683_8dcc, 0x6e95_46a2, 0xd075_8619, 0x087b_9b9a, 0xd231_a01d, 0xaf46_d415, 0x0970_60fd, 0xd920_f657, 0x882d_3f9f, 0x3ae7_c3c9, 0xe8a0_0d9b, 0x4fe6_7ebe, 0x2ef8_0eb2, 0xc191_6b0c, 0xf4df_fea0, 0xb97e_b3eb, 0xfdff_84dd, 0xff8b_14f1, 0xe96b_0572, 0xf64b_508c, 0xae22_0a6e, 0x4423_ae5a, 0xc2be_ce5e, 0xde27_567c, 0xfc93_5c63, 0x4707_5573, 0xe65b_27f0, 0xe121_fd22, 0xf266_8753, 0x2deb_f5d7, 0x8347_e08d, 0xac5e_da03, 0x2a7c_ebe9, 0x3fe8_d92e, 0x2354_2fe4, 0x1fa7_bd50, 0xcf9b_4102, 0x9d0d_ba39, 0x9cb8_902a, 0xa724_9d8b, 0x0f6d_667a, 0x5ebf_a9ec, 0x6a59_4df2, 0x7960_0938, 0x023b_7591, 0xea2c_79c8, 0xc99d_07ea, 0x64cb_5ee1, 0x1a9c_ab3d, 0x76db_9527, 0xc08e_012f, 0x3dfb_481a, 0x872f_22e7, 0x2948_d15c, 0xa478_2c79, 0x6f50_d232, 0x78f0_728a, 0x5a87_aab1, 0xc4e2_c19c, 0xee76_7387, 0x1b2a_1864, 0x7b8d_10d3, 0xd171_3161, 0x0eea_c456, 0xd879_9e06, 0xb645_b548, 0x4043_cb65, 0xa874_fb29, 0x4b12_d030, 0x7d68_7413, 0x18ef_9a1f, 0xd763_1d4c, 0x5829_c7da, 0xcdfa_30fa, 0xc508_4bb0, 0x92cd_20e2, 0xd4c1_6940, 0x0328_3ec0, 0xa917_813f, 0x9a58_7d01, 0x7004_1f8f, 0xdc6a_b1dc, 0xddae_e3d5, 0x3182_9742, 0x198c_022d, 0x1c9e_afcb, 0x5bbc_6c49, 0xd3d3_293a, 0x16d5_0007, 0x04bb_8820, 0x3c5c_2a41, 0x37ee_7af8, 0x8eb0_4025, 0x9313_ecba, 0xbffc_4799, 0x8955_a744, 0xef85_d633, 0x5044_99a7, 0xa6ca_6a86, 0xbb3d_3297, 0xb34a_8236, 0x6dcc_be4f, 0x0614_3394, 0xce19_fc7b, 0xccc3_c6c6, 0xe362_54ae, 0x77b7_eda1, 0xa133_dd9e, 0xebf9_356a, 0x513c_cf88, 0xe2a1_b417, 0x972e_e5bd, 0x8538_24cd, 0x5752_f4ee, 0x6c11_42e8, 0x3ea4_f309, 0xb2b5_934a, 0xdfd6_28aa, 0x59ac_ea3e, 0xa01e_b92c, 0x3899_64bc, 0xda30_5dd4, 0x019a_59b7, 0x11d2_ca93, 0xfaa6_d3b9, 0x4e77_2eca, 0x7265_1776, 0xfb4e_5b0e, 0xa38f_91a8, 0x1d06_63b5, 0x30f4_f192, 0xb500_51b6, 0xb716_ccb3, 0x4abd_1b59, 0x146c_5f26, 0xf134_e2de, 0x00f6_7c6c, 0xb0e1_b795, 0x98aa_4ec7, 0x0cc7_3b34, 0x6542_76a3, 0x8d1b_a871, 0x740a_5216, 0xe0d0_1a23, 0x9ed1_61d6, 0x9f36_a324, 0x993e_bb7f, 0xfeb9_491b, 0x365d_dcdb, 0x810c_ffc5, 0x71ec_0382, 0x2249_e7bf, 0x4881_7046, 0xf3a2_4a5b, 0x4288_e4d9, 0x0bf5_c243, 0x257f_e151, 0x95b6_4c0d, 0x4164_f066, 0xaaf7_db08, 0x73b1_119d, 0x8f9f_7bb8, 0xd684_4596, 0xf07a_34a6, 0x5394_3d0a, 0xf9dd_166d, 0x7a89_57af, 0xf8ba_3ce5, 0x27c9_621e, 0x5cda_e910, 0xc851_8998, 0x9415_38fe, 0x1361_15d8, 0xaba8_443c, 0x4d01_f931, 0x34ed_f760, 0xb45f_266b, 0xd5d4_de14, 0x52d8_ac35, 0x15cf_d885, 0xcbc5_cd21, 0x4cd7_6d4d, 0x7c80_ef54, 0xbc92_ee75, 0x1e56_a1f6, ), ( 0xbaa2_0b6c, 0x9ffb_ad26, 0xe1f7_d738, 0x794a_ec8d, 0xc9e9_cf3c, 0x8a9a_7846, 0xc57c_4685, 0xb9a9_2fed, 0x29cb_141f, 0x52f9_ddb7, 0xf68b_a6bc, 0x19cc_c020, 0x4f58_4aaa, 0x3bf6_a596, 0x003b_7cf7, 0x54f0_ce9a, 0xa7ec_4303, 0x46cf_0077, 0x78d3_3aa1, 0x2152_47d9, 0x74bc_df91, 0x0838_1d30, 0xdac4_3e40, 0x6487_2531, 0x0bef_fe5f, 0xb317_f457, 0xaebb_12da, 0xd5d0_d67b, 0x7d75_c6b4, 0x42a6_d241, 0x1502_d0a9, 0x3fd9_7fff, 0xc6c3_ed28, 0x8186_8d0a, 0x9262_8bc5, 0x8667_9544, 0xfd18_67af, 0x5ca3_ea61, 0x568d_5578, 0x4a2d_71f4, 0x43c9_d549, 0x8d95_de2b, 0x6e5c_74a0, 0x9120_ffc7, 0x0d05_d14a, 0xa930_49d3, 0xbfa8_0e17, 0xf409_6810, 0x043f_5ef5, 0xa673_b4f1, 0x6d78_0298, 0xa484_7783, 0x5ee7_26fb, 0x9934_c281, 0x220a_588c, 0x384e_240f, 0x933d_5c69, 0x39e5_ef47, 0x26e8_b8f3, 0x4c1c_6212, 0x8040_f75d, 0x074b_7093, 0x6625_a8d7, 0x3629_8945, 0x7628_5088, 0x651d_37c3, 0x24f5_274d, 0xdbca_3dab, 0x186b_7ee1, 0xd80f_8182, 0x1421_0c89, 0x943a_3075, 0x4e6e_11c4, 0x4d7e_6bad, 0xf050_64c8, 0x025d_cd97, 0x4bc1_0302, 0x7ced_e572, 0x8f90_a970, 0xab88_eeba, 0xb599_8029, 0x5124_d839, 0xb0ee_b6a3, 0x89dd_abdc, 0xe807_4d76, 0xa146_5223, 0x3251_8cf2, 0x9d39_d4eb, 0xc0d8_4524, 0xe35e_6ea8, 0x7abf_3804, 0x113e_2348, 0x9ae6_069d, 0xb4df_dabb, 0xa8c5_313f, 0x23ea_3f79, 0x530e_36a2, 0xa5fd_228b, 0x95d1_d350, 0x2b14_cc09, 0x4004_2956, 0x879d_05cc, 0x2064_b9ca, 0xacac_a40e, 0xb29c_846e, 0x9676_c9e3, 0x752b_7b8a, 0x7be2_bcc2, 0x6bd5_8f5e, 0xd48f_4c32, 0x6068_35e4, 0x9cd7_c364, 0x2c26_9b7a, 0x3a0d_079c, 0x73b6_83fe, 0x4537_4f1e, 0x10af_a242, 0x577f_8666, 0xddaa_10f6, 0xf34f_561c, 0x3d35_5d6b, 0xe470_48ae, 0xaa13_c492, 0x0503_44fd, 0x2aab_5151, 0xf5b2_6ae5, 0xed91_9a59, 0x5ac6_7900, 0xf1cd_e380, 0x0c79_a11b, 0x3515_33fc, 0xcd4d_8e36, 0x1f85_6005, 0x690b_9fdd, 0xe736_dccf, 0x1d47_bf6a, 0x7f66_c72a, 0x85f2_1b7f, 0x983c_bdb6, 0x01eb_bebf, 0x035f_3b99, 0xeb11_1f34, 0x28ce_fdc6, 0x5bfc_9ecd, 0xf22e_acb0, 0x9e41_cbb2, 0xe0f8_327c, 0x82e3_e26f, 0xfc43_fc86, 0xd0ba_66df, 0x489e_f2a7, 0xd9e0_c81d, 0x6869_0d52, 0xcc45_1367, 0xc223_2e16, 0xe95a_7335, 0x0fda_e19b, 0xff5b_962c, 0x9759_6527, 0xc46d_b333, 0x3ed4_c562, 0xc14c_9d9e, 0x5d6f_aa21, 0x638e_940d, 0xf931_6d58, 0x47b3_b0ea, 0x30ff_cad2, 0xce1b_ba7d, 0x1e61_08e6, 0x2e1e_a33d, 0x507b_f05b, 0xfafe_f94b, 0xd17d_e8e2, 0x5598_b214, 0x1663_f813, 0x17d2_5a2d, 0xeefa_5ff9, 0x582f_4e37, 0x1212_8773, 0xfef1_7ab8, 0x0600_5322, 0xbb32_bbc9, 0x8c89_8508, 0x592c_15f0, 0xd38a_4054, 0x4957_b7d6, 0xd2b8_91db, 0x37bd_2d3e, 0x34ad_20cb, 0x6222_88e9, 0x2dc7_345a, 0xafb4_16c0, 0x1cf4_59b1, 0xdc77_39fa, 0x0a71_1a25, 0x13e1_8a0c, 0x5f72_af4c, 0x6ac8_db11, 0xbe53_c18e, 0x1aa5_69b9, 0xef55_1ea4, 0xa02a_429f, 0xbd16_e790, 0x7eb9_171a, 0x77d6_93d8, 0x8e06_993a, 0x9bde_7560, 0xe580_1987, 0xc37a_09be, 0xb8db_76ac, 0xe208_7294, 0x6c81_616d, 0xb7f3_0fe7, 0xbc9b_82bd, 0xfba4_e4d4, 0xc7b1_012f, 0xa20c_043b, 0xde9f_ebd0, 0x2f92_97ce, 0xe610_aef8, 0x70b0_6f19, 0xc86a_e00b, 0x0e01_988f, 0x4119_2ae0, 0x448c_1cb5, 0xadbe_92ee, 0x7293_a007, 0x1b54_b5b3, 0xd61f_63d1, 0xeae4_0a74, 0x61a7_2b55, 0xec83_a7d5, 0x8894_2806, 0x90a0_7da5, 0xd742_4b95, 0x6774_5b4e, 0xa31a_1853, 0xca60_21ef, 0xdfb5_6c4f, 0xcbc2_d915, 0x3c48_e918, 0x8bae_3c63, 0x6f65_9c71, 0xf8b7_54c1, 0x2782_f3de, 0xf796_f168, 0x7149_2c84, 0x33c0_f5a6, 0x3144_f6ec, 0x25dc_412e, 0xb16c_5743, 0x83a1_fa7e, 0x0997_b101, 0xb627_e6e8, 0xcf33_905c, 0x8456_fb65, ), ( 0xb29b_ea74, 0xc35d_a605, 0x305c_1ca3, 0xd2e9_f5bc, 0x6fd5_bff4, 0xff34_7703, 0xfc45_b163, 0xf498_e068, 0xb712_29fc, 0x81ac_c3fb, 0x7853_8a8b, 0x984e_cf81, 0xa5da_47a4, 0x8f25_9eef, 0x6475_dc65, 0x0818_65b9, 0x49e1_4a3c, 0x19e6_6079, 0xd382_e91b, 0x5b10_9794, 0x3f9f_81e1, 0x4470_a388, 0x4160_1abe, 0xaaf9_f407, 0x8e17_5ef6, 0xed84_2297, 0x893a_4271, 0x1790_839a, 0xd566_a99e, 0x6b41_7dee, 0x75c9_0d23, 0x715e_db31, 0x7235_53f7, 0x9afb_50c9, 0xfbc5_f600, 0xcd3b_6a4e, 0x97ed_0fba, 0x2968_9aec, 0x6313_5c8e, 0xf0e2_6c7e, 0x0692_ae7f, 0xdbb2_08ff, 0x2ede_3e9b, 0x6a65_bebd, 0xd408_67e9, 0xc954_afc5, 0x73b0_8201, 0x7ffd_f809, 0x1195_c24f, 0x1ca5_adca, 0x74bd_6d1f, 0xb393_c455, 0xcadf_d3fa, 0x99f1_3011, 0x0ebc_a813, 0x60e7_91b8, 0x6597_ac7a, 0x18a7_e46b, 0x09cb_49d3, 0x0b27_df6d, 0xcfe5_2f87, 0xcef6_6837, 0xe632_8035, 0xfa87_c592, 0x37ba_ff93, 0xd71f_cc99, 0xdcab_205c, 0x4d7a_5638, 0x4801_2510, 0x6279_7558, 0xb6cf_1fe5, 0xbc31_1834, 0x9c23_73ac, 0x14ec_6175, 0xa439_cbdf, 0x54af_b0ea, 0xd686_960b, 0xfdd0_d47b, 0x7b06_3902, 0x8b78_bac3, 0x26c6_a4d5, 0x5c00_55b6, 0x2376_102e, 0x0411_783e, 0x2aa3_f1cd, 0x51fc_6ea8, 0x701c_e243, 0x9b2a_0abb, 0x0ad9_3733, 0x6e80_d03d, 0xaf62_95d1, 0xf629_896f, 0xa30b_0648, 0x463d_8dd4, 0x963f_84cb, 0x01ff_94f8, 0x8d7f_efdc, 0x5536_11c0, 0xa97c_1719, 0xb96a_f759, 0xe0e3_c95e, 0x0528_335b, 0x21fe_5925, 0x821a_5245, 0x8072_38b1, 0x67f2_3db5, 0xea6b_4eab, 0x0da6_f985, 0xab1b_c85a, 0xef8c_90e4, 0x4526_230e, 0x38eb_8b1c, 0x1b91_cd91, 0x9fce_5f0c, 0xf72c_c72b, 0xc64f_2617, 0xdaf7_857d, 0x7d37_3cf1, 0x28ea_edd7, 0x2038_87d0, 0xc49a_155f, 0xa251_b3b0, 0xf2d4_7ae3, 0x3d9e_f267, 0x4a94_ab2f, 0x7755_a222, 0x0205_e329, 0xc28f_a7a7, 0xaec1_fe51, 0x270f_164c, 0x8c6d_01bf, 0x53b5_bc98, 0xc09d_3feb, 0x8349_86cc, 0x4309_a12c, 0x578b_2a96, 0x3bb7_4b86, 0x6956_1b4a, 0x037e_32f3, 0xde33_5b08, 0xc515_6be0, 0xe7ef_09ad, 0x93b8_34c7, 0xa771_9352, 0x5930_2821, 0xe352_9d26, 0xf961_da76, 0xcb14_2c44, 0xa0f3_b98d, 0x7650_2457, 0x945a_414b, 0x078e_eb12, 0xdff8_de69, 0xeb6c_8c2d, 0xbda9_0c4d, 0xe9c4_4d16, 0x168d_fd66, 0xad64_763b, 0xa65f_d764, 0x95a2_9c06, 0x32d7_713f, 0x40f0_b277, 0x224a_f08f, 0x004c_b5e8, 0x9257_4814, 0x8877_d827, 0x3e5b_2d04, 0x68c2_d5f2, 0x8696_6273, 0x1d43_3ada, 0x8774_988a, 0x3c0e_0bfe, 0xddad_581d, 0x2fd6_54ed, 0x0f47_69fd, 0xc181_ee9d, 0x5fd8_8f61, 0x341d_bb3a, 0x5285_43f9, 0xd922_35cf, 0x1ea8_2eb4, 0xb5cd_790f, 0x91d2_4f1e, 0xa869_e6c2, 0x61f4_74d2, 0xcc20_5add, 0x0c7b_fba9, 0xbf2b_0489, 0xb02d_72d8, 0x2b46_ece6, 0xe4dc_d90a, 0xb8a1_1440, 0xee8a_63b7, 0x854d_d1a1, 0xd1e0_0583, 0x42b4_0e24, 0x9e89_64de, 0xb4b3_5d78, 0xbec7_6f6e, 0x24b9_c620, 0xd8d3_99a6, 0x5adb_2190, 0x2db1_2730, 0x3a58_66af, 0x58c8_fadb, 0x5d88_44e7, 0x8a4b_f380, 0x15a0_1d70, 0x79f5_c028, 0x66be_3b8c, 0xf3e4_2b53, 0x5699_0039, 0x2c0c_3182, 0x5e16_407c, 0xecc0_4515, 0x6c44_0284, 0x4cb6_701a, 0x13bf_c142, 0x9d03_9f6a, 0x4f6e_92c8, 0xa140_7c62, 0x8483_a095, 0xc70a_e1c4, 0xe202_13a2, 0xbaca_fc41, 0x4ecc_12b3, 0x4bee_3646, 0x1fe8_07ae, 0x2521_7f9c, 0x35dd_e5f5, 0x7a7d_d6ce, 0xf89c_ce50, 0xac07_b718, 0x7e73_d2c6, 0xe563_e76c, 0x123c_a536, 0x3948_ca56, 0x9019_dd49, 0x10aa_88d9, 0xc824_51e2, 0x473e_b6d6, 0x506f_e854, 0xe8bb_03a5, 0x332f_4c32, 0xfe1e_1e72, 0xb1ae_572a, 0x7c0d_7bc1, 0xe1c3_7eb2, 0xf542_aa60, 0xf1a4_8ea0, 0xd067_b89f, 0xbbfa_195d, 0x1a04_9b0d, 0x3159_46aa, 0x36d1_b447, 0x6d2e_bdf0, ), ( 0x0d18_8a6d, 0x12ce_a0db, 0x7e63_740e, 0x6a44_4821, 0x253d_234f, 0x6ffc_6597, 0x94a6_bdef, 0x33ee_1b2f, 0x0a6c_00c0, 0x3aa3_36b1, 0x5af5_5d17, 0x265f_b3dc, 0x0e89_cf4d, 0x0786_b008, 0xc800_55b8, 0x6b17_c3ce, 0x72b0_5a74, 0xd21a_8d78, 0xa6b7_0840, 0xfe8e_ae77, 0xed69_565c, 0x55e1_bcf4, 0x585c_2f60, 0xe06f_1a62, 0xad67_c0cd, 0x7712_af88, 0x9cc2_6aca, 0x1888_053d, 0x37eb_853e, 0x9215_abd7, 0xde30_adfc, 0x1f10_38e6, 0x70c5_1c8a, 0x8d58_6c26, 0xf72b_dd90, 0x4dc3_ce15, 0x68ea_eefa, 0xd0e9_c8b9, 0x200f_9c44, 0xddd1_41ba, 0x024b_f1d3, 0x0f64_c9d4, 0xc421_e9e9, 0x9d11_c14c, 0x9a0d_d9e4, 0x5f92_ec19, 0x1b98_0df0, 0x1dcc_4542, 0xb8fe_8c56, 0x0c9c_9167, 0x4e81_eb49, 0xca36_8f27, 0xe360_3b37, 0xea08_accc, 0xac51_6992, 0xc34f_513b, 0x804d_100d, 0x6edc_a4c4, 0xfc91_2939, 0x29d2_19b0, 0x278a_aa3c, 0x4868_da7d, 0x54e8_90b7, 0xb46d_735a, 0x5145_89aa, 0xd6c6_30af, 0x4980_dfe8, 0xbe3c_cc55, 0x59d4_1202, 0x650c_078b, 0xaf3a_9e7b, 0x3ed9_827a, 0x9e79_fc6e, 0xaadb_fbae, 0xc5f7_d803, 0x3daf_7f50, 0x67b4_f465, 0x7340_6e11, 0x3931_3f8c, 0x8a6e_6686, 0xd807_5f1f, 0xd3cb_fed1, 0x69c7_e49c, 0x9305_81e0, 0xe4b1_a5a8, 0xbbc4_5472, 0x09dd_bf58, 0xc91d_687e, 0xbdbf_fda5, 0x88c0_8735, 0xe9e3_6bf9, 0xdb5e_a9b6, 0x9555_9404, 0x08f4_32fb, 0xe24e_a281, 0x6466_3579, 0x000b_8010, 0x7914_e7d5, 0x32fd_0473, 0xd1a7_f0a4, 0x445a_b98e, 0xec72_993f, 0xa29a_4d32, 0xb773_06d8, 0xc7c9_7cf6, 0x7b6a_b645, 0xf5ef_7adf, 0xfb2e_15f7, 0xe747_f757, 0x5e94_4354, 0x234a_2669, 0x47e4_6359, 0x9b9d_11a9, 0x4076_2ced, 0x56f1_de98, 0x1133_4668, 0x890a_9a70, 0x1a29_6113, 0xb3bd_4af5, 0x163b_7548, 0xd51b_4f84, 0xb99b_2abc, 0x3cc1_dc30, 0xa9f0_b56c, 0x8122_72b2, 0x0b23_3a5f, 0xb650_dbf2, 0xf1a0_771b, 0x3656_2b76, 0xdc03_7b0f, 0x104c_97ff, 0xc2ec_98d2, 0x9059_6f22, 0x28b6_620b, 0xdf42_b212, 0xfdbc_4243, 0xf3fb_175e, 0x4a2d_8b00, 0xe8f3_869b, 0x30d6_9bc3, 0x8537_14c8, 0xa775_1d2e, 0x31e5_6dea, 0xd484_0b0c, 0x9685_d783, 0x068c_9333, 0x8fba_032c, 0x76d7_bb47, 0x6d0e_e22b, 0xb546_794b, 0xd971_b894, 0x8b09_d253, 0xa0ad_5761, 0xee77_ba06, 0x4635_9f31, 0x577c_c7ec, 0x5282_5efd, 0xa4be_ed95, 0x9825_c52a, 0xeb48_029a, 0xbaae_59f8, 0xcf49_0ee1, 0xbc99_0164, 0x8ca4_9dfe, 0x4f38_a6e7, 0x2ba9_8389, 0x8228_f538, 0x199f_64ac, 0x01a1_cac5, 0xa8b5_1641, 0x5ce7_2d01, 0x8e5d_f26b, 0x60f2_8e1e, 0xcd5b_e125, 0xe5b3_76bf, 0x1c8d_3116, 0x7132_cbb3, 0xcb7a_e320, 0xc0fa_5366, 0xd765_3e34, 0x971c_88c2, 0xc62c_7dd0, 0x34d0_a3da, 0x868f_6709, 0x7ae6_fa8f, 0x22bb_d523, 0x66cd_3d5b, 0x1ef9_288d, 0xf9cf_58c1, 0x5b78_4e80, 0x7439_a191, 0xae13_4c36, 0x9116_c463, 0x2e9e_1396, 0xf861_1f3a, 0x2d2f_3307, 0x247f_37dd, 0xc1e2_ff9d, 0x43c8_21e5, 0x05ed_5cab, 0xef74_e80a, 0x4cca_6028, 0xf0ac_3cbd, 0x5d87_4b29, 0x6c62_f6a6, 0x4b2a_2ef3, 0xb1aa_2087, 0x62a5_d0a3, 0x0327_221c, 0xb096_b4c6, 0x417e_c693, 0xaba8_40d6, 0x7897_25eb, 0xf4b9_e02d, 0xe6e0_0975, 0xcc04_961a, 0x63f6_24bb, 0x7fa2_1ecb, 0x2c01_ea7f, 0xb241_5005, 0x2a8b_beb5, 0x83b2_b14e, 0xa383_d1a7, 0x5352_f96a, 0x043e_cdad, 0xce19_18a1, 0xfa6b_e6c9, 0x50de_f36f, 0xf6b8_0ce2, 0x4543_ef7c, 0x9953_d651, 0xf257_955d, 0x8724_4914, 0xda1e_0a24, 0xffda_4785, 0x14d3_27a2, 0x3b93_c29f, 0x8406_84b4, 0x61ab_71a0, 0x9f7b_784a, 0x2fd5_70cf, 0x1595_5bde, 0x38f8_d471, 0x3534_a718, 0x133f_b71d, 0x3fd8_0f52, 0x4290_a8be, 0x75ff_44c7, 0xa554_e546, 0xe102_3499, 0xbf26_52e3, 0x7d20_399e, 0xa1df_7e82, 0x1770_92ee, 0x217d_d3f1, 0x7c1f_f8d9, ), ( 0x1211_3f2e, 0xbfbd_0785, 0xf117_93fb, 0xa5bf_f566, 0x83c7_b0e5, 0x72fb_316b, 0x7552_6a9a, 0x41e0_e612, 0x7156_ba09, 0x53ce_7dee, 0x0aa2_6881, 0xa43e_0d7d, 0x3da7_3ca3, 0x1827_61ed, 0xbd50_77ff, 0x56db_4aa0, 0xe792_711c, 0xf0a4_eb1d, 0x7f87_8237, 0xec65_c4e8, 0x08dc_8d43, 0x0f8c_e142, 0x8258_abda, 0xf415_4e16, 0x49de_c2fd, 0xcd8d_5705, 0x6c2c_3a0f, 0x5c12_bb88, 0xeff3_cdb6, 0x2c89_ed8c, 0x7beb_a967, 0x2a14_2157, 0xc6d0_836f, 0xb4f9_7e96, 0x6931_e969, 0x514e_6c7c, 0xa779_2600, 0x0bbb_f780, 0x5967_1bbd, 0x0707_b676, 0x3748_2d93, 0x80af_1479, 0x3805_a60d, 0xe1f4_cac1, 0x580b_3074, 0x30b8_d6ce, 0x05a3_04be, 0xd176_626d, 0xebca_97f3, 0xbb20_1f11, 0x6a1a_fe23, 0xffaa_86e4, 0x62b4_da49, 0x1b66_29f5, 0xf5d9_e092, 0xf37f_3dd1, 0x619b_d45b, 0xa6ec_8e4f, 0x29c8_0939, 0x0c7c_0c34, 0x9cfe_6e48, 0xe65f_d3ac, 0x7361_3b65, 0xb3c6_69f9, 0xbe2e_8a9e, 0x286f_9678, 0x5797_fd13, 0x9980_5d75, 0xcfb6_41c5, 0xa910_74ba, 0x6343_af47, 0x6403_cb46, 0x8894_c8db, 0x2663_034c, 0x3c40_dc5e, 0x0099_5231, 0x9678_9aa2, 0x2efd_e4b9, 0x7dc1_95e1, 0x547d_add5, 0x06a8_ea04, 0xf234_7a63, 0x5e0d_c6f7, 0x8462_dfc2, 0x1e6b_2c3c, 0x9bd2_75b3, 0x91d4_19e2, 0xbcef_d17e, 0xb900_3924, 0xd07e_7320, 0xdef0_495c, 0xc36a_d00e, 0x1785_b1ab, 0x92e2_0bcf, 0xb139_f0e9, 0x675b_b9a1, 0xaecf_a4af, 0x1323_76cb, 0xe845_89d3, 0x79a0_5456, 0xa2f8_60bc, 0x1ae4_f8b5, 0x20df_4db4, 0xa1e1_428b, 0x3bf6_0a1a, 0x27ff_7bf1, 0xcb44_c0e7, 0xf7f5_87c4, 0x1f3b_9b21, 0x9436_8f01, 0x856e_23a4, 0x6f93_de3f, 0x773f_5bbf, 0x8b22_056e, 0xdf41_f654, 0xb824_6ff4, 0x8d57_bff2, 0xd571_67ea, 0xc569_9f22, 0x4073_4ba7, 0x5d5c_2772, 0x0330_20a8, 0xe30a_7c4d, 0xadc4_0fd6, 0x7635_3441, 0x5aa5_229b, 0x8151_6590, 0xda49_f14e, 0x4fa6_72a5, 0x4d9f_ac5f, 0x154b_e230, 0x8a7a_5cc0, 0xce3d_2f84, 0xcca1_5514, 0x5221_360c, 0xaf0f_b81e, 0x5bdd_5873, 0xf682_5f8f, 0x1113_d228, 0x70ad_996c, 0x9332_0051, 0x6047_1c53, 0xe9ba_567b, 0x3a46_2ae3, 0x5f55_e72d, 0x1d3c_5ad7, 0xdcfc_45ec, 0x34d8_12ef, 0xfa96_ee1b, 0x369d_1ef8, 0xc9b1_a189, 0x7c1d_3555, 0x5084_5edc, 0x4bb3_1877, 0x8764_a060, 0x8c9a_9415, 0x230e_1a3a, 0xb05e_9133, 0x242b_9e03, 0xa3b9_9db7, 0xc2d7_fb0a, 0x3333_849d, 0xd272_78d4, 0xb5d3_efa6, 0x78ac_28ad, 0xc7b2_c135, 0x0926_ecf0, 0xc137_4c91, 0x74f1_6d98, 0x2274_084a, 0x3f6d_9cfa, 0x7ac0_a383, 0xb73a_ff1f, 0x3909_a23d, 0x9f16_53ae, 0x4e2f_3e71, 0xca5a_b22a, 0xe01e_3858, 0x90c5_a7eb, 0x3e4a_17df, 0xaa98_7fb0, 0x488b_bd62, 0xb625_062b, 0x2d77_6bb8, 0x43b5_fc08, 0x1490_d532, 0xd6d1_2495, 0x44e8_9845, 0x2fe6_0118, 0x9d9e_f950, 0xac38_133e, 0xd386_4329, 0x017b_255a, 0xfdc2_dd26, 0x2568_51e6, 0x318e_7086, 0x2bfa_4861, 0x89ea_c706, 0xee59_40c6, 0x68c3_bc2f, 0xe260_334b, 0x98da_90bb, 0xf818_f270, 0x4706_d897, 0x212d_3799, 0x4cf7_e5d0, 0xd9c9_649f, 0xa85d_b5cd, 0x35e9_0e82, 0x6b88_1152, 0xab1c_02c7, 0x4675_2b02, 0x664f_598e, 0x45ab_2e64, 0xc4cd_b4b2, 0xba42_107f, 0xea2a_808a, 0x971b_f3de, 0x4a54_a836, 0x4253_aecc, 0x1029_be68, 0x6dcc_9225, 0xe4bc_a56a, 0xc0ae_50b1, 0x7e01_1d94, 0xe59c_162c, 0xd8e5_c340, 0xd470_fa0b, 0xb2be_79dd, 0xd783_889c, 0x1ced_e8f6, 0x8f4c_817a, 0xddb7_85c9, 0x8602_32d8, 0x198a_aad9, 0xa081_4738, 0x3219_cffc, 0x1695_46d2, 0xfc0c_b759, 0x5591_1510, 0x04d5_cec3, 0xed08_cc3b, 0x0d6c_f427, 0xc8e3_8cca, 0x0eee_e3fe, 0x9ee7_d7c8, 0xf9f2_4fa9, 0xdb04_b35d, 0x9ab0_c9e0, 0x651f_4417, 0x028f_8b07, 0x6e28_d9aa, 0xfba9_6319, 0x8ed6_6687, 0xfecb_c58d, 0x954d_db44, ), ( 0x7b0b_dffe, 0x865d_16b1, 0x49a0_58c0, 0x97ab_aa3f, 0xcaac_c75d, 0xaba6_c17d, 0xf874_6f92, 0x6f48_aeed, 0x8841_d4b5, 0xf36a_146a, 0x73c3_90ab, 0xe6fb_558f, 0x87b1_019e, 0x2697_0252, 0x2463_77b2, 0xcbf6_76ae, 0xf923_db06, 0xf738_9116, 0x14c8_1a90, 0x8311_4eb4, 0x8b13_7559, 0x95a8_6a7a, 0xd5b8_da8c, 0xc4df_780e, 0x5a9c_b3e2, 0xe44d_4062, 0xe8dc_8ef6, 0x9d18_0845, 0x817a_d18b, 0xc286_c85b, 0x251f_20de, 0xee6d_5933, 0xf6ed_ef81, 0xd4d1_6c1e, 0xc94a_0c32, 0x8437_fd22, 0x3271_ee43, 0x4257_2aee, 0x5f91_962a, 0x1c52_2d98, 0x59b2_3f0c, 0xd86b_8804, 0x08c6_3531, 0x2c0d_7a40, 0xb97c_4729, 0x0496_4df9, 0x13c7_4a17, 0x5878_362f, 0x4c80_8cd6, 0x092c_b1e0, 0x6df0_2885, 0xa0c2_105e, 0x8aba_9e68, 0x64e0_3057, 0xe5d6_1325, 0x0e43_a628, 0x16db_d62b, 0x2733_d90b, 0x3ae5_7283, 0xc0c1_052c, 0x4b6f_b620, 0x3751_3953, 0xfc89_8bb3, 0x471b_179f, 0xdf6e_66b8, 0xd321_42f5, 0x9b30_fafc, 0x4ed9_2549, 0x105c_6d99, 0x4acd_69ff, 0x2b1a_27d3, 0x6bfc_c067, 0x6301_a278, 0xad36_e6f2, 0xef3f_f64e, 0x56b3_cadb, 0x0184_bb61, 0x17be_b9fd, 0xfaec_6109, 0xa2e1_ffa1, 0x2fd2_24f8, 0x238f_5be6, 0x8f85_70cf, 0xaeb5_f25a, 0x4f1d_3e64, 0x4377_eb24, 0x1fa4_5346, 0xb205_6386, 0x5209_5e76, 0xbb7b_5adc, 0x3514_e472, 0xdde8_1e6e, 0x7ace_a9c4, 0xac15_cc48, 0x71c9_7d93, 0x767f_941c, 0x9110_52a2, 0xffea_09bf, 0xfe3d_dcf0, 0x15eb_f3aa, 0x9235_b8bc, 0x7540_8615, 0x9a72_3437, 0xe1a1_bd38, 0x3354_1b7e, 0x1bdd_6856, 0xb307_e13e, 0x9081_4bb0, 0x51d7_217b, 0x0bb9_2219, 0x689f_4500, 0xc568_b01f, 0x5df3_d2d7, 0x3c0e_cd0d, 0x2a02_44c8, 0x8525_74e8, 0xe72f_23a9, 0x8e26_ed02, 0x2d92_cbdd, 0xdabc_0458, 0xcdf5_feb6, 0x9e4e_8dcc, 0xf4f1_e344, 0x0d8c_436d, 0x4427_603b, 0xbdd3_7fda, 0x8050_5f26, 0x8c7d_2b8e, 0xb732_73c5, 0x3973_62ea, 0x618a_3811, 0x608b_fb88, 0x06f7_d714, 0x212e_4677, 0x28ef_cead, 0x076c_0371, 0x36a3_a4d9, 0x5487_b455, 0x3429_a365, 0x65d4_67ac, 0x78ee_7eeb, 0x99bf_12b7, 0x4d12_9896, 0x772a_5601, 0xcce2_84c7, 0x2ed8_5c21, 0xd099_e8a4, 0xa179_158a, 0x6ac0_ab1a, 0x299a_4807, 0xbe67_a58d, 0xdc19_544a, 0xb894_9b54, 0x8d31_5779, 0xb6f8_49c1, 0x53c5_ac34, 0x66de_92a5, 0xf195_dd13, 0x318d_3a73, 0x301e_c542, 0x0cc4_0da6, 0xf253_ade4, 0x467e_e566, 0xea55_85ec, 0x3baf_19bb, 0x7de9_f480, 0x7900_6e7c, 0xa9b7_a197, 0xa44b_d8f1, 0xfb2b_a739, 0xec34_2fd4, 0xed4f_d32d, 0x3d17_89ba, 0x400f_5d7f, 0xc798_f594, 0x4506_a847, 0x034c_0a95, 0xe216_2c9d, 0x55a9_cfd0, 0x692d_832e, 0xcf9d_b2ca, 0x5e22_87e9, 0xd261_0ef3, 0x1ae7_ecc2, 0x4839_9ca0, 0xa7e4_269b, 0x6ee3_a0af, 0x7065_bfe1, 0xa6ff_e708, 0x2256_804c, 0x7476_e21b, 0x41b0_796c, 0x7c24_3b05, 0x000a_950f, 0x1858_416b, 0xf5a5_3c89, 0xe9fe_f823, 0x3f44_3275, 0xe0cb_f091, 0x0af2_7b84, 0x3ebb_0f27, 0x1de6_f7f4, 0xc31c_29f7, 0xb166_de3d, 0x1293_2ec3, 0x9c0c_0674, 0x5cda_81b9, 0xd1bd_9d12, 0xaffd_7c82, 0x8962_bca7, 0xa342_c4a8, 0x6245_7151, 0x8208_9f03, 0xeb49_c670, 0x5b5f_6530, 0x7e28_bad2, 0x2088_0ba3, 0xf0fa_afcd, 0xce82_b56f, 0x0275_335c, 0xc18e_8afb, 0xde60_1d69, 0xba9b_820a, 0xc8a2_be4f, 0xd7ca_c335, 0xd9a7_3741, 0x115e_974d, 0x7f5a_c21d, 0x383b_f9c6, 0xbcae_b75f, 0xfd03_50ce, 0xb5d0_6b87, 0x9820_e03c, 0x72d5_f163, 0xe364_4fc9, 0xa546_4c4b, 0x5704_8fcb, 0x9690_c9df, 0xdbf9_eafa, 0xbff4_649a, 0x053c_00e3, 0xb4b6_1136, 0x6759_3dd1, 0x503e_e960, 0x9fb4_993a, 0x1983_1810, 0xc670_d518, 0xb05b_51d8, 0x0f3a_1ce5, 0x6caa_1f9c, 0xaacc_31be, 0x949e_d050, 0x1ead_07e7, 0xa847_9abd, 0xd6cf_fcd5, 0x9369_93ef, ), ( 0x472e_91cb, 0x5444_b5b6, 0x62be_5861, 0x1be1_02c7, 0x63e4_b31e, 0xe81f_71b7, 0x9e23_17c9, 0x39a4_08ae, 0x5180_24f4, 0x1731_c66f, 0x68cb_c918, 0x71fb_0c9e, 0xd03b_7fdd, 0x7d62_22eb, 0x9057_eda3, 0x1a34_a407, 0x8cc2_253d, 0xb6f6_979d, 0x8356_75dc, 0xf319_be9f, 0xbe1c_d743, 0x4d32_fee4, 0x77e7_d887, 0x37e9_ebfd, 0x15f8_51e8, 0x23dc_3706, 0x19d7_8385, 0xbd50_6933, 0xa13a_d4a6, 0x913f_1a0e, 0xdde5_60b9, 0x9a5f_0996, 0xa65a_0435, 0x48d3_4c4d, 0xe908_39a7, 0x8abb_a54e, 0x6fd1_3ce1, 0xc7ee_bd3c, 0x0e29_7602, 0x58b9_bbb4, 0xef79_01e6, 0x64a2_8a62, 0xa509_875a, 0xf883_4442, 0x2702_c709, 0x0735_3f31, 0x3b39_f665, 0xf5b1_8b49, 0x4010_ae37, 0x784d_e00b, 0x7a11_21e9, 0xde91_8ed3, 0xc852_9dcd, 0x816a_5d05, 0x02ed_8298, 0x04e3_dd84, 0xfd2b_c3e2, 0xaf16_7089, 0x96af_367e, 0xa4da_6232, 0x18ff_7325, 0x05f9_a9f1, 0x4fef_b9f9, 0xcd94_eaa5, 0xbfaa_5069, 0xa0b8_c077, 0x60d8_6f57, 0xfe71_c813, 0x29eb_d2c8, 0x4ca8_6538, 0x6bf1_a030, 0xa237_b88a, 0xaa8a_f41d, 0xe1f7_b6ec, 0xe214_d953, 0x3305_7879, 0x49ca_a736, 0xfa45_cff3, 0xc063_b411, 0xba7e_27d0, 0x3153_3819, 0x2a00_4ac1, 0x210e_fc3f, 0x2646_885e, 0x6672_7dcf, 0x9d7f_bf54, 0xa8dd_0ea8, 0x3447_cace, 0x3f0c_14db, 0xb838_2aac, 0x4ace_3539, 0x0a51_8d51, 0x9517_8981, 0x35ae_e2ca, 0x73f0_f7e3, 0x9428_1140, 0x59d0_e523, 0xd292_cb88, 0x565d_1b27, 0x7ec8_fbaf, 0x069a_f08d, 0xc127_fd24, 0x0bc7_7b10, 0x5f03_e7ef, 0x453e_99ba, 0xeed9_ff7f, 0x87b5_5215, 0x7915_ab4c, 0xd389_a358, 0x5e75_ce6d, 0x28d6_55c0, 0xdad2_6c73, 0x2e25_10ff, 0x9fa7_eecc, 0x1d06_29c3, 0xdc9c_9c46, 0x2d67_ecd7, 0xe75e_94bd, 0x3d64_9e2a, 0x6c41_3a2b, 0x706f_0d7c, 0xdfb0_127b, 0x4e36_6b55, 0x2c82_5650, 0x2420_5720, 0xb5c9_98f7, 0x3e95_462c, 0x756e_5c72, 0x3259_488f, 0x11e8_771a, 0xa7c0_a617, 0x5776_63e5, 0x089b_6401, 0x8eab_1941, 0xae55_ef8c, 0x3aac_5460, 0xd4e6_262f, 0x5d97_9a47, 0xb198_23b0, 0x7f8d_6a0c, 0xffa0_8683, 0x0170_cd0f, 0x858c_d5d8, 0x5396_1c90, 0xc4c6_1556, 0x41f2_f226, 0xcfcd_062d, 0xf24c_03b8, 0xea81_df5b, 0x7be2_fa52, 0xb361_f98b, 0xc290_1316, 0x55ba_4bbc, 0x93b2_34a9, 0x0fbc_6603, 0x80a9_6822, 0x6d60_491f, 0x22bd_00f8, 0xbcad_5aad, 0x52f3_f13b, 0x42fd_2b28, 0xb41d_d01c, 0xc52c_93bf, 0xfc66_3094, 0x8f58_d100, 0x43fe_cc08, 0xc633_1e5d, 0xe648_0f66, 0xca84_7204, 0x4bdf_1da0, 0x30cc_2efb, 0x13e0_2dea, 0xfb49_ac45, 0xf9d4_434f, 0xf47c_5b9c, 0x1488_79c2, 0x039f_c234, 0xa3db_9bfc, 0xd1a1_dc5c, 0x763d_7cd4, 0xed6d_2f93, 0xab13_af6e, 0x1e8e_054a, 0xd68f_4f9a, 0xc304_84b3, 0xd7d5_0afa, 0x6930_855f, 0xcc07_db95, 0xce74_6db1, 0x744e_967d, 0xf16c_f575, 0x8643_e8b5, 0xf0ea_e38e, 0xe52d_e1d1, 0x6587_dae0, 0x0c4b_8121, 0x1c7a_c567, 0xac0d_b20a, 0x36c3_a812, 0x5b1a_4514, 0xa9a3_f868, 0xb926_3baa, 0xcb3c_e9d2, 0xe44f_b1a4, 0x9221_bc82, 0xb293_90fe, 0x6ab4_1863, 0x974a_3e2e, 0x89f5_31c5, 0x255c_a13e, 0x8b65_d348, 0xec24_8f78, 0xd8fc_16f0, 0x50ec_deee, 0x0901_0792, 0x3c7d_1fb2, 0xeba5_426b, 0x847b_417a, 0x468b_40d9, 0x8dc4_e680, 0x7cc1_f391, 0x2f1e_b086, 0x6e5b_aa6a, 0xe0b3_95da, 0xe31b_2cf6, 0xd969_0b0d, 0x729e_c464, 0x3840_3dde, 0x610b_80a2, 0x5cf4_33ab, 0xb078_5fc4, 0xd512_e4c6, 0xbbb7_d699, 0x5a86_591b, 0x10cf_5376, 0x12bf_9f4b, 0x980f_baa1, 0x992a_4e70, 0x20fa_7ae7, 0xf799_6ebb, 0xc918_a2be, 0x82de_74f2, 0xad54_209b, 0xf66b_4d74, 0x1fc5_b771, 0x169d_9229, 0x8877_61df, 0x00b6_67d5, 0xdb42_5e59, 0xb72f_2844, 0x9b0a_c1f5, 0x9c73_7e3a, 0x2b85_476c, 0x6722_add6, 0x44a6_3297, 0x0d68_8ced, ), ( 0xabc5_9484, 0x4107_778a, 0x8ad9_4c6f, 0xfe83_df90, 0x0f64_053f, 0xd129_2e9d, 0xc574_4356, 0x8dd1_abb4, 0x4c4e_7667, 0xfb4a_7fc1, 0x74f4_02cb, 0x70f0_6afd, 0xa822_86f2, 0x918d_d076, 0x7a97_c5ce, 0x48f7_bde3, 0x6a04_d11d, 0xac24_3ef7, 0x33ac_10ca, 0x2f7a_341e, 0x5f75_157a, 0xf477_3381, 0x591c_870e, 0x78df_8cc8, 0x22f3_adb0, 0x251a_5993, 0x09fb_ef66, 0x7969_42a8, 0x9754_1d2e, 0x2373_daa9, 0x1bd2_f142, 0xb57e_8eb2, 0xe1a5_bfdb, 0x7d0e_fa92, 0xb344_2c94, 0xd2cb_6447, 0x386a_c97e, 0x66d6_1805, 0xbdad_a15e, 0x11bc_1aa7, 0x14e9_f6ea, 0xe533_a0c0, 0xf935_ee0a, 0x8fee_8a04, 0x810d_6d85, 0x7c68_b6d6, 0x4edc_9aa2, 0x956e_897d, 0xed87_581a, 0x264b_e9d7, 0xff4d_db29, 0x8238_57c2, 0xe005_a9a0, 0xf1cc_2450, 0x6f99_51e1, 0xaade_2310, 0xe70c_75f5, 0x83e1_a31f, 0x4f7d_de8e, 0xf723_b563, 0x368e_0928, 0x8636_2b71, 0x21e8_982d, 0xdfb3_f92b, 0x4467_6352, 0x99ef_ba31, 0x2eab_4e1c, 0xfc6c_a5e7, 0x0ebe_5d4e, 0xa071_7d0c, 0xb64f_8199, 0x946b_31a1, 0x5656_cbc6, 0xcffe_c3ef, 0x6227_66c9, 0xfa21_1e35, 0x52f9_8b89, 0x6d01_674b, 0x4978_a802, 0xf651_f701, 0x15b0_d43d, 0xd6ff_4683, 0x3463_855f, 0x672b_a29c, 0xbc12_8312, 0x4626_a70d, 0xc892_7a5a, 0xb848_1cf9, 0x1c96_2262, 0xa211_96ba, 0xbaba_5ee9, 0x5bb1_62d0, 0x6994_3bd1, 0x0c47_e35c, 0x8cc9_619a, 0xe284_d948, 0x271b_f264, 0xc27f_b398, 0x4bc7_0897, 0x60cf_202c, 0x7f42_d6aa, 0xa5a1_3506, 0x5d3e_8860, 0xcea6_3d3c, 0x63bf_0a8f, 0xf02e_9efa, 0xb17b_0674, 0xb072_b1d3, 0x06e5_723b, 0x3737_e436, 0x24aa_49c7, 0x0ded_0d18, 0xdb25_6b14, 0x58b2_7877, 0xecb4_9f54, 0x6c40_256a, 0x6ea9_2ffb, 0x3906_aa4c, 0xc986_6fd5, 0x4549_323e, 0xa7b8_5fab, 0x1918_cc27, 0x7308_d7b5, 0x1e16_c7ad, 0x7185_0b37, 0x3095_fd78, 0xa63b_70e6, 0xd880_e2ae, 0x3e28_2769, 0xa39b_a6bc, 0x9870_0fa3, 0xf34c_53e8, 0x288a_f426, 0xb99d_930f, 0xf5b9_9df1, 0xe9d0_c8cf, 0x5ac8_405d, 0x50e7_217b, 0x511f_bbbe, 0x2ca2_e639, 0xc020_301b, 0x356d_bc00, 0x8e43_ddb9, 0x4d32_7b4a, 0xf20f_f3ed, 0x1dbb_29bd, 0x43d4_4779, 0xa1b6_8f70, 0x6114_455b, 0xe63d_280b, 0x6bf6_ff65, 0x10fc_39e5, 0x3dae_126e, 0xc1d7_cf11, 0xcb60_b795, 0x1789_d5b3, 0x9bca_36b7, 0x0830_6075, 0x8461_5608, 0x8b3a_0186, 0xe88f_becd, 0x7ba4_7c4d, 0x2de4_4dac, 0x653f_e58d, 0xcca0_b968, 0xd7fa_0e72, 0x9390_1780, 0x1f2c_26cc, 0xae59_5b6b, 0xa9ec_ea9b, 0xe3db_f8c4, 0x319c_c130, 0x1298_1196, 0x01a3_a4de, 0x32c4_54b6, 0x755b_d817, 0x3cd8_71e4, 0xa48b_b8da, 0x02fd_ec09, 0xfd2d_c2e2, 0x9e57_8088, 0x9a9f_916d, 0x4065_fe6c, 0x1853_999e, 0xc779_3f23, 0xdc10_16bb, 0x9693_55ff, 0x7ef2_92f6, 0xcdce_4adc, 0x05e2_4416, 0x85c1_6c46, 0xd441_d37f, 0x57bd_6855, 0x8746_f54f, 0x9ca7_73df, 0x770b_ae22, 0x5482_8413, 0xb75e_4b19, 0x04c3_5c03, 0xbf7c_ca07, 0x2955_c4dd, 0x721d_b041, 0xb239_4f33, 0x03f5_1387, 0x89b7_3c9f, 0x0b17_37f3, 0x07e6_9024, 0x9231_d245, 0x7619_3861, 0x8815_9c15, 0xdeb5_52d9, 0xd976_7e40, 0x20c6_c0c3, 0x4281_977c, 0xf8af_e1e0, 0xd32a_0751, 0x3fc2_7432, 0xddf1_dcc5, 0x6858_1f34, 0x3bcd_5025, 0x0091_b2ee, 0x4aeb_6944, 0x1602_e743, 0xea09_eb58, 0xef0a_2a8b, 0x641e_03a5, 0xeb50_e021, 0x5c8c_cef8, 0x802f_f0b8, 0xd5e3_edfe, 0xc4dd_1b49, 0x5334_cd2a, 0x13f8_2d2f, 0x4745_0c20, 0x55da_fbd2, 0xbec0_c6f4, 0xb45d_7959, 0x3ad3_6e8c, 0x0aa8_ac57, 0x1a3c_8d73, 0xe45a_afb1, 0x9f66_4838, 0xc688_0053, 0xd003_9bbf, 0xee5f_19eb, 0xca00_41d8, 0xbbea_3aaf, 0xda62_8291, 0x9d5c_95d4, 0xadd5_04a6, 0xc39a_b482, 0x5e9e_14a4, 0x2be0_65f0, 0x2a13_fc3a, 0x9052_e8ec, 0xaf6f_5afc, ), ( 0x519a_a8b5, 0xbb30_3da9, 0xe00e_2b10, 0xdfa6_c1db, 0x2e6b_952e, 0xee10_dc23, 0x3793_6d09, 0x1fc4_2e92, 0x39b2_5a9f, 0x13ff_89f4, 0xc8f5_3fea, 0x1850_0bc7, 0x95a0_379d, 0x98f7_51c2, 0x2289_c42f, 0xa21e_4098, 0x6f39_1f41, 0xf27e_7e58, 0x0d0d_f887, 0x4b79_d540, 0x8e84_09aa, 0x71fe_46f8, 0x688a_9b29, 0x3f08_b548, 0x84ab_e03a, 0x5e91_b6c1, 0xfde4_c2ae, 0x251d_0e72, 0x92d4_fee5, 0xf937_1967, 0x9175_108f, 0xe6e8_1835, 0x8c8c_b8ee, 0xb55a_67b3, 0xcef1_38cc, 0x8b25_6268, 0x00d8_15f5, 0xe881_0812, 0x7782_6189, 0xea73_267d, 0x19b9_0f8d, 0x45c3_3bb4, 0x8247_7056, 0xe177_0075, 0x0946_7aa6, 0xa7c6_f54a, 0x7976_8742, 0x61b8_6bca, 0xd664_4a44, 0xe33f_0171, 0xc229_fbcd, 0x41b0_8feb, 0xd190_3e30, 0x65ec_9080, 0x563d_6fbd, 0xf56d_a488, 0xebf6_4cd8, 0x4934_426b, 0x7c85_92fc, 0x6aca_8cf2, 0x1cea_111b, 0x3a57_ee7a, 0xace1_1c0d, 0x9942_d85e, 0xc461_3407, 0xfa8e_643b, 0x327f_c701, 0x4ca9_be82, 0x3352_526d, 0x2c04_7f63, 0xf3a8_f7dd, 0x1a4a_98a8, 0x762e_d4d1, 0x27c7_5008, 0xbdf4_97c0, 0x7a7b_84df, 0x315c_28ab, 0x801f_93e3, 0xf19b_0ca1, 0x8f14_e46a, 0xe48b_a333, 0x9605_e625, 0xf03e_cb60, 0x6038_5f2d, 0x9028_45ba, 0x7f96_d66f, 0x24bf_f05c, 0x2820_730b, 0x9471_33cb, 0xd444_828a, 0xb343_f6f1, 0x0bef_4705, 0x8da5_74f9, 0x01e2_5d6c, 0x1732_793e, 0x4f0f_7b27, 0x364b_7117, 0xb2d1_da77, 0xa6c5_f1e9, 0x574c_a5b1, 0x386a_3076, 0xad68_94d6, 0x1156_d7fa, 0xa48d_1d9a, 0x4794_c0af, 0x150c_0aa0, 0x26d3_48ac, 0x29fd_eabe, 0xa5de_de53, 0x8167_1e8e, 0x594e_e3bf, 0xa96c_56e6, 0x3426_a726, 0xc597_6579, 0xbc22_e5e4, 0xc100_6319, 0xdaaf_dd2a, 0xa1a1_aa83, 0x3bad_d0e7, 0xc3b1_4981, 0xd770_b155, 0xccd7_c693, 0x42e9_44c5, 0x03e0_064f, 0xca95_b4ef, 0x3dee_81c3, 0xfbbc_d98c, 0x1e07_e15b, 0x667c_e949, 0xe7d6_773f, 0x21b6_124b, 0x6b2a_6ef7, 0xd327_8a9c, 0x9a98_8304, 0x75d2_ae9b, 0xfe49_e2ff, 0x9bc2_4f46, 0x74cc_2cf6, 0xa313_9f36, 0x6c9e_f35a, 0x9fc1_dffe, 0x9e5f_acdc, 0xaadc_8bbb, 0x5abd_bc5f, 0x44b3_b390, 0xf754_efa7, 0x5fe3_bdb7, 0x4e59_c886, 0x06a4_c984, 0xa033_8878, 0xcd51_3cd7, 0x63eb_d27e, 0x8aba_80ad, 0x50da_144e, 0x5d9f_4e97, 0x025b_751c, 0x2d58_0200, 0xb6c0_5837, 0x580a_a15d, 0x5402_2a6e, 0xb41a_5415, 0x4863_fab6, 0xb0b7_9957, 0x46d0_d159, 0xdc2b_8650, 0x20a7_bb0c, 0x4a03_2974, 0xec86_36a2, 0x8548_f24c, 0xf6a2_bf16, 0x1088_f4b0, 0x0c2f_3a94, 0x525d_c396, 0x1406_5785, 0x2b4d_ca52, 0x08ae_ed39, 0xabed_fc99, 0xb1db_cf18, 0x87f8_5bbc, 0xae3a_ff61, 0x433c_cd70, 0x5b23_cc64, 0x7b45_3213, 0x5355_c545, 0x9318_ec0a, 0x7869_2d31, 0x0a21_693d, 0xd566_6814, 0x05fb_59d9, 0xc719_85b2, 0x2abb_8e0e, 0xcf6e_6c91, 0xd9cf_e7c6, 0xefe7_132c, 0x9711_ab28, 0x3ce5_2732, 0x12d5_16d2, 0x7209_a0d0, 0xd278_d306, 0x70fa_4b7b, 0x1d40_7dd3, 0xdb0b_eba4, 0xbfd9_7621, 0xa8be_21e1, 0x1b6f_1b66, 0x3065_0dda, 0xba7d_dbb9, 0x7df9_53fb, 0x9d1c_3902, 0xedf0_e8d5, 0xb874_1ae0, 0x0f24_0565, 0x62cd_438b, 0xc616_a924, 0xaf7a_96a3, 0x3536_5538, 0xe583_af4d, 0x7341_5eb8, 0x2317_6a47, 0xfc9c_cee8, 0x7efc_9de2, 0x695e_03cf, 0xf8ce_66d4, 0x88b4_781d, 0x67dd_9c03, 0x3e8f_9e73, 0xc0c9_5c51, 0xbe31_4d22, 0x55aa_0795, 0xcb1b_b011, 0xe980_fdc8, 0x9c62_b7ce, 0xde2d_239e, 0x042c_adf3, 0xffdf_04de, 0x5ce6_a60f, 0xd8c8_31ed, 0xb7b5_b9ec, 0xb9cb_f962, 0xe253_b254, 0x0735_ba1f, 0x16ac_917f, 0xdd60_7c2b, 0x64a3_35c4, 0x4015_9a7c, 0x8692_22f0, 0x6ef2_1769, 0x839d_20a5, 0xd03b_24c9, 0xf412_601e, 0x6d72_a243, 0x0e01_8dfd, 0x89f3_721a, 0xc94f_4134, 0x2f99_2f20, 0x4d87_253c, ), ) def __init__(self, data=b""): self.chunk_size = (16 - self.output_words) * 4 self.h = [0] * self.output_words self.buf = bytearray() self.msg_len = 0 self.finalized = False if data: self.update(data) return def copy(self): other = self.__class__() other.h = list(self.h) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.finalized = self.finalized return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if self.finalized: raise ValueError("hash object already finalized") if not data: return self self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.chunk_size: chunk = bytes(self.buf[:self.chunk_size]) del self.buf[:self.chunk_size] self.process_chunk(chunk) return self def digest(self): c = self.copy() c.finalize() fmt = ">" + str(c.output_words) + "I" return struct.pack(fmt, *c.h) def hexdigest(self): return self.digest().hex() def finalize(self): if self.finalized: return if len(self.buf) > 0: chunk = bytes(self.buf) + b"\x00" * (self.chunk_size - len(self.buf)) self.buf.clear() self.process_chunk(chunk) bit_len = self.msg_len * 8 hi = (bit_len >> 32) & 0xffff_ffff lo = bit_len & 0xffff_ffff input_words = list(self.h) + [0] * (16 - self.output_words) input_words[14] = hi input_words[15] = lo self.h = self.hash512(input_words) self.finalized = True return def process_chunk(self, chunk): words = list(struct.unpack(f">{len(chunk) // 4}I", chunk)) input_words = list(self.h) + words self.h = self.hash512(input_words) return def hash512(self, input_words): def ror32(x, shift): x &= 0xffff_ffff return ((x >> shift) | (x << (32 - shift))) & 0xffff_ffff shift_table = (16, 8, 16, 24) b = input_words[::] for index in range(self.security_level): s0 = self.standard_sboxes[2 * index + 0] s1 = self.standard_sboxes[2 * index + 1] for byte_in_word in range(4): for i in range(16): if i & 0b10 == 0: s = s0 else: s = s1 sbe = s[b[i] & 0xff] b[(i + 15) % 16] ^= sbe b[(i + 1) % 16] ^= sbe shift = shift_table[byte_in_word] for i in range(16): b[i] = ror32(b[i], shift) output = [] output.append(input_words[0] ^ b[15]) output.append(input_words[1] ^ b[14]) output.append(input_words[2] ^ b[13]) output.append(input_words[3] ^ b[12]) if self.output_words == 4: return output output.append(input_words[4] ^ b[11]) output.append(input_words[5] ^ b[10]) output.append(input_words[6] ^ b[9]) output.append(input_words[7] ^ b[8]) if self.output_words == 8: return output raise ValueError("bad output_words") class Snefru128(SnefruBase): output_words = 4 digest_size = 16 class Snefru256(SnefruBase): output_words = 8 digest_size = 32 class StreebogBase: block_size = 64 PI = ( 0xfc, 0xee, 0xdd, 0x11, 0xcf, 0x6e, 0x31, 0x16, 0xfb, 0xc4, 0xfa, 0xda, 0x23, 0xc5, 0x04, 0x4d, 0xe9, 0x77, 0xf0, 0xdb, 0x93, 0x2e, 0x99, 0xba, 0x17, 0x36, 0xf1, 0xbb, 0x14, 0xcd, 0x5f, 0xc1, 0xf9, 0x18, 0x65, 0x5a, 0xe2, 0x5c, 0xef, 0x21, 0x81, 0x1c, 0x3c, 0x42, 0x8b, 0x01, 0x8e, 0x4f, 0x05, 0x84, 0x02, 0xae, 0xe3, 0x6a, 0x8f, 0xa0, 0x06, 0x0b, 0xed, 0x98, 0x7f, 0xd4, 0xd3, 0x1f, 0xeb, 0x34, 0x2c, 0x51, 0xea, 0xc8, 0x48, 0xab, 0xf2, 0x2a, 0x68, 0xa2, 0xfd, 0x3a, 0xce, 0xcc, 0xb5, 0x70, 0x0e, 0x56, 0x08, 0x0c, 0x76, 0x12, 0xbf, 0x72, 0x13, 0x47, 0x9c, 0xb7, 0x5d, 0x87, 0x15, 0xa1, 0x96, 0x29, 0x10, 0x7b, 0x9a, 0xc7, 0xf3, 0x91, 0x78, 0x6f, 0x9d, 0x9e, 0xb2, 0xb1, 0x32, 0x75, 0x19, 0x3d, 0xff, 0x35, 0x8a, 0x7e, 0x6d, 0x54, 0xc6, 0x80, 0xc3, 0xbd, 0x0d, 0x57, 0xdf, 0xf5, 0x24, 0xa9, 0x3e, 0xa8, 0x43, 0xc9, 0xd7, 0x79, 0xd6, 0xf6, 0x7c, 0x22, 0xb9, 0x03, 0xe0, 0x0f, 0xec, 0xde, 0x7a, 0x94, 0xb0, 0xbc, 0xdc, 0xe8, 0x28, 0x50, 0x4e, 0x33, 0x0a, 0x4a, 0xa7, 0x97, 0x60, 0x73, 0x1e, 0x00, 0x62, 0x44, 0x1a, 0xb8, 0x38, 0x82, 0x64, 0x9f, 0x26, 0x41, 0xad, 0x45, 0x46, 0x92, 0x27, 0x5e, 0x55, 0x2f, 0x8c, 0xa3, 0xa5, 0x7d, 0x69, 0xd5, 0x95, 0x3b, 0x07, 0x58, 0xb3, 0x40, 0x86, 0xac, 0x1d, 0xf7, 0x30, 0x37, 0x6b, 0xe4, 0x88, 0xd9, 0xe7, 0x89, 0xe1, 0x1b, 0x83, 0x49, 0x4c, 0x3f, 0xf8, 0xfe, 0x8d, 0x53, 0xaa, 0x90, 0xca, 0xd8, 0x85, 0x61, 0x20, 0x71, 0x67, 0xa4, 0x2d, 0x2b, 0x09, 0x5b, 0xcb, 0x9b, 0x25, 0xd0, 0xbe, 0xe5, 0x6c, 0x52, 0x59, 0xa6, 0x74, 0xd2, 0xe6, 0xf4, 0xb4, 0xc0, 0xd1, 0x66, 0xaf, 0xc2, 0x39, 0x4b, 0x63, 0xb6, ) A_ROWS = ( 0x8e20_faa7_2ba0_b470, 0x4710_7ddd_9b50_5a38, 0xad08_b0e0_c328_2d1c, 0xd804_5870_ef14_980e, 0x6c02_2c38_f90a_4c07, 0x3601_161c_f205_268d, 0x1b8e_0b0e_798c_13c8, 0x8347_8b07_b246_8764, 0xa011_d380_818e_8f40, 0x5086_e740_ce47_c920, 0x2843_fd20_67ad_ea10, 0x14af_f010_bdd8_7508, 0x0ad9_7808_d06c_b404, 0x05e2_3c04_6836_5a02, 0x8c71_1e02_341b_2d01, 0x46b6_0f01_1a83_988e, 0x90da_b52a_387a_e76f, 0x486d_d415_1c3d_fdb9, 0x24b8_6a84_0e90_f0d2, 0x125c_3542_0748_7869, 0x092e_9421_8d24_3cba, 0x8a17_4a9e_c812_1e5d, 0x4585_254f_6409_0fa0, 0xaccc_9ca9_328a_8950, 0x9d4d_f05d_5f66_1451, 0xc0a8_78a0_a133_0aa6, 0x6054_3c50_de97_0553, 0x302a_1e28_6fc5_8ca7, 0x1815_0f14_b9ec_46dd, 0x0c84_890a_d276_23e0, 0x0642_ca05_693b_9f70, 0x0321_658c_ba93_c138, 0x8627_5df0_9ce8_aaa8, 0x439d_a078_4e74_5554, 0xafc0_503c_273a_a42a, 0xd960_281e_9d1d_5215, 0xe230_140f_c080_2984, 0x7118_0a89_6040_9a42, 0xb60c_05ca_3020_4d21, 0x5b06_8c65_1810_a89e, 0x456c_3488_7a38_05b9, 0xac36_1a44_3d1c_8cd2, 0x561b_0d22_900e_4669, 0x2b83_8811_4807_23ba, 0x9bcf_4486_248d_9f5d, 0xc3e9_2243_12c8_c1a0, 0xeffa_11af_0964_ee50, 0xf97d_86d9_8a32_7728, 0xe4fa_2054_a80b_329c, 0x727d_102a_548b_194e, 0x39b0_0815_2acb_8227, 0x9258_0484_15eb_419d, 0x492c_0242_84fb_aec0, 0xaa16_0121_42f3_5760, 0x550b_8e9e_21f7_a530, 0xa48b_474f_9ef5_dc18, 0x70a6_a56e_2440_598e, 0x3853_dc37_1220_a247, 0x1ca7_6e95_0910_51ad, 0x0edd_37c4_8a08_a6d8, 0x07e0_9562_4504_536c, 0x8d70_c431_ac02_a736, 0xc838_6296_5601_dd1b, 0x641c_314b_2b8e_e083, ) C_BYTES = ( bytes.fromhex( "b1085bda1ecadae9ebcb2f81c0657c1f2f6a76432e45d016714eb88d7585c4fc" "4b7ce09192676901a2422a08a460d31505767436cc744d23dd806559f2a64507" ), bytes.fromhex( "6fa3b58aa99d2f1a4fe39d460f70b5d7f3feea720a232b9861d55e0f16b50131" "9ab5176b12d699585cb561c2db0aa7ca55dda21bd7cbcd56e679047021b19bb7" ), bytes.fromhex( "f574dcac2bce2fc70a39fc286a3d843506f15e5f529c1f8bf2ea7514b1297b7b" "d3e20fe490359eb1c1c93a376062db09c2b6f443867adb31991e96f50aba0ab2" ), bytes.fromhex( "ef1fdfb3e81566d2f948e1a05d71e4dd488e857e335c3c7d9d721cad685e353f" "a9d72c82ed03d675d8b71333935203be3453eaa193e837f1220cbebc84e3d12e" ), bytes.fromhex( "4bea6bacad4747999a3f410c6ca923637f151c1f1686104a359e35d7800fffbd" "bfcd1747253af5a3dfff00b723271a167a56a27ea9ea63f5601758fd7c6cfe57" ), bytes.fromhex( "ae4faeae1d3ad3d96fa4c33b7a3039c02d66c4f95142a46c187f9ab49af08ec6" "cffaa6b71c9ab7b40af21f66c2bec6b6bf71c57236904f35fa68407a46647d6e" ), bytes.fromhex( "f4c70e16eeaac5ec51ac86febf240954399ec6c7e6bf87c9d3473e33197a93c9" "0992abc52d822c3706476983284a05043517454ca23c4af38886564d3a14d493" ), bytes.fromhex( "9b1f5b424d93c9a703e7aa020c6e41414eb7f8719c36de1e89b4443b4ddbc49a" "f4892bcb929b069069d18d2bd1a5c42f36acc2355951a8d9a47f0dd4bf02e71e" ), bytes.fromhex( "378f5a541631229b944c9ad8ec165fde3a7d3a1b258942243cd955b7e00d0984" "800a440bdbb2ceb17b2b8a9aa6079c540e38dc92cb1f2a607261445183235adb" ), bytes.fromhex( "abbedea680056f52382ae548b2e4f3f38941e71cff8a78db1fffe18a1b336103" "9fe76702af69334b7a1e6c303b7652f43698fad1153bb6c374b4c7fb98459ced" ), bytes.fromhex( "7bcd9ed0efc889fb3002c6cd635afe94d8fa6bbbebab07612001802114846679" "8a1d71efea48b9caefbacd1d7d476e98dea2594ac06fd85d6bcaa4cd81f32d1b" ), bytes.fromhex( "378ee767f11631bad21380b00449b17acda43c32bcdf1d77f82012d430219f9b" "5d80ef9d1891cc86e71da4aa88e12852faf417d5d9b21b9948bc924af11bd720" ), ) @classmethod def ensure_table(cls): if hasattr(Hash.StreebogBase, "MUL_TABLE"): return mul = [[0] * 256 for _ in range(8)] for i in range(8): for j in range(256): t = 0 p = cls.PI[j] for k in range(8): if p & (1 << k): t ^= cls.A_ROWS[(i << 3) | (7 - k)] # byte swaps because of little-endian (exactly the C code) t = ((t << 8) & 0xff00_ff00_ff00_ff00) | ((t >> 8) & 0x00ff_00ff_00ff_00ff) t = ((t << 16) & 0xffff_0000_ffff_0000) | ((t >> 16) & 0x0000_ffff_0000_ffff) t = ((t << 32) & 0xffff_ffff_0000_0000) | ((t >> 32) & 0x0000_0000_ffff_ffff) mul[i][j] = t Hash.StreebogBase.MUL_TABLE = mul return def __init__(self, data=b""): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf = bytearray() self.msg_len = 0 self.h = b"" self.ensure_table() if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.h = self.h return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) return self def digest(self): c = self.copy() c.finalize() return c.h def hexdigest(self): return self.digest().hex() def compute(self, msg, iv): def xor_bytes(a, b): return bytes(x ^ y for x, y in zip(a, b)) def sum512(a, b): c = bytearray(64) carry = 0 for i in range(63, -1, -1): s = a[i] + b[i] + carry carry = s >> 8 c[i] = s & 0xff return bytes(c) def lps(inp): mul = self.MUL_TABLE out_words = [0] * 8 for i in range(8): t = mul[0][inp[i]] for k in range(1, 8): t ^= mul[k][inp[i | (k << 3)]] out_words[i] = t return b"".join(w.to_bytes(8, "little") for w in out_words) def E(k, m): kk = k mm = m for i in range(12): mm = lps(xor_bytes(mm, kk)) kk = lps(xor_bytes(kk, self.C_BYTES[i])) return xor_bytes(mm, kk) def g(h, m, n): k = lps(xor_bytes(h, n)) e = E(k, m) return xor_bytes(xor_bytes(e, h), m) def tohex512(n): out = bytearray(64) out[56:64] = (n & 0xffff_ffff_ffff_ffff).to_bytes(8, "big") return bytes(out) h = iv N = bytes(64) sigma = bytes(64) size = len(msg) M = msg while size >= 64: m = M[:64][::-1] h = g(h, m, N) N = sum512(N, tohex512(512)) sigma = sum512(sigma, m) M = M[64:] size -= 64 m = bytearray(64) m[64 - size - 1] = 1 m[64 - size:] = M[::-1] m = bytes(m) h = g(h, m, N) N = sum512(N, tohex512(size * 8)) sigma = sum512(sigma, m) z = bytes(64) h = g(h, N, z) h = g(h, sigma, z) return h class Streebog256(StreebogBase): digest_size = 32 def finalize(self): full = self.compute(bytes(self.buf), bytes([0x01] * 64)) self.h = full[:32][::-1] return class Streebog512(StreebogBase): digest_size = 64 def finalize(self): full = self.compute(bytes(self.buf), bytes(64)) self.h = full[::-1] return class AsconBase: block_size = 8 round_constants = ( 0x0000_0000_0000_00f0, 0x0000_0000_0000_00e1, 0x0000_0000_0000_00d2, 0x0000_0000_0000_00c3, 0x0000_0000_0000_00b4, 0x0000_0000_0000_00a5, 0x0000_0000_0000_0096, 0x0000_0000_0000_0087, 0x0000_0000_0000_0078, 0x0000_0000_0000_0069, 0x0000_0000_0000_005a, 0x0000_0000_0000_004b, ) init_state = (0, 0, 0, 0, 0) b_rounds = 12 digest_size = 32 def __init__(self, data=b""): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.x0, self.x1, self.x2, self.x3, self.x4 = self.init_state self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.x0 = self.x0 other.x1 = self.x1 other.x2 = self.x2 other.x3 = self.x3 other.x4 = self.x4 other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.absorb_block(block) self.permute(self.b_rounds) return self def digest(self, length=None): c = self.copy() c.finalize() if length is None: length = c.digest_size out = c.squeeze(length) return out def hexdigest(self, length=None): return self.digest(length).hex() def finalize(self): m = len(self.buf) if m == 0: block = b"\x80" + b"\x00" * (self.block_size - 1) else: pad_len = self.block_size - m block = bytes(self.buf) + (b"\x80" + b"\x00" * (pad_len - 1)) self.buf.clear() self.absorb_block(block) self.permute(12) return def absorb_block(self, block): if len(block) != self.block_size: raise ValueError("invalid block size") self.x0 ^= int.from_bytes(block, "big") self.x0 &= 0xffff_ffff_ffff_ffff return def squeeze(self, out_len): if not isinstance(out_len, int) or out_len < 0: raise ValueError("out_len must be a non-negative integer") out = bytearray() while len(out) < out_len: out.extend(int(self.x0 & 0xffff_ffff_ffff_ffff).to_bytes(8, "big")) self.permute(self.b_rounds) return bytes(out[:out_len]) def permute(self, rounds): def ror64(x, n): x &= 0xffff_ffff_ffff_ffff n &= 63 return ((x >> n) | ((x << (64 - n)) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff if rounds not in (6, 8, 12): raise ValueError("invalid round count") start = 12 - rounds for r in range(start, 12): self.x2 ^= self.round_constants[r] x0 = self.x0 x1 = self.x1 x2 = self.x2 x3 = self.x3 x4 = self.x4 x0 ^= x4 x4 ^= x3 x2 ^= x1 t0 = (~x0) & x1 t1 = (~x1) & x2 t2 = (~x2) & x3 t3 = (~x3) & x4 t4 = (~x4) & x0 x0 ^= t1 x1 ^= t2 x2 ^= t3 x3 ^= t4 x4 ^= t0 x1 ^= x0 x0 ^= x4 x3 ^= x2 x2 = ~x2 x0 ^= ror64(x0, 19) ^ ror64(x0, 28) x1 ^= ror64(x1, 61) ^ ror64(x1, 39) x2 ^= ror64(x2, 1) ^ ror64(x2, 6) x3 ^= ror64(x3, 10) ^ ror64(x3, 17) x4 ^= ror64(x4, 7) ^ ror64(x4, 41) self.x0 = x0 self.x1 = x1 self.x2 = x2 self.x3 = x3 self.x4 = x4 return class Ascon(AsconBase): digest_size = 32 b_rounds = 12 init_state = ( 0xee93_98aa_db67_f03d, 0x8bb2_1831_c60f_1002, 0xb48a_92db_98d5_da62, 0x4318_9921_b8f8_e3e8, 0x348f_a5c9_d525_e140, ) class AsconA(AsconBase): digest_size = 32 b_rounds = 8 init_state = ( 0x0147_0194_fc65_28a6, 0x738e_c38a_c0ad_ffa7, 0x2ec8_e329_6c76_384c, 0xd6f6_a54d_7f52_377d, 0xa13c_42a2_23be_8d87, ) class AsconX(AsconBase): digest_size = 128 b_rounds = 12 init_state = ( 0xb57e_273b_814c_d416, 0x2b51_0425_62ae_2420, 0x66a3_a776_8ddf_2218, 0x5aad_0a7a_8153_650c, 0x4f3e_0e32_5394_93b6, ) class AsconXA(AsconBase): digest_size = 128 b_rounds = 8 init_state = ( 0x4490_6568_b77b_9832, 0xcd8d_6cae_5345_5532, 0xf7b5_2127_5642_2129, 0x2468_85e1_de0d_225b, 0xa8cb_5ce3_3449_973f, ) class RSHash: digest_size = 4 def __init__(self, data=b""): self.b = 378551 self.a = 63689 self.hash = 0 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for c in bytes(data): self.hash = ((self.hash * self.a) + c) & 0xffff_ffff self.a = (self.a * self.b) & 0xffff_ffff return self def digest(self): return self.hash.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class JSHash: digest_size = 4 def __init__(self, data=b""): self.hash = 1315423911 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for c in bytes(data): self.hash ^= ((self.hash << 5) + c + (self.hash >> 2)) & 0xffff_ffff return self def digest(self): return self.hash.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class PJWHash: digest_size = 4 def __init__(self, data=b""): self.hash = 0 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for c in bytes(data): self.hash = ((self.hash << 4) + c) & 0xffff_ffff high = self.hash & 0xf000_0000 if high: self.hash = (self.hash ^ (high >> 24)) & 0x0fff_ffff return self def digest(self): return self.hash.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class ELFHash: digest_size = 4 def __init__(self, data=b""): self.hash = 0 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for c in bytes(data): self.hash = ((self.hash << 4) + c) & 0xffff_ffff high = self.hash & 0xf000_0000 if high: self.hash = (self.hash ^ (high >> 24)) & ~high return self def digest(self): return self.hash.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class BKDRHash: digest_size = 4 def __init__(self, data=b""): self.seed = 131 self.hash = 0 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for c in bytes(data): self.hash = ((self.hash * self.seed) + c) & 0xffff_ffff return self def digest(self): return self.hash.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class SDBMHash: digest_size = 4 def __init__(self, data=b""): self.hash = 0 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for c in bytes(data): self.hash = (c + (self.hash << 6) + (self.hash << 16) - self.hash) & 0xffff_ffff return self def digest(self): return self.hash.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class DJB2Hash: digest_size = 4 def __init__(self, data=b""): self.hash = 5381 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for c in bytes(data): self.hash = (((self.hash << 5) + self.hash) + c) & 0xffff_ffff return self def digest(self): return self.hash.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class DEKHash: digest_size = 4 def __init__(self, data=b""): self.buf = bytearray() if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf += data return self def digest(self): h = len(self.buf) for c in self.buf: h = ((h << 5) ^ (h >> 27) ^ c) & 0xffff_ffff return h.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class APHash: digest_size = 4 def __init__(self, data=b""): self.hash = 0xaaaa_aaaa if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for i, c in enumerate(bytes(data)): if (i & 1) == 0: self.hash ^= (self.hash << 7) ^ (c * (self.hash >> 3)) else: self.hash ^= ~((self.hash << 11) + (c ^ (self.hash >> 5))) self.hash &= 0xffff_ffff return self def digest(self): return self.hash.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class JOAAT: # Jenkins One-At-A-Time digest_size = 4 def __init__(self, data=b""): self.hash = 0 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for c in bytes(data): self.hash = (self.hash + c) & 0xffff_ffff self.hash = (self.hash + (self.hash << 10)) & 0xffff_ffff self.hash = (self.hash ^ (self.hash >> 6)) & 0xffff_ffff return self def digest(self): h = self.hash h = (h + (h << 3)) & 0xffff_ffff h = (h ^ (h >> 11)) & 0xffff_ffff h = (h + (h << 15)) & 0xffff_ffff return h.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class Adler: digest_size = 4 def __init__(self, data=b""): self.a = 1 self.b = 0 if data: self.update(data) return def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") for d in data: self.a = (self.a + d) % 65521 self.b = (self.a + self.b) % 65521 return def digest(self): h = (self.b << 16) + self.a return h.to_bytes(4, "big") def hexdigest(self): return self.digest().hex() class T1HABase: block_size = 32 digest_size = 8 prime_0 = 0xec99_bf0d_8372_caab prime_1 = 0x8243_4fe9_0edc_ef39 prime_2 = 0xd4f0_6db9_9d67_be4b prime_3 = 0xbd9c_acc2_2c6e_9571 prime_4 = 0x9c06_faf4_d023_e3ab prime_5 = 0xc060_724a_8424_f345 prime_6 = 0xcb5a_f53a_e3aa_ac31 def __init__(self, data=b"", seed=0): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.seed = seed & 0xffff_ffff_ffff_ffff self.buf = bytearray() if data: self.update(data) return def copy(self): other = self.__class__(seed=self.seed) other.buf = bytearray(self.buf) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf.extend(data) return self def digest(self): return self.digest_bytes(bytes(self.buf)) def hexdigest(self): return self.digest().hex() def ror32(self, x, n): x &= 0xffff_ffff return ((x >> n) | ((x << (32 - n)) & 0xffff_ffff)) & 0xffff_ffff def ror64(self, x, n): x &= 0xffff_ffff_ffff_ffff return ((x >> n) | ((x << (64 - n)) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff def u32le(self, data, off): return int.from_bytes(data[off:off + 4], "little") def u64le(self, data, off): return int.from_bytes(data[off:off + 8], "little") def mul64x64_128(self, a, b): p = (a & 0xffff_ffff_ffff_ffff) * (b & 0xffff_ffff_ffff_ffff) lo = p & 0xffff_ffff_ffff_ffff hi = (p >> 64) & 0xffff_ffff_ffff_ffff return lo, hi def mux64(self, v, prime): lo, hi = self.mul64x64_128(v, prime) return lo ^ hi def mixup64(self, a, b, v, prime): lo, hi = self.mul64x64_128(b + v, prime) a ^= lo b = (b + hi) & 0xffff_ffff_ffff_ffff return a & 0xffff_ffff_ffff_ffff, b def t1ha2_update(self, a, b, c, d, w0, w1, w2, w3): d02 = (w0 + self.ror64(w2 + d, 56)) & 0xffff_ffff_ffff_ffff c13 = (w1 + self.ror64(w3 + c, 19)) & 0xffff_ffff_ffff_ffff d ^= (b + self.ror64(w1, 38)) & 0xffff_ffff_ffff_ffff c ^= (a + self.ror64(w0, 57)) & 0xffff_ffff_ffff_ffff b ^= (self.prime_6 * ((c13 + w2) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff a ^= (self.prime_5 * ((d02 + w3) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff return a, b, c, d class T1HA0_32(T1HABase): block_size = 16 digest_size = 4 prime32_0 = 0x92d7_8269 prime32_1 = 0xca9b_4735 prime32_2 = 0xa4ab_a1c3 prime32_3 = 0xf649_9843 prime32_4 = 0x86f0_fd61 prime32_5 = 0xca2d_a6fb prime32_6 = 0xc4bb_3575 def mixup32(self, a, b, v, prime): p = ((b + v) & 0xffff_ffff) * prime a ^= p & 0xffff_ffff b = (b + ((p >> 32) & 0xffff_ffff)) & 0xffff_ffff return a & 0xffff_ffff, b def t1ha0_32le(self, data, seed): length = len(data) a = (self.ror32(length, 17) + (seed & 0xffff_ffff)) & 0xffff_ffff b = ((length & 0xffff_ffff) ^ ((seed >> 32) & 0xffff_ffff)) & 0xffff_ffff off = 0 if length > 16: c = (~a) & 0xffff_ffff d = self.ror32(b, 5) detent = length - 15 while off < detent: w0 = self.u32le(data, off + 0) w1 = self.u32le(data, off + 4) w2 = self.u32le(data, off + 8) w3 = self.u32le(data, off + 12) off += 16 d13 = (w1 + self.ror32(w3 + d, 17)) & 0xffff_ffff c02 = (w0 ^ self.ror32(w2 + c, 11)) & 0xffff_ffff d ^= self.ror32(a + w0, 3) c ^= self.ror32(b + w1, 7) b = (self.prime32_1 * ((c02 + w3) & 0xffff_ffff)) & 0xffff_ffff a = (self.prime32_0 * (d13 ^ w2)) & 0xffff_ffff c = (c + a) & 0xffff_ffff d = (d + b) & 0xffff_ffff a ^= (self.prime32_6 * ((self.ror32(c, 16) + d) & 0xffff_ffff)) & 0xffff_ffff b ^= (self.prime32_5 * ((self.ror32(d, 16) + c) & 0xffff_ffff)) & 0xffff_ffff tail = length & 15 else: tail = length tail_off = off if tail > 12: v = self.u32le(data, tail_off) a, b = self.mixup32(a, b, v, self.prime32_4) tail_off += 4 if tail > 8: v = self.u32le(data, tail_off) b, a = self.mixup32(b, a, v, self.prime32_3) tail_off += 4 if tail > 4: v = self.u32le(data, tail_off) a, b = self.mixup32(a, b, v, self.prime32_2) tail_off += 4 if tail != 0: v = self.u32le(data + b"\0" * (4 - (tail & 3)), tail_off) b, a = self.mixup32(b, a, v, self.prime32_1) return self.final32(a, b) def final32(self, a, b): l = ((b ^ self.ror32(a, 13)) | ((a & 0xffff_ffff) << 32)) & 0xffff_ffff_ffff_ffff # noqa: E741 l = (l * self.prime_0) & 0xffff_ffff_ffff_ffff # noqa: E741 l ^= l >> 41 # noqa: E741 l = (l * self.prime_4) & 0xffff_ffff_ffff_ffff # noqa: E741 l ^= l >> 47 # noqa: E741 l = (l * self.prime_6) & 0xffff_ffff_ffff_ffff # noqa: E741 return l def digest_bytes(self, data): v = self.t1ha0_32le(data, self.seed) & 0xffff_ffff return struct.pack(">I", v) class T1HA1_64(T1HABase): block_size = 32 digest_size = 8 def t1ha1_le(self, data, seed): length = len(data) a = seed & 0xffff_ffff_ffff_ffff b = length & 0xffff_ffff_ffff_ffff off = 0 tail = length if tail > 32: c = (self.ror64(tail, 17) + seed) & 0xffff_ffff_ffff_ffff d = self.ror64(seed, 17) ^ tail detent = tail - 31 while off < detent: w0 = self.u64le(data, off + 0) w1 = self.u64le(data, off + 8) w2 = self.u64le(data, off + 16) w3 = self.u64le(data, off + 24) off += 32 d02 = w0 ^ self.ror64(w2 + d, 17) c13 = w1 ^ self.ror64(w3 + c, 17) d = (d - (b ^ self.ror64(w1, 31))) & 0xffff_ffff_ffff_ffff c = (c + (a ^ self.ror64(w0, 41))) & 0xffff_ffff_ffff_ffff b ^= (self.prime_0 * ((c13 + w2) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff a ^= (self.prime_1 * ((d02 + w3) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff a ^= (self.prime_6 * ((self.ror64(c, 17) + d) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff b ^= (self.prime_5 * ((self.ror64(d, 17) + c) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff tail &= 31 tail_off = off if tail > 24: b = (b + self.mux64(self.u64le(data, tail_off), self.prime_4)) & 0xffff_ffff_ffff_ffff tail_off += 8 if tail > 16: a = (a + self.mux64(self.u64le(data, tail_off), self.prime_3)) & 0xffff_ffff_ffff_ffff tail_off += 8 if tail > 8: b = (b + self.mux64(self.u64le(data, tail_off), self.prime_2)) & 0xffff_ffff_ffff_ffff tail_off += 8 if tail != 0: a = (a + self.mux64(self.u64le(data + b"\0" * (8 - (tail & 7)), tail_off), self.prime_1)) & 0xffff_ffff_ffff_ffff return self.final_weak_avalanche(a, b) def mix64(self, v, p): v = (v * p) & 0xffff_ffff_ffff_ffff return (v ^ self.ror64(v, 41)) & 0xffff_ffff_ffff_ffff def final_weak_avalanche(self, a, b): r = self.mux64(self.ror64(a + b, 17), self.prime_4) r = (r + self.mix64(a ^ b, self.prime_0)) & 0xffff_ffff_ffff_ffff return r def digest_bytes(self, data): v = self.t1ha1_le(data, self.seed) return struct.pack(">Q", v) class T1HA2_64(T1HABase): block_size = 32 digest_size = 8 def t1ha2_squash(self, a, b, c, d): a ^= (self.prime_6 * ((self.ror64(d, 23) + c) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff b ^= (self.prime_5 * ((self.ror64(c, 19) + d) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff return a, b def t1ha2_atonce(self, data, seed): length = len(data) a = seed & 0xffff_ffff_ffff_ffff b = length & 0xffff_ffff_ffff_ffff off = 0 if length > 32: c = (self.ror64(length, 23) + ((~seed) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff d = (((~length) & 0xffff_ffff_ffff_ffff) + self.ror64(seed, 19)) & 0xffff_ffff_ffff_ffff detent = length - 31 while off < detent: w0 = self.u64le(data, off + 0) w1 = self.u64le(data, off + 8) w2 = self.u64le(data, off + 16) w3 = self.u64le(data, off + 24) a, b, c, d = self.t1ha2_update(a, b, c, d, w0, w1, w2, w3) off += 32 a, b = self.t1ha2_squash(a, b, c, d) tail = length & 31 else: tail = length tail_off = off if tail > 24: w = self.u64le(data, tail_off) a, b = self.mixup64(a, b, w, self.prime_4) tail_off += 8 if tail > 16: w = self.u64le(data, tail_off) b, a = self.mixup64(b, a, w, self.prime_3) tail_off += 8 if tail > 8: w = self.u64le(data, tail_off) a, b = self.mixup64(a, b, w, self.prime_2) tail_off += 8 if tail != 0: w = self.u64le(data + b"\0" * (8 - (tail & 7)), tail_off) b, a = self.mixup64(b, a, w, self.prime_1) return self.final64(a, b) def final64(self, a, b): x = ((self.ror64(b, 41) + a) & 0xffff_ffff_ffff_ffff) * self.prime_0 y = ((self.ror64(a, 23) + b) & 0xffff_ffff_ffff_ffff) * self.prime_6 return self.mux64((x ^ y) & 0xffff_ffff_ffff_ffff, self.prime_5) & 0xffff_ffff_ffff_ffff def digest_bytes(self, data): v = self.t1ha2_atonce(data, self.seed) return struct.pack(">Q", v) class T1HA2_128(T1HABase): block_size = 32 digest_size = 16 def t1ha2_atonce128(self, data, seed): length = len(data) a = seed & 0xffff_ffff_ffff_ffff b = length & 0xffff_ffff_ffff_ffff c = (self.ror64(length, 23) + ((~seed) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff d = (((~length) & 0xffff_ffff_ffff_ffff) + self.ror64(seed, 19)) & 0xffff_ffff_ffff_ffff off = 0 if length > 32: detent = length - 31 while off < detent: w0 = self.u64le(data, off + 0) w1 = self.u64le(data, off + 8) w2 = self.u64le(data, off + 16) w3 = self.u64le(data, off + 24) a, b, c, d = self.t1ha2_update(a, b, c, d, w0, w1, w2, w3) off += 32 tail = length & 31 else: tail = length tail_off = off if tail > 24: w = self.u64le(data, tail_off) a, d = self.mixup64(a, d, w, self.prime_4) tail_off += 8 if tail > 16: w = self.u64le(data, tail_off) b, a = self.mixup64(b, a, w, self.prime_3) tail_off += 8 if tail > 8: w = self.u64le(data, tail_off) c, b = self.mixup64(c, b, w, self.prime_2) tail_off += 8 if tail != 0: w = self.u64le(data + b"\0" * (8 - (tail & 7)), tail_off) d, c = self.mixup64(d, c, w, self.prime_1) return self.t1ha2_final128(a, b, c, d) def t1ha2_final128(self, a, b, c, d): a, b = self.mixup64(a, b, self.ror64(c, 41) ^ d, self.prime_0) b, c = self.mixup64(b, c, self.ror64(d, 23) ^ a, self.prime_6) c, d = self.mixup64(c, d, self.ror64(a, 19) ^ b, self.prime_5) d, a = self.mixup64(d, a, self.ror64(b, 31) ^ c, self.prime_4) hi = (c + d) & 0xffff_ffff_ffff_ffff lo = (a ^ b) & 0xffff_ffff_ffff_ffff return lo, hi def digest_bytes(self, data): lo, hi = self.t1ha2_atonce128(data, self.seed) return struct.pack(">QQ", hi, lo) class KupynaBase: matrix = (0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04) s0 = ( 0xa8, 0x43, 0x5f, 0x06, 0x6b, 0x75, 0x6c, 0x59, 0x71, 0xdf, 0x87, 0x95, 0x17, 0xf0, 0xd8, 0x09, 0x6d, 0xf3, 0x1d, 0xcb, 0xc9, 0x4d, 0x2c, 0xaf, 0x79, 0xe0, 0x97, 0xfd, 0x6f, 0x4b, 0x45, 0x39, 0x3e, 0xdd, 0xa3, 0x4f, 0xb4, 0xb6, 0x9a, 0x0e, 0x1f, 0xbf, 0x15, 0xe1, 0x49, 0xd2, 0x93, 0xc6, 0x92, 0x72, 0x9e, 0x61, 0xd1, 0x63, 0xfa, 0xee, 0xf4, 0x19, 0xd5, 0xad, 0x58, 0xa4, 0xbb, 0xa1, 0xdc, 0xf2, 0x83, 0x37, 0x42, 0xe4, 0x7a, 0x32, 0x9c, 0xcc, 0xab, 0x4a, 0x8f, 0x6e, 0x04, 0x27, 0x2e, 0xe7, 0xe2, 0x5a, 0x96, 0x16, 0x23, 0x2b, 0xc2, 0x65, 0x66, 0x0f, 0xbc, 0xa9, 0x47, 0x41, 0x34, 0x48, 0xfc, 0xb7, 0x6a, 0x88, 0xa5, 0x53, 0x86, 0xf9, 0x5b, 0xdb, 0x38, 0x7b, 0xc3, 0x1e, 0x22, 0x33, 0x24, 0x28, 0x36, 0xc7, 0xb2, 0x3b, 0x8e, 0x77, 0xba, 0xf5, 0x14, 0x9f, 0x08, 0x55, 0x9b, 0x4c, 0xfe, 0x60, 0x5c, 0xda, 0x18, 0x46, 0xcd, 0x7d, 0x21, 0xb0, 0x3f, 0x1b, 0x89, 0xff, 0xeb, 0x84, 0x69, 0x3a, 0x9d, 0xd7, 0xd3, 0x70, 0x67, 0x40, 0xb5, 0xde, 0x5d, 0x30, 0x91, 0xb1, 0x78, 0x11, 0x01, 0xe5, 0x00, 0x68, 0x98, 0xa0, 0xc5, 0x02, 0xa6, 0x74, 0x2d, 0x0b, 0xa2, 0x76, 0xb3, 0xbe, 0xce, 0xbd, 0xae, 0xe9, 0x8a, 0x31, 0x1c, 0xec, 0xf1, 0x99, 0x94, 0xaa, 0xf6, 0x26, 0x2f, 0xef, 0xe8, 0x8c, 0x35, 0x03, 0xd4, 0x7f, 0xfb, 0x05, 0xc1, 0x5e, 0x90, 0x20, 0x3d, 0x82, 0xf7, 0xea, 0x0a, 0x0d, 0x7e, 0xf8, 0x50, 0x1a, 0xc4, 0x07, 0x57, 0xb8, 0x3c, 0x62, 0xe3, 0xc8, 0xac, 0x52, 0x64, 0x10, 0xd0, 0xd9, 0x13, 0x0c, 0x12, 0x29, 0x51, 0xb9, 0xcf, 0xd6, 0x73, 0x8d, 0x81, 0x54, 0xc0, 0xed, 0x4e, 0x44, 0xa7, 0x2a, 0x85, 0x25, 0xe6, 0xca, 0x7c, 0x8b, 0x56, 0x80, ) s1 = ( 0xce, 0xbb, 0xeb, 0x92, 0xea, 0xcb, 0x13, 0xc1, 0xe9, 0x3a, 0xd6, 0xb2, 0xd2, 0x90, 0x17, 0xf8, 0x42, 0x15, 0x56, 0xb4, 0x65, 0x1c, 0x88, 0x43, 0xc5, 0x5c, 0x36, 0xba, 0xf5, 0x57, 0x67, 0x8d, 0x31, 0xf6, 0x64, 0x58, 0x9e, 0xf4, 0x22, 0xaa, 0x75, 0x0f, 0x02, 0xb1, 0xdf, 0x6d, 0x73, 0x4d, 0x7c, 0x26, 0x2e, 0xf7, 0x08, 0x5d, 0x44, 0x3e, 0x9f, 0x14, 0xc8, 0xae, 0x54, 0x10, 0xd8, 0xbc, 0x1a, 0x6b, 0x69, 0xf3, 0xbd, 0x33, 0xab, 0xfa, 0xd1, 0x9b, 0x68, 0x4e, 0x16, 0x95, 0x91, 0xee, 0x4c, 0x63, 0x8e, 0x5b, 0xcc, 0x3c, 0x19, 0xa1, 0x81, 0x49, 0x7b, 0xd9, 0x6f, 0x37, 0x60, 0xca, 0xe7, 0x2b, 0x48, 0xfd, 0x96, 0x45, 0xfc, 0x41, 0x12, 0x0d, 0x79, 0xe5, 0x89, 0x8c, 0xe3, 0x20, 0x30, 0xdc, 0xb7, 0x6c, 0x4a, 0xb5, 0x3f, 0x97, 0xd4, 0x62, 0x2d, 0x06, 0xa4, 0xa5, 0x83, 0x5f, 0x2a, 0xda, 0xc9, 0x00, 0x7e, 0xa2, 0x55, 0xbf, 0x11, 0xd5, 0x9c, 0xcf, 0x0e, 0x0a, 0x3d, 0x51, 0x7d, 0x93, 0x1b, 0xfe, 0xc4, 0x47, 0x09, 0x86, 0x0b, 0x8f, 0x9d, 0x6a, 0x07, 0xb9, 0xb0, 0x98, 0x18, 0x32, 0x71, 0x4b, 0xef, 0x3b, 0x70, 0xa0, 0xe4, 0x40, 0xff, 0xc3, 0xa9, 0xe6, 0x78, 0xf9, 0x8b, 0x46, 0x80, 0x1e, 0x38, 0xe1, 0xb8, 0xa8, 0xe0, 0x0c, 0x23, 0x76, 0x1d, 0x25, 0x24, 0x05, 0xf1, 0x6e, 0x94, 0x28, 0x9a, 0x84, 0xe8, 0xa3, 0x4f, 0x77, 0xd3, 0x85, 0xe2, 0x52, 0xf2, 0x82, 0x50, 0x7a, 0x2f, 0x74, 0x53, 0xb3, 0x61, 0xaf, 0x39, 0x35, 0xde, 0xcd, 0x1f, 0x99, 0xac, 0xad, 0x72, 0x2c, 0xdd, 0xd0, 0x87, 0xbe, 0x5e, 0xa6, 0xec, 0x04, 0xc6, 0x03, 0x34, 0xfb, 0xdb, 0x59, 0xb6, 0xc2, 0x01, 0xf0, 0x5a, 0xed, 0xa7, 0x66, 0x21, 0x7f, 0x8a, 0x27, 0xc7, 0xc0, 0x29, 0xd7, ) s2 = ( 0x93, 0xd9, 0x9a, 0xb5, 0x98, 0x22, 0x45, 0xfc, 0xba, 0x6a, 0xdf, 0x02, 0x9f, 0xdc, 0x51, 0x59, 0x4a, 0x17, 0x2b, 0xc2, 0x94, 0xf4, 0xbb, 0xa3, 0x62, 0xe4, 0x71, 0xd4, 0xcd, 0x70, 0x16, 0xe1, 0x49, 0x3c, 0xc0, 0xd8, 0x5c, 0x9b, 0xad, 0x85, 0x53, 0xa1, 0x7a, 0xc8, 0x2d, 0xe0, 0xd1, 0x72, 0xa6, 0x2c, 0xc4, 0xe3, 0x76, 0x78, 0xb7, 0xb4, 0x09, 0x3b, 0x0e, 0x41, 0x4c, 0xde, 0xb2, 0x90, 0x25, 0xa5, 0xd7, 0x03, 0x11, 0x00, 0xc3, 0x2e, 0x92, 0xef, 0x4e, 0x12, 0x9d, 0x7d, 0xcb, 0x35, 0x10, 0xd5, 0x4f, 0x9e, 0x4d, 0xa9, 0x55, 0xc6, 0xd0, 0x7b, 0x18, 0x97, 0xd3, 0x36, 0xe6, 0x48, 0x56, 0x81, 0x8f, 0x77, 0xcc, 0x9c, 0xb9, 0xe2, 0xac, 0xb8, 0x2f, 0x15, 0xa4, 0x7c, 0xda, 0x38, 0x1e, 0x0b, 0x05, 0xd6, 0x14, 0x6e, 0x6c, 0x7e, 0x66, 0xfd, 0xb1, 0xe5, 0x60, 0xaf, 0x5e, 0x33, 0x87, 0xc9, 0xf0, 0x5d, 0x6d, 0x3f, 0x88, 0x8d, 0xc7, 0xf7, 0x1d, 0xe9, 0xec, 0xed, 0x80, 0x29, 0x27, 0xcf, 0x99, 0xa8, 0x50, 0x0f, 0x37, 0x24, 0x28, 0x30, 0x95, 0xd2, 0x3e, 0x5b, 0x40, 0x83, 0xb3, 0x69, 0x57, 0x1f, 0x07, 0x1c, 0x8a, 0xbc, 0x20, 0xeb, 0xce, 0x8e, 0xab, 0xee, 0x31, 0xa2, 0x73, 0xf9, 0xca, 0x3a, 0x1a, 0xfb, 0x0d, 0xc1, 0xfe, 0xfa, 0xf2, 0x6f, 0xbd, 0x96, 0xdd, 0x43, 0x52, 0xb6, 0x08, 0xf3, 0xae, 0xbe, 0x19, 0x89, 0x32, 0x26, 0xb0, 0xea, 0x4b, 0x64, 0x84, 0x82, 0x6b, 0xf5, 0x79, 0xbf, 0x01, 0x5f, 0x75, 0x63, 0x1b, 0x23, 0x3d, 0x68, 0x2a, 0x65, 0xe8, 0x91, 0xf6, 0xff, 0x13, 0x58, 0xf1, 0x47, 0x0a, 0x7f, 0xc5, 0xa7, 0xe7, 0x61, 0x5a, 0x06, 0x46, 0x44, 0x42, 0x04, 0xa0, 0xdb, 0x39, 0x86, 0x54, 0xaa, 0x8c, 0x34, 0x21, 0x8b, 0xf8, 0x0c, 0x74, 0x67, ) s3 = ( 0x68, 0x8d, 0xca, 0x4d, 0x73, 0x4b, 0x4e, 0x2a, 0xd4, 0x52, 0x26, 0xb3, 0x54, 0x1e, 0x19, 0x1f, 0x22, 0x03, 0x46, 0x3d, 0x2d, 0x4a, 0x53, 0x83, 0x13, 0x8a, 0xb7, 0xd5, 0x25, 0x79, 0xf5, 0xbd, 0x58, 0x2f, 0x0d, 0x02, 0xed, 0x51, 0x9e, 0x11, 0xf2, 0x3e, 0x55, 0x5e, 0xd1, 0x16, 0x3c, 0x66, 0x70, 0x5d, 0xf3, 0x45, 0x40, 0xcc, 0xe8, 0x94, 0x56, 0x08, 0xce, 0x1a, 0x3a, 0xd2, 0xe1, 0xdf, 0xb5, 0x38, 0x6e, 0x0e, 0xe5, 0xf4, 0xf9, 0x86, 0xe9, 0x4f, 0xd6, 0x85, 0x23, 0xcf, 0x32, 0x99, 0x31, 0x14, 0xae, 0xee, 0xc8, 0x48, 0xd3, 0x30, 0xa1, 0x92, 0x41, 0xb1, 0x18, 0xc4, 0x2c, 0x71, 0x72, 0x44, 0x15, 0xfd, 0x37, 0xbe, 0x5f, 0xaa, 0x9b, 0x88, 0xd8, 0xab, 0x89, 0x9c, 0xfa, 0x60, 0xea, 0xbc, 0x62, 0x0c, 0x24, 0xa6, 0xa8, 0xec, 0x67, 0x20, 0xdb, 0x7c, 0x28, 0xdd, 0xac, 0x5b, 0x34, 0x7e, 0x10, 0xf1, 0x7b, 0x8f, 0x63, 0xa0, 0x05, 0x9a, 0x43, 0x77, 0x21, 0xbf, 0x27, 0x09, 0xc3, 0x9f, 0xb6, 0xd7, 0x29, 0xc2, 0xeb, 0xc0, 0xa4, 0x8b, 0x8c, 0x1d, 0xfb, 0xff, 0xc1, 0xb2, 0x97, 0x2e, 0xf8, 0x65, 0xf6, 0x75, 0x07, 0x04, 0x49, 0x33, 0xe4, 0xd9, 0xb9, 0xd0, 0x42, 0xc7, 0x6c, 0x90, 0x00, 0x8e, 0x6f, 0x50, 0x01, 0xc5, 0xda, 0x47, 0x3f, 0xcd, 0x69, 0xa2, 0xe2, 0x7a, 0xa7, 0xc6, 0x93, 0x0f, 0x0a, 0x06, 0xe6, 0x2b, 0x96, 0xa3, 0x1c, 0xaf, 0x6a, 0x12, 0x84, 0x39, 0xe7, 0xb0, 0x82, 0xf7, 0xfe, 0x9d, 0x87, 0x5c, 0x81, 0x35, 0xde, 0xb4, 0xa5, 0xfc, 0x80, 0xef, 0xcb, 0xbb, 0x6b, 0x76, 0xba, 0x5a, 0x7d, 0x78, 0x0b, 0x95, 0xe3, 0xad, 0x74, 0x98, 0x3b, 0x36, 0x64, 0x6d, 0xdc, 0xf0, 0x59, 0xa9, 0x4c, 0x17, 0x7f, 0x91, 0xb8, 0xc9, 0x57, 0x1b, 0xe0, 0x61, ) C_TEMPLATE = r""" #include #include static inline uint64_t load64_le(const uint8_t *p) { return ((uint64_t)p[0] ) | ((uint64_t)p[1] << 8) | ((uint64_t)p[2] << 16) | ((uint64_t)p[3] << 24) | ((uint64_t)p[4] << 32) | ((uint64_t)p[5] << 40) | ((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56); } static inline uint64_t sub_word( uint64_t w, const uint8_t *s0, const uint8_t *s1, const uint8_t *s2, const uint8_t *s3) { return ((uint64_t)s0[(w >> 0) & 0xff]) | ((uint64_t)s1[(w >> 8) & 0xff] << 8) | ((uint64_t)s2[(w >> 16) & 0xff] << 16) | ((uint64_t)s3[(w >> 24) & 0xff] << 24) | ((uint64_t)s0[(w >> 32) & 0xff] << 32) | ((uint64_t)s1[(w >> 40) & 0xff] << 40) | ((uint64_t)s2[(w >> 48) & 0xff] << 48) | ((uint64_t)s3[(w >> 56) & 0xff] << 56); } static inline void sub_bytes_u64( uint64_t *s, int columns, const uint8_t *s0, const uint8_t *s1, const uint8_t *s2, const uint8_t *s3) { int col; for (col = 0; col < columns; col++) { s[col] = sub_word(s[col], s0, s1, s2, s3); } } static inline void shift_rows_u64(uint64_t *s, int columns, int shift7) { uint64_t src[16]; int row; int col; memcpy(src, s, (size_t)columns * sizeof(uint64_t)); for (col = 0; col < columns; col++) { s[col] = 0; } for (row = 0; row < 8; row++) { int off = (row == 7) ? shift7 : row; int shift = row * 8; for (col = 0; col < columns; col++) { int dst_col = (col + off) % columns; s[dst_col] |= ((src[col] >> shift) & 0xffULL) << shift; } } } static inline uint64_t mix_word( uint64_t w, const uint8_t *t4, const uint8_t *t5, const uint8_t *t6, const uint8_t *t7, const uint8_t *t8) { uint64_t b0 = (w >> 0) & 0xff; uint64_t b1 = (w >> 8) & 0xff; uint64_t b2 = (w >> 16) & 0xff; uint64_t b3 = (w >> 24) & 0xff; uint64_t b4 = (w >> 32) & 0xff; uint64_t b5 = (w >> 40) & 0xff; uint64_t b6 = (w >> 48) & 0xff; uint64_t b7 = (w >> 56) & 0xff; uint64_t o0 = b0 ^ b1 ^ t5[b2] ^ b3 ^ t8[b4] ^ t6[b5] ^ t7[b6] ^ t4[b7]; uint64_t o1 = t4[b0] ^ b1 ^ b2 ^ t5[b3] ^ b4 ^ t8[b5] ^ t6[b6] ^ t7[b7]; uint64_t o2 = t7[b0] ^ t4[b1] ^ b2 ^ b3 ^ t5[b4] ^ b5 ^ t8[b6] ^ t6[b7]; uint64_t o3 = t6[b0] ^ t7[b1] ^ t4[b2] ^ b3 ^ b4 ^ t5[b5] ^ b6 ^ t8[b7]; uint64_t o4 = t8[b0] ^ t6[b1] ^ t7[b2] ^ t4[b3] ^ b4 ^ b5 ^ t5[b6] ^ b7; uint64_t o5 = b0 ^ t8[b1] ^ t6[b2] ^ t7[b3] ^ t4[b4] ^ b5 ^ b6 ^ t5[b7]; uint64_t o6 = t5[b0] ^ b1 ^ t8[b2] ^ t6[b3] ^ t7[b4] ^ t4[b5] ^ b6 ^ b7; uint64_t o7 = b0 ^ t5[b1] ^ b2 ^ t8[b3] ^ t6[b4] ^ t7[b5] ^ t4[b6] ^ b7; return o0 | (o1 << 8) | (o2 << 16) | (o3 << 24) | (o4 << 32) | (o5 << 40) | (o6 << 48) | (o7 << 56); } static inline void mix_columns_u64( uint64_t *s, int columns, const uint8_t *t4, const uint8_t *t5, const uint8_t *t6, const uint8_t *t7, const uint8_t *t8) { int col; for (col = 0; col < columns; col++) { s[col] = mix_word(s[col], t4, t5, t6, t7, t8); } } static inline void perm_p_u64( uint64_t *s, int columns, int rounds, int shift7, const uint8_t *s0, const uint8_t *s1, const uint8_t *s2, const uint8_t *s3, const uint8_t *t4, const uint8_t *t5, const uint8_t *t6, const uint8_t *t7, const uint8_t *t8) { int rnd; int col; for (rnd = 0; rnd < rounds; rnd++) { for (col = 0; col < columns; col++) { s[col] ^= (uint64_t)((col << 4) ^ rnd); } shift_rows_u64(s, columns, shift7); sub_bytes_u64(s, columns, s0, s1, s2, s3); mix_columns_u64(s, columns, t4, t5, t6, t7, t8); } } static inline void perm_q_u64( uint64_t *s, int columns, int rounds, int shift7, const uint8_t *s0, const uint8_t *s1, const uint8_t *s2, const uint8_t *s3, const uint8_t *t4, const uint8_t *t5, const uint8_t *t6, const uint8_t *t7, const uint8_t *t8) { int rnd; int col; for (rnd = 0; rnd < rounds; rnd++) { uint64_t rc = ((((uint64_t)(((columns - 1) << 4) ^ rnd)) << 56) | 0x00f0f0f0f0f0f0f3ULL); for (col = 0; col < columns; col++) { s[col] += rc; rc -= 0x1000000000000000ULL; } shift_rows_u64(s, columns, shift7); sub_bytes_u64(s, columns, s0, s1, s2, s3); mix_columns_u64(s, columns, t4, t5, t6, t7, t8); } } void kupyna_process_block( uint64_t *state, const uint8_t *block, int columns, int rounds, int shift7, const uint8_t *s0, const uint8_t *s1, const uint8_t *s2, const uint8_t *s3, const uint8_t *t4, const uint8_t *t5, const uint8_t *t6, const uint8_t *t7, const uint8_t *t8) { uint64_t words[16]; uint64_t t1[16]; uint64_t t2[16]; int i; for (i = 0; i < columns; i++) { words[i] = load64_le(block + (i * 8)); t1[i] = state[i] ^ words[i]; t2[i] = words[i]; } perm_p_u64(t1, columns, rounds, shift7, s0, s1, s2, s3, t4, t5, t6, t7, t8); perm_q_u64(t2, columns, rounds, shift7, s0, s1, s2, s3, t4, t5, t6, t7, t8); for (i = 0; i < columns; i++) { state[i] ^= t1[i] ^ t2[i]; } } void kupyna_output_transform( uint64_t *state, int columns, int rounds, int shift7, const uint8_t *s0, const uint8_t *s1, const uint8_t *s2, const uint8_t *s3, const uint8_t *t4, const uint8_t *t5, const uint8_t *t6, const uint8_t *t7, const uint8_t *t8) { uint64_t t[16]; int i; for (i = 0; i < columns; i++) { t[i] = state[i]; } perm_p_u64(t, columns, rounds, shift7, s0, s1, s2, s3, t4, t5, t6, t7, t8); for (i = 0; i < columns; i++) { state[i] ^= t[i]; } } """ DEF_TEMPLATE = r""" void kupyna_process_block( uint64_t *state, const uint8_t *block, int columns, int rounds, int shift7, const uint8_t *s0, const uint8_t *s1, const uint8_t *s2, const uint8_t *s3, const uint8_t *t4, const uint8_t *t5, const uint8_t *t6, const uint8_t *t7, const uint8_t *t8); void kupyna_output_transform( uint64_t *state, int columns, int rounds, int shift7, const uint8_t *s0, const uint8_t *s1, const uint8_t *s2, const uint8_t *s3, const uint8_t *t4, const uint8_t *t5, const uint8_t *t6, const uint8_t *t7, const uint8_t *t8); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.KupynaBase if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if base_class in base_class.cffi_cache: ffi, lib = base_class.cffi_cache[base_class] else: try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) base_class.cffi_cache[base_class] = (ffi, lib) except Exception: self.USE_CFFI = False return # Different for each class def pack_u8(seq): buf = ffi.new("uint8_t[]", len(seq)) for i, v in enumerate(seq): buf[i] = v & 0xff return buf try: c_s0 = pack_u8(self.s0) c_s1 = pack_u8(self.s1) c_s2 = pack_u8(self.s2) c_s3 = pack_u8(self.s3) c_t4 = pack_u8(self.mul_tables[4]) c_t5 = pack_u8(self.mul_tables[5]) c_t6 = pack_u8(self.mul_tables[6]) c_t7 = pack_u8(self.mul_tables[7]) c_t8 = pack_u8(self.mul_tables[8]) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib c_s0 c_s1 c_s2 c_s3 c_t4 c_t5 c_t6 c_t7 c_t8")( ffi, lib, c_s0, c_s1, c_s2, c_s3, c_t4, c_t5, c_t6, c_t7, c_t8, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return @classmethod def ensure_tables(cls): if hasattr(Hash.KupynaBase, "mul_tables"): return def gf_mul(a, b): res = 0 aa = a & 0xff bb = b & 0xff while bb: if bb & 1: res ^= aa aa <<= 1 if aa & 0x100: aa ^= 0x11d aa &= 0x1ff bb >>= 1 return res & 0xff Hash.KupynaBase.mul_tables = { 0x04: tuple(gf_mul(0x04, x) for x in range(256)), 0x05: tuple(gf_mul(0x05, x) for x in range(256)), 0x06: tuple(gf_mul(0x06, x) for x in range(256)), 0x07: tuple(gf_mul(0x07, x) for x in range(256)), 0x08: tuple(gf_mul(0x08, x) for x in range(256)), } return def __init__(self, data=b""): if self.columns == 8: self.shift_offsets = (0, 1, 2, 3, 4, 5, 6, 7) else: self.shift_offsets = (0, 1, 2, 3, 4, 5, 6, 11) self.state = [0] * self.columns self.state[0] = self.block_size self.buf = bytearray() self.msg_len_bits = 0 self.ensure_tables() self.init_cffi_backend() if data: self.update(data) return def copy(self): other = self.__class__() other.state = list(self.state) other.buf = bytearray(self.buf) other.msg_len_bits = self.msg_len_bits return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not data: return self self.msg_len_bits += len(data) * 8 self.buf.extend(data) block_size = self.block_size process_block = self.process_block while len(self.buf) >= block_size: process_block(self.buf[:block_size]) del self.buf[:block_size] return self def digest(self): c = self.copy() c.finalize() out = c.digest_from_state() return out def hexdigest(self): out = self.digest().hex() return out def finalize(self): bit_len = self.msg_len_bits & ((1 << 96) - 1) self.buf.append(0x80) block_size = self.block_size len_pos = block_size - 12 process_block = self.process_block if len(self.buf) > len_pos: while len(self.buf) < block_size: self.buf.append(0x00) process_block(self.buf[:block_size]) del self.buf[:block_size] while len(self.buf) < len_pos: self.buf.append(0x00) self.buf.extend(bit_len.to_bytes(12, "little")) while len(self.buf) >= block_size: process_block(self.buf[:block_size]) del self.buf[:block_size] self.output_transform() return def digest_from_state(self): needed_cols = self.digest_size // 8 start_col = self.columns - needed_cols out = b"".join(self.state[i].to_bytes(8, "little") for i in range(start_col, self.columns)) return out def process_block(self, block): if self.USE_CFFI: state_buf = self.cffi.ffi.new("uint64_t[]", [x & 0xffff_ffff_ffff_ffff for x in self.state]) block_buf = self.cffi.ffi.new("uint8_t[]", bytes(block)) self.cffi.lib.kupyna_process_block( state_buf, block_buf, self.columns, self.rounds, self.shift_offsets[7], self.cffi.c_s0, self.cffi.c_s1, self.cffi.c_s2, self.cffi.c_s3, self.cffi.c_t4, self.cffi.c_t5, self.cffi.c_t6, self.cffi.c_t7, self.cffi.c_t8, ) for i in range(self.columns): self.state[i] = int(state_buf[i]) return columns = self.columns state = self.state words = [0] * columns for i in range(columns): j = i * 8 words[i] = int.from_bytes(block[j:j + 8], "little") t1 = [0] * columns for i in range(columns): t1[i] = state[i] ^ words[i] t2 = list(words) self.perm_p(t1) self.perm_q(t2) for i in range(columns): state[i] ^= t1[i] ^ t2[i] return def output_transform(self): if self.USE_CFFI: state_buf = self.cffi.ffi.new("uint64_t[]", [x & 0xffff_ffff_ffff_ffff for x in self.state]) self.cffi.lib.kupyna_output_transform(state_buf, self.columns, self.rounds, self.shift_offsets[7], self.cffi.c_s0, self.cffi.c_s1, self.cffi.c_s2, self.cffi.c_s3, self.cffi.c_t4, self.cffi.c_t5, self.cffi.c_t6, self.cffi.c_t7, self.cffi.c_t8, ) for i in range(self.columns): self.state[i] = int(state_buf[i]) return t = list(self.state) self.perm_p(t) for i in range(self.columns): self.state[i] ^= t[i] return def perm_p(self, s): for rnd in range(self.rounds): for col in range(self.columns): s[col] ^= (col << 4) ^ rnd self.shift_rows(s) self.sub_bytes(s) self.mix_columns(s) return def perm_q(self, s): for rnd in range(self.rounds): rc = ((((self.columns - 1) << 4) ^ rnd) << 56) | 0x00f0_f0f0_f0f0_f0f3 for col in range(self.columns): s[col] = (s[col] + rc) & 0xffff_ffff_ffff_ffff rc = (rc - 0x1000_0000_0000_0000) & 0xffff_ffff_ffff_ffff self.shift_rows(s) self.sub_bytes(s) self.mix_columns(s) return def sub_bytes(self, s): s0 = self.s0 s1 = self.s1 s2 = self.s2 s3 = self.s3 for col in range(self.columns): w = s[col] s[col] = ( s0[(w >> 0) & 0xff] | (s1[(w >> 8) & 0xff] << 8) | (s2[(w >> 16) & 0xff] << 16) | (s3[(w >> 24) & 0xff] << 24) | (s0[(w >> 32) & 0xff] << 32) | (s1[(w >> 40) & 0xff] << 40) | (s2[(w >> 48) & 0xff] << 48) | (s3[(w >> 56) & 0xff] << 56) ) return def shift_rows(self, s): columns = self.columns offsets = self.shift_offsets src = list(s) for col in range(columns): s[col] = 0 for row in range(8): off = offsets[row] shift = row * 8 for col in range(columns): dst_col = (col + off) % columns s[dst_col] |= ((src[col] >> shift) & 0xff) << shift return def mix_columns(self, s): tables = self.mul_tables t4 = tables[0x04] t5 = tables[0x05] t6 = tables[0x06] t7 = tables[0x07] t8 = tables[0x08] for col in range(self.columns): w = s[col] b0 = (w >> 0) & 0xff b1 = (w >> 8) & 0xff b2 = (w >> 16) & 0xff b3 = (w >> 24) & 0xff b4 = (w >> 32) & 0xff b5 = (w >> 40) & 0xff b6 = (w >> 48) & 0xff b7 = (w >> 56) & 0xff o0 = b0 ^ b1 ^ t5[b2] ^ b3 ^ t8[b4] ^ t6[b5] ^ t7[b6] ^ t4[b7] o1 = t4[b0] ^ b1 ^ b2 ^ t5[b3] ^ b4 ^ t8[b5] ^ t6[b6] ^ t7[b7] o2 = t7[b0] ^ t4[b1] ^ b2 ^ b3 ^ t5[b4] ^ b5 ^ t8[b6] ^ t6[b7] o3 = t6[b0] ^ t7[b1] ^ t4[b2] ^ b3 ^ b4 ^ t5[b5] ^ b6 ^ t8[b7] o4 = t8[b0] ^ t6[b1] ^ t7[b2] ^ t4[b3] ^ b4 ^ b5 ^ t5[b6] ^ b7 o5 = b0 ^ t8[b1] ^ t6[b2] ^ t7[b3] ^ t4[b4] ^ b5 ^ b6 ^ t5[b7] o6 = t5[b0] ^ b1 ^ t8[b2] ^ t6[b3] ^ t7[b4] ^ t4[b5] ^ b6 ^ b7 o7 = b0 ^ t5[b1] ^ b2 ^ t8[b3] ^ t6[b4] ^ t7[b5] ^ t4[b6] ^ b7 s[col] = (o0 | (o1 << 8) | (o2 << 16) | (o3 << 24) | (o4 << 32) | (o5 << 40) | (o6 << 48) | (o7 << 56)) return class Kupyna256(KupynaBase): block_size = 64 digest_size = 32 columns = 8 rounds = 10 class Kupyna384(KupynaBase): block_size = 128 digest_size = 48 columns = 16 rounds = 14 class Kupyna512(KupynaBase): block_size = 128 digest_size = 64 columns = 16 rounds = 14 class CubeHash: C_TEMPLATE = r""" #include void cube_round(uint32_t *s) { int i; for (i = 0; i < 16; i++) s[i + 16] += s[i]; for (i = 0; i < 16; i++) s[i] = (s[i] << 7) | (s[i] >> 25); for (i = 0; i < 8; i++) { uint32_t t = s[i]; s[i] = s[i + 8]; s[i + 8] = t; } for (i = 0; i < 16; i++) s[i] ^= s[i + 16]; { int a[] = {16, 17, 20, 21, 24, 25, 28, 29}; for (i = 0; i < 8; i++) { uint32_t t = s[a[i]]; s[a[i]] = s[a[i] + 2]; s[a[i] + 2] = t; } } for (i = 0; i < 16; i++) s[i + 16] += s[i]; for (i = 0; i < 16; i++) s[i] = (s[i] << 11) | (s[i] >> 21); { int a[] = {0, 1, 2, 3, 8, 9, 10,11}; for (i = 0; i < 8; i++) { uint32_t t = s[a[i]]; s[a[i]] = s[a[i] + 4]; s[a[i] + 4] = t; } } for (i = 0; i < 16; i++) s[i] ^= s[i + 16]; { int a[] = {16, 18, 20, 22, 24, 26, 28, 30}; for (i = 0; i < 8; i++) { uint32_t t = s[a[i]]; s[a[i]] = s[a[i] + 1]; s[a[i] + 1] = t; } } } void cube_transform(uint32_t *s, int n_rounds) { int i; for (i = 0; i < n_rounds; i++) { cube_round(s); } return; } """ DEF_TEMPLATE = r""" void cube_round(uint32_t *s); void cube_transform(uint32_t *s, int n_rounds); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.CubeHash if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib")( ffi, lib, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return def __init__(self, data=b"", params="CubeHash160+16/32+160-256"): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf = bytearray() self.msg_len = 0 self.finalized = False self.set_params(params) self.init_cffi_backend() self.init_state() if data: self.update(data) return def set_params(self, params): if not isinstance(params, str): raise TypeError("params must be str") m = re.fullmatch(r"CubeHash(\d+)\+(\d+)/(\d+)\+(\d+)-(\d+)", params) if not m: raise ValueError("invalid params format") self.i = int(m.group(1)) self.r = int(m.group(2)) self.b = int(m.group(3)) self.f = int(m.group(4)) self.h = int(m.group(5)) if not (0 <= self.i): raise ValueError("i must be >= 0") if not (0 <= self.f): raise ValueError("f must be >= 0") if not (1 <= self.r <= 128): raise ValueError("r must be 1..128") if not (1 <= self.b <= 128): raise ValueError("b must be 1..128") if self.h % 8 != 0 or not (8 <= self.h <= 512): raise ValueError("h must be multiple of 8 and 8..512") if (self.h // 8) > 128: raise ValueError("h/8 too large") self.params = f"CubeHash{self.i}+{self.r}/{self.b}+{self.f}-{self.h}" self.block_size = self.b self.digest_size = self.h // 8 return def init_state(self): if self.USE_CFFI: self.state = self.cffi.ffi.new("uint32_t[32]") else: self.state = [0] * 32 self.state[0] = self.h // 8 self.state[1] = self.b self.state[2] = self.r self.transform(self.i) return def copy(self): other = self.__class__(b"", params=self.params) if self.USE_CFFI: for i in range(32): other.state[i] = self.state[i] else: other.state = list(self.state) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.finalized = self.finalized return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if self.finalized: raise ValueError("already finalized") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.b: block = bytes(self.buf[:self.b]) del self.buf[:self.b] self.xor_block(block) self.transform(self.r) return self def finalize(self): if self.finalized: return self.buf.append(0x80) while (len(self.buf) % self.b) != 0: self.buf.append(0x00) while len(self.buf) >= self.b: block = bytes(self.buf[:self.b]) del self.buf[:self.b] self.xor_block(block) self.transform(self.r) self.state[31] ^= 1 self.transform(self.f) self.finalized = True return def digest(self): c = self.copy() c.finalize() raw = struct.pack("<32I", *[(w & 0xffff_ffff) for w in c.state]) return raw[:self.digest_size] def hexdigest(self): return self.digest().hex() def transform(self, n_rounds): if self.USE_CFFI: self.cffi.lib.cube_transform(self.state, n_rounds) return # --- pure Python --- state = self.state for _ in range(n_rounds): for i in range(16): state[i + 16] = (state[i] + state[i + 16]) & 0xffff_ffff for i in range(16): state[i] = ((state[i] << 7) | (state[i] >> 25)) & 0xffff_ffff for i in range(8): state[i], state[i + 8] = state[i + 8], state[i] for i in range(16): state[i] ^= state[i + 16] for i in [16, 17, 20, 21, 24, 25, 28, 29]: state[i], state[i + 2] = state[i + 2], state[i] for i in range(16): state[i + 16] = (state[i] + state[i + 16]) & 0xffff_ffff for i in range(16): state[i] = ((state[i] << 11) | (state[i] >> 21)) & 0xffff_ffff for i in [0, 1, 2, 3, 8, 9, 10, 11]: state[i], state[i + 4] = state[i + 4], state[i] for i in range(16): state[i] ^= state[i + 16] for i in [16, 18, 20, 22, 24, 26, 28, 30]: state[i], state[i + 1] = state[i + 1], state[i] return def xor_block(self, block): x = self.state n_full = self.b // 4 rem = self.b % 4 if n_full > 0: words = struct.unpack(f"<{n_full}I", block[:n_full * 4]) for w in range(n_full): x[w] ^= words[w] if rem: v = int.from_bytes(block[n_full * 4:], "little") x[n_full] ^= v self.state = x return class ECHOBase: sbox = ( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ) @classmethod def ensure_tables(cls): t0, t1, t2, t3 = [], [], [], [] for i in range(256): s = cls.sbox[i] s2 = ((s << 1) ^ 0x1b) & 0xff if (s & 0x80) else (s << 1) s3 = s2 ^ s t0.append((s2, s, s, s3)) t1.append((s3, s2, s, s)) t2.append((s, s3, s2, s)) t3.append((s, s, s3, s2)) Hash.ECHOBase.T0 = tuple(t0) Hash.ECHOBase.T1 = tuple(t1) Hash.ECHOBase.T2 = tuple(t2) Hash.ECHOBase.T3 = tuple(t3) return def __init__(self, data=b"", salt=b""): self.ensure_tables() if not isinstance(salt, (bytes, bytearray, memoryview)): raise TypeError("salt must be bytes-like") if len(salt) not in (0, 16): raise ValueError("salt must be 16 bytes or empty") if len(salt) == 0: self.salt = bytearray(16) else: self.salt = bytearray(salt) self.buf = bytearray() self.msg_len = 0 self.total_bits = 0 word = self.hash_size.to_bytes(2, "little") + b"\x00" * 14 self.v = bytearray(word * self.cv_words) if data: self.update(data) return def copy(self): other = self.__class__() other.salt = bytearray(self.salt) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.total_bits = self.total_bits other.v = bytearray(self.v) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not data: return self self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] Ci = self.total_bits + self.block_size * 8 self.compress(block, Ci) self.total_bits = Ci return self def digest(self): c = self.copy() c.finalize() return bytes(c.v[:self.digest_size]) def hexdigest(self): return self.digest().hex() def finalize(self): L = self.msg_len * 8 block_bytes = self.block_size pos = len(self.buf) total = self.total_bits + pos * 8 if total != L: raise AssertionError("internal length mismatch") self.buf.append(0x80) pos += 1 processed_extra = False if pos > block_bytes - 18: if pos < block_bytes: self.buf.extend(b"\x00" * (block_bytes - pos)) self.compress(bytes(self.buf[:block_bytes]), total) processed_extra = True self.buf = bytearray() pos = 0 if pos < block_bytes - 18: self.buf.extend(b"\x00" * (block_bytes - 18 - pos)) self.buf.extend(self.hash_size.to_bytes(2, "little")) self.buf.extend(L.to_bytes(16, "little")) if len(self.buf) != block_bytes: raise AssertionError("final block size mismatch") Ci = total if (not processed_extra and L != self.total_bits) else 0 self.compress(bytes(self.buf), Ci) self.buf = bytearray() self.total_bits = total return def compress(self, block, Ci): v_words = [list(self.v[i * 16:(i + 1) * 16]) for i in range(self.cv_words)] m_words = [list(block[i * 16:(i + 1) * 16]) for i in range(self.msg_words)] w = [None] * 16 for i in range(self.cv_words): w[i] = v_words[i][:] for i in range(self.msg_words): w[self.cv_words + i] = m_words[i][:] counter = (Ci & 0xffff_ffff_ffff_ffff) if Ci else 0 for _ in range(self.rounds): counter = self.sub_words(w, counter) self.shift_rows_words(w) self.mix_columns_words(w) out_words = [] if self.cv_words == 4: for r in range(4): tmp = [0] * 16 for j in range(16): tmp[j] = ( v_words[r][j] ^ m_words[r][j] ^ m_words[r + 4][j] ^ m_words[r + 8][j] ^ w[r][j] ^ w[r + 4][j] ^ w[r + 8][j] ^ w[r + 12][j] ) & 0xff out_words.append(tmp) else: for r in range(8): tmp = [0] * 16 for j in range(16): tmp[j] = ( v_words[r][j] ^ m_words[r][j] ^ w[r][j] ^ w[r + 8][j] ) & 0xff out_words.append(tmp) self.v = bytearray(sum(out_words, [])) return def sub_words(self, w, counter): k = counter & 0xffff_ffff_ffff_ffff salt_key = list(self.salt) for i in range(16): k1 = list(k.to_bytes(8, "little") + b"\x00" * 8) w[i] = self.aes_round(w[i], k1) w[i] = self.aes_round(w[i], salt_key) k = (k + 1) & 0xffff_ffff_ffff_ffff return k def aes_round(self, state, rk): t0 = self.T0 t1 = self.T1 t2 = self.T2 t3 = self.T3 v0 = t0[state[0]] v1 = t1[state[5]] v2 = t2[state[10]] v3 = t3[state[15]] c0 = v0[0] ^ v1[0] ^ v2[0] ^ v3[0] ^ rk[0] c1 = v0[1] ^ v1[1] ^ v2[1] ^ v3[1] ^ rk[1] c2 = v0[2] ^ v1[2] ^ v2[2] ^ v3[2] ^ rk[2] c3 = v0[3] ^ v1[3] ^ v2[3] ^ v3[3] ^ rk[3] v0 = t0[state[4]] v1 = t1[state[9]] v2 = t2[state[14]] v3 = t3[state[3]] c4 = v0[0] ^ v1[0] ^ v2[0] ^ v3[0] ^ rk[4] c5 = v0[1] ^ v1[1] ^ v2[1] ^ v3[1] ^ rk[5] c6 = v0[2] ^ v1[2] ^ v2[2] ^ v3[2] ^ rk[6] c7 = v0[3] ^ v1[3] ^ v2[3] ^ v3[3] ^ rk[7] v0 = t0[state[8]] v1 = t1[state[13]] v2 = t2[state[2]] v3 = t3[state[7]] c8 = v0[0] ^ v1[0] ^ v2[0] ^ v3[0] ^ rk[8] c9 = v0[1] ^ v1[1] ^ v2[1] ^ v3[1] ^ rk[9] c10 = v0[2] ^ v1[2] ^ v2[2] ^ v3[2] ^ rk[10] c11 = v0[3] ^ v1[3] ^ v2[3] ^ v3[3] ^ rk[11] v0 = t0[state[12]] v1 = t1[state[1]] v2 = t2[state[6]] v3 = t3[state[11]] c12 = v0[0] ^ v1[0] ^ v2[0] ^ v3[0] ^ rk[12] c13 = v0[1] ^ v1[1] ^ v2[1] ^ v3[1] ^ rk[13] c14 = v0[2] ^ v1[2] ^ v2[2] ^ v3[2] ^ rk[14] c15 = v0[3] ^ v1[3] ^ v2[3] ^ v3[3] ^ rk[15] return [c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15] def shift_rows_words(self, w): w[1], w[5], w[9], w[13] = w[5], w[9], w[13], w[1] w[2], w[6], w[10], w[14] = w[10], w[14], w[2], w[6] w[3], w[7], w[11], w[15] = w[15], w[3], w[7], w[11] return def mix_columns_words(self, w): def xtime8(x): return ((x << 1) ^ 0x1b) & 0xff if (x & 0x80) else (x << 1) def mix_column4(a0, a1, a2, a3): t = (a0 ^ a1 ^ a2 ^ a3) & 0xff u = a0 return ( (a0 ^ t ^ xtime8(a0 ^ a1)) & 0xff, (a1 ^ t ^ xtime8(a1 ^ a2)) & 0xff, (a2 ^ t ^ xtime8(a2 ^ a3)) & 0xff, (a3 ^ t ^ xtime8(a3 ^ u)) & 0xff, ) for col in range(4): base = col * 4 for b in range(16): r0, r1, r2, r3 = mix_column4( w[base][b], w[base + 1][b], w[base + 2][b], w[base + 3][b] ) w[base][b] = r0 w[base + 1][b] = r1 w[base + 2][b] = r2 w[base + 3][b] = r3 return class ECHO224(ECHOBase): block_size = 192 digest_size = 28 hash_size = 224 cv_words = 4 msg_words = 12 rounds = 8 class ECHO256(ECHOBase): block_size = 192 digest_size = 32 hash_size = 256 cv_words = 4 msg_words = 12 rounds = 8 class ECHO384(ECHOBase): block_size = 128 digest_size = 48 hash_size = 384 cv_words = 8 msg_words = 8 rounds = 10 class ECHO512(ECHOBase): block_size = 128 digest_size = 64 hash_size = 512 cv_words = 8 msg_words = 8 rounds = 10 class FSBBase: class FSBPiBin: size_bytes = 0x04_2800 digits = size_bytes * 8 guard_digits = 0x20 c3_over_24 = 0x26_dd04_1d87_8000 a_const = 0xcf_6371 b_const = 0x207e_2da6 k_const = 0x06_8380 sqrt_const = 0x2715 def __init__(self): self.gmpy2 = None try: self.gmpy2 = __import__("gmpy2") except Exception: self.gmpy2 = None return def enable_unlimited_int_to_str(self): try: sys.set_int_max_str_digits(0) except Exception: pass return def make_int(self, x): if self.gmpy2 is None: return int(x) return self.gmpy2.mpz(int(x)) def isqrt(self, x): if self.gmpy2 is None: import math return math.isqrt(int(x)) return self.gmpy2.isqrt(x) def pow10(self, n): if self.gmpy2 is None: return 10 ** int(n) return self.gmpy2.mpz(10) ** int(n) def bs(self, a, b): if b - a == 1: if a == 0: P = self.make_int(1) Q = self.make_int(1) else: k = self.make_int(a) P = (self.make_int(6) * k - self.make_int(5)) * \ (self.make_int(2) * k - self.make_int(1)) * \ (self.make_int(6) * k - self.make_int(1)) Q = k * k * k * self.make_int(self.c3_over_24) T = P * (self.make_int(self.a_const) + self.make_int(self.b_const) * self.make_int(a)) if a & 1: T = -T return P, Q, T m = (a + b) // 2 P1, Q1, T1 = self.bs(a, m) P2, Q2, T2 = self.bs(m, b) P = P1 * P2 Q = Q1 * Q2 T = T1 * Q2 + T2 * P1 return P, Q, T def pi_scaled(self, digits): digits = int(digits) n_terms = digits // 14 + 1 P, Q, T = self.bs(0, n_terms) one = self.pow10(digits) sqrt_10005 = self.isqrt(self.make_int(self.sqrt_const) * one * one) pi_scaled = (self.make_int(self.k_const) * sqrt_10005 * Q) // T return pi_scaled def pi_fractional_digits(self, n_digits): self.enable_unlimited_int_to_str() n_digits = int(n_digits) guard = int(self.guard_digits) digits_total = n_digits + guard x = self.pi_scaled(digits_total) if guard: x = x // self.pow10(guard) s = str(int(x)) if self.gmpy2 is not None else str(x) need = n_digits + 1 if len(s) < need: s = ("0" * (need - len(s))) + s frac = s[1:1 + n_digits] return frac def pack_parity_stream(self, frac_digits): digit_bytes = frac_digits.encode("ascii") out = bytearray(self.size_bytes) pos = 0 for i in range(self.size_bytes): b0 = (digit_bytes[pos + 0] & 1) << 7 b1 = (digit_bytes[pos + 1] & 1) << 6 b2 = (digit_bytes[pos + 2] & 1) << 5 b3 = (digit_bytes[pos + 3] & 1) << 4 b4 = (digit_bytes[pos + 4] & 1) << 3 b5 = (digit_bytes[pos + 5] & 1) << 2 b6 = (digit_bytes[pos + 6] & 1) << 1 b7 = (digit_bytes[pos + 7] & 1) << 0 out[i] = b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 pos += 8 return bytes(out) def make_pi_bin(self): cache_path = os.path.join(GEF_TEMP_DIR, "FSB_hash_pi.bin") try: st = os.stat(cache_path) if st.st_size == self.size_bytes: with open(cache_path, "rb") as f: data = f.read() if len(data) == self.size_bytes: return data except Exception: pass frac = self.pi_fractional_digits(self.digits) data = self.pack_parity_stream(frac) try: with open(cache_path, "wb") as f: f.write(data) except Exception: pass return data def __init__(self, data=b""): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.init_state() self.h = bytearray(self.output_bytes) self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def init_state(self): self.pi_data = self.FSBPiBin().make_pi_bin() if self.block_size is None or self.digest_size is None: raise RuntimeError("invalid parameters") if self.n is None or self.w is None or self.r is None or self.p is None or self.s is None: raise RuntimeError("invalid parameters") if (self.r % 8) != 0: raise RuntimeError("r must be multiple of 8") if (self.p <= self.r) is False: pass if (self.s - self.r) <= 0: raise RuntimeError("s must be greater than r") if ((self.s - self.r) % self.w) != 0: raise RuntimeError("(s-r) must be divisible by w") if (self.r % self.w) != 0: raise RuntimeError("r must be divisible by w") self.output_bytes = self.r // 8 self.shift_message = self.r // self.w self.batch_size = (self.s - self.r) // self.w self.batch_mask = (1 << self.batch_size) - 1 self.n_div_w = self.n // self.w self.p_extra_bits = 8 - (self.p % 8) if self.p_extra_bits == 0: self.p_extra_bits = 8 self.p_mask = (1 << self.p) - 1 self.p_shift_prefix = self.p - self.r size_vectors = (self.p // 8) + 1 vectors_count = self.n // self.r need = vectors_count * size_vectors if len(self.pi_data) < need: raise RuntimeError("FSB_hash_pi.bin is too short for this variant") vecs = [] for i in range(vectors_count): start = i * size_vectors end = start + size_vectors chunk = self.pi_data[start:end] v = int.from_bytes(chunk, "big") >> self.p_extra_bits vecs.append(v) self.vectors = vecs return def copy(self): other = self.__class__() other.h = bytearray(self.h) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) bs = self.block_size while len(self.buf) >= bs: block = bytes(self.buf[:bs]) del self.buf[:bs] self.compress(block) return self def finalize(self): bs = self.block_size bit_len = (self.msg_len * 8) & 0xffff_ffff_ffff_ffff self.buf.append(0x80) while (len(self.buf) % bs) != (bs - 8): self.buf.append(0x00) self.buf.extend(struct.pack(">Q", bit_len)) while len(self.buf) >= bs: block = bytes(self.buf[:bs]) del self.buf[:bs] self.compress(block) return def digest(self): c = self.copy() c.finalize() wh = Hash.Whirlpool(bytes(c.h)) full = wh.digest() return full[: self.digest_size] def hexdigest(self): return self.digest().hex() def compress(self, block): if len(block) != self.block_size: raise ValueError("invalid block length") msg_int = int.from_bytes(block, "big") divided = [0] * self.w for i in range(self.w): shift = (self.w - 1 - i) * self.batch_size divided[i] = (msg_int >> shift) & self.batch_mask state = self.h acc = 0 for i in range(self.w): w_index = (i * self.n_div_w) + state[i] + (divided[i] << self.shift_message) chosen_vec = w_index // self.r shift_value = w_index % self.r v = self.vectors[chosen_vec] if shift_value != 0: v = ((v >> shift_value) | (v << (self.p - shift_value))) & self.p_mask prefix = v >> self.p_shift_prefix acc ^= prefix self.h = bytearray(acc.to_bytes(self.output_bytes, "big")) return class FSB160(FSBBase): block_size = 0x3c digest_size = 0x14 n = 0x14_0000 w = 0x50 r = 0x280 p = 0x28d s = 0x460 class FSB224(FSBBase): block_size = 0x54 digest_size = 0x1c n = 0x1c_0000 w = 0x70 r = 0x380 p = 0x38b s = 0x620 class FSB256(FSBBase): block_size = 0x60 digest_size = 0x20 n = 0x20_0000 w = 0x80 r = 0x400 p = 0x425 s = 0x700 class FSB384(FSBBase): block_size = 0x73 digest_size = 0x30 n = 0x17_0000 w = 0xb8 r = 0x5c0 p = 0x5cb s = 0x958 class FSB512(FSBBase): block_size = 0x9b digest_size = 0x40 n = 0x1f_0000 w = 0xf8 r = 0x7c0 p = 0x7c3 s = 0xc98 class SHAvite3Base: sbox = ( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ) def __init__(self, data=b"", salt=0): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if salt != 0: raise ValueError("only salt=0 is supported") self.h = list(self.iv) self.buf = bytearray(self.block_size) self.ptr = 0 self.count0 = 0 self.count1 = 0 if self.block_size != 0x40: self.count2 = 0 self.count3 = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.h = list(self.h) other.buf = bytearray(self.buf) other.ptr = self.ptr other.count0 = self.count0 other.count1 = self.count1 if self.block_size != 0x40: other.count2 = self.count2 other.count3 = self.count3 return other def rol32(self, x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff def swap32(self, x): x &= 0xffff_ffff return ((x & 0x0000_00ff) << 0x18) | ((x & 0x0000_ff00) << 0x08) | \ ((x & 0x00ff_0000) >> 0x08) | ((x & 0xff00_0000) >> 0x18) def dec32le(self, b, off): return b[off] | (b[off + 1] << 0x08) | (b[off + 2] << 0x10) | (b[off + 3] << 0x18) def enc32le(self, x): return (x & 0xffff_ffff).to_bytes(4, "little") def xtime(self, x): x &= 0xff return ((x << 1) ^ (0x1b if (x & 0x80) else 0)) & 0xff def not32(self, x): return x ^ 0xffff_ffff def init_aes_tables(self): if hasattr(self, "aes0"): return te0 = [0] * 256 for i in range(256): s = self.sbox[i] s2 = self.xtime(s) s3 = s2 ^ s te0[i] = ((s2 << 0x18) | (s << 0x10) | (s << 0x08) | s3) & 0xffff_ffff te1 = [self.rol32(x, 0x08) for x in te0] te2 = [self.rol32(x, 0x10) for x in te0] te3 = [self.rol32(x, 0x18) for x in te0] Hash.SHAvite3Base.aes0 = [self.swap32(x) for x in te0] Hash.SHAvite3Base.aes1 = [self.swap32(x) for x in te3] Hash.SHAvite3Base.aes2 = [self.swap32(x) for x in te2] Hash.SHAvite3Base.aes3 = [self.swap32(x) for x in te1] return def aes_round_nokey(self, x0, x1, x2, x3): self.init_aes_tables() a0 = self.aes0 a1 = self.aes1 a2 = self.aes2 a3 = self.aes3 y0 = a0[(x0 >> 0x00) & 0xff] ^ a1[(x1 >> 0x08) & 0xff] ^ a2[(x2 >> 0x10) & 0xff] ^ a3[(x3 >> 0x18) & 0xff] y1 = a0[(x1 >> 0x00) & 0xff] ^ a1[(x2 >> 0x08) & 0xff] ^ a2[(x3 >> 0x10) & 0xff] ^ a3[(x0 >> 0x18) & 0xff] y2 = a0[(x2 >> 0x00) & 0xff] ^ a1[(x3 >> 0x08) & 0xff] ^ a2[(x0 >> 0x10) & 0xff] ^ a3[(x1 >> 0x18) & 0xff] y3 = a0[(x3 >> 0x00) & 0xff] ^ a1[(x0 >> 0x08) & 0xff] ^ a2[(x1 >> 0x10) & 0xff] ^ a3[(x2 >> 0x18) & 0xff] return y0, y1, y2, y3 def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") off = 0 length = len(data) while length > 0: c_len = self.block_size - self.ptr if c_len > length: c_len = length self.buf[self.ptr:self.ptr + c_len] = data[off:off + c_len] self.ptr += c_len off += c_len length -= c_len if self.ptr == self.block_size: if self.block_size == 64: self.count0 = (self.count0 + 0x200) & 0xffff_ffff if self.count0 == 0: self.count1 = (self.count1 + 1) & 0xffff_ffff self.c256(bytes(self.buf)) else: self.count0 = (self.count0 + 0x400) & 0xffff_ffff if self.count0 == 0: self.count1 = (self.count1 + 1) & 0xffff_ffff if self.count1 == 0: self.count2 = (self.count2 + 1) & 0xffff_ffff if self.count2 == 0: self.count3 = (self.count3 + 1) & 0xffff_ffff self.c512(bytes(self.buf)) self.ptr = 0 return self def c256(self, block): rk = [0] * 0x90 for i in range(16): rk[i] = self.dec32le(block, i * 4) count_injections = { 0x10: ((0, self.count0), (1, self.not32(self.count1))), 0x38: ((1, self.count1), (2, self.not32(self.count0))), 0x54: ((2, self.count1), (3, self.not32(self.count0))), 0x7c: ((0, self.count0), (3, self.not32(self.count1))), } # Key schedule u = 0x10 for _ in range(4): for _ in range(4): x0 = rk[u - 15] x1 = rk[u - 14] x2 = rk[u - 13] x3 = rk[u - 16] x0, x1, x2, x3 = self.aes_round_nokey(x0, x1, x2, x3) rk[u + 0] = x0 ^ rk[u - 4] rk[u + 1] = x1 ^ rk[u - 3] rk[u + 2] = x2 ^ rk[u - 2] rk[u + 3] = x3 ^ rk[u - 1] for off, val in count_injections.get(u, ()): rk[u + off] ^= val u += 4 for _ in range(4): rk[u + 0] = rk[u - 16] ^ rk[u - 3] rk[u + 1] = rk[u - 15] ^ rk[u - 2] rk[u + 2] = rk[u - 14] ^ rk[u - 1] rk[u + 3] = rk[u - 13] ^ rk[u - 0] u += 4 # Compression p = list(self.h) u = 0 for _ in range(6): for lo, hi in ((4, 0), (0, 4)): x0 = p[lo] ^ rk[u] x1 = p[lo + 1] ^ rk[u + 1] x2 = p[lo + 2] ^ rk[u + 2] x3 = p[lo + 3] ^ rk[u + 3] u += 4 for _ in range(2): x0, x1, x2, x3 = self.aes_round_nokey(x0, x1, x2, x3) x0 ^= rk[u] x1 ^= rk[u + 1] x2 ^= rk[u + 2] x3 ^= rk[u + 3] u += 4 x0, x1, x2, x3 = self.aes_round_nokey(x0, x1, x2, x3) p[hi] ^= x0 p[hi + 1] ^= x1 p[hi + 2] ^= x2 p[hi + 3] ^= x3 for i in range(8): self.h[i] ^= p[i] return def c512(self, block): rk = [0] * 0x1c0 for i in range(32): rk[i] = self.dec32le(block, i * 4) count_injections = { 0x020: ((0, self.count0), (1, self.count1), (2, self.count2), (3, self.not32(self.count3))), 0x0a4: ((0, self.count3), (1, self.count2), (2, self.count1), (3, self.not32(self.count0))), 0x13c: ((0, self.count2), (1, self.count3), (2, self.count0), (3, self.not32(self.count1))), 0x1b8: ((0, self.count1), (1, self.count0), (2, self.count3), (3, self.not32(self.count2))), } # Key schedule u = 0x20 while True: for _ in range(4): for _ in range(2): x0 = rk[u - 31] x1 = rk[u - 30] x2 = rk[u - 29] x3 = rk[u - 32] x0, x1, x2, x3 = self.aes_round_nokey(x0, x1, x2, x3) rk[u + 0] = x0 ^ rk[u - 4] rk[u + 1] = x1 ^ rk[u - 3] rk[u + 2] = x2 ^ rk[u - 2] rk[u + 3] = x3 ^ rk[u - 1] for off, val in count_injections.get(u, ()): rk[u + off] ^= val u += 4 if u == 0x1c0: break for _ in range(8): rk[u + 0] = rk[u - 32] ^ rk[u - 7] rk[u + 1] = rk[u - 31] ^ rk[u - 6] rk[u + 2] = rk[u - 30] ^ rk[u - 5] rk[u + 3] = rk[u - 29] ^ rk[u - 4] u += 4 # Compression p = list(self.h) u = 0 for _ in range(14): for lo, hi in ((4, 0), (12, 8)): x0 = p[lo + 0] ^ rk[u + 0] x1 = p[lo + 1] ^ rk[u + 1] x2 = p[lo + 2] ^ rk[u + 2] x3 = p[lo + 3] ^ rk[u + 3] u += 4 for _ in range(3): x0, x1, x2, x3 = self.aes_round_nokey(x0, x1, x2, x3) x0 ^= rk[u + 0] x1 ^= rk[u + 1] x2 ^= rk[u + 2] x3 ^= rk[u + 3] u += 4 x0, x1, x2, x3 = self.aes_round_nokey(x0, x1, x2, x3) p[hi + 0] ^= x0 p[hi + 1] ^= x1 p[hi + 2] ^= x2 p[hi + 3] ^= x3 for j in range(4): p[j], p[j + 4], p[j + 8], p[j + 12] = p[j + 12], p[j], p[j + 4], p[j + 8] for i in range(16): self.h[i] ^= p[i] return def finalize(self): buf = self.buf ptr = self.ptr if self.block_size == 64: self.count0 = (self.count0 + (ptr << 3)) & 0xffff_ffff count0 = self.count0 count1 = self.count1 if ptr == 0: buf[0] = 0x80 for i in range(1, 54): buf[i] = 0 self.count0 = 0 self.count1 = 0 elif ptr < 54: buf[ptr] = 0x80 ptr += 1 for i in range(ptr, 54): buf[i] = 0 else: buf[ptr] = 0x80 ptr += 1 for i in range(ptr, 64): buf[i] = 0 self.c256(bytes(buf)) for i in range(54): buf[i] = 0 self.count0 = 0 self.count1 = 0 buf[54:58] = self.enc32le(count0) buf[58:62] = self.enc32le(count1) out_w = self.digest_size // 4 buf[62] = (out_w << 5) & 0xff buf[63] = (out_w >> 3) & 0xff self.c256(bytes(buf)) out = bytearray() for u in range(out_w): out.extend(self.enc32le(self.h[u])) return bytes(out) self.count0 = (self.count0 + (ptr << 3)) & 0xffff_ffff count0 = self.count0 count1 = self.count1 count2 = self.count2 count3 = self.count3 if ptr == 0: buf[0] = 0x80 for i in range(1, 110): buf[i] = 0 self.count0 = 0 self.count1 = 0 self.count2 = 0 self.count3 = 0 elif ptr < 110: buf[ptr] = 0x80 ptr += 1 for i in range(ptr, 110): buf[i] = 0 else: buf[ptr] = 0x80 ptr += 1 for i in range(ptr, 0x80): buf[i] = 0 self.c512(bytes(buf)) for i in range(110): buf[i] = 0 self.count0 = 0 self.count1 = 0 self.count2 = 0 self.count3 = 0 buf[110:114] = self.enc32le(count0) buf[114:118] = self.enc32le(count1) buf[118:122] = self.enc32le(count2) buf[122:126] = self.enc32le(count3) out_w = self.digest_size // 4 buf[126] = (out_w << 5) & 0xff buf[127] = (out_w >> 3) & 0xff self.c512(bytes(buf)) out = bytearray() for u in range(out_w): out.extend(self.enc32le(self.h[u])) return bytes(out) def digest(self): c = self.copy() return c.finalize() def hexdigest(self): return self.digest().hex() class SHAvite3_224(SHAvite3Base): block_size = 0x40 digest_size = 0x1c iv = ( 0x6774_f31c, 0x990a_e210, 0xc87d_4274, 0xc954_6371, 0x62b2_aea8, 0x4b58_01d8, 0x1b70_2860, 0x842f_3017, ) class SHAvite3_256(SHAvite3Base): block_size = 0x40 digest_size = 0x20 iv = ( 0x49bb_3e47, 0x2674_860d, 0xa8b3_92ac, 0x021a_c4e6, 0x4092_83cf, 0x620e_5d86, 0x6d92_9dcb, 0x96cc_2a8b, ) class SHAvite3_384(SHAvite3Base): block_size = 0x80 digest_size = 0x30 iv = ( 0x83df_1545, 0xf9aa_ec13, 0xf480_3cb0, 0x11fe_1f47, 0xda6c_d269, 0x4f53_fcd7, 0x9505_29a2, 0x9790_8147, 0xb0a4_d7af, 0x2b91_32bf, 0x226e_607d, 0x3c0f_8d7c, 0x487b_3f0f, 0x0436_3e22, 0x0155_c99c, 0xec2e_20d3, ) class SHAvite3_512(SHAvite3Base): block_size = 0x80 digest_size = 0x40 iv = ( 0x72fc_cdd8, 0x79ca_4727, 0x128a_077b, 0x40d5_5aec, 0xd190_1a06, 0x430a_e307, 0xb29f_5cd1, 0xdf07_fbfc, 0x8e45_d73d, 0x681a_b538, 0xbde8_6578, 0xdd57_7e47, 0xe275_eade, 0x502d_9fcd, 0xb935_7178, 0x022a_4b9a, ) class LaneBase: sbox = ( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ) @classmethod def ensure_tables(self): if hasattr(Hash.LaneBase, "k_table"): return k = [0] * 768 k[0] = 0x07fc_703d for i in range(1, 768): prev = k[i - 1] v = prev >> 1 if prev & 1: v ^= 0xd000_0001 k[i] = v & 0xffff_ffff Hash.LaneBase.k_table = tuple(k) kb = [0] * (768 * 4) p = 0 for word in k: kb[p] = (word >> 24) & 0xff kb[p + 1] = (word >> 16) & 0xff kb[p + 2] = (word >> 8) & 0xff kb[p + 3] = word & 0xff p += 4 Hash.LaneBase.k_bytes = tuple(kb) m2 = [0] * 256 m3 = [0] * 256 for a in range(256): b = a << 1 if a & 0x80: b ^= 0x11b b &= 0xff m2[a] = b m3[a] = b ^ a Hash.LaneBase.mul2_table = tuple(m2) Hash.LaneBase.mul3_table = tuple(m3) return def __init__(self, data=b''): self.hashbitlen = self.digest_size * 8 self.databitcount = 0 self.finalized = False self.final_digest = None self.ensure_tables() self.initialize() if data: self.update(data) return def initialize(self): self.hash = bytearray(64) self.buffer = bytearray(128) self.buffer[0] = 2 self.buffer[1] = (self.hashbitlen >> 24) & 0xff self.buffer[2] = (self.hashbitlen >> 16) & 0xff self.buffer[3] = (self.hashbitlen >> 8) & 0xff self.buffer[4] = (self.hashbitlen >> 0) & 0xff if self.hashbitlen <= 256: self.lane256_transform(self.buffer, 0) else: self.lane512_transform(self.buffer, 0) return def copy(self): other = self.__class__() other.databitcount = self.databitcount other.buffer[:] = self.buffer other.hash[:] = self.hash other.finalized = self.finalized other.final_digest = self.final_digest return other def update(self, data): if self.finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") mv = data if isinstance(data, memoryview) else memoryview(data) blocksize = self.block_size total = len(mv) offset = 0 buffill = (self.databitcount >> 3) & (blocksize - 1) if buffill: n = blocksize - buffill if n > total: n = total self.buffer[buffill:buffill + n] = mv[:n] self.databitcount += n << 3 if buffill + n == blocksize: if blocksize == 64: self.lane256_transform(self.buffer, self.databitcount) else: self.lane512_transform(self.buffer, self.databitcount) offset = n remain = total - offset while remain >= blocksize: self.databitcount += blocksize << 3 block = mv[offset:offset + blocksize] if blocksize == 64: self.lane256_transform(block, self.databitcount) else: self.lane512_transform(block, self.databitcount) offset += blocksize remain -= blocksize if remain: self.buffer[:remain] = mv[offset:offset + remain] self.databitcount += remain << 3 return def digest(self): if self.finalized: return self.final_digest tmp = self.copy() tmp.finalize() return tmp.final_digest def hexdigest(self): return self.digest().hex() def finalize(self): if self.finalized: return blocksize = self.block_size used = (self.databitcount >> 3) & (blocksize - 1) if used: self.buffer[used:blocksize] = b'\x00' * (blocksize - used) if blocksize == 64: self.lane256_transform(self.buffer, self.databitcount) else: self.lane512_transform(self.buffer, self.databitcount) self.buffer[:blocksize] = b'\x00' * blocksize counter = self.databitcount & 0xffff_ffff_ffff_ffff self.buffer[1] = (counter >> 56) & 0xff self.buffer[2] = (counter >> 48) & 0xff self.buffer[3] = (counter >> 40) & 0xff self.buffer[4] = (counter >> 32) & 0xff self.buffer[5] = (counter >> 24) & 0xff self.buffer[6] = (counter >> 16) & 0xff self.buffer[7] = (counter >> 8) & 0xff self.buffer[8] = counter & 0xff if blocksize == 64: self.lane256_transform(self.buffer, 0) else: self.lane512_transform(self.buffer, 0) self.final_digest = bytes(self.hash[:self.digest_size]) self.finalized = True return def lane256_transform(self, buffer, counter): def add_constants_2(r, a): kb = self.k_bytes base = 32 * r for block in range(2): a_base = block * 16 w_base = base + block * 16 for col in range(4): w = w_base + col * 4 a[a_base + col] ^= kb[w] a[a_base + col + 4] ^= kb[w + 1] a[a_base + col + 8] ^= kb[w + 2] a[a_base + col + 12] ^= kb[w + 3] return def sub_shift_mix_2(a): s = self.sbox m2 = self.mul2_table m3 = self.mul3_table shifted = (0, 1, 2, 3, 5, 6, 7, 4, 10, 11, 8, 9, 15, 12, 13, 14) for p in (0, 16): x = [s[a[p + i]] for i in shifted] for col in range(4): x0 = x[col] x1 = x[4 + col] x2 = x[8 + col] x3 = x[12 + col] a[p + col] = m2[x0] ^ m3[x1] ^ x2 ^ x3 a[p + 4 + col] = m2[x1] ^ m3[x2] ^ x3 ^ x0 a[p + 8 + col] = m2[x2] ^ m3[x3] ^ x0 ^ x1 a[p + 12 + col] = m2[x3] ^ m3[x0] ^ x1 ^ x2 return def swap_columns256(a): p0, p1 = 0, 16 for row in range(0, 16, 4): for col0, col1 in ((2, 0), (3, 1)): a[p0 + row + col0], a[p1 + row + col1] = a[p1 + row + col1], a[p0 + row + col0] return def xor_state_2(a, b): for i in range(32): a[i] ^= b[i] return def expand_message256(hashval, buffer, w0, w1, w2, w3, w4, w5): for col in range(4): bc = col * 4 for row in range(4): idx = bc + row pos = row * 4 + col h0, h1 = hashval[idx], hashval[idx + 16] b0, b1, b2, b3 = buffer[idx], buffer[idx + 16], buffer[idx + 32], buffer[idx + 48] w0[pos], w0[pos + 16] = h0 ^ b0 ^ b1 ^ b2 ^ b3, h1 ^ b0 ^ b2 w1[pos], w1[pos + 16] = h0 ^ h1 ^ b0 ^ b2 ^ b3, h0 ^ b1 ^ b2 w2[pos], w2[pos + 16] = h0 ^ h1 ^ b0 ^ b1 ^ b2, h0 ^ b0 ^ b3 w3[pos], w3[pos + 16] = h0, h1 w4[pos], w4[pos + 16] = b0, b1 w5[pos], w5[pos + 16] = b2, b3 return def store_hash256(hashval, w0): for col in range(4): bc = col * 4 for row in range(4): pos = row * 4 + col hashval[bc + row] = w0[pos] hashval[16 + bc + row] = w0[16 + pos] return def permute_p256(j, a, c0, c1, c2, c3, c4, c5, c6, c7): for i in range(5): r = 5 * j + i sub_shift_mix_2(a) add_constants_2(r, a) if r & 1: a[3], a[7], a[11], a[15] = a[3] ^ c4, a[7] ^ c5, a[11] ^ c6, a[15] ^ c7 else: a[3], a[7], a[11], a[15] = a[3] ^ c0, a[7] ^ c1, a[11] ^ c2, a[15] ^ c3 swap_columns256(a) sub_shift_mix_2(a) swap_columns256(a) return def permute_q256(j, a, c0, c1, c2, c3, c4, c5, c6, c7): for i in range(2): r = 30 + 2 * j + i sub_shift_mix_2(a) add_constants_2(r, a) if r & 1: a[3], a[7], a[11], a[15] = a[3] ^ c4, a[7] ^ c5, a[11] ^ c6, a[15] ^ c7 else: a[3], a[7], a[11], a[15] = a[3] ^ c0, a[7] ^ c1, a[11] ^ c2, a[15] ^ c3 swap_columns256(a) sub_shift_mix_2(a) swap_columns256(a) return w0 = [0] * 32 w1 = [0] * 32 w2 = [0] * 32 w3 = [0] * 32 w4 = [0] * 32 w5 = [0] * 32 expand_message256(self.hash, buffer, w0, w1, w2, w3, w4, w5) c0 = (counter >> 56) & 0xff c1 = (counter >> 48) & 0xff c2 = (counter >> 40) & 0xff c3 = (counter >> 32) & 0xff c4 = (counter >> 24) & 0xff c5 = (counter >> 16) & 0xff c6 = (counter >> 8) & 0xff c7 = counter & 0xff permute_p256(0, w0, c0, c1, c2, c3, c4, c5, c6, c7) permute_p256(1, w1, c0, c1, c2, c3, c4, c5, c6, c7) permute_p256(2, w2, c0, c1, c2, c3, c4, c5, c6, c7) permute_p256(3, w3, c0, c1, c2, c3, c4, c5, c6, c7) permute_p256(4, w4, c0, c1, c2, c3, c4, c5, c6, c7) permute_p256(5, w5, c0, c1, c2, c3, c4, c5, c6, c7) xor_state_2(w0, w1) xor_state_2(w0, w2) xor_state_2(w3, w4) xor_state_2(w3, w5) permute_q256(0, w0, c0, c1, c2, c3, c4, c5, c6, c7) permute_q256(1, w3, c0, c1, c2, c3, c4, c5, c6, c7) xor_state_2(w0, w3) store_hash256(self.hash, w0) return def lane512_transform(self, buffer, counter): def add_constants_4(r, a): kb = self.k_bytes base = 64 * r for part in range(4): a_base = part * 16 w_base = base + part * 16 for col in range(4): w = w_base + col * 4 a[a_base + col] ^= kb[w] a[a_base + col + 4] ^= kb[w + 1] a[a_base + col + 8] ^= kb[w + 2] a[a_base + col + 12] ^= kb[w + 3] return def sub_shift_mix_4(a): s = self.sbox m2 = self.mul2_table m3 = self.mul3_table shifted = (0, 1, 2, 3, 5, 6, 7, 4, 10, 11, 8, 9, 15, 12, 13, 14) for part in range(4): p = part * 16 x = [s[a[p + i]] for i in shifted] for col in range(4): x0 = x[col] x1 = x[4 + col] x2 = x[8 + col] x3 = x[12 + col] a[p + col] = m2[x0] ^ m3[x1] ^ x2 ^ x3 a[p + 4 + col] = m2[x1] ^ m3[x2] ^ x3 ^ x0 a[p + 8 + col] = m2[x2] ^ m3[x3] ^ x0 ^ x1 a[p + 12 + col] = m2[x3] ^ m3[x0] ^ x1 ^ x2 return def swap_columns512(a): bases = (0, 16, 32, 48) for row_base in range(0, 16, 4): for src_block in range(4): for dst_block in range(src_block + 1, 4): src_index = bases[src_block] + row_base + dst_block dst_index = bases[dst_block] + row_base + src_block a[src_index], a[dst_index] = a[dst_index], a[src_index] return def xor_state_4(a, b): for i in range(64): a[i] ^= b[i] return def expand_message512(hashval, buffer, w0, w1, w2, w3, w4, w5): for col in range(4): bc = col * 4 for row in range(4): idx = bc + row pos = row * 4 + col h0, h1, h2, h3 = hashval[idx], hashval[idx + 16], hashval[idx + 32], hashval[idx + 48] b0, b1, b2, b3 = buffer[idx], buffer[idx + 16], buffer[idx + 32], buffer[idx + 48] b4, b5, b6, b7 = buffer[idx + 64], buffer[idx + 80], buffer[idx + 96], buffer[idx + 112] w0[pos], w0[pos + 16] = h0 ^ b0 ^ b2 ^ b4 ^ b6, h1 ^ b1 ^ b3 ^ b5 ^ b7 w1[pos], w1[pos + 16] = h0 ^ h2 ^ b0 ^ b4 ^ b6, h1 ^ h3 ^ b1 ^ b5 ^ b7 w2[pos], w2[pos + 16] = h0 ^ h2 ^ b0 ^ b2 ^ b4, h1 ^ h3 ^ b1 ^ b3 ^ b5 w3[pos], w3[pos + 16] = h0, h1 w4[pos], w4[pos + 16] = b0, b1 w5[pos], w5[pos + 16] = b4, b5 w0[pos + 32], w0[pos + 48] = h2 ^ b0 ^ b4, h3 ^ b1 ^ b5 w1[pos + 32], w1[pos + 48] = h0 ^ b2 ^ b4, h1 ^ b3 ^ b5 w2[pos + 32], w2[pos + 48] = h0 ^ b0 ^ b6, h1 ^ b1 ^ b7 w3[pos + 32], w3[pos + 48] = h2, h3 w4[pos + 32], w4[pos + 48] = b2, b3 w5[pos + 32], w5[pos + 48] = b6, b7 return def store_hash512(hashval, w0): for col in range(4): bc = col * 4 for row in range(4): pos = row * 4 + col hashval[bc + row] = w0[pos] hashval[16 + bc + row] = w0[16 + pos] hashval[32 + bc + row] = w0[32 + pos] hashval[48 + bc + row] = w0[48 + pos] return def permute_p512(j, a, c0, c1, c2, c3, c4, c5, c6, c7): for i in range(7): r = 7 * j + i sub_shift_mix_4(a) add_constants_4(r, a) if r & 1: a[3], a[7], a[11], a[15] = a[3] ^ c4, a[7] ^ c5, a[11] ^ c6, a[15] ^ c7 else: a[3], a[7], a[11], a[15] = a[3] ^ c0, a[7] ^ c1, a[11] ^ c2, a[15] ^ c3 swap_columns512(a) sub_shift_mix_4(a) swap_columns512(a) return def permute_q512(j, a, c0, c1, c2, c3, c4, c5, c6, c7): for i in range(3): r = 42 + 3 * j + i sub_shift_mix_4(a) add_constants_4(r, a) if r & 1: a[3], a[7], a[11], a[15] = a[3] ^ c4, a[7] ^ c5, a[11] ^ c6, a[15] ^ c7 else: a[3], a[7], a[11], a[15] = a[3] ^ c0, a[7] ^ c1, a[11] ^ c2, a[15] ^ c3 swap_columns512(a) sub_shift_mix_4(a) swap_columns512(a) return w0 = [0] * 64 w1 = [0] * 64 w2 = [0] * 64 w3 = [0] * 64 w4 = [0] * 64 w5 = [0] * 64 expand_message512(self.hash, buffer, w0, w1, w2, w3, w4, w5) c0 = (counter >> 56) & 0xff c1 = (counter >> 48) & 0xff c2 = (counter >> 40) & 0xff c3 = (counter >> 32) & 0xff c4 = (counter >> 24) & 0xff c5 = (counter >> 16) & 0xff c6 = (counter >> 8) & 0xff c7 = counter & 0xff permute_p512(0, w0, c0, c1, c2, c3, c4, c5, c6, c7) permute_p512(1, w1, c0, c1, c2, c3, c4, c5, c6, c7) permute_p512(2, w2, c0, c1, c2, c3, c4, c5, c6, c7) permute_p512(3, w3, c0, c1, c2, c3, c4, c5, c6, c7) permute_p512(4, w4, c0, c1, c2, c3, c4, c5, c6, c7) permute_p512(5, w5, c0, c1, c2, c3, c4, c5, c6, c7) xor_state_4(w0, w1) xor_state_4(w0, w2) xor_state_4(w3, w4) xor_state_4(w3, w5) permute_q512(0, w0, c0, c1, c2, c3, c4, c5, c6, c7) permute_q512(1, w3, c0, c1, c2, c3, c4, c5, c6, c7) xor_state_4(w0, w3) store_hash512(self.hash, w0) return class Lane224(LaneBase): digest_size = 0x1c block_size = 0x40 class Lane256(LaneBase): digest_size = 0x20 block_size = 0x40 class Lane384(LaneBase): digest_size = 0x30 block_size = 0x80 class Lane512(LaneBase): digest_size = 0x40 block_size = 0x80 class FastHashBase: block_size = 8 def __init__(self, data=b"", seed=0): self.buf = bytearray() self.msg_len = 0 self.seed = seed self.value = 0 if data: self.update(data) return def copy(self): other = self.__class__(seed=self.seed) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.value = self.value return other def fasthash64(self, message, seed): def mix64(h): mix_const = 0x2127_599b_f432_5c37 h &= 0xffff_ffff_ffff_ffff h ^= (h >> 23) h = (h * mix_const) & 0xffff_ffff_ffff_ffff h ^= (h >> 47) return h m = 0x8803_55f2_1e6d_1965 length = len(message) h = (seed & 0xffff_ffff_ffff_ffff) ^ ((length * m) & 0xffff_ffff_ffff_ffff) index = 0 end = length & ~7 while index < end: v = struct.unpack_from("> 32)) & 0xffff_ffff return v class SuperFastHash: block_size = 4 digest_size = 4 def __init__(self, data=b""): self.buf = bytearray() self.msg_len = 0 self.value = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.value = self.value return other def u32(self, x): x &= 0xffff_ffff return x def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf.extend(data) self.msg_len += len(data) return self def finalize(self): message = bytes(self.buf) length = len(message) index = 0 digest = self.u32(length) for _ in range(length >> 2): w1 = message[index] | (message[index + 1] << 8) w2 = message[index + 2] | (message[index + 3] << 8) index += 4 digest = self.u32(digest + w1) digest ^= self.u32(digest << 16) ^ self.u32(w2 << 11) digest = self.u32(digest + (digest >> 11)) rem = length & 3 if rem == 3: digest = self.u32(digest + (message[index] | (message[index + 1] << 8))) digest ^= self.u32(digest << 16) digest ^= self.u32(message[index + 2] << 18) digest = self.u32(digest + (digest >> 11)) elif rem == 2: digest = self.u32(digest + (message[index] | (message[index + 1] << 8))) digest ^= self.u32(digest << 11) digest = self.u32(digest + (digest >> 17)) elif rem == 1: digest = self.u32(digest + message[index]) digest ^= self.u32(digest << 10) digest = self.u32(digest + (digest >> 1)) digest ^= self.u32(digest << 3) digest = self.u32(digest + (digest >> 5)) digest ^= self.u32(digest << 4) digest = self.u32(digest + (digest >> 17)) digest ^= self.u32(digest << 25) digest = self.u32(digest + (digest >> 6)) self.value = digest return def digest(self): c = self.copy() c.finalize() return struct.pack("> (32 - n))) & 0xffff_ffff if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if len(data) != 1: raise ValueError("data must be exactly 1 byte") b = data[0] if self.bufpos == self.n: self.overflow = True self.bufpos = 0 state = self.state state = rol32(state, 1) if self.overflow: toshift = self.bytehash[self.buf[self.bufpos]] state ^= rol32(toshift, self.bshiftn) self.buf[self.bufpos] = b self.bufpos += 1 state ^= self.bytehash[b] self.state = state return state def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) for b in data: self.update_byte(bytes([b])) return self def finalize(self): return def digest(self): return self.state.to_bytes(4, "little") def hexdigest(self): h = self.digest().hex() return h class NHash: digest_size = 0 block_size = 1 primes = ( 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35, 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, ) def __init__(self, data=b"", divisors=None): self.total = 0 self.i = 0 if divisors is None: self.divisors = [] else: self.divisors = list(divisors) if data: self.update(data) return def copy(self): other = self.__class__() other.total = self.total other.i = self.i other.divisors = list(self.divisors) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) for b in data: self.i = (self.i + 0x1c) % 0x1d self.total += self.primes[self.i] * b return self def finalize(self): return def digest(self): def moddiv_list(): if not self.divisors: return [] tmp = self.total ret = [] for div in reversed(self.divisors): if not isinstance(div, int) or div <= 0: raise ValueError("all divisors must be positive int") ret.append(tmp % div) tmp //= div ret.reverse() return ret def moddiv_string(): if not self.divisors: s = str(self.total) return s parts = [str(x) for x in self.moddiv_list()] s = "/".join(parts) return s if self.divisors: return moddiv_string().encode("ascii") v = self.total nbytes = (v.bit_length() + 7) // 8 if nbytes == 0: nbytes = 1 return v.to_bytes(nbytes, "big") def hexdigest(self): h = self.digest().hex() return h class LSHBase: tau = (0x03, 0x02, 0x00, 0x01, 0x07, 0x04, 0x05, 0x06, 0x0b, 0x0a, 0x08, 0x09, 0x0f, 0x0c, 0x0d, 0x0e) sigma = (0x06, 0x04, 0x05, 0x07, 0x0c, 0x0f, 0x0e, 0x0d, 0x02, 0x00, 0x01, 0x03, 0x08, 0x0b, 0x0a, 0x09) gamma32 = (0x00, 0x08, 0x10, 0x18, 0x18, 0x10, 0x08, 0x00) gamma64 = (0x00, 0x10, 0x20, 0x30, 0x08, 0x18, 0x28, 0x38) sc0_32 = ( 0x917c_af90, 0x6c1b_10a2, 0x6f35_2943, 0xcf77_8243, 0x2ceb_7472, 0x29e9_6ff2, 0x8a9b_a428, 0x2eeb_2642, ) sc0_64 = ( 0x9788_4283_c938_982a, 0xba1f_ca93_533e_2355, 0xc519_a2e8_7aeb_1c03, 0x9a0f_c954_62af_17b1, 0xfc3d_da8a_b019_a82b, 0x0282_5d07_9a89_5407, 0x79f2_d0a7_ee06_a6f7, 0xd76d_15ee_d9fd_f5fe, ) def __init__(self, data=b""): if self.word_bits not in (32, 64): raise ValueError("invalid word_bits") if self.ns not in (26, 28): raise ValueError("invalid ns") if self.block_size not in (128, 256): raise ValueError("invalid block_size") if self.digest_size not in (28, 32, 48, 64): raise ValueError("invalid digest_size") if not isinstance(self.iv, (list, tuple)) or len(self.iv) != 16: raise ValueError("invalid iv") self.word_bytes = self.word_bits // 8 self.mask = (1 << self.word_bits) - 1 self.buf = bytearray() self.msg_len = 0 self.cv = list(self.iv) self.sc = self.make_step_constants() if data: self.update(data) return def copy(self): other = object.__new__(self.__class__) other.word_bits = self.word_bits other.ns = self.ns other.block_size = self.block_size other.digest_size = self.digest_size other.iv = self.iv other.word_bytes = self.word_bytes other.mask = self.mask other.sc = self.sc other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.cv = list(self.cv) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def digest(self): c = self.copy() c.finalize() h = [(c.cv[i] ^ c.cv[i + 8]) & c.mask for i in range(8)] out = b"".join(int(x).to_bytes(c.word_bytes, "little") for x in h) return out[:c.digest_size] def hexdigest(self): return self.digest().hex() def finalize(self): # One-zeros padding self.buf.append(0x80) while (len(self.buf) % self.block_size) != 0: self.buf.append(0x00) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return def rol(self, x, n): x &= self.mask if n == 0: return x return ((x << n) | (x >> (self.word_bits - n))) & self.mask def make_step_constants(self): if self.word_bits == 32: sc0 = self.sc0_32 else: sc0 = self.sc0_64 sc = [list(sc0)] for j in range(1, self.ns): prev = sc[j - 1] sc.append([(prev[l] + self.rol(prev[l], 8)) & self.mask for l in range(8)]) # noqa: E741 return sc def compress(self, block): def msg_add(x, y): return [x[i] ^ y[i] for i in range(16)] def alpha_beta(j): if self.word_bits == 32: if (j & 1) == 0: return 0x1d, 0x01 return 0x05, 0x11 if (j & 1) == 0: return 0x17, 0x3b return 0x07, 0x03 def gamma(l): # noqa: E741 if self.word_bits == 32: return self.gamma32[l] return self.gamma64[l] def mix_pair(j, l, x, y): # noqa: E741 alpha, beta = alpha_beta(j) g = gamma(l) x = (x + y) & self.mask x = self.rol(x, alpha) x ^= self.sc[j][l] y = (x + y) & self.mask y = self.rol(y, beta) x = (x + y) & self.mask y = self.rol(y, g) return x, y def mix(j, t): t = list(t) for l in range(8): # noqa: E741 x, y = mix_pair(j, l, t[l], t[l + 8]) t[l] = x t[l + 8] = y return t def word_perm(x): return [x[self.sigma[i]] for i in range(16)] def step(j, t, mj): t = msg_add(t, mj) t = mix(j, t) t = word_perm(t) return t def msg_expand(m32): sub = [m32[:16], m32[16:32]] for j in range(2, self.ns + 1): prev1 = sub[j - 1] prev2 = sub[j - 2] sub.append([(prev1[l] + prev2[self.tau[l]]) & self.mask for l in range(16)]) # noqa: E741 return sub if self.word_bits == 32: fmt = "<32I" else: fmt = "<32Q" m = list(struct.unpack(fmt, block)) sub = msg_expand(m) t = list(self.cv) for j in range(self.ns): t = step(j, t, sub[j]) self.cv = msg_add(t, sub[self.ns]) return class LSH256_224(LSHBase): word_bits = 32 ns = 26 block_size = 128 digest_size = 28 iv = ( 0x0686_08d3, 0x62d8_f7a7, 0xd766_52ab, 0x4c60_0a43, 0xbdc4_0aa8, 0x1eca_0b68, 0xda1a_89be, 0x3147_d354, 0x707e_b4f9, 0xf65b_3862, 0x6b0b_2abe, 0x56b8_ec0a, 0xcf23_7286, 0xee0d_1727, 0x3363_6595, 0x8bb8_d05f, ) class LSH256_256(LSHBase): word_bits = 32 ns = 26 block_size = 128 digest_size = 32 iv = ( 0x46a1_0f1f, 0xfddc_e486, 0xb414_43a8, 0x198e_6b9d, 0x3304_388d, 0xb0f5_a3c7, 0xb360_61c4, 0x7adb_d553, 0x105d_5378, 0x2f74_de54, 0x5c2f_2d95, 0xf255_3fbe, 0x8051_357a, 0x1386_68c8, 0x47aa_4484, 0xe01a_fb41, ) class LSH512_224(LSHBase): word_bits = 64 ns = 28 block_size = 256 digest_size = 28 iv = ( 0x0c40_1e9f_e881_3a55, 0x4a5f_4462_68fd_3d35, 0xff13_e452_334f_612a, 0xf822_7661_037e_354a, 0xa5f2_2372_3c9c_a29d, 0x95d9_65a1_1aed_3979, 0x01e2_3835_b9ab_02cc, 0x52d4_9cba_d5b3_0616, 0x9e5c_2027_773f_4ed3, 0x66a5_c880_1925_b701, 0x22bb_c85b_4c67_79d9, 0xc131_71a4_2c55_9c23, 0x31e2_b67d_25be_3813, 0xd522_c4de_ed8e_4d83, 0xa79f_5509_b43f_bafe, 0xe00d_2cd8_8b4b_6c6a, ) class LSH512_256(LSHBase): word_bits = 64 ns = 28 block_size = 256 digest_size = 32 iv = ( 0x6dc5_7c33_df98_9423, 0xd8ea_7f6e_8342_c199, 0x76df_8356_f860_3ac4, 0x40f1_b44d_e838_223a, 0x39ff_e7cf_c314_84cd, 0x39c4_326c_c528_1548, 0x8a2f_f85a_3460_45d8, 0xff20_2aa4_6dbd_d61e, 0xcf78_5b3c_d5fc_db8b, 0x1f03_23b6_4a81_50bf, 0xff75_d972_f29e_a355, 0x2e56_7f30_bf1c_a9e1, 0xb596_875b_f8ff_6dba, 0xfcca_39b0_89ef_4615, 0xecff_4017_d020_b4b6, 0x7e77_384c_772e_d802, ) class LSH512_384(LSHBase): word_bits = 64 ns = 28 block_size = 256 digest_size = 48 iv = ( 0x5315_6a66_2928_08f6, 0xb2c4_f362_b204_c2bc, 0xb84b_7213_bfa0_5c4e, 0x976c_eb7c_1b29_9f73, 0xdf0c_c63c_0570_ae97, 0xda44_41ba_a486_ce3f, 0x6559_f5d9_b5f2_acc2, 0x22da_cf19_b4b5_2a16, 0xbbcd_acef_de80_953a, 0xc989_1a28_7972_5b3e, 0x7c9f_e633_0237_e440, 0xa30b_a550_553f_7431, 0xbb08_043f_b34e_3e30, 0xa0de_c48d_5461_8ead, 0x1503_1726_7464_bc57, 0x32d1_501f_de63_dc93, ) class LSH512_512(LSHBase): word_bits = 64 ns = 28 block_size = 256 digest_size = 64 iv = ( 0xadd5_0f3c_7f07_094e, 0xe3f3_cee8_f941_8a4f, 0xb527_ecde_5b3d_0ae9, 0x2ef6_dec6_8076_f501, 0x8cb9_94ca_e5ac_a216, 0xfbb9_eae4_bba4_8cc7, 0x650a_5261_7472_5fea, 0x1f9a_61a7_3f8d_8085, 0xb660_7378_173b_539b, 0x1bc9_9853_b0c0_b9ed, 0xdf72_7fc1_9b18_2d47, 0xdbef_360c_f893_a457, 0x4981_f5e5_7014_7e80, 0xd00c_4490_ca7d_3e30, 0x5d73_940c_0e4a_e1ec, 0x8940_85e2_edb2_d819, ) class FugueBase: state_words = 36 # Fugue-384/512: 36, Fugue-224/256: 30 @classmethod def ensure_tables(cls): if hasattr(Hash.FugueBase, "T"): return def rol8(x, n): x &= 0xff n &= 7 return ((x << n) | (x >> (8 - n))) & 0xff def gf_mul(a, b): a &= 0xff b &= 0xff res = 0 for _ in range(8): if b & 1: res ^= a hi = a & 0x80 a = (a << 1) & 0xff if hi: a ^= 0x1b b >>= 1 return res & 0xff def gf_pow(a, e): res = 1 base = a & 0xff while e: if e & 1: res = gf_mul(res, base) base = gf_mul(base, base) e >>= 1 return res & 0xff def linear_map(U): M = ((1, 4, 7, 1), (1, 1, 4, 7), (7, 1, 1, 4), (4, 7, 1, 1)) V = [[0] * 4 for _ in range(4)] for i in range(4): for j in range(4): acc = 0 for k in range(4): coeff = M[i][k] if coeff == 1: acc ^= U[k][j] else: acc ^= gf_mul(U[k][j], coeff) V[i][j] = acc & 0xff d = [0] * 4 for i in range(4): acc = 0 for j in range(4): if j != i: acc ^= U[i][j] d[i] = acc & 0xff W = [[0] * 4 for _ in range(4)] for i in range(4): for j in range(4): coeff = M[j][i] add = d[i] if coeff == 1 else gf_mul(d[i], coeff) W[i][j] = (V[i][j] ^ add) & 0xff for i in range(4): n = i & 3 if n: W[i] = W[i][n:] + W[i][:n] y0 = ((W[0][0] << 24) | (W[1][0] << 16) | (W[2][0] << 8) | W[3][0]) & 0xffff_ffff y1 = ((W[0][1] << 24) | (W[1][1] << 16) | (W[2][1] << 8) | W[3][1]) & 0xffff_ffff y2 = ((W[0][2] << 24) | (W[1][2] << 16) | (W[2][2] << 8) | W[3][2]) & 0xffff_ffff y3 = ((W[0][3] << 24) | (W[1][3] << 16) | (W[2][3] << 8) | W[3][3]) & 0xffff_ffff return y0, y1, y2, y3 sbox = [0] * 256 for x in range(256): inv = 0 if x == 0 else gf_pow(x, 254) y = inv & 0xff sb = (y ^ rol8(y, 1) ^ rol8(y, 2) ^ rol8(y, 3) ^ rol8(y, 4) ^ 0x63) sbox[x] = sb & 0xff Hash.FugueBase.sbox = sbox T = [[[0 for _ in range(256)] for _ in range(4)] for _ in range(4)] for c in range(4): for r in range(4): for val in range(256): U = [[0]*4 for _ in range(4)] U[r][c] = sbox[val] y0, y1, y2, y3 = linear_map(U) T[c][r][val] = (y0 << 96) | (y1 << 64) | (y2 << 32) | y3 Hash.FugueBase.T = T return def __init__(self, data=b""): self.ensure_tables() self.S = [0] * 36 self.bit_count = 0 self.partial = 0 self.partial_len = 0 self.rshift = 0 iv = self.init_val if iv: n = self.state_words off = n - len(iv) for i, v in enumerate(iv): self.S[off + i] = v & 0xffff_ffff if data: self.update(data) return def copy(self): other = self.__class__() other.S = list(self.S) other.bit_count = self.bit_count other.partial = self.partial other.partial_len = self.partial_len other.rshift = self.rshift return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.bit_count = ( self.bit_count + ((len(data) & 0xffff_ffff_ffff_ffff) << 3) ) & 0xffff_ffff_ffff_ffff for b in data: self.partial = ((self.partial << 8) | (b & 0xff)) & 0xffff_ffff self.partial_len += 1 if self.partial_len == 4: self.process(self.partial) self.partial = 0 self.partial_len = 0 return self def finalize(self): if self.partial_len != 0: while self.partial_len < 4: self.partial = (self.partial << 8) & 0xffff_ffff self.partial_len += 1 self.process(self.partial) self.partial = 0 self.partial_len = 0 high = (self.bit_count >> 32) & 0xffff_ffff low = self.bit_count & 0xffff_ffff self.process(high) self.process(low) out = bytearray(self.digest_size) self.process_final(out) return bytes(out) def digest(self): c = self.copy() return c.finalize() def hexdigest(self): return self.digest().hex() def ror(self, rc, length): rc %= length if rc == 0: return tmp = self.S[length - rc:length] self.S[rc:length] = self.S[0:length - rc] self.S[0:rc] = tmp return def cmix30(self): S = self.S S[0] ^= S[4] S[1] ^= S[5] S[2] ^= S[6] S[15] ^= S[4] S[16] ^= S[5] S[17] ^= S[6] for i in (0, 1, 2, 15, 16, 17): S[i] &= 0xffff_ffff return def cmix36(self): S = self.S S[0] ^= S[4] S[1] ^= S[5] S[2] ^= S[6] S[18] ^= S[4] S[19] ^= S[5] S[20] ^= S[6] for i in (0, 1, 2, 18, 19, 20): S[i] &= 0xffff_ffff return def smix(self, i0, i1, i2, i3): S = self.S x0 = S[i0] x1 = S[i1] x2 = S[i2] x3 = S[i3] Tp = self.T out = Tp[0][0][(x0 >> 24) & 0xff] out ^= Tp[0][1][(x0 >> 16) & 0xff] out ^= Tp[0][2][(x0 >> 8) & 0xff] out ^= Tp[0][3][x0 & 0xff] out ^= Tp[1][0][(x1 >> 24) & 0xff] out ^= Tp[1][1][(x1 >> 16) & 0xff] out ^= Tp[1][2][(x1 >> 8) & 0xff] out ^= Tp[1][3][x1 & 0xff] out ^= Tp[2][0][(x2 >> 24) & 0xff] out ^= Tp[2][1][(x2 >> 16) & 0xff] out ^= Tp[2][2][(x2 >> 8) & 0xff] out ^= Tp[2][3][x2 & 0xff] out ^= Tp[3][0][(x3 >> 24) & 0xff] out ^= Tp[3][1][(x3 >> 16) & 0xff] out ^= Tp[3][2][(x3 >> 8) & 0xff] out ^= Tp[3][3][x3 & 0xff] S[i0] = (out >> 96) & 0xffff_ffff S[i1] = (out >> 64) & 0xffff_ffff S[i2] = (out >> 32) & 0xffff_ffff S[i3] = out & 0xffff_ffff return def process_final_common(self, out, state_len, init_rot_mul, cmix_fn, pre_rounds, loop_steps, final_xors, out_words): def xor_from_zero(indices): s0 = self.S[0] for idx in indices: self.S[idx] ^= s0 return def encode_words(out, indices): for pos, idx in enumerate(indices): off = pos * 4 if off >= len(out): break x = self.S[idx] out[off + 0] = (x >> 24) & 0xff out[off + 1] = (x >> 16) & 0xff out[off + 2] = (x >> 8) & 0xff out[off + 3] = x & 0xff return self.ror(init_rot_mul * self.rshift, state_len) for _ in range(pre_rounds): self.ror(3, state_len) cmix_fn() self.smix(0, 1, 2, 3) for _ in range(13): for xor_indices, rot in loop_steps: xor_from_zero(xor_indices) self.ror(rot, state_len) self.smix(0, 1, 2, 3) xor_from_zero(final_xors) encode_words(out, out_words) return class FugueCore30(FugueBase): state_words = 30 def process(self, w): S = self.S w &= 0xffff_ffff rs = self.rshift if rs == 1: S[4] ^= S[24] S[24] = w S[2] ^= S[24] S[25] ^= S[18] S[21] ^= S[25] S[22] ^= S[26] S[23] ^= S[27] S[6] ^= S[25] S[7] ^= S[26] S[8] ^= S[27] self.smix(21, 22, 23, 24) S[18] ^= S[22] S[19] ^= S[23] S[20] ^= S[24] S[3] ^= S[22] S[4] ^= S[23] S[5] ^= S[24] self.smix(18, 19, 20, 21) self.rshift = 2 elif rs == 2: S[28] ^= S[18] S[18] = w S[26] ^= S[18] S[19] ^= S[12] S[15] ^= S[19] S[16] ^= S[20] S[17] ^= S[21] S[0] ^= S[19] S[1] ^= S[20] S[2] ^= S[21] self.smix(15, 16, 17, 18) S[12] ^= S[16] S[13] ^= S[17] S[14] ^= S[18] S[27] ^= S[16] S[28] ^= S[17] S[29] ^= S[18] self.smix(12, 13, 14, 15) self.rshift = 3 elif rs == 3: S[22] ^= S[12] S[12] = w S[20] ^= S[12] S[13] ^= S[6] S[9] ^= S[13] S[10] ^= S[14] S[11] ^= S[15] S[24] ^= S[13] S[25] ^= S[14] S[26] ^= S[15] self.smix(9, 10, 11, 12) S[6] ^= S[10] S[7] ^= S[11] S[8] ^= S[12] S[21] ^= S[10] S[22] ^= S[11] S[23] ^= S[12] self.smix(6, 7, 8, 9) self.rshift = 4 elif rs == 4: S[16] ^= S[6] S[6] = w S[14] ^= S[6] S[7] ^= S[0] S[3] ^= S[7] S[4] ^= S[8] S[5] ^= S[9] S[18] ^= S[7] S[19] ^= S[8] S[20] ^= S[9] self.smix(3, 4, 5, 6) S[0] ^= S[4] S[1] ^= S[5] S[2] ^= S[6] S[15] ^= S[4] S[16] ^= S[5] S[17] ^= S[6] self.smix(0, 1, 2, 3) self.rshift = 0 else: S[10] ^= S[0] S[0] = w S[8] ^= S[0] S[1] ^= S[24] S[27] ^= S[1] S[28] ^= S[2] S[29] ^= S[3] S[12] ^= S[1] S[13] ^= S[2] S[14] ^= S[3] self.smix(27, 28, 29, 0) S[24] ^= S[28] S[25] ^= S[29] S[26] ^= S[0] S[9] ^= S[28] S[10] ^= S[29] S[11] ^= S[0] self.smix(24, 25, 26, 27) self.rshift = 1 return FINAL_LOOP_STEPS = ( ((4, 15), 15), ((4, 16), 14), ) FINAL_XORS = (4, 15) FINAL_OUT_WORDS = (1, 2, 3, 4, 15, 16, 17, 18) def process_final(self, out): self.process_final_common( out=out, state_len=30, init_rot_mul=6, cmix_fn=self.cmix30, pre_rounds=10, loop_steps=self.FINAL_LOOP_STEPS, final_xors=self.FINAL_XORS, out_words=self.FINAL_OUT_WORDS, ) return class Fugue224(FugueCore30): block_size = 28 digest_size = 28 init_val = ( 0xf4c9_120d, 0x6286_f757, 0xee39_e01c, 0xe074_e3cb, 0xa112_7c62, 0x9a43_d215, 0xbd8d_679a, ) class Fugue256(FugueCore30): block_size = 32 digest_size = 32 init_val = ( 0xe952_bdde, 0x6671_135f, 0xe0d4_f668, 0xd2b0_b594, 0xf96c_621d, 0xfbf9_29de, 0x9149_e899, 0x34f8_c248, ) class Fugue384(FugueBase): block_size = 48 digest_size = 48 init_val = ( 0xaa61_ec0d, 0x3125_2e1f, 0xa01d_b4c7, 0x0060_0985, 0x215e_f44a, 0x741b_5e9c, 0xfa69_3e9a, 0x473e_b040, 0xe502_ae8a, 0xa99c_25e0, 0xbc95_517c, 0x5c10_95a1, ) def process(self, w): S = self.S w &= 0xffff_ffff rs = self.rshift if rs == 1: S[7] ^= S[27] S[27] = w S[35] ^= S[27] S[28] ^= S[18] S[31] ^= S[21] S[24] ^= S[28] S[25] ^= S[29] S[26] ^= S[30] S[6] ^= S[28] S[7] ^= S[29] S[8] ^= S[30] self.smix(24, 25, 26, 27) S[21] ^= S[25] S[22] ^= S[26] S[23] ^= S[27] S[3] ^= S[25] S[4] ^= S[26] S[5] ^= S[27] self.smix(21, 22, 23, 24) S[18] ^= S[22] S[19] ^= S[23] S[20] ^= S[24] S[0] ^= S[22] S[1] ^= S[23] S[2] ^= S[24] self.smix(18, 19, 20, 21) self.rshift = 2 elif rs == 2: S[34] ^= S[18] S[18] = w S[26] ^= S[18] S[19] ^= S[9] S[22] ^= S[12] S[15] ^= S[19] S[16] ^= S[20] S[17] ^= S[21] S[33] ^= S[19] S[34] ^= S[20] S[35] ^= S[21] self.smix(15, 16, 17, 18) S[12] ^= S[16] S[13] ^= S[17] S[14] ^= S[18] S[30] ^= S[16] S[31] ^= S[17] S[32] ^= S[18] self.smix(12, 13, 14, 15) S[9] ^= S[13] S[10] ^= S[14] S[11] ^= S[15] S[27] ^= S[13] S[28] ^= S[14] S[29] ^= S[15] self.smix(9, 10, 11, 12) self.rshift = 3 elif rs == 3: S[25] ^= S[9] S[9] = w S[17] ^= S[9] S[10] ^= S[0] S[13] ^= S[3] S[6] ^= S[10] S[7] ^= S[11] S[8] ^= S[12] S[24] ^= S[10] S[25] ^= S[11] S[26] ^= S[12] self.smix(6, 7, 8, 9) S[3] ^= S[7] S[4] ^= S[8] S[5] ^= S[9] S[21] ^= S[7] S[22] ^= S[8] S[23] ^= S[9] self.smix(3, 4, 5, 6) S[0] ^= S[4] S[1] ^= S[5] S[2] ^= S[6] S[18] ^= S[4] S[19] ^= S[5] S[20] ^= S[6] self.smix(0, 1, 2, 3) self.rshift = 0 else: S[16] ^= S[0] S[0] = w S[8] ^= S[0] S[1] ^= S[27] S[4] ^= S[30] S[33] ^= S[1] S[34] ^= S[2] S[35] ^= S[3] S[15] ^= S[1] S[16] ^= S[2] S[17] ^= S[3] self.smix(33, 34, 35, 0) S[30] ^= S[34] S[31] ^= S[35] S[32] ^= S[0] S[12] ^= S[34] S[13] ^= S[35] S[14] ^= S[0] self.smix(30, 31, 32, 33) S[27] ^= S[31] S[28] ^= S[32] S[29] ^= S[33] S[9] ^= S[31] S[10] ^= S[32] S[11] ^= S[33] self.smix(27, 28, 29, 30) self.rshift = 1 return FINAL_LOOP_STEPS = ( ((4, 12, 24), 12), ((4, 13, 24), 12), ((4, 13, 25), 11), ) FINAL_XORS = (4, 12, 24) FINAL_OUT_WORDS = (1, 2, 3, 4, 12, 13, 14, 15, 24, 25, 26, 27) def process_final(self, out): self.process_final_common( out=out, state_len=36, init_rot_mul=9, cmix_fn=self.cmix36, pre_rounds=18, loop_steps=self.FINAL_LOOP_STEPS, final_xors=self.FINAL_XORS, out_words=self.FINAL_OUT_WORDS, ) return class Fugue512(FugueBase): block_size = 64 digest_size = 64 init_val = ( 0x8807_a57e, 0xe616_af75, 0xc5d3_e4db, 0xac9a_b027, 0xd915_f117, 0xb6ee_cc54, 0x06e8_020b, 0x4a92_efd1, 0xaac6_e2c9, 0xddb2_1398, 0xcae6_5838, 0x437f_203f, 0x25ea_78e7, 0x951f_ddd6, 0xda6e_d11d, 0xe13e_3567, ) def process(self, w): S = self.S w &= 0xffff_ffff rs = self.rshift if rs == 1: S[10] ^= S[24] S[24] = w S[32] ^= S[24] S[25] ^= S[12] S[28] ^= S[15] S[31] ^= S[18] S[21] ^= S[25] S[22] ^= S[26] S[23] ^= S[27] S[3] ^= S[25] S[4] ^= S[26] S[5] ^= S[27] self.smix(21, 22, 23, 24) S[18] ^= S[22] S[19] ^= S[23] S[20] ^= S[24] S[0] ^= S[22] S[1] ^= S[23] S[2] ^= S[24] self.smix(18, 19, 20, 21) S[15] ^= S[19] S[16] ^= S[20] S[17] ^= S[21] S[33] ^= S[19] S[34] ^= S[20] S[35] ^= S[21] self.smix(15, 16, 17, 18) S[12] ^= S[16] S[13] ^= S[17] S[14] ^= S[18] S[30] ^= S[16] S[31] ^= S[17] S[32] ^= S[18] self.smix(12, 13, 14, 15) self.rshift = 2 elif rs == 2: S[34] ^= S[12] S[12] = w S[20] ^= S[12] S[13] ^= S[0] S[16] ^= S[3] S[19] ^= S[6] S[9] ^= S[13] S[10] ^= S[14] S[11] ^= S[15] S[27] ^= S[13] S[28] ^= S[14] S[29] ^= S[15] self.smix(9, 10, 11, 12) S[6] ^= S[10] S[7] ^= S[11] S[8] ^= S[12] S[24] ^= S[10] S[25] ^= S[11] S[26] ^= S[12] self.smix(6, 7, 8, 9) S[3] ^= S[7] S[4] ^= S[8] S[5] ^= S[9] S[21] ^= S[7] S[22] ^= S[8] S[23] ^= S[9] self.smix(3, 4, 5, 6) S[0] ^= S[4] S[1] ^= S[5] S[2] ^= S[6] S[18] ^= S[4] S[19] ^= S[5] S[20] ^= S[6] self.smix(0, 1, 2, 3) self.rshift = 0 else: S[22] ^= S[0] S[0] = w S[8] ^= S[0] S[1] ^= S[24] S[4] ^= S[27] S[7] ^= S[30] S[33] ^= S[1] S[34] ^= S[2] S[35] ^= S[3] S[15] ^= S[1] S[16] ^= S[2] S[17] ^= S[3] self.smix(33, 34, 35, 0) S[30] ^= S[34] S[31] ^= S[35] S[32] ^= S[0] S[12] ^= S[34] S[13] ^= S[35] S[14] ^= S[0] self.smix(30, 31, 32, 33) S[27] ^= S[31] S[28] ^= S[32] S[29] ^= S[33] S[9] ^= S[31] S[10] ^= S[32] S[11] ^= S[33] self.smix(27, 28, 29, 30) S[24] ^= S[28] S[25] ^= S[29] S[26] ^= S[30] S[6] ^= S[28] S[7] ^= S[29] S[8] ^= S[30] self.smix(24, 25, 26, 27) self.rshift = 1 return FINAL_LOOP_STEPS = ( ((4, 9, 18, 27), 9), ((4, 10, 18, 27), 9), ((4, 10, 19, 27), 9), ((4, 10, 19, 28), 8), ) FINAL_XORS = (4, 9, 18, 27) FINAL_OUT_WORDS = (1, 2, 3, 4, 9, 10, 11, 12, 18, 19, 20, 21, 27, 28, 29, 30) def process_final(self, out): self.process_final_common( out=out, state_len=36, init_rot_mul=12, cmix_fn=self.cmix36, pre_rounds=32, loop_steps=self.FINAL_LOOP_STEPS, final_xors=self.FINAL_XORS, out_words=self.FINAL_OUT_WORDS, ) return class HamsiBase: alpha_n = ( 0xff00_f0f0, 0xcccc_aaaa, 0xf0f0_cccc, 0xff00_aaaa, 0xcccc_aaaa, 0xf0f0_ff00, 0xaaaa_cccc, 0xf0f0_ff00, 0xf0f0_cccc, 0xaaaa_ff00, 0xcccc_ff00, 0xaaaa_f0f0, 0xaaaa_f0f0, 0xff00_cccc, 0xcccc_f0f0, 0xff00_aaaa, 0xcccc_aaaa, 0xff00_f0f0, 0xff00_aaaa, 0xf0f0_cccc, 0xf0f0_ff00, 0xcccc_aaaa, 0xf0f0_ff00, 0xaaaa_cccc, 0xaaaa_ff00, 0xf0f0_cccc, 0xaaaa_f0f0, 0xcccc_ff00, 0xff00_cccc, 0xaaaa_f0f0, 0xff00_aaaa, 0xcccc_f0f0, ) alpha_f = ( 0xcaf9_639c, 0x0ff0_f9c0, 0x639c_0ff0, 0xcaf9_f9c0, 0x0ff0_f9c0, 0x639c_caf9, 0xf9c0_0ff0, 0x639c_caf9, 0x639c_0ff0, 0xf9c0_caf9, 0x0ff0_caf9, 0xf9c0_639c, 0xf9c0_639c, 0xcaf9_0ff0, 0x0ff0_639c, 0xcaf9_f9c0, 0x0ff0_f9c0, 0xcaf9_639c, 0xcaf9_f9c0, 0x639c_0ff0, 0x639c_caf9, 0x0ff0_f9c0, 0x639c_caf9, 0xf9c0_0ff0, 0xf9c0_caf9, 0x639c_0ff0, 0xf9c0_639c, 0x0ff0_caf9, 0xcaf9_0ff0, 0xf9c0_639c, 0xcaf9_f9c0, 0x0ff0_639c, ) t256 = ( 0x7495_1000, 0x5a2b_467e, 0x88fd_1d2b, 0x1ee6_8292, 0xcba9_0000, 0x9027_3769, 0xbbdc_f407, 0xd0f4_af61, 0xcba9_0000, 0x9027_3769, 0xbbdc_f407, 0xd0f4_af61, 0xbf3c_1000, 0xca0c_7117, 0x3321_e92c, 0xce12_2df3, 0xe92a_2000, 0xb457_8cfc, 0x11fa_3a57, 0x3dc9_0524, 0x9753_0000, 0x204f_6ed3, 0x77b9_e80f, 0xa1ec_5ec1, 0x9753_0000, 0x204f_6ed3, 0x77b9_e80f, 0xa1ec_5ec1, 0x7e79_2000, 0x9418_e22f, 0x6643_d258, 0x9c25_5be5, 0x121b_4000, 0x5b17_d9e8, 0x8dfa_cfab, 0xce36_cc72, 0xe657_0000, 0x4bb3_3a25, 0x8485_98ba, 0x1041_003e, 0xe657_0000, 0x4bb3_3a25, 0x8485_98ba, 0x1041_003e, 0xf44c_4000, 0x10a4_e3cd, 0x097f_5711, 0xde77_cc4c, 0xe478_8000, 0x8596_73c1, 0xb5fb_2452, 0x29cc_5edf, 0x045f_0000, 0x9c4a_93c9, 0x62fc_79d0, 0x731e_bdc2, 0x045f_0000, 0x9c4a_93c9, 0x62fc_79d0, 0x731e_bdc2, 0xe027_8000, 0x19dc_e008, 0xd707_5d82, 0x5ad2_e31d, 0xb7a4_0100, 0x8a1f_31d8, 0x8589_d8ab, 0xe6c4_6464, 0x734c_0000, 0x956f_a7d6, 0xa29d_1297, 0x6ee5_6854, 0x734c_0000, 0x956f_a7d6, 0xa29d_1297, 0x6ee5_6854, 0xc4e8_0100, 0x1f70_960e, 0x2714_ca3c, 0x8821_0c30, 0xa7b8_0200, 0x1f12_8433, 0x60e5_f9f2, 0x9e14_7576, 0xee26_0000, 0x124b_683e, 0x80c2_d68f, 0x3bf3_ab2c, 0xee26_0000, 0x124b_683e, 0x80c2_d68f, 0x3bf3_ab2c, 0x499e_0200, 0x0d59_ec0d, 0xe027_2f7d, 0xa5e7_de5a, 0x8f3e_0400, 0x0d9d_c877, 0x6fc5_48e1, 0x898d_2cd6, 0x14bd_0000, 0x2fba_37ff, 0x6a72_e5bb, 0x247f_ebe6, 0x14bd_0000, 0x2fba_37ff, 0x6a72_e5bb, 0x247f_ebe6, 0x9b83_0400, 0x2227_ff88, 0x05b7_ad5a, 0xadf2_c730, 0xde32_0800, 0x2883_50fe, 0x7185_2ac7, 0xa6bf_9f96, 0xe18b_0000, 0x5459_887d, 0xbf12_83d3, 0x1b66_6a73, 0xe18b_0000, 0x5459_887d, 0xbf12_83d3, 0x1b66_6a73, 0x3fb9_0800, 0x7cda_d883, 0xce97_a914, 0xbdd9_f5e5, 0x515c_0010, 0x40f3_72fb, 0xfce7_2602, 0x7157_5061, 0x2e39_0000, 0x64dd_6689, 0x3cd4_06fc, 0xb1f4_90bc, 0x2e39_0000, 0x64dd_6689, 0x3cd4_06fc, 0xb1f4_90bc, 0x7f65_0010, 0x242e_1472, 0xc033_20fe, 0xc0a3_c0dd, 0xa2b8_0020, 0x81e7_e5f6, 0xf9ce_4c04, 0xe2af_a0c0, 0x5c72_0000, 0xc9ba_cd12, 0x79a9_0df9, 0x63e9_2178, 0x5c72_0000, 0xc9ba_cd12, 0x79a9_0df9, 0x63e9_2178, 0xfeca_0020, 0x485d_28e4, 0x8067_41fd, 0x8146_81b8, 0x4dce_0040, 0x3b5b_ec7e, 0x3665_6ba8, 0x2363_3a05, 0x78ab_0000, 0xa0cd_5a34, 0x5d5c_a0f7, 0x7277_84cb, 0x78ab_0000, 0xa0cd_5a34, 0x5d5c_a0f7, 0x7277_84cb, 0x3565_0040, 0x9b96_b64a, 0x6b39_cb5f, 0x5114_bece, 0x5bd2_0080, 0x450f_18ec, 0xc2c4_6c55, 0xf362_b233, 0x39a6_0000, 0x4ab7_53eb, 0xd14e_094b, 0xb772_b42b, 0x39a6_0000, 0x4ab7_53eb, 0xd14e_094b, 0xb772_b42b, 0x6274_0080, 0x0fb8_4b07, 0x138a_651e, 0x4410_0618, 0xc04e_0001, 0x33b9_c010, 0xae0e_bb05, 0xb5a4_c63b, 0xc8f1_0000, 0x0b2d_e782, 0x6bf6_48a4, 0x539c_bdbf, 0xc8f1_0000, 0x0b2d_e782, 0x6bf6_48a4, 0x539c_bdbf, 0x08bf_0001, 0x3894_2792, 0xc5f8_f3a1, 0xe638_7b84, 0x8823_0002, 0x5fe7_a7b3, 0x99e5_85aa, 0x8d75_f7f1, 0x51ac_0000, 0x25e3_0f14, 0x79e2_2a4c, 0x1298_bd46, 0x51ac_0000, 0x25e3_0f14, 0x79e2_2a4c, 0x1298_bd46, 0xd98f_0002, 0x7a04_a8a7, 0xe007_afe6, 0x9fed_4ab7, 0xd008_0004, 0x8c76_8f77, 0x9dc5_b050, 0xaf4a_29da, 0x6ba9_0000, 0x40eb_f9aa, 0x9832_1c3d, 0x76ac_c733, 0x6ba9_0000, 0x40eb_f9aa, 0x9832_1c3d, 0x76ac_c733, 0xbba1_0004, 0xcc9d_76dd, 0x05f7_ac6d, 0xd9e6_eee9, 0xa8ae_0008, 0x2079_397d, 0xfe73_9301, 0xb8a9_2831, 0x171c_0000, 0xb26e_3344, 0x9e6a_837e, 0x58f8_485f, 0x171c_0000, 0xb26e_3344, 0x9e6a_837e, 0x58f8_485f, 0xbfb2_0008, 0x9217_0a39, 0x6019_107f, 0xe051_606e, ) t512 = ( 0xef0b_0270, 0x3afd_0000, 0x5dae_0000, 0x6949_0000, 0x9b0f_3c06, 0x4405_b5f9, 0x6614_0a51, 0x924f_5d0a, 0xc96b_0030, 0xe725_0000, 0x2f84_0000, 0x264f_0000, 0x0869_5bf9, 0x6dfc_f137, 0x509f_6984, 0x9e69_af68, 0xc96b_0030, 0xe725_0000, 0x2f84_0000, 0x264f_0000, 0x0869_5bf9, 0x6dfc_f137, 0x509f_6984, 0x9e69_af68, 0x2660_0240, 0xddd8_0000, 0x722a_0000, 0x4f06_0000, 0x9366_67ff, 0x29f9_44ce, 0x368b_63d5, 0x0c26_f262, 0x145a_3c00, 0xb9e9_0000, 0x6127_0000, 0xf161_0000, 0xce61_3d6c, 0xb049_3d78, 0x47a9_6720, 0xe18e_24c5, 0x2367_1400, 0xc8b9_0000, 0xf4c7_0000, 0xfb75_0000, 0x73cd_2465, 0xf8a6_a549, 0x02c4_0a3f, 0xdc24_e61f, 0x2367_1400, 0xc8b9_0000, 0xf4c7_0000, 0xfb75_0000, 0x73cd_2465, 0xf8a6_a549, 0x02c4_0a3f, 0xdc24_e61f, 0x373d_2800, 0x7150_0000, 0x95e0_0000, 0x0a14_0000, 0xbdac_1909, 0x48ef_9831, 0x456d_6d1f, 0x3daa_c2da, 0x5428_5c00, 0xeaed_0000, 0xc5d6_0000, 0xa1c5_0000, 0xb3a2_6770, 0x94a5_c4e1, 0x6bb0_419d, 0x551b_3782, 0x9cbb_1800, 0xb0d3_0000, 0x9251_0000, 0xed93_0000, 0x593a_4345, 0xe114_d5f4, 0x4306_33da, 0x78ca_ce29, 0x9cbb_1800, 0xb0d3_0000, 0x9251_0000, 0xed93_0000, 0x593a_4345, 0xe114_d5f4, 0x4306_33da, 0x78ca_ce29, 0xc893_4400, 0x5a3e_0000, 0x5787_0000, 0x4c56_0000, 0xea98_2435, 0x75b1_1115, 0x28b6_7247, 0x2dd1_f9ab, 0x2944_9c00, 0x64e7_0000, 0xf24b_0000, 0xc2f3_0000, 0x0ede_4e8f, 0x56c2_3745, 0xf3e0_4259, 0x8d0d_9ec4, 0x466d_0c00, 0x0862_0000, 0xdd5d_0000, 0xbadd_0000, 0x6a92_7942, 0x441f_2b93, 0x218a_ce6f, 0xbf2c_0be2, 0x466d_0c00, 0x0862_0000, 0xdd5d_0000, 0xbadd_0000, 0x6a92_7942, 0x441f_2b93, 0x218a_ce6f, 0xbf2c_0be2, 0x6f29_9000, 0x6c85_0000, 0x2f16_0000, 0x782e_0000, 0x644c_37cd, 0x12dd_1cd6, 0xd26a_8c36, 0x3221_9526, 0xf680_0005, 0x3443_c000, 0x2407_0000, 0x8f3d_0000, 0x2137_3bfb, 0x0ab8_d5ae, 0xcdc5_8b19, 0xd795_ba31, 0xa67f_0001, 0x7137_8000, 0x19fc_0000, 0x96db_0000, 0x3a8b_6dfd, 0xebca_aef3, 0x2c6d_478f, 0xac8e_6c88, 0xa67f_0001, 0x7137_8000, 0x19fc_0000, 0x96db_0000, 0x3a8b_6dfd, 0xebca_aef3, 0x2c6d_478f, 0xac8e_6c88, 0x50ff_0004, 0x4574_4000, 0x3dfb_0000, 0x19e6_0000, 0x1bbc_5606, 0xe172_7b5d, 0xe1a8_cc96, 0x7b1b_d6b9, 0xf775_0009, 0xcf3c_c000, 0xc3d6_0000, 0x0492_0000, 0x0295_19a9, 0xf8e8_36ba, 0x7a87_f14e, 0x9e16_981a, 0xd46a_0000, 0x8dc8_c000, 0xa5af_0000, 0x4a29_0000, 0xfc4e_427a, 0xc9b4_866c, 0x9836_9604, 0xf746_c320, 0xd46a_0000, 0x8dc8_c000, 0xa5af_0000, 0x4a29_0000, 0xfc4e_427a, 0xc9b4_866c, 0x9836_9604, 0xf746_c320, 0x231f_0009, 0x42f4_0000, 0x6679_0000, 0x4ebb_0000, 0xfedb_5bd3, 0x315c_b0d6, 0xe2b1_674a, 0x6950_5b3a, 0x7744_00f0, 0xf15a_0000, 0xf5b2_0000, 0x3414_0000, 0x8937_7e8c, 0x5a8b_ec25, 0x0bc3_cd1e, 0xcf37_75cb, 0xf46c_0050, 0x9618_0000, 0x14a5_0000, 0x031f_0000, 0x4294_7eb8, 0x66bf_7e19, 0x9ca4_70d2, 0x8a34_1574, 0xf46c_0050, 0x9618_0000, 0x14a5_0000, 0x031f_0000, 0x4294_7eb8, 0x66bf_7e19, 0x9ca4_70d2, 0x8a34_1574, 0x8328_00a0, 0x6742_0000, 0xe117_0000, 0x370b_0000, 0xcba3_0034, 0x3c34_923c, 0x9767_bdcc, 0x4503_60bf, 0xe887_0170, 0x9d72_0000, 0x12db_0000, 0xd422_0000, 0xf288_6b27, 0xa921_e543, 0x4ef8_b518, 0x6188_13b1, 0xb437_0060, 0x0c4c_0000, 0x56c2_0000, 0x5cae_0000, 0x9454_1f3f, 0x3b3e_f825, 0x1b36_5f3d, 0xf3d4_5758, 0xb437_0060, 0x0c4c_0000, 0x56c2_0000, 0x5cae_0000, 0x9454_1f3f, 0x3b3e_f825, 0x1b36_5f3d, 0xf3d4_5758, 0x5cb0_0110, 0x913e_0000, 0x4419_0000, 0x888c_0000, 0x66dc_7418, 0x921f_1d66, 0x55ce_ea25, 0x925c_44e9, 0x0c72_0000, 0x49e5_0f00, 0x4279_0000, 0x5cea_0000, 0x33aa_301a, 0x1582_2514, 0x95a3_4b7b, 0xb44b_0090, 0xfe22_0000, 0xa758_0500, 0x25d1_0000, 0xf760_0000, 0x8931_78da, 0x1fd4_f860, 0x4ed0_a315, 0xa123_ff9f, 0xfe22_0000, 0xa758_0500, 0x25d1_0000, 0xf760_0000, 0x8931_78da, 0x1fd4_f860, 0x4ed0_a315, 0xa123_ff9f, 0xf250_0000, 0xeebd_0a00, 0x67a8_0000, 0xab8a_0000, 0xba9b_48c0, 0x0a56_dd74, 0xdb73_e86e, 0x1568_ff0f, 0x4518_0000, 0xa5b5_1700, 0xf96a_0000, 0x3b48_0000, 0x1ecc_142c, 0x2313_95d6, 0x16bc_a6b0, 0xdf33_f4df, 0xb83d_0000, 0x1671_0600, 0x379a_0000, 0xf5b1_0000, 0x2281_61ac, 0xae48_f145, 0x6624_1616, 0xc5c1_eb3e, 0xb83d_0000, 0x1671_0600, 0x379a_0000, 0xf5b1_0000, 0x2281_61ac, 0xae48_f145, 0x6624_1616, 0xc5c1_eb3e, 0xfd25_0000, 0xb3c4_1100, 0xcef0_0000, 0xcef9_0000, 0x3c4d_7580, 0x8d5b_6493, 0x7098_b0a6, 0x1af2_1fe1, 0x75a4_0000, 0xc28b_2700, 0x94a4_0000, 0x90f5_0000, 0xfb78_57e0, 0x49ce_0bae, 0x1767_c483, 0xaedf_667e, 0xd166_0000, 0x1bbc_0300, 0x9eec_0000, 0xf694_0000, 0x0302_4527, 0xcf70_fcf2, 0xb443_1b17, 0x857f_3c2b, 0xd166_0000, 0x1bbc_0300, 0x9eec_0000, 0xf694_0000, 0x0302_4527, 0xcf70_fcf2, 0xb443_1b17, 0x857f_3c2b, 0xa4c2_0000, 0xd937_2400, 0x0a48_0000, 0x6661_0000, 0xf87a_12c7, 0x86be_f75c, 0xa324_df94, 0x2ba0_5a55, 0x75c9_0003, 0x0e10_c000, 0xd120_0000, 0xbaea_0000, 0x8bc4_2f3e, 0x8758_b757, 0xbb28_761d, 0x00b7_2e2b, 0xeecf_0001, 0x6f56_4000, 0xf33e_0000, 0xa79e_0000, 0xbdb5_7219, 0xb711_ebc5, 0x4a3b_40ba, 0xfeab_f254, 0xeecf_0001, 0x6f56_4000, 0xf33e_0000, 0xa79e_0000, 0xbdb5_7219, 0xb711_ebc5, 0x4a3b_40ba, 0xfeab_f254, 0x9b06_0002, 0x6146_8000, 0x221e_0000, 0x1d74_0000, 0x3671_5d27, 0x3049_5c92, 0xf113_36a7, 0xfe1c_dc7f, 0x8679_0000, 0x3f39_0002, 0xe19a_e000, 0x9856_0000, 0x9565_670e, 0x4e88_c8ea, 0xd3dd_4944, 0x161d_dab9, 0x30b7_0000, 0xe5d0_0000, 0xf4f4_6000, 0x42c4_0000, 0x63b8_3d6a, 0x78ba_9460, 0x21af_a1ea, 0xb0a5_1834, 0x30b7_0000, 0xe5d0_0000, 0xf4f4_6000, 0x42c4_0000, 0x63b8_3d6a, 0x78ba_9460, 0x21af_a1ea, 0xb0a5_1834, 0xb6ce_0000, 0xdae9_0002, 0x156e_8000, 0xda92_0000, 0xf6dd_5a64, 0x3632_5c8a, 0xf272_e8ae, 0xa6b8_c28d, 0x1419_0000, 0x23ca_003c, 0x50df_0000, 0x44b6_0000, 0x1b6c_67b0, 0x3cf3_ac75, 0x61e6_10b0, 0xdbca_db80, 0xe343_0000, 0x3a4e_0014, 0xf2c6_0000, 0xaa4e_0000, 0xdb1e_42a6, 0x256b_be15, 0x123d_b156, 0x3a4e_99d7, 0xe343_0000, 0x3a4e_0014, 0xf2c6_0000, 0xaa4e_0000, 0xdb1e_42a6, 0x256b_be15, 0x123d_b156, 0x3a4e_99d7, 0xf75a_0000, 0x1984_0028, 0xa219_0000, 0xeef8_0000, 0xc072_2516, 0x1998_1260, 0x73db_a1e6, 0xe184_4257, 0x5450_0000, 0x0671_005c, 0x25ae_0000, 0x6a1e_0000, 0x2ea5_4edf, 0x664e_8512, 0xbfba_18c3, 0x7e71_5d17, 0xbc8d_0000, 0xfc3b_0018, 0x1983_0000, 0xd10b_0000, 0xae18_78c4, 0x42a6_9856, 0x0012_da37, 0x2c3b_504e, 0xbc8d_0000, 0xfc3b_0018, 0x1983_0000, 0xd10b_0000, 0xae18_78c4, 0x42a6_9856, 0x0012_da37, 0x2c3b_504e, 0xe8dd_0000, 0xfa4a_0044, 0x3c2d_0000, 0xbb15_0000, 0x80bd_361b, 0x24e8_1d44, 0xbfa8_c2f4, 0x524a_0d59, 0x6951_0000, 0xd4e1_009c, 0xc323_0000, 0xac2f_0000, 0xe495_0bae, 0xcea4_15dc, 0x87ec_287c, 0xbce1_a3ce, 0xc673_0000, 0xaf8d_000c, 0xa4c1_0000, 0x218d_0000, 0x2311_1587, 0x7913_512f, 0x1d28_ac88, 0x378d_d173, 0xc673_0000, 0xaf8d_000c, 0xa4c1_0000, 0x218d_0000, 0x2311_1587, 0x7913_512f, 0x1d28_ac88, 0x378d_d173, 0xaf22_0000, 0x7b6c_0090, 0x67e2_0000, 0x8da2_0000, 0xc784_1e29, 0xb7b7_44f3, 0x9ac4_84f4, 0x8b6c_72bd, 0xcc14_0000, 0xa563_0000, 0x5ab9_0780, 0x3b50_0000, 0x4bd0_13ff, 0x879b_3418, 0x6943_48c1, 0xca5a_87fe, 0x819e_0000, 0xec57_0000, 0x6632_0280, 0x95f3_0000, 0x5da9_2802, 0x48f4_3cbc, 0xe65a_a22d, 0x8e67_b7fa, 0x819e_0000, 0xec57_0000, 0x6632_0280, 0x95f3_0000, 0x5da9_2802, 0x48f4_3cbc, 0xe65a_a22d, 0x8e67_b7fa, 0x4d8a_0000, 0x4934_0000, 0x3c8b_0500, 0xaea3_0000, 0x1679_3bfd, 0xcf6f_08a4, 0x8f19_eaec, 0x443d_3004, 0x7823_0000, 0x12fc_0000, 0xa93a_0b80, 0x90a5_0000, 0x713e_2879, 0x7ee9_8924, 0xf08c_a062, 0x636f_8bab, 0x02af_0000, 0xb728_0000, 0xba1c_0300, 0x5698_0000, 0xba8d_45d3, 0x8048_c667, 0xa95c_149a, 0xf4f6_ea7b, 0x02af_0000, 0xb728_0000, 0xba1c_0300, 0x5698_0000, 0xba8d_45d3, 0x8048_c667, 0xa95c_149a, 0xf4f6_ea7b, 0x7a8c_0000, 0xa5d4_0000, 0x1326_0880, 0xc63d_0000, 0xcbb3_6daa, 0xfea1_4f43, 0x59d0_b4f8, 0x9799_61d0, 0xac48_0000, 0x1ba6_0000, 0x45fb_1380, 0x0343_0000, 0x5a85_316a, 0x1fb2_50b6, 0xfe72_c7fe, 0x91e4_78f6, 0x1e4e_0000, 0xdecf_0000, 0x6df8_0180, 0x7724_0000, 0xec47_079e, 0xf4a0_694e, 0xcda3_1812, 0x98aa_496e, 0x1e4e_0000, 0xdecf_0000, 0x6df8_0180, 0x7724_0000, 0xec47_079e, 0xf4a0_694e, 0xcda3_1812, 0x98aa_496e, 0xb206_0000, 0xc569_0000, 0x2803_1200, 0x7467_0000, 0xb6c2_36f4, 0xeb12_39f8, 0x33d1_dfec, 0x094e_3198, 0xaec3_0000, 0x9c4f_0001, 0x79d1_e000, 0x2c15_0000, 0x45cc_75b3, 0x6650_b736, 0xab92_f78f, 0xa312_567b, 0xdb25_0000, 0x0929_0000, 0x49aa_c000, 0x81e1_0000, 0xcafe_6b59, 0x4279_3431, 0x4356_6b76, 0xe86c_ba2e, 0xdb25_0000, 0x0929_0000, 0x49aa_c000, 0x81e1_0000, 0xcafe_6b59, 0x4279_3431, 0x4356_6b76, 0xe86c_ba2e, 0x75e6_0000, 0x9566_0001, 0x307b_2000, 0xadf4_0000, 0x8f32_1eea, 0x2429_8307, 0xe8c4_9cf9, 0x4b7e_ec55, 0x5843_0000, 0x807e_0000, 0x7833_0001, 0xc66b_3800, 0xe737_5cdc, 0x79ad_3fdd, 0xac73_fe6f, 0x3a44_79b1, 0x1d5a_0000, 0x2b72_0000, 0x488d_0000, 0xaf61_1800, 0x25cb_2ec5, 0xc879_bfd0, 0x81a2_0429, 0x1e75_36a6, 0x1d5a_0000, 0x2b72_0000, 0x488d_0000, 0xaf61_1800, 0x25cb_2ec5, 0xc879_bfd0, 0x81a2_0429, 0x1e75_36a6, 0x4519_0000, 0xab0c_0000, 0x30be_0001, 0x690a_2000, 0xc2fc_7219, 0xb1d4_800d, 0x2dd1_fa46, 0x2431_4f17, 0xa53b_0000, 0x1426_0000, 0x4e30_001e, 0x7cae_0000, 0x8f9e_0dd5, 0x78df_aa3d, 0xf731_68d8, 0x0b1b_4946, 0x07ed_0000, 0xb250_0000, 0x8774_000a, 0x970d_0000, 0x4372_23ae, 0x48c7_6ea4, 0xf478_6222, 0x9075_b1ce, 0x07ed_0000, 0xb250_0000, 0x8774_000a, 0x970d_0000, 0x4372_23ae, 0x48c7_6ea4, 0xf478_6222, 0x9075_b1ce, 0xa2d6_0000, 0xa676_0000, 0xc944_0014, 0xeba3_0000, 0xccec_2e7b, 0x3018_c499, 0x0349_0afa, 0x9b6e_f888, 0x8898_0000, 0x1f94_0000, 0x7fcf_002e, 0xfb4e_0000, 0xf158_079a, 0x61ae_9167, 0xa895_706c, 0xe610_7494, 0x0bc2_0000, 0xdb63_0000, 0x7e88_000c, 0x1586_0000, 0x91fd_48f3, 0x7581_bb43, 0xf460_449e, 0xd8b6_1463, 0x0bc2_0000, 0xdb63_0000, 0x7e88_000c, 0x1586_0000, 0x91fd_48f3, 0x7581_bb43, 0xf460_449e, 0xd8b6_1463, 0x835a_0000, 0xc4f7_0000, 0x0147_0022, 0xeec8_0000, 0x60a5_4f69, 0x142f_2a24, 0x5cf5_34f2, 0x3ea6_60f7, 0x5250_0000, 0x2954_0000, 0x6a61_004e, 0xf0ff_0000, 0x9a31_7eec, 0x4523_41ce, 0xcf56_8fe5, 0x5303_130f, 0x538d_0000, 0xa9fc_0000, 0x9ef7_0006, 0x56ff_0000, 0x0ae4_004e, 0x92c5_cdf9, 0xa944_4018, 0x7f97_5691, 0x538d_0000, 0xa9fc_0000, 0x9ef7_0006, 0x56ff_0000, 0x0ae4_004e, 0x92c5_cdf9, 0xa944_4018, 0x7f97_5691, 0x01dd_0000, 0x80a8_0000, 0xf496_0048, 0xa600_0000, 0x90d5_7ea2, 0xd7e6_8c37, 0x6612_cffd, 0x2c94_459e, 0xe628_0000, 0x4c4b_0000, 0xa855_0000, 0xd3d0_02e0, 0xd861_30b8, 0x98a7_b0da, 0x2895_06b4, 0xd75a_4897, 0xf0c5_0000, 0x5923_0000, 0x4582_0000, 0xe18d_00c0, 0x3b6d_0631, 0xc2ed_5699, 0xcbe0_fe1c, 0x56a7_b19f, 0xf0c5_0000, 0x5923_0000, 0x4582_0000, 0xe18d_00c0, 0x3b6d_0631, 0xc2ed_5699, 0xcbe0_fe1c, 0x56a7_b19f, 0x16ed_0000, 0x1568_0000, 0xedd7_0000, 0x325d_0220, 0xe30c_3689, 0x5a4a_e643, 0xe375_f8a8, 0x81fd_f908, 0xb431_0000, 0x7733_0000, 0xb15d_0000, 0x7fd0_04e0, 0x78a2_6138, 0xd116_c35d, 0xd256_d489, 0x4e6f_74de, 0xe306_0000, 0xbdc1_0000, 0x8713_0000, 0xbff2_0060, 0x2eba_0a1a, 0x8db5_3751, 0x73c5_ab06, 0x5bd6_1539, 0xe306_0000, 0xbdc1_0000, 0x8713_0000, 0xbff2_0060, 0x2eba_0a1a, 0x8db5_3751, 0x73c5_ab06, 0x5bd6_1539, 0x5737_0000, 0xcaf2_0000, 0x364e_0000, 0xc022_0480, 0x5618_6b22, 0x5ca3_f40c, 0xa193_7f8f, 0x15b9_61e7, 0x02f2_0000, 0xa281_0000, 0x873f_0000, 0xe36c_7800, 0x1e1d_74ef, 0x073d_2bd6, 0xc4c2_3237, 0x7f32_259e, 0xbadd_0000, 0x13ad_0000, 0xb7e7_0000, 0xf728_2800, 0xdf45_144d, 0x361a_c33a, 0xea5a_8d14, 0x2a2c_18f0, 0xbadd_0000, 0x13ad_0000, 0xb7e7_0000, 0xf728_2800, 0xdf45_144d, 0x361a_c33a, 0xea5a_8d14, 0x2a2c_18f0, 0xb82f_0000, 0xb12c_0000, 0x30d8_0000, 0x1444_5000, 0xc158_60a2, 0x3127_e8ec, 0x2e98_bf23, 0x551e_3d6e, 0x1e6c_0000, 0xc442_0000, 0x8a2e_0000, 0xbcb6_b800, 0x2c44_13b6, 0x8bfd_d3da, 0x6a0c_1bc8, 0xb99d_c2eb, 0x9256_0000, 0x1eda_0000, 0xea51_0000, 0xe8b1_3000, 0xa935_56a5, 0xebfb_6199, 0xb15c_2254, 0x33c5_244f, 0x9256_0000, 0x1eda_0000, 0xea51_0000, 0xe8b1_3000, 0xa935_56a5, 0xebfb_6199, 0xb15c_2254, 0x33c5_244f, 0x8c3a_0000, 0xda98_0000, 0x607f_0000, 0x5407_8800, 0x8571_4513, 0x6006_b243, 0xdb50_399c, 0x8a58_e6a4, 0x033d_0000, 0x08b3_0000, 0xf33a_0000, 0x3ac2_0007, 0x5129_8a50, 0x6b6e_661f, 0x0ea5_cfe3, 0xe6da_7ffe, 0xa8da_0000, 0x96be_0000, 0x5c1d_0000, 0x07da_0002, 0x7d66_9583, 0x1f98_708a, 0xbb66_8808, 0xda87_8000, 0xa8da_0000, 0x96be_0000, 0x5c1d_0000, 0x07da_0002, 0x7d66_9583, 0x1f98_708a, 0xbb66_8808, 0xda87_8000, 0xabe7_0000, 0x9e0d_0000, 0xaf27_0000, 0x3d18_0005, 0x2c4f_1fd3, 0x74f6_1695, 0xb5c3_47eb, 0x3c5d_fffe, 0x0193_0000, 0xe782_0000, 0xedfb_0000, 0xcf0c_000b, 0x8dd0_8d58, 0xbca3_b42e, 0x0636_61e1, 0x536f_9e7b, 0x9228_0000, 0xdc85_0000, 0x57fa_0000, 0x56dc_0003, 0xbae9_2316, 0x5aef_a30c, 0x90ce_f752, 0x7b16_75d7, 0x9228_0000, 0xdc85_0000, 0x57fa_0000, 0x56dc_0003, 0xbae9_2316, 0x5aef_a30c, 0x90ce_f752, 0x7b16_75d7, 0x93bb_0000, 0x3b07_0000, 0xba01_0000, 0x99d0_0008, 0x3739_ae4e, 0xe64c_1722, 0x96f8_96b3, 0x2879_ebac, 0x5fa8_0000, 0x5603_0000, 0x43ae_0000, 0x64f3_0013, 0x257e_86bf, 0x1311_944e, 0x541e_95bf, 0x8ea4_db69, 0x0044_0000, 0x7f48_0000, 0xda7c_0000, 0x2a23_0001, 0x3bad_c9cc, 0xa9b6_9c87, 0x030a_9e60, 0xbe0a_679e, 0x0044_0000, 0x7f48_0000, 0xda7c_0000, 0x2a23_0001, 0x3bad_c9cc, 0xa9b6_9c87, 0x030a_9e60, 0xbe0a_679e, 0x5fec_0000, 0x294b_0000, 0x99d2_0000, 0x4ed0_0012, 0x1ed3_4f73, 0xbaa7_08c9, 0x5714_0bdf, 0x30ae_bcf7, 0xee93_0000, 0xd607_0000, 0x92c1_0000, 0x2b98_01e0, 0x9451_287c, 0x3b6c_fb57, 0x4531_2374, 0x201f_6a64, 0x7b28_0000, 0x5742_0000, 0xa9e5_0000, 0x6343_00a0, 0x9edb_442f, 0x6d99_95bb, 0x27f8_3b03, 0xc7ff_60f0, 0x7b28_0000, 0x5742_0000, 0xa9e5_0000, 0x6343_00a0, 0x9edb_442f, 0x6d99_95bb, 0x27f8_3b03, 0xc7ff_60f0, 0x95bb_0000, 0x8145_0000, 0x3b24_0000, 0x48db_0140, 0x0a8a_6c53, 0x56f5_6eec, 0x62c9_1877, 0xe7e0_0a94, ) C_TEMPLATE = r""" #include #include static inline void sbox(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) { uint32_t t = *a; *a = (*a & *c) ^ *d; *c ^= *b; *c ^= *a; *d = (*d | t) ^ *b; t ^= *c; *b = *d; *d = (*d | t) ^ *a; *a &= *b; t ^= *a; *b ^= *d; *b ^= t; *a = *c; *c = *b; *b = *d; *d = (~t) & 0xffffffffu; } static inline void linear(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) { *a = ((*a << 13) | (*a >> 19)) & 0xffffffffu; *c = ((*c << 3) | (*c >> 29)) & 0xffffffffu; *b ^= *a ^ *c; *d ^= *c ^ ((*a << 3) & 0xffffffffu); *b = ((*b << 1) | (*b >> 31)) & 0xffffffffu; *d = ((*d << 7) | (*d >> 25)) & 0xffffffffu; *a ^= *b ^ *d; *c ^= *d ^ ((*b << 7) & 0xffffffffu); *a = ((*a << 5) | (*a >> 27)) & 0xffffffffu; *c = ((*c << 22) | (*c >> 10)) & 0xffffffffu; } void compress_small( uint32_t *h, const uint8_t *block, int final_round, const uint32_t *alpha, const uint32_t *tables) { const uint32_t *t0 = tables + (size_t)block[0] * 8; const uint32_t *t1 = tables + 256*8 + (size_t)block[1] * 8; const uint32_t *t2 = tables + 512*8 + (size_t)block[2] * 8; const uint32_t *t3 = tables + 768*8 + (size_t)block[3] * 8; uint32_t m[8]; for (int i = 0; i < 8; i++) m[i] = t0[i] ^ t1[i] ^ t2[i] ^ t3[i]; uint32_t s0 = m[0], s1 = m[1], s2 = h[0], s3 = h[1]; uint32_t s4 = h[2], s5 = h[3], s6 = m[2], s7 = m[3]; uint32_t s8 = m[4], s9 = m[5], s10 = h[4], s11 = h[5]; uint32_t s12 = h[6], s13 = h[7], s14 = m[6], s15 = m[7]; int rounds = final_round ? 6 : 3; for (int rc = 0; rc < rounds; rc++) { s0 ^= alpha[0x00]; s1 ^= alpha[0x01] ^ (uint32_t)rc; s2 ^= alpha[0x02]; s3 ^= alpha[0x03]; s4 ^= alpha[0x08]; s5 ^= alpha[0x09]; s6 ^= alpha[0x0a]; s7 ^= alpha[0x0b]; s8 ^= alpha[0x10]; s9 ^= alpha[0x11]; s10 ^= alpha[0x12]; s11 ^= alpha[0x13]; s12 ^= alpha[0x18]; s13 ^= alpha[0x19]; s14 ^= alpha[0x1a]; s15 ^= alpha[0x1b]; sbox(&s0, &s4, &s8, &s12); sbox(&s1, &s5, &s9, &s13); sbox(&s2, &s6, &s10, &s14); sbox(&s3, &s7, &s11, &s15); linear(&s0, &s5, &s10, &s15); linear(&s1, &s6, &s11, &s12); linear(&s2, &s7, &s8, &s13); linear(&s3, &s4, &s9, &s14); } h[0] ^= s0; h[1] ^= s1; h[2] ^= s2; h[3] ^= s3; h[4] ^= s8; h[5] ^= s9; h[6] ^= s10; h[7] ^= s11; } void compress_big( uint32_t *h, const uint8_t *block, int final_round, const uint32_t *alpha, const uint32_t *tables) { const size_t stride = 256 * 16; const uint32_t *tt[8]; for (int i = 0; i < 8; i++) tt[i] = tables + i * stride + (size_t)block[i] * 16; uint32_t m[16]; for (int i = 0; i < 16; i++) m[i] = tt[0][i] ^ tt[1][i] ^ tt[2][i] ^ tt[3][i] ^ tt[4][i] ^ tt[5][i] ^ tt[6][i] ^ tt[7][i]; uint32_t s00=m[0], s01=m[1], s02=h[0], s03=h[1]; uint32_t s04=m[2], s05=m[3], s06=h[2], s07=h[3]; uint32_t s08=h[4], s09=h[5], s0a=m[4], s0b=m[5]; uint32_t s0c=h[6], s0d=h[7], s0e=m[6], s0f=m[7]; uint32_t s10=m[8], s11=m[9], s12=h[8], s13=h[9]; uint32_t s14=m[10], s15=m[11], s16=h[10], s17=h[11]; uint32_t s18=h[12], s19=h[13], s1a=m[12], s1b=m[13]; uint32_t s1c=h[14], s1d=h[15], s1e=m[14], s1f=m[15]; int rounds = final_round ? 12 : 6; for (int rc = 0; rc < rounds; rc++) { s00 ^= alpha[0x00]; s01 ^= alpha[0x01] ^ (uint32_t)rc; s02 ^= alpha[0x02]; s03 ^= alpha[0x03]; s04 ^= alpha[0x04]; s05 ^= alpha[0x05]; s06 ^= alpha[0x06]; s07 ^= alpha[0x07]; s08 ^= alpha[0x08]; s09 ^= alpha[0x09]; s0a ^= alpha[0x0a]; s0b ^= alpha[0x0b]; s0c ^= alpha[0x0c]; s0d ^= alpha[0x0d]; s0e ^= alpha[0x0e]; s0f ^= alpha[0x0f]; s10 ^= alpha[0x10]; s11 ^= alpha[0x11]; s12 ^= alpha[0x12]; s13 ^= alpha[0x13]; s14 ^= alpha[0x14]; s15 ^= alpha[0x15]; s16 ^= alpha[0x16]; s17 ^= alpha[0x17]; s18 ^= alpha[0x18]; s19 ^= alpha[0x19]; s1a ^= alpha[0x1a]; s1b ^= alpha[0x1b]; s1c ^= alpha[0x1c]; s1d ^= alpha[0x1d]; s1e ^= alpha[0x1e]; s1f ^= alpha[0x1f]; sbox(&s00,&s08,&s10,&s18); sbox(&s01,&s09,&s11,&s19); sbox(&s02,&s0a,&s12,&s1a); sbox(&s03,&s0b,&s13,&s1b); sbox(&s04,&s0c,&s14,&s1c); sbox(&s05,&s0d,&s15,&s1d); sbox(&s06,&s0e,&s16,&s1e); sbox(&s07,&s0f,&s17,&s1f); linear(&s00,&s09,&s12,&s1b); linear(&s01,&s0a,&s13,&s1c); linear(&s02,&s0b,&s14,&s1d); linear(&s03,&s0c,&s15,&s1e); linear(&s04,&s0d,&s16,&s1f); linear(&s05,&s0e,&s17,&s18); linear(&s06,&s0f,&s10,&s19); linear(&s07,&s08,&s11,&s1a); linear(&s00,&s02,&s05,&s07); linear(&s10,&s13,&s15,&s16); linear(&s09,&s0b,&s0c,&s0e); linear(&s19,&s1a,&s1c,&s1f); } h[0] ^= s00; h[1] ^= s01; h[2] ^= s02; h[3] ^= s03; h[4] ^= s04; h[5] ^= s05; h[6] ^= s06; h[7] ^= s07; h[8] ^= s10; h[9] ^= s11; h[10] ^= s12; h[11] ^= s13; h[12] ^= s14; h[13] ^= s15; h[14] ^= s16; h[15] ^= s17; } """ DEF_TEMPLATE = r""" void compress_small(uint32_t *h, const uint8_t *block, int final_round, const uint32_t *alpha, const uint32_t *tables); void compress_big(uint32_t *h, const uint8_t *block, int final_round, const uint32_t *alpha, const uint32_t *tables); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.HamsiBase if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib if base_class in base_class.cffi_cache: ffi, lib = base_class.cffi_cache[base_class] else: try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) base_class.cffi_cache[base_class] = (ffi, lib) except Exception: self.USE_CFFI = False return # Different for each class def pack_alpha(alpha_seq): buf = ffi.new("uint32_t[]", len(alpha_seq)) for i, v in enumerate(alpha_seq): buf[i] = v & 0xffff_ffff return buf def pack_tables(tables, size): total_words = size * 256 * (size * 2) buf = ffi.new("uint32_t[]", total_words) pos = 0 for t in tables: for row in t: for v in row: buf[pos] = v & 0xffff_ffff pos += 1 return buf try: alpha_n = pack_alpha(self.alpha_n) alpha_f = pack_alpha(self.alpha_f) if self.block_size == 4: tables = pack_tables(self.expand_small_tables, 4) elif self.block_size == 8: tables = pack_tables(self.expand_big_tables, 8) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib alpha_n alpha_f tables")( ffi, lib, alpha_n, alpha_f, tables, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return @classmethod def build_expand_small_tables(cls): if hasattr(Hash.HamsiBase, "expand_small_tables"): return tables = [] t = cls.t256 for u in range(4): pos = [] base_u = u * 64 for byte in range(256): acc = [0] * 8 db = byte off = base_u for _ in range(8): if db & 1: for i in range(8): acc[i] ^= t[off + i] db >>= 1 off += 8 pos.append(tuple(acc)) tables.append(tuple(pos)) Hash.HamsiBase.expand_small_tables = tuple(tables) return @classmethod def build_expand_big_tables(cls): if hasattr(Hash.HamsiBase, "expand_big_tables"): return tables = [] t = cls.t512 for u in range(8): pos = [] base_u = u * 128 for byte in range(256): acc = [0] * 16 db = byte off = base_u for _ in range(8): if db & 1: for i in range(16): acc[i] ^= t[off + i] db >>= 1 off += 16 pos.append(tuple(acc)) tables.append(tuple(pos)) Hash.HamsiBase.expand_big_tables = tuple(tables) return def __init__(self, data=b""): self.h = list(self.iv) self.buf = bytearray() self.msg_len = 0 if self.block_size == 4: self.build_expand_small_tables() self.compress = self.compress_small elif self.block_size == 8: self.build_expand_big_tables() self.compress = self.compress_big self.init_cffi_backend() if data: self.update(data) return def copy(self): other = self.__class__() other.h = list(self.h) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not data: return self mv = memoryview(data) self.msg_len += len(mv) if self.buf: need = self.block_size - len(self.buf) if len(mv) < need: self.buf.extend(mv) return self self.buf.extend(mv[:need]) self.compress(self.buf, False) self.buf.clear() mv = mv[need:] full = (len(mv) // self.block_size) * self.block_size for off in range(0, full, self.block_size): self.compress(mv[off:off + self.block_size], False) if full < len(mv): self.buf.extend(mv[full:]) return self def digest(self): c = self.copy() c.finalize() return c.output() def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 if self.block_size == 4: pad = self.buf + b"\x80" + (b"\x00" * (3 - len(self.buf))) + bit_len.to_bytes(8, "big") self.compress(pad[0:4], False) self.compress(pad[4:8], False) self.compress(pad[8:12], True) elif self.block_size == 8: pad = self.buf + b"\x80" + (b"\x00" * (7 - len(self.buf))) self.compress(pad, False) self.compress(bit_len.to_bytes(8, "big"), True) else: raise ValueError("invalid block_size") self.buf = bytearray() return def output(self): if self.block_size == 4: words = self.h[:self.digest_size >> 2] elif self.digest_size == 48: h = self.h words = [h[0], h[1], h[3], h[4], h[5], h[6], h[8], h[9], h[10], h[12], h[13], h[15]] else: words = self.h return struct.pack(">" + ("I" * len(words)), *words) def sbox(self, a, b, c, d): t = a a = (a & c) ^ d c ^= b c ^= a d = (d | t) ^ b t ^= c b = d d = (d | t) ^ a a &= b t ^= a b ^= d b ^= t a = c c = b b = d d = (~t) & 0xffff_ffff return a, b, c, d def linear(self, a, b, c, d): a = (((a << 13) | (a >> 19)) & 0xffff_ffff) c = (((c << 3) | (c >> 29)) & 0xffff_ffff) b ^= a ^ c d ^= c ^ ((a << 3) & 0xffff_ffff) b = (((b << 1) | (b >> 31)) & 0xffff_ffff) d = (((d << 7) | (d >> 25)) & 0xffff_ffff) a ^= b ^ d c ^= d ^ ((b << 7) & 0xffff_ffff) a = (((a << 5) | (a >> 27)) & 0xffff_ffff) c = (((c << 22) | (c >> 10)) & 0xffff_ffff) return a, b, c, d def compress_small(self, block, final_round): if self.USE_CFFI: hbuf = self.cffi.ffi.new("uint32_t[]", len(self.h)) for i, v in enumerate(self.h): hbuf[i] = v blk = self.cffi.ffi.new("uint8_t[]", bytes(block)) alpha = self.cffi.alpha_f if final_round else self.cffi.alpha_n self.cffi.lib.compress_small(hbuf, blk, int(final_round), alpha, self.cffi.tables) for i in range(len(self.h)): self.h[i] = hbuf[i] return # --- pure Python --- t0, t1, t2, t3 = self.expand_small_tables a0, a1, a2, a3 = t0[block[0]], t1[block[1]], t2[block[2]], t3[block[3]] m = [x0 ^ x1 ^ x2 ^ x3 for x0, x1, x2, x3 in zip(a0, a1, a2, a3)] h = self.h s0, s1, s2, s3 = m[0], m[1], h[0], h[1] s4, s5, s6, s7 = h[2], h[3], m[2], m[3] s8, s9, s10, s11 = m[4], m[5], h[4], h[5] s12, s13, s14, s15 = h[6], h[7], m[6], m[7] alpha = self.alpha_f if final_round else self.alpha_n rounds = 6 if final_round else 3 for rc in range(rounds): s0 ^= alpha[0x00] s1 ^= alpha[0x01] ^ rc s2 ^= alpha[0x02] s3 ^= alpha[0x03] s4 ^= alpha[0x08] s5 ^= alpha[0x09] s6 ^= alpha[0x0a] s7 ^= alpha[0x0b] s8 ^= alpha[0x10] s9 ^= alpha[0x11] s10 ^= alpha[0x12] s11 ^= alpha[0x13] s12 ^= alpha[0x18] s13 ^= alpha[0x19] s14 ^= alpha[0x1a] s15 ^= alpha[0x1b] s0, s4, s8, s12 = self.sbox(s0, s4, s8, s12) s1, s5, s9, s13 = self.sbox(s1, s5, s9, s13) s2, s6, s10, s14 = self.sbox(s2, s6, s10, s14) s3, s7, s11, s15 = self.sbox(s3, s7, s11, s15) s0, s5, s10, s15 = self.linear(s0, s5, s10, s15) s1, s6, s11, s12 = self.linear(s1, s6, s11, s12) s2, s7, s8, s13 = self.linear(s2, s7, s8, s13) s3, s4, s9, s14 = self.linear(s3, s4, s9, s14) h[7] ^= s11 h[6] ^= s10 h[5] ^= s9 h[4] ^= s8 h[3] ^= s3 h[2] ^= s2 h[1] ^= s1 h[0] ^= s0 return def compress_big(self, block, final_round): if self.USE_CFFI: hbuf = self.cffi.ffi.new("uint32_t[]", len(self.h)) for i, v in enumerate(self.h): hbuf[i] = v blk = self.cffi.ffi.new("uint8_t[]", bytes(block)) alpha = self.cffi.alpha_f if final_round else self.cffi.alpha_n self.cffi.lib.compress_big(hbuf, blk, int(final_round), alpha, self.cffi.tables) for i in range(len(self.h)): self.h[i] = hbuf[i] return # --- pure Python --- t0, t1, t2, t3, t4, t5, t6, t7 = self.expand_big_tables a0, a1, a2, a3 = t0[block[0]], t1[block[1]], t2[block[2]], t3[block[3]] a4, a5, a6, a7 = t4[block[4]], t5[block[5]], t6[block[6]], t7[block[7]] m = [x0 ^ x1 ^ x2 ^ x3 ^ x4 ^ x5 ^ x6 ^ x7 for x0, x1, x2, x3, x4, x5, x6, x7 in zip(a0, a1, a2, a3, a4, a5, a6, a7)] h = self.h s00, s01, s02, s03 = m[0], m[1], h[0], h[1] s04, s05, s06, s07 = m[2], m[3], h[2], h[3] s08, s09, s0a, s0b = h[4], h[5], m[4], m[5] s0c, s0d, s0e, s0f = h[6], h[7], m[6], m[7] s10, s11, s12, s13 = m[8], m[9], h[8], h[9] s14, s15, s16, s17 = m[10], m[11], h[10], h[11] s18, s19, s1a, s1b = h[12], h[13], m[12], m[13] s1c, s1d, s1e, s1f = h[14], h[15], m[14], m[15] alpha = self.alpha_f if final_round else self.alpha_n rounds = 12 if final_round else 6 for rc in range(rounds): s00 ^= alpha[0x00] s01 ^= alpha[0x01] ^ rc s02 ^= alpha[0x02] s03 ^= alpha[0x03] s04 ^= alpha[0x04] s05 ^= alpha[0x05] s06 ^= alpha[0x06] s07 ^= alpha[0x07] s08 ^= alpha[0x08] s09 ^= alpha[0x09] s0a ^= alpha[0x0a] s0b ^= alpha[0x0b] s0c ^= alpha[0x0c] s0d ^= alpha[0x0d] s0e ^= alpha[0x0e] s0f ^= alpha[0x0f] s10 ^= alpha[0x10] s11 ^= alpha[0x11] s12 ^= alpha[0x12] s13 ^= alpha[0x13] s14 ^= alpha[0x14] s15 ^= alpha[0x15] s16 ^= alpha[0x16] s17 ^= alpha[0x17] s18 ^= alpha[0x18] s19 ^= alpha[0x19] s1a ^= alpha[0x1a] s1b ^= alpha[0x1b] s1c ^= alpha[0x1c] s1d ^= alpha[0x1d] s1e ^= alpha[0x1e] s1f ^= alpha[0x1f] s00, s08, s10, s18 = self.sbox(s00, s08, s10, s18) s01, s09, s11, s19 = self.sbox(s01, s09, s11, s19) s02, s0a, s12, s1a = self.sbox(s02, s0a, s12, s1a) s03, s0b, s13, s1b = self.sbox(s03, s0b, s13, s1b) s04, s0c, s14, s1c = self.sbox(s04, s0c, s14, s1c) s05, s0d, s15, s1d = self.sbox(s05, s0d, s15, s1d) s06, s0e, s16, s1e = self.sbox(s06, s0e, s16, s1e) s07, s0f, s17, s1f = self.sbox(s07, s0f, s17, s1f) s00, s09, s12, s1b = self.linear(s00, s09, s12, s1b) s01, s0a, s13, s1c = self.linear(s01, s0a, s13, s1c) s02, s0b, s14, s1d = self.linear(s02, s0b, s14, s1d) s03, s0c, s15, s1e = self.linear(s03, s0c, s15, s1e) s04, s0d, s16, s1f = self.linear(s04, s0d, s16, s1f) s05, s0e, s17, s18 = self.linear(s05, s0e, s17, s18) s06, s0f, s10, s19 = self.linear(s06, s0f, s10, s19) s07, s08, s11, s1a = self.linear(s07, s08, s11, s1a) s00, s02, s05, s07 = self.linear(s00, s02, s05, s07) s10, s13, s15, s16 = self.linear(s10, s13, s15, s16) s09, s0b, s0c, s0e = self.linear(s09, s0b, s0c, s0e) s19, s1a, s1c, s1f = self.linear(s19, s1a, s1c, s1f) h[15] ^= s17 h[14] ^= s16 h[13] ^= s15 h[12] ^= s14 h[11] ^= s13 h[10] ^= s12 h[9] ^= s11 h[8] ^= s10 h[7] ^= s07 h[6] ^= s06 h[5] ^= s05 h[4] ^= s04 h[3] ^= s03 h[2] ^= s02 h[1] ^= s01 h[0] ^= s00 return class Hamsi224(HamsiBase): block_size = 4 digest_size = 28 iv = ( 0xc396_7a67, 0xc3bc_6c20, 0x4bc3_bcc3, 0xa7c3_bc6b, 0x2c20_4b61, 0x7468_6f6c, 0x6965_6b65, 0x2055_6e69, ) class Hamsi256(HamsiBase): block_size = 4 digest_size = 32 iv = ( 0x7665_7273, 0x6974_6569, 0x7420_4c65, 0x7576_656e, 0x2c20_4465, 0x7061_7274, 0x656d_656e, 0x7420_456c, ) class Hamsi384(HamsiBase): block_size = 8 digest_size = 48 iv = ( 0x656b_7472, 0x6f74_6563, 0x686e_6965, 0x6b2c_2043, 0x6f6d_7075, 0x7465_7220, 0x5365_6375, 0x7269_7479, 0x2061_6e64, 0x2049_6e64, 0x7573_7472, 0x6961_6c20, 0x4372_7970, 0x746f_6772, 0x6170_6879, 0x2c20_4b61, ) class Hamsi512(HamsiBase): block_size = 8 digest_size = 64 iv = ( 0x7374_6565, 0x6c70_6172, 0x6b20_4172, 0x656e_6265, 0x7267_2031, 0x302c_2062, 0x7573_2032, 0x3434_362c, 0x2042_2d33, 0x3030_3120, 0x4c65_7576, 0x656e_2d48, 0x6576_6572, 0x6c65_652c, 0x2042_656c, 0x6769_756d, ) class LuffaBase: block_size = 0x20 # 256 bits # Appendix A: Starting Variables V = ( (0x6d25_1e69, 0x44b0_51e0, 0x4eaa_6fb4, 0xdbf7_8465, 0x6e29_2011, 0x9015_2df4, 0xee05_8139, 0xdef6_10bb), (0xc3b4_4b95, 0xd9d2_f256, 0x70ee_e9a0, 0xde09_9fa3, 0x5d9b_0557, 0x8fc9_44b3, 0xcf1c_cf0e, 0x746c_d581), (0xf7ef_c89d, 0x5dba_5781, 0x0401_6ce5, 0xad65_9c05, 0x0306_194f, 0x666d_1836, 0x24aa_230a, 0x8b26_4ae7), (0x8580_75d5, 0x36d7_9cce, 0xe571_f7d7, 0x204b_1f67, 0x3587_0c6a, 0x57e9_e923, 0x14bc_b808, 0x7cde_72ce), (0x6c68_e9be, 0x5ec4_1e22, 0xc825_b7c7, 0xaffb_4363, 0xf5df_3999, 0x0fc6_88f1, 0xb072_24cc, 0x03e8_6cea), ) # Appendix B-1: Constant generator initial values (c^(0)_{j,L}, c^(0)_{j,R}) C_INIT = ( (0x181c_ca53, 0x380c_de06), (0x5b6f_0876, 0xf16f_8594), (0x7e10_6ce9, 0x3897_9cb0), (0xbb62_f364, 0x92e9_3c29), (0x9a02_5047, 0xcff2_a940), ) # Section 3.2.1: Message Injection matrices (coefficients in GF(2^8)) MI_MAT = { 0x03: ( (0x03, 0x02, 0x02, 0x01), (0x02, 0x03, 0x02, 0x02), (0x02, 0x02, 0x03, 0x04), ), 0x04: ( (0x04, 0x06, 0x06, 0x07, 0x01), (0x07, 0x04, 0x06, 0x06, 0x02), (0x06, 0x07, 0x04, 0x06, 0x04), (0x06, 0x06, 0x07, 0x04, 0x08), ), 0x05: ( (0x0f, 0x08, 0x0a, 0x0a, 0x08, 0x01), (0x08, 0x0f, 0x08, 0x0a, 0x0a, 0x02), (0x0a, 0x08, 0x0f, 0x08, 0x0a, 0x04), (0x0a, 0x0a, 0x08, 0x0f, 0x08, 0x08), (0x08, 0x0a, 0x0a, 0x08, 0x0f, 0x10), ), } C_TEMPLATE = r""" #include static uint32_t rol32(uint32_t x, int n) { return ((x << n) | (x >> (32 - n))) & 0xffffffffU; } static void scalar_mul(const uint32_t *vec, unsigned int coeff, uint32_t *out) { uint32_t tmp[8]; uint32_t res[8]; int i, k; coeff &= 0xffU; for (k = 0; k < 8; k++) { tmp[k] = vec[k]; res[k] = 0; } if (coeff == 0) { for (k = 0; k < 8; k++) { out[k] = 0; } return; } if (coeff == 1) { for (k = 0; k < 8; k++) { out[k] = vec[k]; } return; } for (i = 0; i < 8; i++) { if ((coeff >> i) & 1U) { for (k = 0; k < 8; k++) { res[k] ^= tmp[k]; } } { uint32_t a0 = tmp[0]; uint32_t a1 = tmp[1]; uint32_t a2 = tmp[2]; uint32_t a3 = tmp[3]; uint32_t a4 = tmp[4]; uint32_t a5 = tmp[5]; uint32_t a6 = tmp[6]; uint32_t a7 = tmp[7]; uint32_t t = a7; tmp[0] = t; tmp[1] = a0 ^ t; tmp[2] = a1; tmp[3] = a2 ^ t; tmp[4] = a3 ^ t; tmp[5] = a4; tmp[6] = a5; tmp[7] = a6; } } for (k = 0; k < 8; k++) { out[k] = res[k]; } } static void subcrumb(uint32_t *r0, uint32_t *r1, uint32_t *r2, uint32_t *r3) { uint32_t t4 = *r0; *r0 |= *r1; *r2 ^= *r3; *r1 = (~(*r1)) & 0xffffffffU; *r0 ^= *r3; *r3 &= t4; *r1 ^= *r3; *r3 ^= *r2; *r2 &= *r0; *r0 = (~(*r0)) & 0xffffffffU; *r2 ^= *r1; *r1 |= *r3; t4 ^= *r1; *r3 ^= *r2; *r2 &= *r1; *r1 ^= *r0; *r0 = t4; } static void mixword(uint32_t *xk, uint32_t *xk4) { uint32_t yk4 = *xk4 ^ *xk; uint32_t yk = rol32(*xk, 2); yk ^= yk4; yk4 = rol32(yk4, 14); yk4 ^= yk; yk = rol32(yk, 10); yk ^= yk4; yk4 = rol32(yk4, 1); *xk = yk; *xk4 = yk4; } void luffa_round(uint32_t *state, const uint32_t *m, int w, const uint32_t *step_const, const uint8_t *mi_mat) { uint32_t xs[5][8]; uint32_t term[8]; int j, k, idx, r; int input_count = w + 1; for (j = 0; j < w; j++) { for (k = 0; k < 8; k++) { xs[j][k] = 0; } for (idx = 0; idx < input_count; idx++) { unsigned int coeff = mi_mat[j * input_count + idx]; const uint32_t *vec; if (coeff == 0) { continue; } if (idx < w) { vec = state + (idx * 8); } else { vec = m; } if (coeff == 1) { for (k = 0; k < 8; k++) { xs[j][k] ^= vec[k]; } } else { scalar_mul(vec, coeff, term); for (k = 0; k < 8; k++) { xs[j][k] ^= term[k]; } } } } for (j = 0; j < w; j++) { uint32_t a[8]; for (k = 0; k < 8; k++) { a[k] = xs[j][k] & 0xffffffffU; } for (k = 4; k < 8; k++) { if (j != 0) { a[k] = rol32(a[k], j); } } for (r = 0; r < 8; r++) { uint32_t t0, t1, t2, t3; subcrumb(&a[0], &a[1], &a[2], &a[3]); t0 = a[5]; t1 = a[6]; t2 = a[7]; t3 = a[4]; subcrumb(&t0, &t1, &t2, &t3); a[5] = t0; a[6] = t1; a[7] = t2; a[4] = t3; for (k = 0; k < 4; k++) { mixword(&a[k], &a[k + 4]); } a[0] ^= step_const[j * 16 + (r * 2) + 0]; a[4] ^= step_const[j * 16 + (r * 2) + 1]; } for (k = 0; k < 8; k++) { state[j * 8 + k] = a[k]; } } } """ DEF_TEMPLATE = r""" void luffa_round(uint32_t *state, const uint32_t *m, int w, const uint32_t *step_const, const uint8_t *mi_mat); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.LuffaBase if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib if base_class in base_class.cffi_cache: ffi, lib = base_class.cffi_cache[base_class] else: try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) base_class.cffi_cache[base_class] = (ffi, lib) except Exception: self.USE_CFFI = False return # Different for each class try: step_const = ffi.new("uint32_t[]", self.step_const_flat) mi_mat = ffi.new("uint8_t[]", self.mi_mat_flat) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib step_const mi_mat")( ffi, lib, step_const, mi_mat, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return @classmethod def prepare_constants(cls): if not hasattr(cls, "step_const"): # Section 4.4: generates (c_{j,0}, c_{j,4}) for r=0..7 using the constant generator out = [] for j in range(cls.w): tl, tr = cls.C_INIT[j] jconst = [] for _ in range(8): # Section 4.4: one step of constant generator (pseudo code in the spec) c = (tl >> 31) & 1 tl2 = ((tl << 1) | (tr >> 31)) & 0xffff_ffff tr2 = (tr << 1) & 0xffff_ffff if c == 1: tl2 ^= 0xc4d6_496c tr2 ^= 0x55c6_1c8d tl, tr = tr2, tl2 c0 = tr c = (tl >> 31) & 1 tl2 = ((tl << 1) | (tr >> 31)) & 0xffff_ffff tr2 = (tr << 1) & 0xffff_ffff if c == 1: tl2 ^= 0xc4d6_496c tr2 ^= 0x55c6_1c8d tl, tr = tr2, tl2 c4 = tr jconst.append((c0, c4)) out.append(tuple(jconst)) cls.step_const = tuple(out) if not hasattr(cls, "step_const_flat"): step_const_flat = [] for j in range(cls.w): for r in range(8): c0, c4 = cls.step_const[j][r] step_const_flat.append(c0) step_const_flat.append(c4) cls.step_const_flat = tuple(step_const_flat) if not hasattr(cls, "mi_mat_flat"): mi_mat_flat = [] for row in cls.MI_MAT[cls.w]: mi_mat_flat.extend(row) cls.mi_mat_flat = bytes(mi_mat_flat) return def __init__(self, data=b""): if self.w is None or self.digest_size is None: raise ValueError("LuffaBase must be subclassed with w and digest_size") self.H = [list(self.V[i]) for i in range(self.w)] self.buf = bytearray() self.msg_len = 0 self.prepare_constants() self.init_cffi_backend() if data: self.update(data) return def copy(self): other = self.__class__() other.H = [list(block) for block in self.H] other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return c.output_digest_bytes() def hexdigest(self): return self.digest().hex() def finalize(self): # Section 3.1: append '1' bit then '0' until length is multiple of 256 bits self.buf.append(0x80) while (len(self.buf) % self.block_size) != 0: self.buf.append(0x00) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return def compress(self, block): if not isinstance(block, (bytes, bytearray, memoryview)): raise TypeError("block must be bytes-like") block = bytes(block) if len(block) != self.block_size: raise ValueError("block must be 32 bytes") m = list(struct.unpack(">8I", block)) self.round_function(m) return def output_digest_bytes(self): # Section 3.3: finalization (blank round + OF, then additional rounds with 0 as needed) zero_m = [0] * 8 self.round_function(zero_m) z0 = self.output_function() words = [] if self.digest_size == 0x1c: words.extend(z0[:7]) elif self.digest_size == 0x20: words.extend(z0[:8]) elif self.digest_size == 0x30: words.extend(z0[:8]) self.round_function(zero_m) z1 = self.output_function() words.extend(z1[:4]) elif self.digest_size == 0x40: words.extend(z0[:8]) self.round_function(zero_m) z1 = self.output_function() words.extend(z1[:8]) else: raise ValueError("unsupported digest size") out = b"".join(struct.pack(">I", w) for w in words) return out def output_function(self): z = [0] * 8 for j in range(self.w): for k in range(8): z[k] ^= self.H[j][k] return z def round_function(self, m): if self.USE_CFFI: state_flat = [] for j in range(self.w): state_flat.extend(self.H[j]) state_buf = self.cffi.ffi.new("uint32_t[]", state_flat) m_buf = self.cffi.ffi.new("uint32_t[]", m) self.cffi.lib.luffa_round(state_buf, m_buf, self.w, self.cffi.step_const, self.cffi.mi_mat) for j in range(self.w): base = j * 8 self.H[j] = [int(state_buf[base + k]) for k in range(8)] return # --- pure Python --- xs = self.message_injection(m) for j in range(self.w): self.H[j] = self.permute(xs[j], j) return def message_injection(self, m): mat = self.MI_MAT[self.w] inputs = [self.H[j] for j in range(self.w)] + [m] outs = [] for row in mat: acc = [0] * 8 for coeff, vec in zip(row, inputs): if coeff == 0: continue if coeff == 1: term = vec else: term = self.scalar_mul(vec, coeff) for k in range(8): acc[k] ^= term[k] outs.append(acc) return outs def scalar_mul(self, vec, coeff): coeff &= 0xff if coeff == 0: return [0] * 8 if coeff == 1: return list(vec) res = [0] * 8 tmp = list(vec) for i in range(8): if (coeff >> i) & 1: for k in range(8): res[k] ^= tmp[k] # Appendix E: multiplication by x in GF(2^8)^32 (phi(x)=x^8+x^4+x^3+x+1) t = tmp[7] tmp = [t, tmp[0] ^ t, tmp[1], tmp[2] ^ t, tmp[3] ^ t, tmp[4], tmp[5], tmp[6]] return res def permute(self, words, j): def rol32(x, n): return ((x << n) | (x >> (32 - n))) & 0xffff_ffff def mixword(xk, xk4): # Section 4.3 (sigma1=2,sigma2=14,sigma3=10,sigma4=1), rotations yk4 = xk4 ^ xk yk = rol32(xk, 2) yk ^= yk4 yk4 = rol32(yk4, 14) yk4 ^= yk yk = rol32(yk, 10) yk ^= yk4 yk4 = rol32(yk4, 1) return yk, yk4 def subcrumb(r0, r1, r2, r3): # Appendix D Table 4: bit-sliced Sbox schedule r4 = r0 r0 |= r1 r2 ^= r3 r1 = (~r1) & 0xffff_ffff r0 ^= r3 r3 &= r4 r1 ^= r3 r3 ^= r2 r2 &= r0 r0 = (~r0) & 0xffff_ffff r2 ^= r1 r1 |= r3 r4 ^= r1 r3 ^= r2 r2 &= r1 r1 ^= r0 return r4, r1, r2, r3 # Section 4.1: Permute(a, j) = Q_j a = [w & 0xffff_ffff for w in words] # Section 4.5: tweak (rotate least significant four words by j bits to the left) for k in range(4, 8): a[k] = rol32(a[k], j) for r in range(8): a[0], a[1], a[2], a[3] = subcrumb(a[0], a[1], a[2], a[3]) # IMPORTANT: the latter four words are input to SubCrumb in different order a[5], a[6], a[7], a[4] = subcrumb(a[5], a[6], a[7], a[4]) for k in range(4): a[k], a[k + 4] = mixword(a[k], a[k + 4]) c0, c4 = self.step_const[j][r] a[0] ^= c0 a[4] ^= c4 return a class Luffa224(LuffaBase): digest_size = 0x1c w = 0x03 class Luffa256(LuffaBase): digest_size = 0x20 w = 0x03 class Luffa384(LuffaBase): digest_size = 0x30 w = 0x04 class Luffa512(LuffaBase): digest_size = 0x40 w = 0x05 class ESCHBase: rcon = ( 0xb7e1_5162, 0xbf71_5880, 0x38b4_da56, 0x324e_7738, 0xbb11_85eb, 0x4f7c_7b57, 0xcfbf_a1c8, 0xc2b3_293d, ) const_m0 = 0x01_00_00_00 const_m1 = 0x02_00_00_00 pack4 = struct.Struct("<4I") unpack4 = struct.Struct("<4I") def __init__(self, data=b""): self.state = [0] * (2 * self.branches) self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.state = list(self.state) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") mv = memoryview(data) data_len = len(mv) self.msg_len += data_len block_size = self.block_size off = 0 if self.buf: need = block_size - len(self.buf) if data_len <= need: self.buf.extend(mv) return self self.buf.extend(mv[:need]) self.absorb_block(self.buf) self.buf.clear() off = need limit = data_len - block_size while off < limit: self.absorb_block(mv[off:off + block_size]) off += block_size if off < data_len: self.buf.extend(mv[off:]) return self def digest(self): c = self.copy() c.finalize() return c.squeeze() def hexdigest(self): return self.digest().hex() def finalize(self): if len(self.buf) == self.block_size: last = self.buf const_m = self.const_m1 else: self.buf.append(0x80) pad_len = self.block_size - len(self.buf) if pad_len: self.buf.extend(b"\x00" * pad_len) last = self.buf const_m = self.const_m0 self.absorb_last_block(last, const_m) self.buf.clear() return def squeeze(self): out = bytearray() need = self.digest_size pack4 = self.pack4.pack state = self.state out.extend(pack4(state[0], state[1], state[2], state[3])) while len(out) < need: self.sparkle(self.slim_steps) state = self.state out.extend(pack4(state[0], state[1], state[2], state[3])) return bytes(out[:need]) def alzette(self, x, y, c): x = (x + ((y >> 31) | ((y << 1) & 0xffff_ffff))) & 0xffff_ffff y ^= ((x >> 24) | ((x << 8) & 0xffff_ffff)) x ^= c x = (x + ((y >> 17) | ((y << 15) & 0xffff_ffff))) & 0xffff_ffff y ^= ((x >> 17) | ((x << 15) & 0xffff_ffff)) x ^= c x = (x + y) & 0xffff_ffff y ^= ((x >> 31) | ((x << 1) & 0xffff_ffff)) x ^= c x = (x + ((y >> 24) | ((y << 8) & 0xffff_ffff))) & 0xffff_ffff y ^= ((x >> 16) | ((x << 16) & 0xffff_ffff)) x ^= c return x, y def inject_block_xor(self, block): w0, w1, w2, w3 = self.unpack4.unpack(block) state = self.state tx = w0 ^ w2 ty = w1 ^ w3 tx ^= (tx << 16) & 0xffff_ffff ty ^= (ty << 16) & 0xffff_ffff lx = ((tx >> 16) | ((tx << 16) & 0xffff_ffff)) & 0xffff_ffff ly = ((ty >> 16) | ((ty << 16) & 0xffff_ffff)) & 0xffff_ffff state[0] ^= w0 ^ ly state[1] ^= w1 ^ lx state[2] ^= w2 ^ ly state[3] ^= w3 ^ lx state[4] ^= ly state[5] ^= lx if self.m_w == 4: state[6] ^= ly state[7] ^= lx return def absorb_block(self, block): self.inject_block_xor(block) self.sparkle(self.slim_steps) return def absorb_last_block(self, block, const_m_word): self.inject_block_xor(block) self.state[2 * self.m_w - 1] ^= const_m_word self.sparkle(self.big_steps) return def sparkle_6(self, steps): state = self.state rcon = self.rcon for s in range(steps): state[1] ^= rcon[s & 7] state[3] ^= s for i in range(6): state[i * 2], state[i * 2 + 1] = self.alzette(state[i * 2], state[i * 2 + 1], rcon[i]) tx = state[0] ^ state[2] ^ state[4] ty = state[1] ^ state[3] ^ state[5] tx ^= (tx << 16) & 0xffff_ffff ty ^= (ty << 16) & 0xffff_ffff tx = ((tx >> 16) | ((tx << 16) & 0xffff_ffff)) & 0xffff_ffff ty = ((ty >> 16) | ((ty << 16) & 0xffff_ffff)) & 0xffff_ffff s0 = state[0] s1 = state[1] s2 = state[2] s3 = state[3] s4 = state[4] s5 = state[5] s6 = state[6] ^ s0 ^ ty s7 = state[7] ^ s1 ^ tx s8 = state[8] ^ s2 ^ ty s9 = state[9] ^ s3 ^ tx s10 = state[10] ^ s4 ^ ty s11 = state[11] ^ s5 ^ tx state[0] = s8 state[1] = s9 state[2] = s10 state[3] = s11 state[4] = s6 state[5] = s7 state[6] = s0 state[7] = s1 state[8] = s2 state[9] = s3 state[10] = s4 state[11] = s5 return def sparkle_8(self, steps): state = self.state rcon = self.rcon for s in range(steps): state[1] ^= rcon[s & 7] state[3] ^= s for i in range(8): state[i * 2], state[i * 2 + 1] = self.alzette(state[i * 2], state[i * 2 + 1], rcon[i]) tx = state[0] ^ state[2] ^ state[4] ^ state[6] ty = state[1] ^ state[3] ^ state[5] ^ state[7] tx ^= (tx << 16) & 0xffff_ffff ty ^= (ty << 16) & 0xffff_ffff tx = ((tx >> 16) | ((tx << 16) & 0xffff_ffff)) & 0xffff_ffff ty = ((ty >> 16) | ((ty << 16) & 0xffff_ffff)) & 0xffff_ffff s0 = state[0] s1 = state[1] s2 = state[2] s3 = state[3] s4 = state[4] s5 = state[5] s6 = state[6] s7 = state[7] s8 = state[8] ^ s0 ^ ty s9 = state[9] ^ s1 ^ tx s10 = state[10] ^ s2 ^ ty s11 = state[11] ^ s3 ^ tx s12 = state[12] ^ s4 ^ ty s13 = state[13] ^ s5 ^ tx s14 = state[14] ^ s6 ^ ty s15 = state[15] ^ s7 ^ tx state[0] = s10 state[1] = s11 state[2] = s12 state[3] = s13 state[4] = s14 state[5] = s15 state[6] = s8 state[7] = s9 state[8] = s0 state[9] = s1 state[10] = s2 state[11] = s3 state[12] = s4 state[13] = s5 state[14] = s6 state[15] = s7 return def sparkle(self, steps): if self.branches == 6: self.sparkle_6(steps) else: self.sparkle_8(steps) return class ESCH256(ESCHBase): block_size = 16 digest_size = 32 branches = 6 slim_steps = 7 big_steps = 11 m_w = 3 class ESCH384(ESCHBase): block_size = 16 digest_size = 48 branches = 8 slim_steps = 8 big_steps = 12 m_w = 4 class SIMDBase: P = ( 0x04, 0x06, 0x00, 0x02, 0x07, 0x05, 0x03, 0x01, 0x0f, 0x0b, 0x0c, 0x08, 0x09, 0x0d, 0x0a, 0x0e, 0x11, 0x12, 0x17, 0x14, 0x16, 0x15, 0x10, 0x13, 0x1e, 0x18, 0x19, 0x1f, 0x1b, 0x1d, 0x1c, 0x1a, ) round_pis = ( (0x03, 0x17, 0x11, 0x1b), (0x1c, 0x13, 0x16, 0x07), (0x1d, 0x09, 0x0f, 0x05), (0x04, 0x0d, 0x0a, 0x19), ) C_TEMPLATE = r""" #include static uint32_t rol32(uint32_t x, int n) { return ((x << n) | (x >> (32 - n))) & 0xffffffffU; } static int lift257(int v) { if (v > 0x80) return v - 0x101; return v; } static uint32_t pack_code(int x, int y, int c) { uint32_t lo = ((uint32_t)(c * lift257(x))) & 0xffffU; uint32_t hi = ((uint32_t)(c * lift257(y))) & 0xffffU; return lo | (hi << 16); } static const uint8_t perm_table[32] = { 0x04, 0x06, 0x00, 0x02, 0x07, 0x05, 0x03, 0x01, 0x0f, 0x0b, 0x0c, 0x08, 0x09, 0x0d, 0x0a, 0x0e, 0x11, 0x12, 0x17, 0x14, 0x16, 0x15, 0x10, 0x13, 0x1e, 0x18, 0x19, 0x1f, 0x1b, 0x1d, 0x1c, 0x1a, }; static const uint8_t round_pis[4][4] = { {0x03, 0x17, 0x11, 0x1b}, {0x1c, 0x13, 0x16, 0x07}, {0x1d, 0x09, 0x0f, 0x05}, {0x04, 0x0d, 0x0a, 0x19}, }; static void step_if( uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, const uint32_t *w_vec, int lanes, int r, int s, int p) { uint32_t new_a[8], new_b[8], new_c[8], new_d[8]; int j; for (j = 0; j < lanes; j++) { uint32_t phi = c[j] ^ (a[j] & (b[j] ^ c[j])); uint32_t t = (d[j] + w_vec[j] + phi) & 0xffffffffU; uint32_t u = 0; t = rol32(t, s); u = rol32(a[j ^ p], r); new_a[j] = (t + u) & 0xffffffffU; new_b[j] = rol32(a[j], r); new_c[j] = b[j]; new_d[j] = c[j]; } for (j = 0; j < lanes; j++) { a[j] = new_a[j]; b[j] = new_b[j]; c[j] = new_c[j]; d[j] = new_d[j]; } } static void step_maj( uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, const uint32_t *w_vec, int lanes, int r, int s, int p) { uint32_t new_a[8]; uint32_t new_b[8]; uint32_t new_c[8]; uint32_t new_d[8]; int j; for (j = 0; j < lanes; j++) { uint32_t phi = (a[j] & b[j]) | (c[j] & (a[j] | b[j])); uint32_t t = (d[j] + w_vec[j] + phi) & 0xffffffffU; uint32_t u = 0; t = rol32(t, s); u = rol32(a[j ^ p], r); new_a[j] = (t + u) & 0xffffffffU; new_b[j] = rol32(a[j], r); new_c[j] = b[j]; new_d[j] = c[j]; } for (j = 0; j < lanes; j++) { a[j] = new_a[j]; b[j] = new_b[j]; c[j] = new_c[j]; d[j] = new_d[j]; } } static void compress_generic( uint32_t *state, const uint8_t *block, int final_flag, const uint16_t *rows_normal, const uint16_t *rows_final, const uint8_t *p_xor_table, int lanes, int block_size, int ntt_n, int z_stride) { const uint16_t *rows = final_flag ? rows_final : rows_normal; int row_stride = block_size + 1; int half_n = ntt_n / 2; int offset0 = ntt_n + half_n - 1; int offset1 = ntt_n - 1; int y[256]; uint32_t z[32][8]; uint32_t w[32][8]; uint32_t m_words[32]; uint32_t a[8], b[8], c[8], d[8]; int i = 0; int j = 0; for (i = 0; i < ntt_n; i++) { int acc = rows[i * row_stride + block_size]; for (j = 0; j < block_size; j++) { acc += ((int)block[j]) * ((int)rows[i * row_stride + j]); } y[i] = acc % 0x101; } for (i = 0; i < 32; i++) { int base = z_stride * i; for (j = 0; j < lanes; j++) { int idx = base + 2 * j; if (i <= 15) { z[i][j] = pack_code(y[idx], y[idx + 1], 0xb9); } else if (i <= 23) { z[i][j] = pack_code(y[idx - ntt_n], y[idx - half_n], 0xe9); } else { z[i][j] = pack_code(y[idx - offset0], y[idx - offset1], 0xe9); } } } for (i = 0; i < 32; i++) { int src = perm_table[i]; for (j = 0; j < lanes; j++) { w[i][j] = z[src][j]; } } for (i = 0; i < lanes * 4; i++) { int off = i * 4; m_words[i] = ( ((uint32_t)block[off + 0]) | ((uint32_t)block[off + 1] << 8) | ((uint32_t)block[off + 2] << 16) | ((uint32_t)block[off + 3] << 24) ); } for (j = 0; j < lanes; j++) { a[j] = state[j] ^ m_words[j]; b[j] = state[lanes + j] ^ m_words[lanes + j]; c[j] = state[(lanes * 2) + j] ^ m_words[(lanes * 2) + j]; d[j] = state[(lanes * 3) + j] ^ m_words[(lanes * 3) + j]; } { int step_index = 0; for (i = 0; i < 4; i++) { int pi0 = round_pis[i][0]; int pi1 = round_pis[i][1]; int pi2 = round_pis[i][2]; int pi3 = round_pis[i][3]; step_if(a, b, c, d, w[step_index], lanes, pi0, pi1, p_xor_table[step_index]); step_index++; step_if(a, b, c, d, w[step_index], lanes, pi1, pi2, p_xor_table[step_index]); step_index++; step_if(a, b, c, d, w[step_index], lanes, pi2, pi3, p_xor_table[step_index]); step_index++; step_if(a, b, c, d, w[step_index], lanes, pi3, pi0, p_xor_table[step_index]); step_index++; step_maj(a, b, c, d, w[step_index], lanes, pi0, pi1, p_xor_table[step_index]); step_index++; step_maj(a, b, c, d, w[step_index], lanes, pi1, pi2, p_xor_table[step_index]); step_index++; step_maj(a, b, c, d, w[step_index], lanes, pi2, pi3, p_xor_table[step_index]); step_index++; step_maj(a, b, c, d, w[step_index], lanes, pi3, pi0, p_xor_table[step_index]); step_index++; } step_if(a, b, c, d, state, lanes, 0x04, 0x0d, p_xor_table[step_index]); step_index++; step_if(a, b, c, d, state + lanes, lanes, 0x0d, 0x0a, p_xor_table[step_index]); step_index++; step_if(a, b, c, d, state + (lanes * 2), lanes, 0x0a, 0x19, p_xor_table[step_index]); step_index++; step_if(a, b, c, d, state + (lanes * 3), lanes, 0x19, 0x04, p_xor_table[step_index]); } for (j = 0; j < lanes; j++) { state[j] = a[j]; state[lanes + j] = b[j]; state[(lanes * 2) + j] = c[j]; state[(lanes * 3) + j] = d[j]; } } void compress_small( uint32_t *state, const uint8_t *block, int final_flag, const uint16_t *rows_normal, const uint16_t *rows_final, const uint8_t *p_xor_table) { compress_generic(state, block, final_flag, rows_normal, rows_final, p_xor_table, 4, 64, 128, 8); } void compress_big( uint32_t *state, const uint8_t *block, int final_flag, const uint16_t *rows_normal, const uint16_t *rows_final, const uint8_t *p_xor_table) { compress_generic(state, block, final_flag, rows_normal, rows_final, p_xor_table, 8, 128, 256, 16); } """ DEF_TEMPLATE = r""" void compress_small( uint32_t *state, const uint8_t *block, int final_flag, const uint16_t *rows_normal, const uint16_t *rows_final, const uint8_t *p_xor_table); void compress_big( uint32_t *state, const uint8_t *block, int final_flag, const uint16_t *rows_normal, const uint16_t *rows_final, const uint8_t *p_xor_table); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.SIMDBase if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib if base_class in base_class.cffi_cache: ffi, lib = base_class.cffi_cache[base_class] else: try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) base_class.cffi_cache[base_class] = (ffi, lib) except Exception: self.USE_CFFI = False return # Different for each class def flatten_ntt_rows(rows): flat = [] for coeffs, add_const in rows: flat.extend(coeffs) flat.append(add_const) return tuple(flat) try: if self.lanes == 4: rows_normal = ffi.new("uint16_t[]", flatten_ntt_rows(self.ntt_rows_normal)) rows_final = ffi.new("uint16_t[]", flatten_ntt_rows(self.ntt_rows_final)) p_xor_table = ffi.new("uint8_t[]", tuple(self.p_xor_table)) elif self.lanes == 8: rows_normal = ffi.new("uint16_t[]", flatten_ntt_rows(self.ntt_rows_normal)) rows_final = ffi.new("uint16_t[]", flatten_ntt_rows(self.ntt_rows_final)) p_xor_table = ffi.new("uint8_t[]", tuple(self.p_xor_table)) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib rows_normal rows_final p_xor_table")( ffi, lib, rows_normal, rows_final, p_xor_table, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return def prepare_constants(self): cls = self.__class__ if not hasattr(cls, "root_pows"): root_pows = [1] * self.order for i in range(1, self.order): root_pows[i] = (root_pows[i - 1] * self.root) % 0x101 cls.root_pows = root_pows if not hasattr(cls, "ntt_rows_normal"): root_pows = self.root_pows ntt_rows_normal = [None] * self.ntt_n ntt_rows_final = [None] * self.ntt_n for i in range(self.ntt_n): row = [0] * self.block_size for j in range(self.block_size): row[j] = root_pows[(i * j) % self.order] add_normal = root_pows[(self.const_exps[0] * i) % self.order] add_final = add_normal + root_pows[(self.const_exps[1] * i) % self.order] ntt_rows_normal[i] = (tuple(row), add_normal) ntt_rows_final[i] = (tuple(row), add_final) cls.ntt_rows_normal = tuple(ntt_rows_normal) cls.ntt_rows_final = tuple(ntt_rows_final) return def __init__(self, data=b""): self.state = list(self.iv_words) self.buf = bytearray() self.msg_len = 0 self.prepare_constants() self.init_cffi_backend() if data: self.update(data) return def copy(self): other = self.__class__() other.state = list(self.state) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block, 0) return self def digest(self): c = self.copy() c.finalize() words = c.state[: c.lanes * 2][: c.out_words] out = b"".join(struct.pack(" 0x80: return v - 0x101 return v def pack_code(x, y, c): lo = (c * lift257(x)) & 0xffff hi = (c * lift257(y)) & 0xffff return lo | (hi << 0x10) def message_expansion(block, final_flag): if final_flag: y = ntt(block, self.ntt_rows_final) else: y = ntt(block, self.ntt_rows_normal) z = [None] * 32 lanes = self.lanes z_stride = self.z_stride ntt_n = self.ntt_n half_n = ntt_n // 2 for i in range(32): row = [0] * lanes base = z_stride * i if i <= 15: for j in range(lanes): idx = base + 2 * j row[j] = pack_code(y[idx], y[idx + 1], 0xb9) elif i <= 23: for j in range(lanes): idx = base + 2 * j row[j] = pack_code(y[idx - ntt_n], y[idx - half_n], 0xe9) else: offset0 = ntt_n + half_n - 1 offset1 = ntt_n - 1 for j in range(lanes): idx = base + 2 * j row[j] = pack_code(y[idx - offset0], y[idx - offset1], 0xe9) z[i] = row w = [None] * 32 for i in range(32): w[i] = z[self.P[i]] return w def rol32(x, n): return ((x << n) | (x >> (0x20 - n))) & 0xffff_ffff def step_if(a, b, c, d, w_vec, r, s, p): new_a = [0] * self.lanes new_b = [0] * self.lanes new_c = [0] * self.lanes new_d = [0] * self.lanes for j in range(self.lanes): phi = c[j] ^ (a[j] & (b[j] ^ c[j])) t = (d[j] + w_vec[j] + phi) & 0xffff_ffff t = rol32(t, s) u = rol32(a[j ^ p], r) new_a[j] = (t + u) & 0xffff_ffff new_b[j] = rol32(a[j], r) new_c[j] = b[j] new_d[j] = c[j] return new_a, new_b, new_c, new_d def step_maj(a, b, c, d, w_vec, r, s, p): new_a = [0] * self.lanes new_b = [0] * self.lanes new_c = [0] * self.lanes new_d = [0] * self.lanes for j in range(self.lanes): phi = (a[j] & b[j]) | (c[j] & (a[j] | b[j])) t = (d[j] + w_vec[j] + phi) & 0xffff_ffff t = rol32(t, s) u = rol32(a[j ^ p], r) new_a[j] = (t + u) & 0xffff_ffff new_b[j] = rol32(a[j], r) new_c[j] = b[j] new_d[j] = c[j] return new_a, new_b, new_c, new_d if len(block) != self.block_size: raise ValueError("invalid block size") if self.USE_CFFI: state_buf = self.cffi.ffi.new("uint32_t[]", self.state) block_buf = self.cffi.ffi.new("uint8_t[]", bytes(block)) if self.lanes == 4: clib_compress = self.cffi.lib.compress_small else: clib_compress = self.cffi.lib.compress_big clib_compress( state_buf, block_buf, int(final_flag), self.cffi.rows_normal, self.cffi.rows_final, self.cffi.p_xor_table, ) self.state = [state_buf[i] for i in range(self.lanes * 4)] return w = message_expansion(block, final_flag) m_words = struct.unpack("<" + "I" * (self.lanes * 4), block) s_words = [0] * (self.lanes * 4) for i in range(self.lanes * 4): s_words[i] = self.state[i] ^ m_words[i] lanes = self.lanes a = list(s_words[0 : lanes]) b = list(s_words[lanes : lanes * 2]) c = list(s_words[lanes * 2 : lanes * 3]) d = list(s_words[lanes * 3 : lanes * 4]) ptab = self.p_xor_table step_index = 0 for rnd in range(4): pi0, pi1, pi2, pi3 = self.round_pis[rnd] a, b, c, d = step_if(a, b, c, d, w[step_index], pi0, pi1, ptab[step_index]) step_index += 1 a, b, c, d = step_if(a, b, c, d, w[step_index], pi1, pi2, ptab[step_index]) step_index += 1 a, b, c, d = step_if(a, b, c, d, w[step_index], pi2, pi3, ptab[step_index]) step_index += 1 a, b, c, d = step_if(a, b, c, d, w[step_index], pi3, pi0, ptab[step_index]) step_index += 1 a, b, c, d = step_maj(a, b, c, d, w[step_index], pi0, pi1, ptab[step_index]) step_index += 1 a, b, c, d = step_maj(a, b, c, d, w[step_index], pi1, pi2, ptab[step_index]) step_index += 1 a, b, c, d = step_maj(a, b, c, d, w[step_index], pi2, pi3, ptab[step_index]) step_index += 1 a, b, c, d = step_maj(a, b, c, d, w[step_index], pi3, pi0, ptab[step_index]) step_index += 1 iv_a = self.state[0 : lanes] iv_b = self.state[lanes : lanes * 2] iv_c = self.state[lanes * 2 : lanes * 3] iv_d = self.state[lanes * 3 : lanes * 4] a, b, c, d = step_if(a, b, c, d, iv_a, 0x04, 0x0d, ptab[step_index]) step_index += 1 a, b, c, d = step_if(a, b, c, d, iv_b, 0x0d, 0x0a, ptab[step_index]) step_index += 1 a, b, c, d = step_if(a, b, c, d, iv_c, 0x0a, 0x19, ptab[step_index]) step_index += 1 a, b, c, d = step_if(a, b, c, d, iv_d, 0x19, 0x04, ptab[step_index]) self.state = list(a + b + c + d) return class SIMD224(SIMDBase): block_size = 0x40 digest_size = 28 lanes = 4 ntt_n = 0x80 order = 0x80 z_stride = 0x08 root = 0x8b const_exps = [0x7f, 0x7d] out_words = 7 p_xor_table = ( 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, ) iv_words = ( 0x3358_6e9f, 0x12ff_f033, 0xb2d9_f64d, 0x6f8f_ea53, 0xde94_3106, 0x2742_e439, 0x4fba_b5ac, 0x62b9_ff96, 0x22e7_b0af, 0xc862_b3a8, 0x33e0_0cdc, 0x236b_86a6, 0xf64a_e77c, 0xfa37_3b76, 0x7dc1_ee5b, 0x7fb2_9ce8, ) class SIMD256(SIMDBase): block_size = 0x40 digest_size = 32 lanes = 4 ntt_n = 0x80 order = 0x80 z_stride = 0x08 root = 0x8b const_exps = [0x7f, 0x7d] out_words = 8 p_xor_table = ( 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, ) iv_words = ( 0x4d56_7983, 0x0719_0ba9, 0x8474_577b, 0x39d7_26e9, 0xaaf3_d925, 0x3ee2_0b03, 0xafd5_e751, 0xc960_06d3, 0xc2c2_ba14, 0x49b3_bcb4, 0xf67c_af46, 0x6686_26c9, 0xe2ea_a8d2, 0x1ff4_7833, 0xd0c6_61a5, 0x5569_3de1, ) class SIMD384(SIMDBase): block_size = 0x80 digest_size = 48 lanes = 8 ntt_n = 0x100 order = 0x100 z_stride = 0x10 root = 0x29 const_exps = [0xff, 0xfd] out_words = 12 p_xor_table = ( 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, ) iv_words = ( 0x8a36_eebc, 0x94a3_bd90, 0xd153_7b83, 0xb25b_070b, 0xf463_f1b5, 0xb6f8_1e20, 0x0055_c339, 0xb4d1_44d1, 0x7360_ca61, 0x1836_1a03, 0x17dc_b4b9, 0x3414_c45a, 0xa699_a9d2, 0xe39e_9664, 0x468b_fe77, 0x51d0_62f8, 0xb9e3_bfe8, 0x63be_ce2a, 0x8fe5_06b9, 0xf8cc_4ac2, 0x7ae1_1542, 0xb1aa_dda1, 0x64b0_6794, 0x28d2_f462, 0xe640_71ec, 0x1deb_91a8, 0x8ac8_db23, 0x3f78_2ab5, 0x039b_5cb8, 0x71dd_d962, 0xfade_2cea, 0x1416_df71, ) class SIMD512(SIMDBase): block_size = 0x80 digest_size = 64 lanes = 8 ntt_n = 0x100 order = 0x100 z_stride = 0x10 root = 0x29 const_exps = [0xff, 0xfd] out_words = 16 p_xor_table = ( 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, 0x06, 0x02, 0x03, 0x05, 0x07, 0x04, 0x01, ) iv_words = ( 0x0ba1_6b95, 0x72f9_99ad, 0x9fec_c2ae, 0xba32_64fc, 0x5e89_4929, 0x8e9f_30e5, 0x2f1d_aa37, 0xf0f2_c558, 0xac50_6643, 0xa906_35a5, 0xe25b_878b, 0xaab7_878f, 0x8881_7f7a, 0x0a02_892b, 0x559a_7550, 0x598f_657e, 0x7eef_60a1, 0x6b70_e3e8, 0x9c17_14d1, 0xb958_e2a8, 0xab02_675e, 0xed1c_014f, 0xcd8d_65bb, 0xfdb7_a257, 0x0925_4899, 0xd699_c7bc, 0x9019_b6dc, 0x2b90_22e4, 0x8fa1_4956, 0x21bf_9bd3, 0xb94d_0943, 0x6ffd_dc22, ) class BMWHBase: W_SCHEDULE = ( (5, "-", 7, "+", 10, "+", 13, "+", 14), (6, "-", 8, "+", 11, "+", 14, "-", 15), (0, "+", 7, "+", 9, "-", 12, "+", 15), (0, "-", 1, "+", 8, "-", 10, "+", 13), (1, "+", 2, "+", 9, "-", 11, "-", 14), (3, "-", 2, "+", 10, "-", 12, "+", 15), (4, "-", 0, "-", 3, "-", 11, "+", 13), (1, "-", 4, "-", 5, "-", 12, "-", 14), (2, "-", 5, "-", 6, "+", 13, "-", 15), (0, "-", 3, "+", 6, "-", 7, "+", 14), (8, "-", 1, "-", 4, "-", 7, "+", 15), (8, "-", 0, "-", 2, "-", 5, "+", 9), (1, "+", 3, "-", 6, "-", 9, "+", 10), (2, "+", 4, "+", 7, "+", 10, "+", 11), (3, "-", 5, "+", 8, "-", 11, "-", 12), (12, "-", 4, "-", 6, "-", 9, "+", 13), ) # For i16=16..31: (j0m, j1m, j3m, j4m, j7m, j10m, j11m) # j1m/j4m/j11m are rotation amounts. M16_TABLE = ( (0, 1, 3, 4, 7, 10, 11), (1, 2, 4, 5, 8, 11, 12), (2, 3, 5, 6, 9, 12, 13), (3, 4, 6, 7, 10, 13, 14), (4, 5, 7, 8, 11, 14, 15), (5, 6, 8, 9, 12, 15, 16), (6, 7, 9, 10, 13, 0, 1), (7, 8, 10, 11, 14, 1, 2), (8, 9, 11, 12, 15, 2, 3), (9, 10, 12, 13, 0, 3, 4), (10, 11, 13, 14, 1, 4, 5), (11, 12, 14, 15, 2, 5, 6), (12, 13, 15, 16, 3, 6, 7), (13, 14, 0, 1, 4, 7, 8), (14, 15, 1, 2, 5, 8, 9), (15, 16, 2, 3, 6, 9, 10), ) def __init__(self, data=b""): if self.word_bits not in (32, 64): raise ValueError("invalid word_bits") if len(self.iv) != 16: raise ValueError("iv missing/invalid") if len(self.final_const) != 16: raise ValueError("final_const missing/invalid") self.H = list(self.iv) self.buf = bytearray() self.msg_len = 0 # in bytes self.result_bytes = b"" if data: self.update(data) return def copy(self): other = self.__class__() other.H = list(self.H) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.result_bytes = self.result_bytes return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.H = self.compress(block, self.H) return self def digest(self): c = self.copy() c.finalize() return c.result_bytes def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 self.buf.append(0x80) while (len(self.buf) % self.block_size) != (self.block_size - 8): self.buf.append(0x00) self.buf.extend(struct.pack("= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.H = self.compress(block, self.H) block2 = bytearray(self.block_size) if self.word_bits == 32: for i, w in enumerate(self.H): struct.pack_into("> (32 - n))) & 0xffff_ffff n &= 63 x &= 0xffff_ffff_ffff_ffff return ((x << n) | (x >> (64 - n))) & 0xffff_ffff_ffff_ffff def add_elt(M, H, i16): j0m, j1m, j3m, j4m, j7m, j10m, j11m = self.M16_TABLE[i16 - 16] if self.word_bits == 32: k = align(i16 * 0x0555_5555) else: k = align(i16 * 0x0555_5555_5555_5555) v = align(rol(M[j0m], j1m) + rol(M[j3m], j4m) - rol(M[j10m], j11m) + k) return v ^ H[j7m] def make_w(M, H, entry): i0, op01, i1, op12, i2, op23, i3, op34, i4 = entry res = M[i0] ^ H[i0] for op, val in [(op01, M[i1] ^ H[i1]), (op12, M[i2] ^ H[i2]), (op23, M[i3] ^ H[i3]), (op34, M[i4] ^ H[i4])]: if op == "+": res = res + val else: res = res - val return align(res) def expand1(Q, M, H, i16): base = i16 - 16 if self.word_bits == 32: funcs = [ss1, ss2, ss3, ss0] * 4 else: funcs = [sb1, sb2, sb3, sb0] * 4 s = 0 for k in range(16): s += funcs[k](Q[base + k]) s += add_elt(M, H, i16) return align(s) def expand2(Q, M, H, i16): base = i16 - 16 if self.word_bits == 32: s = ( Q[base + 0] + rol(Q[base + 1], 3) + Q[base + 2] + rol(Q[base + 3], 7) + Q[base + 4] + rol(Q[base + 5], 13) + Q[base + 6] + rol(Q[base + 7], 16) + Q[base + 8] + rol(Q[base + 9], 19) + Q[base + 10] + rol(Q[base + 11], 23) + Q[base + 12] + rol(Q[base + 13], 27) + ((Q[base + 14] >> 1) ^ Q[base + 14]) + ((Q[base + 15] >> 2) ^ Q[base + 15]) + add_elt(M, H, i16) ) else: s = ( Q[base + 0] + rol(Q[base + 1], 5) + Q[base + 2] + rol(Q[base + 3], 11) + Q[base + 4] + rol(Q[base + 5], 27) + Q[base + 6] + rol(Q[base + 7], 32) + Q[base + 8] + rol(Q[base + 9], 37) + Q[base + 10] + rol(Q[base + 11], 43) + Q[base + 12] + rol(Q[base + 13], 53) + ((Q[base + 14] >> 1) ^ Q[base + 14]) + ((Q[base + 15] >> 2) ^ Q[base + 15]) + add_elt(M, H, i16) ) return align(s) def ss0(x): return align((x >> 1) ^ (x << 3) ^ rol(x, 4) ^ rol(x, 19)) def ss1(x): return align((x >> 1) ^ (x << 2) ^ rol(x, 8) ^ rol(x, 23)) def ss2(x): return align((x >> 2) ^ (x << 1) ^ rol(x, 12) ^ rol(x, 25)) def ss3(x): return align((x >> 2) ^ (x << 2) ^ rol(x, 15) ^ rol(x, 29)) def ss4(x): return align((x >> 1) ^ x) def sb0(x): return align((x >> 1) ^ (x << 3) ^ rol(x, 4) ^ rol(x, 37)) def sb1(x): return align((x >> 1) ^ (x << 2) ^ rol(x, 13) ^ rol(x, 43)) def sb2(x): return align((x >> 2) ^ (x << 1) ^ rol(x, 19) ^ rol(x, 53)) def sb3(x): return align((x >> 2) ^ (x << 2) ^ rol(x, 28) ^ rol(x, 59)) def sb4(x): return align((x >> 1) ^ x) if self.word_bits == 32: init_funcs = [ss0, ss1, ss2, ss3, ss4] M = list(struct.unpack("<16I", block)) else: init_funcs = [sb0, sb1, sb2, sb3, sb4] M = list(struct.unpack("<16Q", block)) Q = [0] * 32 W = [make_w(M, H, e) for e in self.W_SCHEDULE] for j in range(15): Q[j] = align(init_funcs[j % 5](W[j]) + H[(j + 1) & 15]) Q[15] = align(init_funcs[0](W[15]) + H[0]) Q[16] = expand1(Q, M, H, 16) Q[17] = expand1(Q, M, H, 17) for j in range(18, 32): Q[j] = expand2(Q, M, H, j) xl = Q[16] ^ Q[17] ^ Q[18] ^ Q[19] ^ Q[20] ^ Q[21] ^ Q[22] ^ Q[23] xh = xl ^ Q[24] ^ Q[25] ^ Q[26] ^ Q[27] ^ Q[28] ^ Q[29] ^ Q[30] ^ Q[31] dh = [0] * 16 dh[0] = align(((xh << 5) ^ (Q[16] >> 5) ^ M[0]) + (xl ^ Q[24] ^ Q[0])) dh[1] = align(((xh >> 7) ^ (Q[17] << 8) ^ M[1]) + (xl ^ Q[25] ^ Q[1])) dh[2] = align(((xh >> 5) ^ (Q[18] << 5) ^ M[2]) + (xl ^ Q[26] ^ Q[2])) dh[3] = align(((xh >> 1) ^ (Q[19] << 5) ^ M[3]) + (xl ^ Q[27] ^ Q[3])) dh[4] = align(((xh >> 3) ^ (Q[20] << 0) ^ M[4]) + (xl ^ Q[28] ^ Q[4])) dh[5] = align(((xh << 6) ^ (Q[21] >> 6) ^ M[5]) + (xl ^ Q[29] ^ Q[5])) dh[6] = align(((xh >> 4) ^ (Q[22] << 6) ^ M[6]) + (xl ^ Q[30] ^ Q[6])) dh[7] = align(((xh >> 11) ^ (Q[23] << 2) ^ M[7]) + (xl ^ Q[31] ^ Q[7])) dh[8] = align(rol(dh[4], 9) + (xh ^ Q[24] ^ M[8]) + ((xl << 8) ^ Q[23] ^ Q[8])) dh[9] = align(rol(dh[5], 10) + (xh ^ Q[25] ^ M[9]) + ((xl >> 6) ^ Q[16] ^ Q[9])) dh[10] = align(rol(dh[6], 11) + (xh ^ Q[26] ^ M[10]) + ((xl << 6) ^ Q[17] ^ Q[10])) dh[11] = align(rol(dh[7], 12) + (xh ^ Q[27] ^ M[11]) + ((xl << 4) ^ Q[18] ^ Q[11])) dh[12] = align(rol(dh[0], 13) + (xh ^ Q[28] ^ M[12]) + ((xl >> 3) ^ Q[19] ^ Q[12])) dh[13] = align(rol(dh[1], 14) + (xh ^ Q[29] ^ M[13]) + ((xl >> 4) ^ Q[20] ^ Q[13])) dh[14] = align(rol(dh[2], 15) + (xh ^ Q[30] ^ M[14]) + ((xl >> 7) ^ Q[21] ^ Q[14])) dh[15] = align(rol(dh[3], 16) + (xh ^ Q[31] ^ M[15]) + ((xl >> 2) ^ Q[22] ^ Q[15])) return dh class BMW224(BMWHBase): block_size = 64 digest_size = 28 word_bits = 32 out_words = 7 iv = ( 0x0001_0203, 0x0405_0607, 0x0809_0a0b, 0x0c0d_0e0f, 0x1011_1213, 0x1415_1617, 0x1819_1a1b, 0x1c1d_1e1f, 0x2021_2223, 0x2425_2627, 0x2829_2a2b, 0x2c2d_2e2f, 0x3031_3233, 0x3435_3637, 0x3839_3a3b, 0x3c3d_3e3f, ) final_const = ( 0xaaaa_aaa0, 0xaaaa_aaa1, 0xaaaa_aaa2, 0xaaaa_aaa3, 0xaaaa_aaa4, 0xaaaa_aaa5, 0xaaaa_aaa6, 0xaaaa_aaa7, 0xaaaa_aaa8, 0xaaaa_aaa9, 0xaaaa_aaaa, 0xaaaa_aaab, 0xaaaa_aaac, 0xaaaa_aaad, 0xaaaa_aaae, 0xaaaa_aaaf, ) class BMW256(BMWHBase): block_size = 64 digest_size = 32 word_bits = 32 out_words = 8 iv = ( 0x4041_4243, 0x4445_4647, 0x4849_4a4b, 0x4c4d_4e4f, 0x5051_5253, 0x5455_5657, 0x5859_5a5b, 0x5c5d_5e5f, 0x6061_6263, 0x6465_6667, 0x6869_6a6b, 0x6c6d_6e6f, 0x7071_7273, 0x7475_7677, 0x7879_7a7b, 0x7c7d_7e7f, ) final_const = ( 0xaaaa_aaa0, 0xaaaa_aaa1, 0xaaaa_aaa2, 0xaaaa_aaa3, 0xaaaa_aaa4, 0xaaaa_aaa5, 0xaaaa_aaa6, 0xaaaa_aaa7, 0xaaaa_aaa8, 0xaaaa_aaa9, 0xaaaa_aaaa, 0xaaaa_aaab, 0xaaaa_aaac, 0xaaaa_aaad, 0xaaaa_aaae, 0xaaaa_aaaf, ) class BMW384(BMWHBase): block_size = 128 digest_size = 48 word_bits = 64 out_words = 6 iv = ( 0x0001_0203_0405_0607, 0x0809_0a0b_0c0d_0e0f, 0x1011_1213_1415_1617, 0x1819_1a1b_1c1d_1e1f, 0x2021_2223_2425_2627, 0x2829_2a2b_2c2d_2e2f, 0x3031_3233_3435_3637, 0x3839_3a3b_3c3d_3e3f, 0x4041_4243_4445_4647, 0x4849_4a4b_4c4d_4e4f, 0x5051_5253_5455_5657, 0x5859_5a5b_5c5d_5e5f, 0x6061_6263_6465_6667, 0x6869_6a6b_6c6d_6e6f, 0x7071_7273_7475_7677, 0x7879_7a7b_7c7d_7e7f, ) final_const = ( 0xaaaa_aaaa_aaaa_aaa0, 0xaaaa_aaaa_aaaa_aaa1, 0xaaaa_aaaa_aaaa_aaa2, 0xaaaa_aaaa_aaaa_aaa3, 0xaaaa_aaaa_aaaa_aaa4, 0xaaaa_aaaa_aaaa_aaa5, 0xaaaa_aaaa_aaaa_aaa6, 0xaaaa_aaaa_aaaa_aaa7, 0xaaaa_aaaa_aaaa_aaa8, 0xaaaa_aaaa_aaaa_aaa9, 0xaaaa_aaaa_aaaa_aaaa, 0xaaaa_aaaa_aaaa_aaab, 0xaaaa_aaaa_aaaa_aaac, 0xaaaa_aaaa_aaaa_aaad, 0xaaaa_aaaa_aaaa_aaae, 0xaaaa_aaaa_aaaa_aaaf, ) class BMW512(BMWHBase): block_size = 128 digest_size = 64 word_bits = 64 out_words = 8 iv = ( 0x8081_8283_8485_8687, 0x8889_8a8b_8c8d_8e8f, 0x9091_9293_9495_9697, 0x9899_9a9b_9c9d_9e9f, 0xa0a1_a2a3_a4a5_a6a7, 0xa8a9_aaab_acad_aeaf, 0xb0b1_b2b3_b4b5_b6b7, 0xb8b9_babb_bcbd_bebf, 0xc0c1_c2c3_c4c5_c6c7, 0xc8c9_cacb_cccd_cecf, 0xd0d1_d2d3_d4d5_d6d7, 0xd8d9_dadb_dcdd_dedf, 0xe0e1_e2e3_e4e5_e6e7, 0xe8e9_eaeb_eced_eeef, 0xf0f1_f2f3_f4f5_f6f7, 0xf8f9_fafb_fcfd_feff, ) final_const = ( 0xaaaa_aaaa_aaaa_aaa0, 0xaaaa_aaaa_aaaa_aaa1, 0xaaaa_aaaa_aaaa_aaa2, 0xaaaa_aaaa_aaaa_aaa3, 0xaaaa_aaaa_aaaa_aaa4, 0xaaaa_aaaa_aaaa_aaa5, 0xaaaa_aaaa_aaaa_aaa6, 0xaaaa_aaaa_aaaa_aaa7, 0xaaaa_aaaa_aaaa_aaa8, 0xaaaa_aaaa_aaaa_aaa9, 0xaaaa_aaaa_aaaa_aaaa, 0xaaaa_aaaa_aaaa_aaab, 0xaaaa_aaaa_aaaa_aaac, 0xaaaa_aaaa_aaaa_aaad, 0xaaaa_aaaa_aaaa_aaae, 0xaaaa_aaaa_aaaa_aaaf, ) class RadioGatunBase: digest_size = 0x20 blank_rounds = 0x10 def __init__(self, data=b""): if self.word_bits is None or self.rotate is None: raise ValueError("word_bits and rotate must be set in subclasses") self.word_bytes = self.word_bits // 8 if self.block_size is None: self.block_size = self.word_bytes * 3 self.word_mask = (1 << self.word_bits) - 1 self.mill = [0] * 0x13 self.belt = [0] * (13 * 3) self.buf = bytearray() self.is_finalized = False self.phase = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.mill = list(self.mill) other.belt = list(self.belt) other.buf = bytearray(self.buf) other.is_finalized = self.is_finalized other.phase = self.phase return other def update(self, data): if self.is_finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[: self.block_size]) del self.buf[: self.block_size] self.absorb_block(block) return self def digest(self): c = self.copy() c.finalize() return c.squeeze(self.digest_size) def hexdigest(self): return self.digest().hex() def finalize(self): if self.is_finalized: return data = bytes(self.buf) + b"\x01" self.buf = bytearray() rem = len(data) % self.block_size if rem != 0: data += b"\x00" * (self.block_size - rem) for off in range(0, len(data), self.block_size): self.absorb_block(data[off : off + self.block_size]) for _ in range(self.blank_rounds): self.beltmill() self.is_finalized = True self.phase = 0 return def squeeze(self, nbytes): def get_word(): if self.phase == 0: self.beltmill() self.phase = 1 out = self.mill[self.phase] self.phase += 1 if self.phase > 2: self.phase = 0 return out if not self.is_finalized: self.finalize() out = bytearray() while len(out) < nbytes: w = get_word() out.extend(w.to_bytes(self.word_bytes, "little")) return bytes(out[:nbytes]) def beltmill(self): def ror(x, r): if r == 0: return x return ((x >> r) | (x << (self.word_bits - r))) & self.word_mask def mill_func(mill): A = [0] * 19 for i in range(19): A[i] = mill[i] ^ (mill[(i + 1) % 19] | (mill[(i + 2) % 19] ^ self.word_mask)) a = [0] * 19 for i in range(19): a[i] = ror(A[(7 * i) % 19], self.rotate[i]) for i in range(19): A[i] = a[i] ^ a[(i + 1) % 19] ^ a[(i + 4) % 19] A[0] ^= 1 return A old_belt = self.belt old_mill = self.mill new_belt = [0] * (13 * 3) for i in range(13): src = (i - 1) % 13 new_belt[i * 3 : (i + 1) * 3] = old_belt[src * 3 : (src + 1) * 3] for i in range(12): stage = i + 1 word = i % 3 new_belt[stage * 3 + word] ^= old_mill[i + 1] new_mill = mill_func(old_mill) base = 12 * 3 new_mill[13] ^= old_belt[base + 0] new_mill[14] ^= old_belt[base + 1] new_mill[15] ^= old_belt[base + 2] self.belt = new_belt self.mill = new_mill return def absorb_block(self, block): w = self.word_bytes p0 = int.from_bytes(block[0 * w:1 * w], "little") p1 = int.from_bytes(block[1 * w:2 * w], "little") p2 = int.from_bytes(block[2 * w:3 * w], "little") self.belt[0] ^= p0 self.belt[1] ^= p1 self.belt[2] ^= p2 self.mill[16] ^= p0 self.mill[17] ^= p1 self.mill[18] ^= p2 self.beltmill() return class RadioGatun32(RadioGatunBase): block_size = 0x0c word_bits = 0x20 rotate = ( 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, 0x04, 0x0d, 0x17, 0x02, 0x0e, 0x1b, 0x09, 0x18, 0x08, 0x19, 0x0b, ) class RadioGatun64(RadioGatunBase): block_size = 0x18 word_bits = 0x40 rotate = ( 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, 0x24, 0x2d, 0x37, 0x02, 0x0e, 0x1b, 0x29, 0x38, 0x08, 0x19, 0x2b, ) class ED2KBase: block_size = 64 digest_size = 16 chunk_size = 9_728_000 def __init__(self, data=b""): self.chunk_hasher = Hash.MD4() self.chunk_len = 0 self.list_hasher = Hash.MD4() self.chunk_count = 0 self.first_chunk_hash = b"\x00" * 16 if data: self.update(data) return def copy(self): other = self.__class__() other.chunk_hasher = self.chunk_hasher.copy() other.chunk_len = self.chunk_len other.list_hasher = self.list_hasher.copy() other.chunk_count = self.chunk_count other.first_chunk_hash = bytes(self.first_chunk_hash) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) pos = 0 total = len(data) while pos < total: free = self.chunk_size - self.chunk_len take = total - pos if take > free: take = free part = data[pos:pos + take] self.chunk_hasher.update(part) self.chunk_len += take pos += take if self.chunk_len == self.chunk_size: self.hash_chunk() return self def digest(self): c = self.copy() return c.finalize() def hexdigest(self): return self.digest().hex() def finalize(self): if self.variant_name == "red": return self.finalize_red() if self.variant_name == "blue": return self.finalize_blue() if self.variant_name == "redblue": return self.finalize_redblue() raise ValueError("unknown ED2K variant") def hash_chunk(self): chunk_hash = self.chunk_hasher.digest() self.chunk_hasher = Hash.MD4() self.chunk_len = 0 if self.chunk_count == 0: self.first_chunk_hash = bytes(chunk_hash) self.chunk_count += 1 self.list_hasher.update(chunk_hash) return def list_digest(self): return self.list_hasher.digest() def finalize_red(self): if self.chunk_count == 0: self.hash_chunk() return bytes(self.first_chunk_hash) self.hash_chunk() return self.list_digest() def finalize_blue(self): if self.chunk_count == 0: self.hash_chunk() return bytes(self.first_chunk_hash) if self.chunk_len != 0: self.hash_chunk() return self.list_digest() if self.chunk_count == 1: return bytes(self.first_chunk_hash) return self.list_digest() def finalize_redblue(self): if self.chunk_count == 0: self.hash_chunk() one = bytes(self.first_chunk_hash) return one + one if self.chunk_len != 0: self.hash_chunk() one = self.list_digest() return one + one if self.chunk_count == 1: blue = bytes(self.first_chunk_hash) else: blue = self.list_digest() self.hash_chunk() red = self.list_digest() return red + blue class ED2KRed(ED2KBase): digest_size = 16 variant_name = "red" class ED2KBlue(ED2KBase): digest_size = 16 variant_name = "blue" class ED2KRedBlue(ED2KBase): digest_size = 32 variant_name = "redblue" class MDC2: block_size = 8 digest_size = 16 IP = ( 0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02, 0x3c, 0x34, 0x2c, 0x24, 0x1c, 0x14, 0x0c, 0x04, 0x3e, 0x36, 0x2e, 0x26, 0x1e, 0x16, 0x0e, 0x06, 0x40, 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, 0x3b, 0x33, 0x2b, 0x23, 0x1b, 0x13, 0x0b, 0x03, 0x3d, 0x35, 0x2d, 0x25, 0x1d, 0x15, 0x0d, 0x05, 0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, ) FP = ( 0x28, 0x08, 0x30, 0x10, 0x38, 0x18, 0x40, 0x20, 0x27, 0x07, 0x2f, 0x0f, 0x37, 0x17, 0x3f, 0x1f, 0x26, 0x06, 0x2e, 0x0e, 0x36, 0x16, 0x3e, 0x1e, 0x25, 0x05, 0x2d, 0x0d, 0x35, 0x15, 0x3d, 0x1d, 0x24, 0x04, 0x2c, 0x0c, 0x34, 0x14, 0x3c, 0x1c, 0x23, 0x03, 0x2b, 0x0b, 0x33, 0x13, 0x3b, 0x1b, 0x22, 0x02, 0x2a, 0x0a, 0x32, 0x12, 0x3a, 0x1a, 0x21, 0x01, 0x29, 0x09, 0x31, 0x11, 0x39, 0x19, ) E = ( 0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x01, ) P = ( 0x10, 0x07, 0x14, 0x15, 0x1d, 0x0c, 0x1c, 0x11, 0x01, 0x0f, 0x17, 0x1a, 0x05, 0x12, 0x1f, 0x0a, 0x02, 0x08, 0x18, 0x0e, 0x20, 0x1b, 0x03, 0x09, 0x13, 0x0d, 0x1e, 0x06, 0x16, 0x0b, 0x04, 0x19, ) PC1 = ( 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, 0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02, 0x3b, 0x33, 0x2b, 0x23, 0x1b, 0x13, 0x0b, 0x03, 0x3c, 0x34, 0x2c, 0x24, 0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, 0x3e, 0x36, 0x2e, 0x26, 0x1e, 0x16, 0x0e, 0x06, 0x3d, 0x35, 0x2d, 0x25, 0x1d, 0x15, 0x0d, 0x05, 0x1c, 0x14, 0x0c, 0x04, ) PC2 = ( 0x0e, 0x11, 0x0b, 0x18, 0x01, 0x05, 0x03, 0x1c, 0x0f, 0x06, 0x15, 0x0a, 0x17, 0x13, 0x0c, 0x04, 0x1a, 0x08, 0x10, 0x07, 0x1b, 0x14, 0x0d, 0x02, 0x29, 0x34, 0x1f, 0x25, 0x2f, 0x37, 0x1e, 0x28, 0x33, 0x2d, 0x21, 0x30, 0x2c, 0x31, 0x27, 0x38, 0x22, 0x35, 0x2e, 0x2a, 0x32, 0x24, 0x1d, 0x20, ) SHIFTS = ( 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, ) SBOX = ( ( (0x0e, 0x04, 0x0d, 0x01, 0x02, 0x0f, 0x0b, 0x08, 0x03, 0x0a, 0x06, 0x0c, 0x05, 0x09, 0x00, 0x07), (0x00, 0x0f, 0x07, 0x04, 0x0e, 0x02, 0x0d, 0x01, 0x0a, 0x06, 0x0c, 0x0b, 0x09, 0x05, 0x03, 0x08), (0x04, 0x01, 0x0e, 0x08, 0x0d, 0x06, 0x02, 0x0b, 0x0f, 0x0c, 0x09, 0x07, 0x03, 0x0a, 0x05, 0x00), (0x0f, 0x0c, 0x08, 0x02, 0x04, 0x09, 0x01, 0x07, 0x05, 0x0b, 0x03, 0x0e, 0x0a, 0x00, 0x06, 0x0d), ), ( (0x0f, 0x01, 0x08, 0x0e, 0x06, 0x0b, 0x03, 0x04, 0x09, 0x07, 0x02, 0x0d, 0x0c, 0x00, 0x05, 0x0a), (0x03, 0x0d, 0x04, 0x07, 0x0f, 0x02, 0x08, 0x0e, 0x0c, 0x00, 0x01, 0x0a, 0x06, 0x09, 0x0b, 0x05), (0x00, 0x0e, 0x07, 0x0b, 0x0a, 0x04, 0x0d, 0x01, 0x05, 0x08, 0x0c, 0x06, 0x09, 0x03, 0x02, 0x0f), (0x0d, 0x08, 0x0a, 0x01, 0x03, 0x0f, 0x04, 0x02, 0x0b, 0x06, 0x07, 0x0c, 0x00, 0x05, 0x0e, 0x09), ), ( (0x0a, 0x00, 0x09, 0x0e, 0x06, 0x03, 0x0f, 0x05, 0x01, 0x0d, 0x0c, 0x07, 0x0b, 0x04, 0x02, 0x08), (0x0d, 0x07, 0x00, 0x09, 0x03, 0x04, 0x06, 0x0a, 0x02, 0x08, 0x05, 0x0e, 0x0c, 0x0b, 0x0f, 0x01), (0x0d, 0x06, 0x04, 0x09, 0x08, 0x0f, 0x03, 0x00, 0x0b, 0x01, 0x02, 0x0c, 0x05, 0x0a, 0x0e, 0x07), (0x01, 0x0a, 0x0d, 0x00, 0x06, 0x09, 0x08, 0x07, 0x04, 0x0f, 0x0e, 0x03, 0x0b, 0x05, 0x02, 0x0c), ), ( (0x07, 0x0d, 0x0e, 0x03, 0x00, 0x06, 0x09, 0x0a, 0x01, 0x02, 0x08, 0x05, 0x0b, 0x0c, 0x04, 0x0f), (0x0d, 0x08, 0x0b, 0x05, 0x06, 0x0f, 0x00, 0x03, 0x04, 0x07, 0x02, 0x0c, 0x01, 0x0a, 0x0e, 0x09), (0x0a, 0x06, 0x09, 0x00, 0x0c, 0x0b, 0x07, 0x0d, 0x0f, 0x01, 0x03, 0x0e, 0x05, 0x02, 0x08, 0x04), (0x03, 0x0f, 0x00, 0x06, 0x0a, 0x01, 0x0d, 0x08, 0x09, 0x04, 0x05, 0x0b, 0x0c, 0x07, 0x02, 0x0e), ), ( (0x02, 0x0c, 0x04, 0x01, 0x07, 0x0a, 0x0b, 0x06, 0x08, 0x05, 0x03, 0x0f, 0x0d, 0x00, 0x0e, 0x09), (0x0e, 0x0b, 0x02, 0x0c, 0x04, 0x07, 0x0d, 0x01, 0x05, 0x00, 0x0f, 0x0a, 0x03, 0x09, 0x08, 0x06), (0x04, 0x02, 0x01, 0x0b, 0x0a, 0x0d, 0x07, 0x08, 0x0f, 0x09, 0x0c, 0x05, 0x06, 0x03, 0x00, 0x0e), (0x0b, 0x08, 0x0c, 0x07, 0x01, 0x0e, 0x02, 0x0d, 0x06, 0x0f, 0x00, 0x09, 0x0a, 0x04, 0x05, 0x03), ), ( (0x0c, 0x01, 0x0a, 0x0f, 0x09, 0x02, 0x06, 0x08, 0x00, 0x0d, 0x03, 0x04, 0x0e, 0x07, 0x05, 0x0b), (0x0a, 0x0f, 0x04, 0x02, 0x07, 0x0c, 0x09, 0x05, 0x06, 0x01, 0x0d, 0x0e, 0x00, 0x0b, 0x03, 0x08), (0x09, 0x0e, 0x0f, 0x05, 0x02, 0x08, 0x0c, 0x03, 0x07, 0x00, 0x04, 0x0a, 0x01, 0x0d, 0x0b, 0x06), (0x04, 0x03, 0x02, 0x0c, 0x09, 0x05, 0x0f, 0x0a, 0x0b, 0x0e, 0x01, 0x07, 0x06, 0x00, 0x08, 0x0d), ), ( (0x04, 0x0b, 0x02, 0x0e, 0x0f, 0x00, 0x08, 0x0d, 0x03, 0x0c, 0x09, 0x07, 0x05, 0x0a, 0x06, 0x01), (0x0d, 0x00, 0x0b, 0x07, 0x04, 0x09, 0x01, 0x0a, 0x0e, 0x03, 0x05, 0x0c, 0x02, 0x0f, 0x08, 0x06), (0x01, 0x04, 0x0b, 0x0d, 0x0c, 0x03, 0x07, 0x0e, 0x0a, 0x0f, 0x06, 0x08, 0x00, 0x05, 0x09, 0x02), (0x06, 0x0b, 0x0d, 0x08, 0x01, 0x04, 0x0a, 0x07, 0x09, 0x05, 0x00, 0x0f, 0x0e, 0x02, 0x03, 0x0c), ), ( (0x0d, 0x02, 0x08, 0x04, 0x06, 0x0f, 0x0b, 0x01, 0x0a, 0x09, 0x03, 0x0e, 0x05, 0x00, 0x0c, 0x07), (0x01, 0x0f, 0x0d, 0x08, 0x0a, 0x03, 0x07, 0x04, 0x0c, 0x05, 0x06, 0x0b, 0x00, 0x0e, 0x09, 0x02), (0x07, 0x0b, 0x04, 0x01, 0x09, 0x0c, 0x0e, 0x02, 0x00, 0x06, 0x0a, 0x0d, 0x0f, 0x03, 0x05, 0x08), (0x02, 0x01, 0x0e, 0x07, 0x04, 0x0a, 0x08, 0x0d, 0x0f, 0x0c, 0x09, 0x00, 0x03, 0x05, 0x06, 0x0b), ), ) C_TEMPLATE = r""" #include static uint64_t fast_permute64(uint64_t value, const uint64_t *tables) { return ( tables[(0 * 256) + ((value >> 56) & 0xffULL)] ^ tables[(1 * 256) + ((value >> 48) & 0xffULL)] ^ tables[(2 * 256) + ((value >> 40) & 0xffULL)] ^ tables[(3 * 256) + ((value >> 32) & 0xffULL)] ^ tables[(4 * 256) + ((value >> 24) & 0xffULL)] ^ tables[(5 * 256) + ((value >> 16) & 0xffULL)] ^ tables[(6 * 256) + ((value >> 8) & 0xffULL)] ^ tables[(7 * 256) + (value & 0xffULL)] ); } static uint64_t fast_permute56(uint64_t value, const uint64_t *tables) { return ( tables[(0 * 256) + ((value >> 48) & 0xffULL)] ^ tables[(1 * 256) + ((value >> 40) & 0xffULL)] ^ tables[(2 * 256) + ((value >> 32) & 0xffULL)] ^ tables[(3 * 256) + ((value >> 24) & 0xffULL)] ^ tables[(4 * 256) + ((value >> 16) & 0xffULL)] ^ tables[(5 * 256) + ((value >> 8) & 0xffULL)] ^ tables[(6 * 256) + (value & 0xffULL)] ); } static uint64_t fast_permute32(uint32_t value, const uint64_t *tables) { return ( tables[(0 * 256) + ((value >> 24) & 0xffU)] ^ tables[(1 * 256) + ((value >> 16) & 0xffU)] ^ tables[(2 * 256) + ((value >> 8) & 0xffU)] ^ tables[(3 * 256) + (value & 0xffU)] ); } static uint32_t rol28(uint32_t x, int n) { return ((x << n) | (x >> (28 - n))) & 0x0fffffffU; } static uint8_t set_odd_parity_byte(uint8_t value) { uint8_t x = value & 0xfeU; uint8_t y = x; int ones = 0; while (y != 0) { ones += (y & 0x01U); y >>= 1; } if ((ones & 1) == 0) x |= 0x01U; return x; } static void set_odd_parity(uint8_t *data, int size) { int i = 0; for (i = 0; i < size; i++) { data[i] = set_odd_parity_byte(data[i]); } } static void des_subkeys( const uint8_t *key, const uint64_t *pc1_tables, const uint64_t *pc2_tables, uint64_t *subkeys) { uint8_t shift_table[16] = { 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, }; uint64_t k = 0; uint64_t k56 = 0; uint32_t c = 0; uint32_t d = 0; int i = 0; for (i = 0; i < 8; i++) { k = (k << 8) | (uint64_t)key[i]; } k56 = fast_permute64(k, pc1_tables); c = (uint32_t)(k56 >> 28); d = (uint32_t)(k56 & 0x0fffffffU); for (i = 0; i < 16; i++) { uint64_t cd = 0; c = rol28(c, shift_table[i]); d = rol28(d, shift_table[i]); cd = (((uint64_t)c) << 28) | (uint64_t)d; subkeys[i] = fast_permute56(cd, pc2_tables); } } static uint32_t des_f(uint32_t r, uint64_t subkey, const uint64_t *e_tables, const uint32_t *sp_tables) { uint64_t x = fast_permute32(r, e_tables) ^ subkey; return ( sp_tables[(0 * 64) + ((x >> 42) & 0x3fULL)] ^ sp_tables[(1 * 64) + ((x >> 36) & 0x3fULL)] ^ sp_tables[(2 * 64) + ((x >> 30) & 0x3fULL)] ^ sp_tables[(3 * 64) + ((x >> 24) & 0x3fULL)] ^ sp_tables[(4 * 64) + ((x >> 18) & 0x3fULL)] ^ sp_tables[(5 * 64) + ((x >> 12) & 0x3fULL)] ^ sp_tables[(6 * 64) + ((x >> 6) & 0x3fULL)] ^ sp_tables[(7 * 64) + (x & 0x3fULL)] ); } static void des_encrypt_block( const uint8_t *block, const uint8_t *key, const uint64_t *ip_tables, const uint64_t *fp_tables, const uint64_t *pc1_tables, const uint64_t *pc2_tables, const uint64_t *e_tables, const uint32_t *sp_tables, uint8_t *out) { uint64_t subkeys[16]; uint64_t x = 0; uint64_t y = 0; uint32_t l = 0; uint32_t r = 0; int i = 0; des_subkeys(key, pc1_tables, pc2_tables, subkeys); for (i = 0; i < 8; i++) { x = (x << 8) | (uint64_t)block[i]; } x = fast_permute64(x, ip_tables); l = (uint32_t)(x >> 32); r = (uint32_t)(x & 0xffffffffU); for (i = 0; i < 16; i++) { uint32_t nl = r; uint32_t nr = l ^ des_f(r, subkeys[i], e_tables, sp_tables); l = nl; r = nr; } y = (((uint64_t)r) << 32) | (uint64_t)l; y = fast_permute64(y, fp_tables); for (i = 7; i >= 0; i--) { out[i] = (uint8_t)(y & 0xffULL); y >>= 8; } } void compress( uint8_t *h, uint8_t *hh, const uint8_t *block, const uint64_t *ip_tables, const uint64_t *fp_tables, const uint64_t *pc1_tables, const uint64_t *pc2_tables, const uint64_t *e_tables, const uint32_t *sp_tables) { uint8_t h_key[8]; uint8_t hh_key[8]; uint8_t d[8]; uint8_t dd[8]; int i = 0; for (i = 0; i < 8; i++) { h_key[i] = h[i]; hh_key[i] = hh[i]; } h_key[0] = (uint8_t)((h_key[0] & 0x9fU) | 0x40U); hh_key[0] = (uint8_t)((hh_key[0] & 0x9fU) | 0x20U); set_odd_parity(h_key, 8); set_odd_parity(hh_key, 8); des_encrypt_block(block, h_key, ip_tables, fp_tables, pc1_tables, pc2_tables, e_tables, sp_tables, d); des_encrypt_block(block, hh_key, ip_tables, fp_tables, pc1_tables, pc2_tables, e_tables, sp_tables, dd); for (i = 0; i < 4; i++) { h[i] = (uint8_t)(block[i] ^ d[i]); hh[i] = (uint8_t)(block[i] ^ dd[i]); } for (i = 4; i < 8; i++) { h[i] = (uint8_t)(block[i] ^ dd[i]); hh[i] = (uint8_t)(block[i] ^ d[i]); } } """ DEF_TEMPLATE = r""" void compress( uint8_t *h, uint8_t *hh, const uint8_t *block, const uint64_t *ip_tables, const uint64_t *fp_tables, const uint64_t *pc1_tables, const uint64_t *pc2_tables, const uint64_t *e_tables, const uint32_t *sp_tables); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.MDC2 if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib if base_class in base_class.cffi_cache: ffi, lib = base_class.cffi_cache[base_class] else: try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) base_class.cffi_cache[base_class] = (ffi, lib) except Exception: self.USE_CFFI = False return # Different for each class def flatten_tables(tables): flat = [] for row in tables: flat.extend(row) return tuple(flat) try: ip_tables = ffi.new("uint64_t[]", flatten_tables(self.IP_tables)) fp_tables = ffi.new("uint64_t[]", flatten_tables(self.FP_tables)) pc1_tables = ffi.new("uint64_t[]", flatten_tables(self.PC1_tables)) pc2_tables = ffi.new("uint64_t[]", flatten_tables(self.PC2_tables)) e_tables = ffi.new("uint64_t[]", flatten_tables(self.E_tables)) sp_tables = ffi.new("uint32_t[]", flatten_tables(self.SP_tables)) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib ip_tables fp_tables pc1_tables pc2_tables e_tables sp_tables")( ffi, lib, ip_tables, fp_tables, pc1_tables, pc2_tables, e_tables, sp_tables, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return @classmethod def ensure_tables(cls): if hasattr(cls, "IP_tables"): return def build_perm_tables(table, in_bits): num_chunks = in_bits // 8 tables = [[0] * 256 for _ in range(num_chunks)] out_bits = len(table) for chunk in range(num_chunks): for val in range(256): out = 0 for i, pos in enumerate(table): bit_idx = pos - 1 if (bit_idx // 8) == chunk: bit = (val >> (7 - (bit_idx % 8))) & 1 out |= (bit << (out_bits - 1 - i)) tables[chunk][val] = out return tables cls.IP_tables = build_perm_tables(cls.IP, 64) cls.FP_tables = build_perm_tables(cls.FP, 64) cls.PC1_tables = build_perm_tables(cls.PC1, 64) cls.PC2_tables = build_perm_tables(cls.PC2, 56) cls.E_tables = build_perm_tables(cls.E, 32) SP_tables = [[0] * 64 for _ in range(8)] for i in range(8): for val in range(64): row = ((val & 0x20) >> 4) | (val & 0x1) col = (val >> 1) & 0x0f sval = cls.SBOX[i][row][col] out = 0 for b in range(4): bit_val = (sval >> (3 - b)) & 1 in_pos = i * 4 + b + 1 out_idx = cls.P.index(in_pos) out |= (bit_val << (31 - out_idx)) SP_tables[i][val] = out cls.SP_tables = SP_tables return def __init__(self, data=b""): self.h = bytearray([0x52] * 8) self.hh = bytearray([0x25] * 8) self.buf = bytearray() self.msg_len = 0 self.pad_type = 1 self.ensure_tables() self.init_cffi_backend() if data: self.update(data) return def copy(self): other = self.__class__() other.h = bytearray(self.h) other.hh = bytearray(self.hh) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.pad_type = self.pad_type return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 8: block = bytes(self.buf[:8]) del self.buf[:8] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return bytes(c.h + c.hh) def hexdigest(self): return self.digest().hex() def finalize(self): if (len(self.buf) > 0) or (self.pad_type == 2): if self.pad_type == 2: self.buf.append(0x80) while len(self.buf) < 8: self.buf.append(0x00) self.compress(bytes(self.buf[:8])) del self.buf[:8] return def compress(self, block): if len(block) != 8: raise ValueError("invalid block size") if self.USE_CFFI: h_buf = self.cffi.ffi.new("uint8_t[]", bytes(self.h)) hh_buf = self.cffi.ffi.new("uint8_t[]", bytes(self.hh)) block_buf = self.cffi.ffi.new("uint8_t[]", bytes(block)) self.cffi.lib.compress( h_buf, hh_buf, block_buf, self.cffi.ip_tables, self.cffi.fp_tables, self.cffi.pc1_tables, self.cffi.pc2_tables, self.cffi.e_tables, self.cffi.sp_tables, ) self.h = bytearray(bytes(self.cffi.ffi.buffer(h_buf, 8))) self.hh = bytearray(bytes(self.cffi.ffi.buffer(hh_buf, 8))) return def set_odd_parity(data): for i in range(len(data)): data[i] = set_odd_parity_byte(data[i]) return def set_odd_parity_byte(value): x = value & 0xfe ones = 0 y = x while y != 0: ones += y & 0x1 y >>= 1 if (ones % 2) == 0: x |= 0x1 return x self.h[0] = (self.h[0] & 0x9f) | 0x40 self.hh[0] = (self.hh[0] & 0x9f) | 0x20 set_odd_parity(self.h) set_odd_parity(self.hh) d = self.des_encrypt_block(block, bytes(self.h)) dd = self.des_encrypt_block(block, bytes(self.hh)) h = bytearray(8) hh = bytearray(8) for i in range(4): h[i] = block[i] ^ d[i] hh[i] = block[i] ^ dd[i] for i in range(4, 8): h[i] = block[i] ^ dd[i] hh[i] = block[i] ^ d[i] self.h = h self.hh = hh return def des_encrypt_block(self, block, key): def fast_permute64(value, tables): return ( tables[0][(value >> 56) & 0xff] ^ tables[1][(value >> 48) & 0xff] ^ tables[2][(value >> 40) & 0xff] ^ tables[3][(value >> 32) & 0xff] ^ tables[4][(value >> 24) & 0xff] ^ tables[5][(value >> 16) & 0xff] ^ tables[6][(value >> 8) & 0xff] ^ tables[7][value & 0xff] ) def fast_permute56(value, tables): return ( tables[0][(value >> 48) & 0xff] ^ tables[1][(value >> 40) & 0xff] ^ tables[2][(value >> 32) & 0xff] ^ tables[3][(value >> 24) & 0xff] ^ tables[4][(value >> 16) & 0xff] ^ tables[5][(value >> 8) & 0xff] ^ tables[6][value & 0xff] ) def fast_permute32(value, tables): return ( tables[0][(value >> 24) & 0xff] ^ tables[1][(value >> 16) & 0xff] ^ tables[2][(value >> 8) & 0xff] ^ tables[3][value & 0xff] ) def rol28(x, n): return ((x << n) | (x >> (28 - n))) & 0x0fff_ffff def des_subkeys(key): k = int.from_bytes(key, "big") k56 = fast_permute64(k, self.PC1_tables) c = k56 >> 28 d = k56 & 0x0fff_ffff subkeys = [] for shift in self.SHIFTS: c = rol28(c, shift) d = rol28(d, shift) cd = (c << 28) | d subkeys.append(fast_permute56(cd, self.PC2_tables)) return subkeys def des_f(r, subkey): x = fast_permute32(r, self.E_tables) ^ subkey SP = self.SP_tables return ( SP[0][(x >> 42) & 0x3f] ^ SP[1][(x >> 36) & 0x3f] ^ SP[2][(x >> 30) & 0x3f] ^ SP[3][(x >> 24) & 0x3f] ^ SP[4][(x >> 18) & 0x3f] ^ SP[5][(x >> 12) & 0x3f] ^ SP[6][(x >> 6) & 0x3f] ^ SP[7][x & 0x3f] ) subkeys = des_subkeys(key) x = int.from_bytes(block, "big") x = fast_permute64(x, self.IP_tables) l = x >> 32 # noqa: E741 r = x & 0xffff_ffff for subkey in subkeys: nl = r nr = l ^ des_f(r, subkey) l = nl # noqa: E741 r = nr y = (r << 32) | l y = fast_permute64(y, self.FP_tables) return y.to_bytes(8, "big") class MarsupilamiFourteen: block_size = 0x88 digest_size = 64 node_block_size = 0x2000 single = bytes([0x07]) intermediate = bytes([0x0b]) final = bytes([0xff, 0xff, 0x06]) first = bytes([0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) round_constants = ( 0x0000_0000_0000_0001, 0x0000_0000_0000_8082, 0x8000_0000_0000_808a, 0x8000_0000_8000_8000, 0x0000_0000_0000_808b, 0x0000_0000_8000_0001, 0x8000_0000_8000_8081, 0x8000_0000_0000_8009, 0x0000_0000_0000_008a, 0x0000_0000_0000_0088, 0x0000_0000_8000_8009, 0x0000_0000_8000_000a, 0x0000_0000_8000_808b, 0x8000_0000_0000_008b, 0x8000_0000_0000_8089, 0x8000_0000_0000_8003, 0x8000_0000_0000_8002, 0x8000_0000_0000_0080, 0x0000_0000_0000_800a, 0x8000_0000_8000_000a, 0x8000_0000_8000_8081, 0x8000_0000_0000_8080, 0x0000_0000_8000_0001, 0x8000_0000_8000_8008, ) class KangarooSponge: def __init__(self, strength, rounds, round_constants): self.strength = strength self.rounds = rounds self.round_constants = round_constants self.rate_bytes = (1600 - (strength << 1)) >> 3 self.state = [0] * 25 self.queue = bytearray(self.rate_bytes) self.bytes_in_queue = 0 self.squeezing = False self.init_sponge() return def copy(self): other = self.__class__(self.strength, self.rounds, self.round_constants) other.state = self.state[:] other.queue = bytearray(self.queue) other.bytes_in_queue = self.bytes_in_queue other.squeezing = self.squeezing return other def init_sponge(self): for i in range(25): self.state[i] = 0 for i in range(self.rate_bytes): self.queue[i] = 0 self.bytes_in_queue = 0 self.squeezing = False return def absorb(self, data, off, length): if self.squeezing: raise ValueError("attempt to absorb while squeezing") count = 0 while count < length: if self.bytes_in_queue == 0 and count <= (length - self.rate_bytes): while True: self.kangaroo_absorb(data, off + count) count += self.rate_bytes if count > (length - self.rate_bytes): break else: partial_block = min(self.rate_bytes - self.bytes_in_queue, length - count) self.queue[self.bytes_in_queue:self.bytes_in_queue + partial_block] = \ data[off + count:off + count + partial_block] self.bytes_in_queue += partial_block count += partial_block if self.bytes_in_queue == self.rate_bytes: self.kangaroo_absorb(self.queue, 0) self.bytes_in_queue = 0 return def pad_and_switch_to_squeezing_phase(self): for i in range(self.bytes_in_queue, self.rate_bytes): self.queue[i] = 0 self.queue[self.rate_bytes - 1] ^= 0x80 self.kangaroo_absorb(self.queue, 0) self.kangaroo_extract() self.bytes_in_queue = self.rate_bytes self.squeezing = True return def squeeze(self, out, off, output_length): if not self.squeezing: self.pad_and_switch_to_squeezing_phase() i = 0 while i < output_length: if self.bytes_in_queue == 0: self.kangaroo_permutation() self.kangaroo_extract() self.bytes_in_queue = self.rate_bytes partial_block = min(self.bytes_in_queue, output_length - i) src = self.rate_bytes - self.bytes_in_queue out[off + i:off + i + partial_block] = self.queue[src:src + partial_block] self.bytes_in_queue -= partial_block i += partial_block return def kangaroo_absorb(self, data, off): count = self.rate_bytes >> 3 off_set = off for i in range(count): lane = int.from_bytes(data[off_set:off_set + 8], "little") self.state[i] ^= lane off_set += 8 self.kangaroo_permutation() return def kangaroo_extract(self): count = self.rate_bytes >> 3 off = 0 for i in range(count): self.queue[off:off + 8] = self.state[i].to_bytes(8, "little") off += 8 return def kangaroo_permutation(self): def rol64(x, n): return ((x << n) | (x >> (64 - n))) & 0xffff_ffff_ffff_ffff rc = ( (10, 1, 1), (1, 6, 44), (6, 9, 20), (9, 22, 61), (22, 14, 39), (14, 20, 18), (20, 2, 62), (2, 12, 43), (12, 13, 25), (13, 19, 8), (19, 23, 56), (23, 15, 41), (15, 4, 27), (4, 24, 14), (24, 21, 2), (21, 8, 55), (8, 16, 45), (16, 5, 36), (5, 3, 28), (3, 18, 21), (18, 17, 15), (17, 11, 10), (11, 7, 6), (7, 10, 3), ) a = self.state base = len(self.round_constants) - self.rounds for i in range(self.rounds): c = [0] * 5 for j in range(5): c[j] = a[j] ^ a[j + 5] ^ a[j + 10] ^ a[j + 15] ^ a[j + 20] d = [0] * 5 for j in range(5): d[(j + 1) % 5] = rol64(c[(j + 1) % 5], 1) ^ c[(j + 4) % 5] for j in range(5): a[j] ^= d[(j + 1) % 5] a[j + 5] ^= d[(j + 1) % 5] a[j + 10] ^= d[(j + 1) % 5] a[j + 15] ^= d[(j + 1) % 5] a[j + 20] ^= d[(j + 1) % 5] new_a = a[::] for dst, src, ri in rc: new_a[dst] = rol64(a[src], ri) a = new_a new_a = [0] * 25 for k in [0, 5, 10, 15, 20]: for j in range(5): new_a[k + j] = a[k + j] ^ ((a[k + (j + 1) % 5] ^ 0xffff_ffff_ffff_ffff) & a[k + (j + 2) % 5]) a = new_a a[0] ^= self.round_constants[base + i] self.state = a return def __init__(self, data=b"", digest_size=64, personal=b""): self.strength = 256 self.rounds = 14 self.output_size = digest_size self.tree = self.KangarooSponge(self.strength, self.rounds, self.round_constants) self.leaf = self.KangarooSponge(self.strength, self.rounds, self.round_constants) self.chain_len = self.strength >> 2 self.personal = b"" self.squeezing = False self.curr_node = 0 self.processed = 0 self.msg_len = 0 self.build_personal(personal) if data: self.update(data) return def copy(self): other = self.__class__(b"", self.output_size) other.tree = self.tree.copy() other.leaf = self.leaf.copy() other.chain_len = self.chain_len other.personal = bytes(self.personal) other.squeezing = self.squeezing other.curr_node = self.curr_node other.processed = self.processed other.msg_len = self.msg_len return other def build_personal(self, personal): if personal is None: personal = b"" if not isinstance(personal, (bytes, bytearray, memoryview)): raise TypeError("personal must be bytes-like") personal = bytes(personal) encoded = self.length_encode(len(personal)) self.personal = personal + encoded return def length_encode(self, value): n = 0 v = value if v != 0: n = 1 while True: v >>= 8 if v == 0: break n += 1 out = bytearray(n + 1) out[n] = n for i in range(n): shift = 8 * (n - i - 1) out[i] = (value >> shift) & 0xff return bytes(out) def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if self.squeezing: raise ValueError("attempt to absorb while squeezing") data = bytes(data) self.msg_len += len(data) self.process_data(data, 0, len(data)) return self def process_data(self, data, off, length): if self.squeezing: raise ValueError("attempt to absorb while squeezing") sponge = self.tree if self.curr_node == 0 else self.leaf space = self.node_block_size - self.processed if space >= length: sponge.absorb(data, off, length) self.processed += length return if space > 0: sponge.absorb(data, off, space) self.processed += space processed = space while processed < length: if self.processed == self.node_block_size: self.switch_leaf(True) data_len = min(length - processed, self.node_block_size) self.leaf.absorb(data, off + processed, data_len) self.processed += data_len processed += data_len return def switch_leaf(self, more_to_come): if self.curr_node == 0: self.tree.absorb(self.first, 0, len(self.first)) else: self.leaf.absorb(self.intermediate, 0, len(self.intermediate)) chain_value = bytearray(self.chain_len) self.leaf.squeeze(chain_value, 0, self.chain_len) self.tree.absorb(chain_value, 0, self.chain_len) self.leaf.init_sponge() if more_to_come: self.curr_node += 1 self.processed = 0 return def switch_to_squeezing(self): self.process_data(self.personal, 0, len(self.personal)) if self.curr_node == 0: self.tree.absorb(self.single, 0, len(self.single)) self.tree.pad_and_switch_to_squeezing_phase() else: self.switch_leaf(False) node_count_encoded = self.length_encode(self.curr_node) self.tree.absorb(node_count_encoded, 0, len(node_count_encoded)) self.tree.absorb(self.final, 0, len(self.final)) self.tree.pad_and_switch_to_squeezing_phase() self.squeezing = True return def finalize(self): if not self.squeezing: self.switch_to_squeezing() return def read(self, length): if length < 0: raise ValueError("invalid output length") if not self.squeezing: self.switch_to_squeezing() out = bytearray(length) self.tree.squeeze(out, 0, length) return bytes(out) def digest(self, length=None): c = self.copy() if length is None: length = c.output_size return c.read(length) def hexdigest(self, length=None): return self.digest(length).hex() class Panama: block_size = 32 digest_size = 32 def __init__(self, data=b""): self.state = [0] * 17 self.buffer = [[0] * 8 for _ in range(32)] self.buffer_ptr = 0 self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.state = list(self.state) other.buffer = [list(row) for row in self.buffer] other.buffer_ptr = self.buffer_ptr other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 32: block = bytes(self.buf[:32]) del self.buf[:32] self.push(block) return self def digest(self): c = self.copy() c.finalize() out = bytearray() for i in range(8): out.extend(struct.pack("> (32 - n))) & 0xffff_ffff def gamma(a): g = [0] * 17 for i in range(17): g[i] = (a[i] ^ (a[(i + 1) % 17] | (~a[(i + 2) % 17]))) & 0xffff_ffff return g def pi(g): p = [0] * 17 p[0] = g[0] p[1] = rol32(g[7], 1) p[2] = rol32(g[14], 3) p[3] = rol32(g[4], 6) p[4] = rol32(g[11], 10) p[5] = rol32(g[1], 15) p[6] = rol32(g[8], 21) p[7] = rol32(g[15], 28) p[8] = rol32(g[5], 4) p[9] = rol32(g[12], 13) p[10] = rol32(g[2], 23) p[11] = rol32(g[9], 2) p[12] = rol32(g[16], 14) p[13] = rol32(g[6], 27) p[14] = rol32(g[13], 9) p[15] = rol32(g[3], 24) p[16] = rol32(g[10], 8) return p def theta(p): t = [0] * 17 for i in range(17): t[i] = p[i] ^ p[(i + 1) % 17] ^ p[(i + 4) % 17] return t a = list(self.state) ptr0 = self.buffer_ptr ptr24 = (ptr0 - 8) & 31 ptr31 = (ptr0 - 1) & 31 pairs = ((0, 2), (1, 3), (2, 4), (3, 5), (4, 6), (5, 7), (6, 0), (7, 1)) for n0, n2 in pairs: self.buffer[ptr24][n0] ^= self.buffer[ptr31][n2] self.buffer[ptr31][n2] ^= inw1[n2] g = gamma(a) p = pi(g) t = theta(p) ptr16 = ptr0 ^ 16 next_state = [0] * 17 next_state[0] = t[0] ^ 1 for i in range(8): next_state[i + 1] = t[i + 1] ^ inw2[i] for i in range(8): next_state[i + 9] = t[i + 9] ^ self.buffer[ptr16][i] self.state = next_state self.buffer_ptr = ptr31 return def push(self, block): x = list(struct.unpack("<8I", block)) self.step(x, x) return def pull(self, num): for _ in range(num): a = self.state inw1 = [a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]] ptr4 = (self.buffer_ptr + 4) & 31 inw2 = list(self.buffer[ptr4]) self.step(inw1, inw2) return class PhotonBeetle: block_size = 4 digest_size = 32 sbox = ( 0x0c, 0x05, 0x06, 0x0b, 0x09, 0x00, 0x0a, 0x0d, 0x03, 0x0e, 0x0f, 0x08, 0x04, 0x07, 0x01, 0x02, ) rc = ( (0x01, 0x00, 0x02, 0x06, 0x0e, 0x0f, 0x0d, 0x09), (0x03, 0x02, 0x00, 0x04, 0x0c, 0x0d, 0x0f, 0x0b), (0x07, 0x06, 0x04, 0x00, 0x08, 0x09, 0x0b, 0x0f), (0x0e, 0x0f, 0x0d, 0x09, 0x01, 0x00, 0x02, 0x06), (0x0d, 0x0c, 0x0e, 0x0a, 0x02, 0x03, 0x01, 0x05), (0x0b, 0x0a, 0x08, 0x0c, 0x04, 0x05, 0x07, 0x03), (0x06, 0x07, 0x05, 0x01, 0x09, 0x08, 0x0a, 0x0e), (0x0c, 0x0d, 0x0f, 0x0b, 0x03, 0x02, 0x00, 0x04), (0x09, 0x08, 0x0a, 0x0e, 0x06, 0x07, 0x05, 0x01), (0x02, 0x03, 0x01, 0x05, 0x0d, 0x0c, 0x0e, 0x0a), (0x05, 0x04, 0x06, 0x02, 0x0a, 0x0b, 0x09, 0x0d), (0x0a, 0x0b, 0x09, 0x0d, 0x05, 0x04, 0x06, 0x02), ) C_TEMPLATE = r""" #include #include static int mix_pair_index(int r, int pair_index, int a, int b) { return ((((r * 4) + pair_index) * 16 + a) * 16) + b; } static uint8_t mix_pair_at(const uint8_t *mix_pair, int r, int pair_index, int a, int b) { return mix_pair[mix_pair_index(r, pair_index, a, b)]; } static void photon256(uint8_t *state, const uint8_t *sbox, const uint8_t *rc, const uint8_t *mix_pair) { uint8_t m[64]; uint8_t n[64]; int i = 0; int j = 0; int rnd = 0; for (i = 0, j = 0; i < 32; i++, j += 2) { m[j] = (uint8_t)(state[i] & 0x0f); m[j + 1] = (uint8_t)(state[i] >> 4); } for (rnd = 0; rnd < 12; rnd++) { const uint8_t *rc_row = rc + (rnd * 8); int r = 0; int c = 0; m[0] ^= rc_row[0]; m[8] ^= rc_row[1]; m[16] ^= rc_row[2]; m[24] ^= rc_row[3]; m[32] ^= rc_row[4]; m[40] ^= rc_row[5]; m[48] ^= rc_row[6]; m[56] ^= rc_row[7]; for (r = 0; r < 8; r++) { int base = r << 3; int shift = r; for (c = 0; c < 8; c++) { n[base + c] = sbox[m[base + ((c + shift) & 7)]]; } } for (c = 0; c < 8; c++) { int p0a = n[c]; int p0b = n[8 + c]; int p1a = n[16 + c]; int p1b = n[24 + c]; int p2a = n[32 + c]; int p2b = n[40 + c]; int p3a = n[48 + c]; int p3b = n[56 + c]; m[c] = mix_pair_at(mix_pair, 0, 0, p0a, p0b) ^ mix_pair_at(mix_pair, 0, 1, p1a, p1b) ^ mix_pair_at(mix_pair, 0, 2, p2a, p2b) ^ mix_pair_at(mix_pair, 0, 3, p3a, p3b); m[8 + c] = mix_pair_at(mix_pair, 1, 0, p0a, p0b) ^ mix_pair_at(mix_pair, 1, 1, p1a, p1b) ^ mix_pair_at(mix_pair, 1, 2, p2a, p2b) ^ mix_pair_at(mix_pair, 1, 3, p3a, p3b); m[16 + c] = mix_pair_at(mix_pair, 2, 0, p0a, p0b) ^ mix_pair_at(mix_pair, 2, 1, p1a, p1b) ^ mix_pair_at(mix_pair, 2, 2, p2a, p2b) ^ mix_pair_at(mix_pair, 2, 3, p3a, p3b); m[24 + c] = mix_pair_at(mix_pair, 3, 0, p0a, p0b) ^ mix_pair_at(mix_pair, 3, 1, p1a, p1b) ^ mix_pair_at(mix_pair, 3, 2, p2a, p2b) ^ mix_pair_at(mix_pair, 3, 3, p3a, p3b); m[32 + c] = mix_pair_at(mix_pair, 4, 0, p0a, p0b) ^ mix_pair_at(mix_pair, 4, 1, p1a, p1b) ^ mix_pair_at(mix_pair, 4, 2, p2a, p2b) ^ mix_pair_at(mix_pair, 4, 3, p3a, p3b); m[40 + c] = mix_pair_at(mix_pair, 5, 0, p0a, p0b) ^ mix_pair_at(mix_pair, 5, 1, p1a, p1b) ^ mix_pair_at(mix_pair, 5, 2, p2a, p2b) ^ mix_pair_at(mix_pair, 5, 3, p3a, p3b); m[48 + c] = mix_pair_at(mix_pair, 6, 0, p0a, p0b) ^ mix_pair_at(mix_pair, 6, 1, p1a, p1b) ^ mix_pair_at(mix_pair, 6, 2, p2a, p2b) ^ mix_pair_at(mix_pair, 6, 3, p3a, p3b); m[56 + c] = mix_pair_at(mix_pair, 7, 0, p0a, p0b) ^ mix_pair_at(mix_pair, 7, 1, p1a, p1b) ^ mix_pair_at(mix_pair, 7, 2, p2a, p2b) ^ mix_pair_at(mix_pair, 7, 3, p3a, p3b); } } for (i = 0, j = 0; i < 32; i++, j += 2) { state[i] = (uint8_t)(m[j] | (m[j + 1] << 4)); } } static void gen_tag(uint8_t *state, uint8_t *out, const uint8_t *sbox, const uint8_t *rc, const uint8_t *mix_pair) { int i = 0; photon256(state, sbox, rc, mix_pair); for (i = 0; i < 16; i++) { out[i] = state[i]; } photon256(state, sbox, rc, mix_pair); for (i = 0; i < 16; i++) { out[16 + i] = state[i]; } } static void absorb4( uint8_t *state, const uint8_t *msg, size_t msg_len, int c0, const uint8_t *sbox, const uint8_t *rc, const uint8_t *mix_pair) { size_t off = 0; size_t full = (msg_len / 4) * 4; size_t rem = 0; size_t i = 0; while (off < full) { photon256(state, sbox, rc, mix_pair); state[0] ^= msg[off]; state[1] ^= msg[off + 1]; state[2] ^= msg[off + 2]; state[3] ^= msg[off + 3]; off += 4; } rem = msg_len - off; if (rem > 0) { photon256(state, sbox, rc, mix_pair); for (i = 0; i < rem; i++) { state[i] ^= msg[off + i]; } state[rem] ^= 0x01; } state[31] ^= (uint8_t)(c0 << 5); } void hash_bytes( const uint8_t *msg, size_t msg_len, uint8_t *out, const uint8_t *sbox, const uint8_t *rc, const uint8_t *mix_pair) { uint8_t state[32]; size_t i = 0; for (i = 0; i < 32; i++) { state[i] = 0; } if (msg_len == 0) { state[31] ^= (uint8_t)(0x01 << 5); gen_tag(state, out, sbox, rc, mix_pair); return; } if (msg_len <= 16) { for (i = 0; i < msg_len; i++) { state[i] = msg[i]; } if (msg_len < 16) { state[msg_len] ^= 0x01; state[31] ^= (uint8_t)(0x01 << 5); } else { state[31] ^= (uint8_t)(0x02 << 5); } gen_tag(state, out, sbox, rc, mix_pair); return; } for (i = 0; i < 16; i++) { state[i] = msg[i]; } if (((msg_len - 16) % 4) == 0) { absorb4(state, msg + 16, msg_len - 16, 0x01, sbox, rc, mix_pair); } else { absorb4(state, msg + 16, msg_len - 16, 0x02, sbox, rc, mix_pair); } gen_tag(state, out, sbox, rc, mix_pair); } """ DEF_TEMPLATE = r""" void hash_bytes( const uint8_t *msg, size_t msg_len, uint8_t *out, const uint8_t *sbox, const uint8_t *rc, const uint8_t *mix_pair); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.PhotonBeetle if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib if base_class in base_class.cffi_cache: ffi, lib = base_class.cffi_cache[base_class] else: try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) base_class.cffi_cache[base_class] = (ffi, lib) except Exception: self.USE_CFFI = False return # Different for each class def flatten_mix_pair(mix_pair): flat = [] for r in mix_pair: for pair_tab in r: for row in pair_tab: flat.extend(row) return tuple(flat) def flatten_rc(rc): flat = [] for row in rc: flat.extend(row) return tuple(flat) try: sbox = ffi.new("uint8_t[]", tuple(self.sbox)) rc = ffi.new("uint8_t[]", flatten_rc(self.rc)) mix_pair = ffi.new("uint8_t[]", flatten_mix_pair(self.MIX_PAIR)) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib sbox rc mix_pair")( ffi, lib, sbox, rc, mix_pair, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return @classmethod def ensure_tables(cls): def gf16_mul(a, b): x = a y = b z = 0 for _ in range(4): if (y & 0x01) != 0: z ^= x carry = x & 0x08 x = (x << 1) & 0x0f if carry != 0: x ^= 0x03 y >>= 1 return z def gf16_matrix_mul(tab, a, b): out = [[0 for j in range(8)] for i in range(8)] for i in range(8): for j in range(8): v = 0 for k in range(8): v ^= tab[a[i][k]][b[k][j]] out[i][j] = v return out if not hasattr(cls, "gf16_mul_table"): tab = [] for a in range(16): row = [] for b in range(16): row.append(gf16_mul(a, b)) tab.append(tuple(row)) cls.gf16_mul_table = tuple(tab) if not hasattr(cls, "m8"): m = ( (0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00), (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01), (0x02, 0x04, 0x02, 0x0b, 0x02, 0x08, 0x05, 0x06), ) m2 = gf16_matrix_mul(cls.gf16_mul_table, m, m) m4 = gf16_matrix_mul(cls.gf16_mul_table, m2, m2) m8 = gf16_matrix_mul(cls.gf16_mul_table, m4, m4) cls.m8 = m8 if not hasattr(cls, "MIX_VAL"): tab = cls.gf16_mul_table m8 = cls.m8 mix_val = [] for r in range(8): row = [] for k in range(8): mul_row = [] coeff = m8[r][k] for v in range(16): mul_row.append(tab[coeff][v]) row.append(tuple(mul_row)) mix_val.append(tuple(row)) cls.MIX_VAL = tuple(mix_val) if not hasattr(cls, "MIX_PAIR"): mix_val = cls.MIX_VAL mix_pair = [] for r in range(8): row = [] for pair_idx in range(4): pair_tab = [] k0 = pair_idx << 1 k1 = k0 + 1 mv0 = mix_val[r][k0] mv1 = mix_val[r][k1] for a in range(16): pair_row = [] v0 = mv0[a] for b in range(16): pair_row.append(v0 ^ mv1[b]) pair_tab.append(tuple(pair_row)) row.append(tuple(pair_tab)) mix_pair.append(tuple(row)) cls.MIX_PAIR = tuple(mix_pair) if not hasattr(cls, "NIBBLE_SPLIT"): split = [] for b in range(256): split.append((b & 0x0f, b >> 4)) cls.NIBBLE_SPLIT = tuple(split) if not hasattr(cls, "NIBBLE_PACK"): pack = [] for lo in range(16): row = [] for hi in range(16): row.append(lo | (hi << 4)) pack.append(tuple(row)) cls.NIBBLE_PACK = tuple(pack) return def __init__(self, data=b""): self.buf = bytearray() self.msg_len = 0 self.finalized = False self.result = b"" self.ensure_tables() self.init_cffi_backend() if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.finalized = self.finalized other.result = bytes(self.result) return other def update(self, data): if self.finalized: raise ValueError("hash already finalized") self.buf.extend(data) self.msg_len += len(data) return self def digest(self): c = self.copy() c.finalize() return bytes(c.result) def hexdigest(self): return self.digest().hex() def finalize(self): if self.finalized: return self.result = self.hash_bytes(bytes(self.buf)) self.finalized = True return def hash_bytes(self, msg): if self.USE_CFFI: if len(msg) != 0: msg_buf = self.cffi.ffi.new("uint8_t[]", bytes(msg)) else: msg_buf = self.cffi.ffi.NULL out_buf = self.cffi.ffi.new("uint8_t[]", self.digest_size) self.cffi.lib.hash_bytes( msg_buf, len(msg), out_buf, self.cffi.sbox, self.cffi.rc, self.cffi.mix_pair, ) return bytes(self.cffi.ffi.buffer(out_buf, self.digest_size)) def photon256(state): split = self.NIBBLE_SPLIT pack = self.NIBBLE_PACK mix_pair = self.MIX_PAIR rc = self.rc sbox = self.sbox m = [0] * 64 n = [0] * 64 i = 0 j = 0 while i < 32: lo, hi = split[state[i]] m[j] = lo m[j + 1] = hi i += 1 j += 2 rnd = 0 while rnd < 12: rc_r = rc[rnd] m[0] ^= rc_r[0] m[8] ^= rc_r[1] m[16] ^= rc_r[2] m[24] ^= rc_r[3] m[32] ^= rc_r[4] m[40] ^= rc_r[5] m[48] ^= rc_r[6] m[56] ^= rc_r[7] r = 0 while r < 8: base = r << 3 shift = r c = 0 while c < 8: n[base + c] = sbox[m[base + ((c + shift) & 7)]] c += 1 r += 1 row0 = mix_pair[0] row1 = mix_pair[1] row2 = mix_pair[2] row3 = mix_pair[3] row4 = mix_pair[4] row5 = mix_pair[5] row6 = mix_pair[6] row7 = mix_pair[7] c = 0 while c < 8: p0a = n[c] p0b = n[8 + c] p1a = n[16 + c] p1b = n[24 + c] p2a = n[32 + c] p2b = n[40 + c] p3a = n[48 + c] p3b = n[56 + c] m[c] = row0[0][p0a][p0b] ^ row0[1][p1a][p1b] ^ row0[2][p2a][p2b] ^ row0[3][p3a][p3b] m[8 + c] = row1[0][p0a][p0b] ^ row1[1][p1a][p1b] ^ row1[2][p2a][p2b] ^ row1[3][p3a][p3b] m[16 + c] = row2[0][p0a][p0b] ^ row2[1][p1a][p1b] ^ row2[2][p2a][p2b] ^ row2[3][p3a][p3b] m[24 + c] = row3[0][p0a][p0b] ^ row3[1][p1a][p1b] ^ row3[2][p2a][p2b] ^ row3[3][p3a][p3b] m[32 + c] = row4[0][p0a][p0b] ^ row4[1][p1a][p1b] ^ row4[2][p2a][p2b] ^ row4[3][p3a][p3b] m[40 + c] = row5[0][p0a][p0b] ^ row5[1][p1a][p1b] ^ row5[2][p2a][p2b] ^ row5[3][p3a][p3b] m[48 + c] = row6[0][p0a][p0b] ^ row6[1][p1a][p1b] ^ row6[2][p2a][p2b] ^ row6[3][p3a][p3b] m[56 + c] = row7[0][p0a][p0b] ^ row7[1][p1a][p1b] ^ row7[2][p2a][p2b] ^ row7[3][p3a][p3b] c += 1 rnd += 1 i = 0 j = 0 while i < 32: state[i] = pack[m[j]][m[j + 1]] i += 1 j += 2 return def gen_tag(state): photon256(state) out = bytearray(32) out[0:16] = state[0:16] photon256(state) out[16:32] = state[0:16] return bytes(out) def absorb4(state, msg, c0): off = 0 full = (len(msg) // 4) * 4 while off < full: photon256(state) state[0] ^= msg[off] state[1] ^= msg[off + 1] state[2] ^= msg[off + 2] state[3] ^= msg[off + 3] off += 4 rem = len(msg) - off if rem > 0: photon256(state) i = 0 while i < rem: state[i] ^= msg[off + i] i += 1 state[rem] ^= 0x01 state[31] ^= (c0 << 5) return state = bytearray(32) mlen = len(msg) if mlen == 0: state[31] ^= (0x01 << 5) out = gen_tag(state) return out if mlen <= 16: state[0:mlen] = msg if mlen < 16: state[mlen] ^= 0x01 c0 = 0x01 if mlen < 16 else 0x02 state[31] ^= (c0 << 5) out = gen_tag(state) return out state[0:16] = msg[0:16] rem = msg[16:] c0 = 0x01 if (len(rem) % 4) == 0 else 0x02 absorb4(state, rem, c0) out = gen_tag(state) return out class VSH1024: block_size = 0x83 digest_size = 0x80 n = int( "1350664108659952233496032162788059699388814756056670275244851438515" "265106048595338339402871505719094417982072821644715513736804197039641917430464965" "892742562393410208643832021103729587257623585096431105640735015081875106765946292" "05563685529475213500852879416377328533906109750544334999811150056977236890927563" ) primes = ( 0x2, 0x3, 0x5, 0x7, 0xb, 0xd, 0x11, 0x13, 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35, 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83, 0x89, 0x8b, 0x95, 0x97, 0x9d, 0xa3, 0xa7, 0xad, 0xb3, 0xb5, 0xbf, 0xc1, 0xc5, 0xc7, 0xd3, 0xdf, 0xe3, 0xe5, 0xe9, 0xef, 0xf1, 0xfb, 0x101, 0x107, 0x10d, 0x10f, 0x115, 0x119, 0x11b, 0x125, 0x133, 0x137, 0x139, 0x13d, 0x14b, 0x151, 0x15b, 0x15d, 0x161, 0x167, 0x16f, 0x175, 0x17b, 0x17f, 0x185, 0x18d, 0x191, 0x199, 0x1a3, 0x1a5, 0x1af, 0x1b1, 0x1b7, 0x1bb, 0x1c1, 0x1c9, 0x1cd, 0x1cf, 0x1d3, 0x1df, 0x1e7, 0x1eb, 0x1f3, 0x1f7, 0x1fd, 0x209, 0x20b, 0x21d, 0x223, 0x22d, 0x233, 0x239, 0x23b, 0x241, 0x24b, 0x251, 0x257, 0x259, 0x25f, 0x265, 0x269, 0x26b, 0x277, 0x281, 0x283, 0x287, 0x28d, 0x293, 0x295, 0x2a1, 0x2a5, 0x2ab, 0x2b3, 0x2bd, 0x2c5, 0x2cf, 0x2d7, 0x2dd, 0x2e3, ) k = 0x83 block_size = 0x83 def __init__(self, data=b""): self.x = 0x1 self.bit_buf = [] self.msg_len_bits = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.x = self.x other.bit_buf = list(self.bit_buf) other.msg_len_bits = self.msg_len_bits return other def update(self, data): data = bytes(data) for b in data: bit = 0x80 while bit != 0: if b & bit: self.bit_buf.append(1) else: self.bit_buf.append(0) self.msg_len_bits += 1 bit >>= 1 if len(self.bit_buf) >= self.k: block_bits = self.bit_buf[:self.k] del self.bit_buf[:self.k] self.compress(block_bits) return self def digest(self): c = self.copy() c.finalize() return c.x.to_bytes(self.digest_size, "big") def hexdigest(self): return self.digest().hex() def finalize(self): if len(self.bit_buf) != 0: block_bits = list(self.bit_buf) while len(block_bits) < self.k: block_bits.append(0x0) self.compress(block_bits) self.bit_buf = [] length_bits = [] value = self.msg_len_bits i = 0 while i < self.k: length_bits.append(value & 0x1) value >>= 0x1 i += 0x1 self.compress(length_bits) return def compress(self, block_bits): mul = 1 i = 0 while i < self.k: if block_bits[i]: mul *= self.primes[i] i += 1 self.x = (self.x * self.x * mul) % self.n return class Xoodyak: block_size = 16 digest_size = 32 round_constants = ( 0x0058, 0x0038, 0x03c0, 0x00d0, 0x0120, 0x0014, 0x0060, 0x002c, 0x0380, 0x00f0, 0x01a0, 0x0012, ) C_TEMPLATE = r""" #include static uint32_t rol32(uint32_t x, int n) { return ((x << n) | (x >> (32 - n))) & 0xffffffffU; } static void plane_shift(const uint32_t *plane, uint32_t *out, int t, int v) { int x = 0; for (x = 0; x < 4; x++) { out[x] = rol32(plane[(x - t) & 3], v); } } void xoodoo_permute(uint8_t *state) { static const uint32_t round_constants[12] = { 0x00000058U, 0x00000038U, 0x000003c0U, 0x000000d0U, 0x00000120U, 0x00000014U, 0x00000060U, 0x0000002cU, 0x00000380U, 0x000000f0U, 0x000001a0U, 0x00000012U, }; uint32_t a[12]; uint32_t p[4]; uint32_t e[4]; uint32_t e2[4]; uint32_t a1[4]; uint32_t a2[4]; uint32_t b0[4]; uint32_t b1[4]; uint32_t b2[4]; int i = 0; int x = 0; for (i = 0; i < 12; i++) { int start = i * 4; a[i] = ( ((uint32_t)state[start + 0]) | ((uint32_t)state[start + 1] << 8) | ((uint32_t)state[start + 2] << 16) | ((uint32_t)state[start + 3] << 24) ); } for (i = 0; i < 12; i++) { uint32_t rc = round_constants[i]; for (x = 0; x < 4; x++) { p[x] = a[x] ^ a[4 + x] ^ a[8 + x]; } plane_shift(p, e, 1, 5); plane_shift(p, e2, 1, 14); for (x = 0; x < 4; x++) { e[x] ^= e2[x]; } for (x = 0; x < 4; x++) { a[x] ^= e[x]; a[4 + x] ^= e[x]; a[8 + x] ^= e[x]; } plane_shift(a + 4, a1, 1, 0); plane_shift(a + 8, a2, 0, 11); for (x = 0; x < 4; x++) { a[4 + x] = a1[x]; a[8 + x] = a2[x]; } a[0] ^= rc; for (x = 0; x < 4; x++) { b0[x] = (a[4 + x] ^ 0xffffffffU) & a[8 + x]; b1[x] = (a[8 + x] ^ 0xffffffffU) & a[x]; b2[x] = (a[x] ^ 0xffffffffU) & a[4 + x]; } for (x = 0; x < 4; x++) { a[x] ^= b0[x]; a[4 + x] ^= b1[x]; a[8 + x] ^= b2[x]; } plane_shift(a + 4, a1, 0, 1); plane_shift(a + 8, a2, 2, 8); for (x = 0; x < 4; x++) { a[4 + x] = a1[x]; a[8 + x] = a2[x]; } } for (i = 0; i < 12; i++) { int start = i * 4; uint32_t word = a[i]; state[start + 0] = word & 0xffU; state[start + 1] = (word >> 8) & 0xffU; state[start + 2] = (word >> 16) & 0xffU; state[start + 3] = (word >> 24) & 0xffU; } } """ DEF_TEMPLATE = r""" void xoodoo_permute(uint8_t *state); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.Xoodyak if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) except Exception: self.USE_CFFI = False return cffi_obj = collections.namedtuple("CFFI", "ffi lib")( ffi, lib, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return def __init__(self, data=b""): self.state = bytearray(48) self.buf = bytearray() self.msg_len = 0 self.phase = "up" self.absorb_started = False self.finalized = False self.init_cffi_backend() if data: self.update(data) return def copy(self): other = self.__class__() other.state = bytearray(self.state) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.phase = self.phase other.absorb_started = self.absorb_started other.finalized = self.finalized return other def update(self, data): if self.finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: chunk = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.absorb_chunk(chunk) return self def digest(self): c = self.copy() c.finalize() return c.squeeze_any(self.digest_size, 0x40) def hexdigest(self): return self.digest().hex() def finalize(self): if self.finalized: return if len(self.buf) != 0 or not self.absorb_started: chunk = bytes(self.buf) self.buf.clear() self.absorb_chunk(chunk) self.finalized = True return def absorb_chunk(self, chunk): if self.phase != "up": self.up(0, 0x00) if self.absorb_started: self.down(chunk, 0x00) else: self.down(chunk, 0x03) self.absorb_started = True return def squeeze_any(self, length, c_u): y = bytearray(self.up(min(length, self.block_size), c_u)) while len(y) < length: self.down(b"", 0x00) y.extend(self.up(min(length - len(y), self.block_size), 0x00)) return bytes(y) def down(self, xi, c_d): n = len(xi) i = 0 while i < n: self.state[i] ^= xi[i] i += 1 self.state[n] ^= 0x01 self.state[47] ^= (c_d & 0x01) self.phase = "down" return def up(self, yi_len, c_u): if self.USE_CFFI: state_buf = self.cffi.ffi.new("uint8_t[]", bytes(self.state)) self.cffi.lib.xoodoo_permute(state_buf) self.state = bytearray(state_buf[i] for i in range(48)) self.phase = "up" return bytes(self.state[:yi_len]) def rol32(x, n): return ((x << n) | (x >> (32 - n))) & 0xffff_ffff def plane_shift(plane, t, v): out = [0, 0, 0, 0] x = 0 while x < 4: out[x] = rol32(plane[(x - t) & 3], v) x += 1 return out a = [] i = 0 while i < 12: start = i * 4 a.append(int.from_bytes(self.state[start:start + 4], "little")) i += 1 for rc in self.round_constants: p = [0, 0, 0, 0] x = 0 while x < 4: p[x] = a[x] ^ a[4 + x] ^ a[8 + x] x += 1 e = plane_shift(p, 1, 5) e2 = plane_shift(p, 1, 14) x = 0 while x < 4: e[x] ^= e2[x] x += 1 x = 0 while x < 4: a[x] ^= e[x] a[4 + x] ^= e[x] a[8 + x] ^= e[x] x += 1 a1 = plane_shift(a[4:8], 1, 0) a2 = plane_shift(a[8:12], 0, 11) x = 0 while x < 4: a[4 + x] = a1[x] a[8 + x] = a2[x] x += 1 a[0] ^= rc b0 = [0, 0, 0, 0] b1 = [0, 0, 0, 0] b2 = [0, 0, 0, 0] x = 0 while x < 4: b0[x] = (a[4 + x] ^ 0xffff_ffff) & a[8 + x] b1[x] = (a[8 + x] ^ 0xffff_ffff) & a[x] b2[x] = (a[x] ^ 0xffff_ffff) & a[4 + x] x += 1 x = 0 while x < 4: a[x] ^= b0[x] a[4 + x] ^= b1[x] a[8 + x] ^= b2[x] x += 1 a1 = plane_shift(a[4:8], 0, 1) a2 = plane_shift(a[8:12], 2, 8) x = 0 while x < 4: a[4 + x] = a1[x] a[8 + x] = a2[x] x += 1 i = 0 while i < 12: start = i * 4 self.state[start:start + 4] = a[i].to_bytes(4, "little") i += 1 self.phase = "up" return bytes(self.state[:yi_len]) class GxHashBase: block_size = 16 sbox = ( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ) keys = ( (0x42, 0x45, 0x78, 0xf2, 0x21, 0x3e, 0x9d, 0xb0, 0xe5, 0x22, 0xc2, 0x89, 0x8e, 0xc2, 0x3b, 0xfc), (0x79, 0xe2, 0xfc, 0x03, 0x9b, 0x2e, 0x6b, 0xcb, 0x58, 0xdc, 0x61, 0xb3, 0xd9, 0x2b, 0x13, 0x39), (0x32, 0x2e, 0x01, 0xd0, 0x7d, 0x2b, 0x9d, 0x68, 0xb7, 0xb1, 0x44, 0x55, 0x2b, 0x12, 0x8b, 0xc7), ) def __init__(self, data=b"", seed=0): self.seed = int(seed) self.buf = bytearray() self.msg_len = 0 self.final_state = None if data: self.update(data) return def copy(self): other = self.__class__(seed=self.seed) other.buf = bytearray(self.buf) other.msg_len = self.msg_len if self.final_state is None: other.final_state = None else: other.final_state = bytes(self.final_state) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf.extend(data) self.msg_len += len(data) self.final_state = None return self def digest(self): c = self.copy() c.finalize() return c.final_state[:self.digest_size] def hexdigest(self): return self.digest().hex() def finalize(self): self.final_state = self.gxhash_state(bytes(self.buf), self.seed) return def gxhash_state(self, data, seed): def create_seed(seed): seed64 = int(seed) & 0xffff_ffff_ffff_ffff lane = list(struct.pack(" self.block_size * 2: v = list(data[ptr:ptr + 0x10]) ptr += self.block_size v0 = self.aes_encrypt(v0, v) if length > self.block_size * 3: v = list(data[ptr:ptr + 0x10]) ptr += self.block_size v0 = self.aes_encrypt(v0, v) if length > self.block_size * 4: hash_vector = self.compress_many(data, ptr, end_address, hash_vector, length) mixed = self.aes_encrypt(self.aes_encrypt(v0, self.keys[0]), self.keys[1]) return self.aes_encrypt_last(hash_vector, mixed) class GxHash32(GxHashBase): digest_size = 4 class GxHash64(GxHashBase): digest_size = 8 class GxHash128(GxHashBase): digest_size = 16 class KomiHash: block_size = 64 digest_size = 8 ival1 = 0x243f_6a88_85a3_08d3 ival2 = 0x1319_8a2e_0370_7344 ival3 = 0xa409_3822_299f_31d0 ival4 = 0x082e_fa98_ec4e_6c89 ival5 = 0x4528_21e6_38d0_1377 ival6 = 0xbe54_66cf_34e9_0c6c ival7 = 0xc0ac_29b7_c97c_50dd ival8 = 0x3f84_d5b5_b547_0917 val01 = 0x5555_5555_5555_5555 val10 = 0xaaaa_aaaa_aaaa_aaaa def __init__(self, data=b"", seed=0): if not isinstance(seed, int): raise TypeError("seed must be int") self.seed = seed self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def copy(self): other = self.__class__(seed=self.seed) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf.extend(data) self.msg_len += len(data) return self def digest(self): h = self.hash_bytes(bytes(self.buf), self.seed) return struct.pack(">Q", h) def hexdigest(self): return self.digest().hex() def hash_bytes(self, data, use_seed=0): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not isinstance(use_seed, int): raise TypeError("use_seed must be int") def u32le(data, pos): return int.from_bytes(data[pos:pos + 4], "little") def u64le(data, pos): return int.from_bytes(data[pos:pos + 8], "little") def m128(u, v, rha): prod = u * v rl = prod & 0xffff_ffff_ffff_ffff rha = (rha + (prod >> 64)) & 0xffff_ffff_ffff_ffff return rl, rha def hash16(data, pos, seed1, seed5): u = u64le(data, pos) ^ seed1 v = u64le(data, pos + 8) ^ seed5 seed1, seed5 = m128(u, v, seed5) seed1 ^= seed5 return seed1, seed5 def hash_round(seed1, seed5): seed1, seed5 = m128(seed1, seed5, seed5) seed1 ^= seed5 return seed1, seed5 def hash_fin(r1h, r2h, seed1, seed5): seed1, seed5 = m128(r1h, r2h, seed5) seed1 ^= seed5 seed1, seed5 = hash_round(seed1, seed5) return seed1 def epi(data, pos, msg_len, seed1, seed5): if msg_len > 31: seed1, seed5 = hash16(data, pos, seed1, seed5) seed1, seed5 = hash16(data, pos + 16, seed1, seed5) msg_len -= 32 pos += 32 if msg_len > 15: seed1, seed5 = hash16(data, pos, seed1, seed5) msg_len -= 16 pos += 16 ml8 = msg_len * 8 if msg_len < 8: ml8 ^= 56 r1h = (u64le(data, pos + msg_len - 8) >> 8) | (1 << 56) r2h = seed5 r1h = (r1h >> ml8) ^ seed1 else: r2h = (u64le(data, pos + msg_len - 8) >> 8) | (1 << 56) ml8 ^= 120 r1h = u64le(data, pos) ^ seed1 r2h = (r2h >> ml8) ^ seed5 return hash_fin(r1h, r2h, seed1, seed5) data = bytes(data) msg_len = len(data) seed1 = self.ival1 ^ (use_seed & self.val01) seed5 = self.ival5 ^ (use_seed & self.val10) seed1, seed5 = hash_round(seed1, seed5) if msg_len < 16: r1h = seed1 r2h = seed5 if msg_len > 7: r1h ^= u64le(data, 0) if msg_len < 12: ml8 = msg_len * 8 m = (data[msg_len - 3] | (data[msg_len - 2] << 8) | (data[msg_len - 1] << 16) | (1 << 24)) ml8 ^= 88 r2h ^= m >> ml8 else: mhs = 128 - msg_len * 8 mh = (u32le(data, msg_len - 4) | (1 << 32)) >> mhs ml = u32le(data, 8) r2h ^= (mh << 32) | ml elif msg_len != 0: ml8 = msg_len * 8 if msg_len < 4: r1h ^= 1 << ml8 r1h ^= data[0] if msg_len != 1: r1h ^= data[1] << 8 if msg_len != 2: r1h ^= data[2] << 16 else: mhs = 64 - ml8 mh = (u32le(data, msg_len - 4) | (1 << 32)) >> mhs ml = u32le(data, 0) r1h ^= (mh << 32) | ml return hash_fin(r1h, r2h, seed1, seed5) pos = 0 if msg_len <= 31: seed1, seed5 = hash16(data, pos, seed1, seed5) ml8 = msg_len * 8 if msg_len < 24: ml8 ^= 184 r1h = (u64le(data, pos + msg_len - 8) >> 8) | (1 << 56) r2h = seed5 r1h = (r1h >> ml8) ^ seed1 return hash_fin(r1h, r2h, seed1, seed5) r2h = (u64le(data, pos + msg_len - 8) >> 8) | (1 << 56) ml8 ^= 248 r1h = u64le(data, pos + 16) ^ seed1 r2h = (r2h >> ml8) ^ seed5 return hash_fin(r1h, r2h, seed1, seed5) if msg_len > 63: seed2 = self.ival2 ^ seed1 seed3 = self.ival3 ^ seed1 seed4 = self.ival4 ^ seed1 seed6 = self.ival6 ^ seed5 seed7 = self.ival7 ^ seed5 seed8 = self.ival8 ^ seed5 while msg_len > 63: seed1, seed5 = m128( u64le(data, pos) ^ seed1, u64le(data, pos + 32) ^ seed5, seed5, ) seed2, seed6 = m128( u64le(data, pos + 8) ^ seed2, u64le(data, pos + 40) ^ seed6, seed6, ) seed3, seed7 = m128( u64le(data, pos + 16) ^ seed3, u64le(data, pos + 48) ^ seed7, seed7, ) seed4, seed8 = m128( u64le(data, pos + 24) ^ seed4, u64le(data, pos + 56) ^ seed8, seed8, ) pos += 64 msg_len -= 64 seed4 ^= seed7 seed1 ^= seed8 seed3 ^= seed6 seed2 ^= seed5 seed5 ^= seed6 ^ seed7 ^ seed8 seed1 ^= seed2 ^ seed3 ^ seed4 return epi(data, pos, msg_len, seed1, seed5) class ParallelHashBase: keccak_round_constants = ( 0x0000_0000_0000_0001, 0x0000_0000_0000_8082, 0x8000_0000_0000_808a, 0x8000_0000_8000_8000, 0x0000_0000_0000_808b, 0x0000_0000_8000_0001, 0x8000_0000_8000_8081, 0x8000_0000_0000_8009, 0x0000_0000_0000_008a, 0x0000_0000_0000_0088, 0x0000_0000_8000_8009, 0x0000_0000_8000_000a, 0x0000_0000_8000_808b, 0x8000_0000_0000_008b, 0x8000_0000_0000_8089, 0x8000_0000_0000_8003, 0x8000_0000_0000_8002, 0x8000_0000_0000_0080, 0x0000_0000_0000_800a, 0x8000_0000_8000_000a, 0x8000_0000_8000_8081, 0x8000_0000_0000_8080, 0x0000_0000_8000_0001, 0x8000_0000_8000_8008, ) C_TEMPLATE = r""" #include #include #include #include static uint64_t rol64(uint64_t x, int n) { return ((x << n) | (x >> (64 - n))) & 0xffffffffffffffffULL; } static uint64_t load64_le(const uint8_t *src) { return ( ((uint64_t)src[0]) | ((uint64_t)src[1] << 8) | ((uint64_t)src[2] << 16) | ((uint64_t)src[3] << 24) | ((uint64_t)src[4] << 32) | ((uint64_t)src[5] << 40) | ((uint64_t)src[6] << 48) | ((uint64_t)src[7] << 56) ); } static void store64_le(uint8_t *dst, uint64_t value) { dst[0] = (uint8_t)(value & 0xffU); dst[1] = (uint8_t)((value >> 8) & 0xffU); dst[2] = (uint8_t)((value >> 16) & 0xffU); dst[3] = (uint8_t)((value >> 24) & 0xffU); dst[4] = (uint8_t)((value >> 32) & 0xffU); dst[5] = (uint8_t)((value >> 40) & 0xffU); dst[6] = (uint8_t)((value >> 48) & 0xffU); dst[7] = (uint8_t)((value >> 56) & 0xffU); } static int encoded_length_u64(uint64_t value) { int n = 1; while ((value >> (8 * n)) != 0 && n < 8) { n++; } return n + 1; } static int write_left_encode_u64(uint8_t *dst, uint64_t value) { int n = 1; int i = 0; while ((value >> (8 * n)) != 0 && n < 8) { n++; } dst[0] = (uint8_t)n; for (i = 0; i < n; i++) { dst[1 + i] = (uint8_t)((value >> (8 * (n - 1 - i))) & 0xffU); } return n + 1; } static void keccak_absorb_block(uint64_t *state, const uint8_t *block, int rate_bytes) { int lanes = rate_bytes / 8; int i = 0; for (i = 0; i < lanes; i++) { state[i] ^= load64_le(block + (i * 8)); } } static void keccak_absorb_padded( uint64_t *state, const uint8_t *tail, size_t tail_len, int rate_bytes, uint8_t domain_suffix) { uint8_t block[168]; size_t i = 0; for (i = 0; i < (size_t)rate_bytes; i++) { block[i] = 0; } for (i = 0; i < tail_len; i++) { block[i] = tail[i]; } block[tail_len] ^= domain_suffix; block[rate_bytes - 1] ^= 0x80; keccak_absorb_block(state, block, rate_bytes); } static void keccak_squeeze_block(const uint64_t *state, uint8_t *out, int out_len) { int full_lanes = out_len / 8; int remain = out_len % 8; int i = 0; for (i = 0; i < full_lanes; i++) { store64_le(out + (i * 8), state[i]); } if (remain != 0) { uint8_t temp[8]; store64_le(temp, state[full_lanes]); for (i = 0; i < remain; i++) { out[(full_lanes * 8) + i] = temp[i]; } } } static void keccak_f1600(uint64_t *state) { uint64_t A00 = state[0], A01 = state[1], A02 = state[2], A03 = state[3], A04 = state[4]; uint64_t A05 = state[5], A06 = state[6], A07 = state[7], A08 = state[8], A09 = state[9]; uint64_t A10 = state[10], A11 = state[11], A12 = state[12], A13 = state[13], A14 = state[14]; uint64_t A15 = state[15], A16 = state[16], A17 = state[17], A18 = state[18], A19 = state[19]; uint64_t A20 = state[20], A21 = state[21], A22 = state[22], A23 = state[23], A24 = state[24]; static const uint64_t round_constants[24] = { 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL, }; int round_index = 0; for (round_index = 0; round_index < 24; round_index++) { uint64_t rc = round_constants[round_index]; uint64_t C0 = A00 ^ A05 ^ A10 ^ A15 ^ A20; uint64_t C1 = A01 ^ A06 ^ A11 ^ A16 ^ A21; uint64_t C2 = A02 ^ A07 ^ A12 ^ A17 ^ A22; uint64_t C3 = A03 ^ A08 ^ A13 ^ A18 ^ A23; uint64_t C4 = A04 ^ A09 ^ A14 ^ A19 ^ A24; uint64_t D0 = C4 ^ rol64(C1, 1); uint64_t D1 = C0 ^ rol64(C2, 1); uint64_t D2 = C1 ^ rol64(C3, 1); uint64_t D3 = C2 ^ rol64(C4, 1); uint64_t D4 = C3 ^ rol64(C0, 1); uint64_t B00 = 0, B01 = 0, B02 = 0, B03 = 0, B04 = 0; uint64_t B05 = 0, B06 = 0, B07 = 0, B08 = 0, B09 = 0; uint64_t B10 = 0, B11 = 0, B12 = 0, B13 = 0, B14 = 0; uint64_t B15 = 0, B16 = 0, B17 = 0, B18 = 0, B19 = 0; uint64_t B20 = 0, B21 = 0, B22 = 0, B23 = 0, B24 = 0; A00 ^= D0; A05 ^= D0; A10 ^= D0; A15 ^= D0; A20 ^= D0; A01 ^= D1; A06 ^= D1; A11 ^= D1; A16 ^= D1; A21 ^= D1; A02 ^= D2; A07 ^= D2; A12 ^= D2; A17 ^= D2; A22 ^= D2; A03 ^= D3; A08 ^= D3; A13 ^= D3; A18 ^= D3; A23 ^= D3; A04 ^= D4; A09 ^= D4; A14 ^= D4; A19 ^= D4; A24 ^= D4; B00 = A00; B01 = rol64(A06, 44); B02 = rol64(A12, 43); B03 = rol64(A18, 21); B04 = rol64(A24, 14); B05 = rol64(A03, 28); B06 = rol64(A09, 20); B07 = rol64(A10, 3); B08 = rol64(A16, 45); B09 = rol64(A22, 61); B10 = rol64(A01, 1); B11 = rol64(A07, 6); B12 = rol64(A13, 25); B13 = rol64(A19, 8); B14 = rol64(A20, 18); B15 = rol64(A04, 27); B16 = rol64(A05, 36); B17 = rol64(A11, 10); B18 = rol64(A17, 15); B19 = rol64(A23, 56); B20 = rol64(A02, 62); B21 = rol64(A08, 55); B22 = rol64(A14, 39); B23 = rol64(A15, 41); B24 = rol64(A21, 2); A00 = B00 ^ ((~B01) & B02); A01 = B01 ^ ((~B02) & B03); A02 = B02 ^ ((~B03) & B04); A03 = B03 ^ ((~B04) & B00); A04 = B04 ^ ((~B00) & B01); A05 = B05 ^ ((~B06) & B07); A06 = B06 ^ ((~B07) & B08); A07 = B07 ^ ((~B08) & B09); A08 = B08 ^ ((~B09) & B05); A09 = B09 ^ ((~B05) & B06); A10 = B10 ^ ((~B11) & B12); A11 = B11 ^ ((~B12) & B13); A12 = B12 ^ ((~B13) & B14); A13 = B13 ^ ((~B14) & B10); A14 = B14 ^ ((~B10) & B11); A15 = B15 ^ ((~B16) & B17); A16 = B16 ^ ((~B17) & B18); A17 = B17 ^ ((~B18) & B19); A18 = B18 ^ ((~B19) & B15); A19 = B19 ^ ((~B15) & B16); A20 = B20 ^ ((~B21) & B22); A21 = B21 ^ ((~B22) & B23); A22 = B22 ^ ((~B23) & B24); A23 = B23 ^ ((~B24) & B20); A24 = B24 ^ ((~B20) & B21); A00 ^= rc; } state[0] = A00; state[1] = A01; state[2] = A02; state[3] = A03; state[4] = A04; state[5] = A05; state[6] = A06; state[7] = A07; state[8] = A08; state[9] = A09; state[10] = A10; state[11] = A11; state[12] = A12; state[13] = A13; state[14] = A14; state[15] = A15; state[16] = A16; state[17] = A17; state[18] = A18; state[19] = A19; state[20] = A20; state[21] = A21; state[22] = A22; state[23] = A23; state[24] = A24; } void keccak_xof_run( const uint8_t *data, size_t data_len, uint64_t out_bits, int rate_bytes, uint8_t domain_suffix, uint8_t *out) { uint64_t state[25]; size_t offset = 0; size_t out_len = (size_t)((out_bits + 7) / 8); size_t written = 0; int i = 0; for (i = 0; i < 25; i++) { state[i] = 0; } while ((offset + (size_t)rate_bytes) <= data_len) { keccak_absorb_block(state, data + offset, rate_bytes); keccak_f1600(state); offset += (size_t)rate_bytes; } keccak_absorb_padded(state, data + offset, data_len - offset, rate_bytes, domain_suffix); keccak_f1600(state); while (written < out_len) { int chunk_len = rate_bytes; if ((size_t)chunk_len > (out_len - written)) { chunk_len = (int)(out_len - written); } keccak_squeeze_block(state, out + written, chunk_len); written += (size_t)chunk_len; if (written < out_len) { keccak_f1600(state); } } if ((out_bits & 7ULL) != 0 && out_len > 0) { out[out_len - 1] &= (uint8_t)((0xffU << (8 - (out_bits & 7ULL))) & 0xffU); } } int cshake_run( const uint8_t *data, size_t data_len, uint64_t out_bits, const uint8_t *function_name, size_t function_name_len, const uint8_t *customization, size_t customization_len, int security_bits, uint8_t *out) { int rate_bytes = 0; if (security_bits == 128) { rate_bytes = 168; } else if (security_bits == 256) { rate_bytes = 136; } else { return 0; } if (function_name_len == 0 && customization_len == 0) { keccak_xof_run(data, data_len, out_bits, rate_bytes, 0x1fU, out); return 1; } { uint64_t function_bits = (uint64_t)function_name_len * 8ULL; uint64_t customization_bits = (uint64_t)customization_len * 8ULL; size_t core_len = 0; size_t prefix_len = 0; size_t padded_len = 0; size_t total_len = 0; size_t cursor = 0; uint8_t *buffer = NULL; core_len += (size_t)encoded_length_u64(function_bits) + function_name_len; core_len += (size_t)encoded_length_u64(customization_bits) + customization_len; prefix_len = (size_t)encoded_length_u64((uint64_t)rate_bytes) + core_len; padded_len = ((prefix_len + (size_t)rate_bytes - 1) / (size_t)rate_bytes) * (size_t)rate_bytes; total_len = padded_len + data_len; buffer = (uint8_t *)malloc(total_len == 0 ? 1 : total_len); if (buffer == NULL) { return 0; } cursor += (size_t)write_left_encode_u64(buffer + cursor, (uint64_t)rate_bytes); cursor += (size_t)write_left_encode_u64(buffer + cursor, function_bits); if (function_name_len != 0) { memcpy(buffer + cursor, function_name, function_name_len); cursor += function_name_len; } cursor += (size_t)write_left_encode_u64(buffer + cursor, customization_bits); if (customization_len != 0) { memcpy(buffer + cursor, customization, customization_len); cursor += customization_len; } while ((cursor % (size_t)rate_bytes) != 0) { buffer[cursor] = 0; cursor++; } if (data_len != 0) { memcpy(buffer + cursor, data, data_len); } keccak_xof_run(buffer, total_len, out_bits, rate_bytes, 0x04U, out); free(buffer); } return 1; } """ DEF_TEMPLATE = r""" void keccak_xof_run( const uint8_t *data, size_t data_len, uint64_t out_bits, int rate_bytes, uint8_t domain_suffix, uint8_t *out); int cshake_run( const uint8_t *data, size_t data_len, uint64_t out_bits, const uint8_t *function_name, size_t function_name_len, const uint8_t *customization, size_t customization_len, int security_bits, uint8_t *out); """ def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.ParallelHashBase if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(base_class.C_TEMPLATE, extra_compile_args=["-O2"]) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib")( ffi, lib, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return def __init__(self, data=b"", block_size=8, digest_bits=None, customization=b""): if not isinstance(block_size, int): raise TypeError("block_size must be int") if block_size <= 0: raise ValueError("block_size must be > 0") if digest_bits is None: digest_bits = self.default_digest_bits if not isinstance(digest_bits, int): raise TypeError("digest_bits must be int") if digest_bits < 0: raise ValueError("digest_bits must be >= 0") if not isinstance(customization, (bytes, bytearray, memoryview)): raise TypeError("customization must be bytes-like") self.block_size = block_size self.digest_bits = digest_bits self.digest_size = (digest_bits + 7) // 8 self.customization = bytes(customization) self.buf = bytearray() self.msg_len = 0 self.n_blocks = 0 self.z = bytearray() self.z.extend(self.left_encode(self.block_size)) self.init_cffi_backend() if data: self.update(data) return def copy(self): other = self.__class__( block_size=self.block_size, digest_bits=self.digest_bits, customization=self.customization, ) other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.n_blocks = self.n_blocks other.z = bytearray(self.z) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data_view = memoryview(data) self.msg_len += len(data_view) self.buf.extend(data_view) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.process_block(block) return self def digest(self): c = self.copy() return c.finalize() def hexdigest(self): return self.digest().hex() def finalize(self): if len(self.buf) > 0: block = bytes(self.buf) self.buf.clear() self.process_block(block) new_x = bytearray(self.z) new_x.extend(self.right_encode(self.n_blocks)) if self.xof_mode: new_x.extend(self.right_encode(0)) else: new_x.extend(self.right_encode(self.digest_bits)) out = self.cshake(bytes(new_x), self.digest_bits, b"ParallelHash", self.customization, self.security_bits) return out def process_block(self, block): def inner_hash(block): out_len = (self.inner_digest_bits + 7) // 8 if self.security_bits == 128: out = hashlib.shake_128(block).digest(out_len) elif self.security_bits == 256: out = hashlib.shake_256(block).digest(out_len) else: raise ValueError("invalid security_bits") if (self.inner_digest_bits % 8) != 0 and len(out) != 0: out_mut = bytearray(out) keep_bits = self.inner_digest_bits % 8 out_mut[-1] &= (0xff << (8 - keep_bits)) & 0xff return bytes(out_mut) return out self.z.extend(inner_hash(block)) self.n_blocks += 1 return def cshake(self, data, out_bits, function_name, customization, security_bits): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not isinstance(function_name, (bytes, bytearray, memoryview)): raise TypeError("function_name must be bytes-like") if not isinstance(customization, (bytes, bytearray, memoryview)): raise TypeError("customization must be bytes-like") def encode_string(data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) return self.left_encode(len(data) * 8) + data data = bytes(data) function_name = bytes(function_name) customization = bytes(customization) if self.USE_CFFI and out_bits <= 0xffff_ffff_ffff_ffff: out_len = (out_bits + 7) // 8 data_buf = self.cffi.ffi.NULL if len(data) == 0 else self.cffi.ffi.new("uint8_t[]", data) function_name_buf = self.cffi.ffi.NULL if len(function_name) == 0 else self.cffi.ffi.new("uint8_t[]", function_name) customization_buf = self.cffi.ffi.NULL if len(customization) == 0 else self.cffi.ffi.new("uint8_t[]", customization) out_buf = self.cffi.ffi.new("uint8_t[]", max(out_len, 1)) rc = self.cffi.lib.cshake_run( data_buf, len(data), out_bits, function_name_buf, len(function_name), customization_buf, len(customization), security_bits, out_buf, ) if rc == 0: raise ValueError("invalid security_bits") return bytes(self.cffi.ffi.buffer(out_buf, out_len)) if security_bits == 128: rate_bytes = 168 elif security_bits == 256: rate_bytes = 136 else: raise ValueError("security_bits must be 128 or 256") if len(function_name) == 0 and len(customization) == 0: return self.keccak_xof(data, out_bits, rate_bytes, 0x1f) prefix = self.bytepad(encode_string(function_name) + encode_string(customization), rate_bytes) return self.keccak_xof(prefix + data, out_bits, rate_bytes, 0x04) def keccak_xof(self, data, out_bits, rate_bytes, domain_suffix): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not isinstance(out_bits, int): raise TypeError("out_bits must be int") if out_bits < 0: raise ValueError("out_bits must be >= 0") if not isinstance(rate_bytes, int): raise TypeError("rate_bytes must be int") if rate_bytes <= 0: raise ValueError("rate_bytes must be > 0") if not isinstance(domain_suffix, int): raise TypeError("domain_suffix must be int") if domain_suffix < 0 or domain_suffix > 0xff: raise ValueError("domain_suffix must be a byte") data = bytes(data) if self.USE_CFFI and out_bits <= 0xffff_ffff_ffff_ffff: out_len = (out_bits + 7) // 8 data_buf = self.cffi.ffi.NULL if len(data) == 0 else self.cffi.ffi.new("uint8_t[]", data) out_buf = self.cffi.ffi.new("uint8_t[]", max(out_len, 1)) self.cffi.lib.keccak_xof_run(data_buf, len(data), out_bits, rate_bytes, domain_suffix, out_buf) return bytes(self.cffi.ffi.buffer(out_buf, out_len)) state = [0] * 25 offset = 0 while offset + rate_bytes <= len(data): block = data[offset:offset + rate_bytes] self.keccak_absorb_block(state, block, rate_bytes) self.keccak_f1600(state) offset += rate_bytes tail = data[offset:] self.keccak_absorb_padded(state, tail, rate_bytes, domain_suffix) self.keccak_f1600(state) out_len = (out_bits + 7) // 8 out = bytearray() while len(out) < out_len: out.extend(self.keccak_squeeze_block(state, rate_bytes)) if len(out) >= out_len: break self.keccak_f1600(state) out = out[:out_len] if (out_bits % 8) != 0 and len(out) > 0: keep_bits = out_bits % 8 out[-1] &= (0xff << (8 - keep_bits)) & 0xff return bytes(out) def keccak_absorb_block(self, state, block, rate_bytes): lanes = rate_bytes // 8 for i in range(lanes): lane = int.from_bytes(block[i * 8:(i + 1) * 8], "little") state[i] ^= lane return def keccak_absorb_padded(self, state, tail, rate_bytes, domain_suffix): block = bytearray(rate_bytes) block[:len(tail)] = tail block[len(tail)] ^= domain_suffix block[rate_bytes - 1] ^= 0x80 self.keccak_absorb_block(state, bytes(block), rate_bytes) return def keccak_squeeze_block(self, state, rate_bytes): lanes = rate_bytes // 8 out = bytearray() for i in range(lanes): out.extend(state[i].to_bytes(8, "little")) return bytes(out) def keccak_f1600(self, state): A = state RC = self.keccak_round_constants # Explicitly load state into local variables A00, A01, A02, A03, A04 = A[0], A[1], A[2], A[3], A[4] A05, A06, A07, A08, A09 = A[5], A[6], A[7], A[8], A[9] A10, A11, A12, A13, A14 = A[10], A[11], A[12], A[13], A[14] A15, A16, A17, A18, A19 = A[15], A[16], A[17], A[18], A[19] A20, A21, A22, A23, A24 = A[20], A[21], A[22], A[23], A[24] for rc in RC: # Theta C0 = A00 ^ A05 ^ A10 ^ A15 ^ A20 C1 = A01 ^ A06 ^ A11 ^ A16 ^ A21 C2 = A02 ^ A07 ^ A12 ^ A17 ^ A22 C3 = A03 ^ A08 ^ A13 ^ A18 ^ A23 C4 = A04 ^ A09 ^ A14 ^ A19 ^ A24 # D0 = C4 ^ rol(C1, 1) D0 = C4 ^ (((C1 << 1) | (C1 >> 63)) & 0xffff_ffff_ffff_ffff) D1 = C0 ^ (((C2 << 1) | (C2 >> 63)) & 0xffff_ffff_ffff_ffff) D2 = C1 ^ (((C3 << 1) | (C3 >> 63)) & 0xffff_ffff_ffff_ffff) D3 = C2 ^ (((C4 << 1) | (C4 >> 63)) & 0xffff_ffff_ffff_ffff) D4 = C3 ^ (((C0 << 1) | (C0 >> 63)) & 0xffff_ffff_ffff_ffff) A00 ^= D0 A05 ^= D0 A10 ^= D0 A15 ^= D0 A20 ^= D0 A01 ^= D1 A06 ^= D1 A11 ^= D1 A16 ^= D1 A21 ^= D1 A02 ^= D2 A07 ^= D2 A12 ^= D2 A17 ^= D2 A22 ^= D2 A03 ^= D3 A08 ^= D3 A13 ^= D3 A18 ^= D3 A23 ^= D3 A04 ^= D4 A09 ^= D4 A14 ^= D4 A19 ^= D4 A24 ^= D4 # Rho and Pi B00 = A00 B01 = ((A06 << 44) | (A06 >> 20)) & 0xffff_ffff_ffff_ffff B02 = ((A12 << 43) | (A12 >> 21)) & 0xffff_ffff_ffff_ffff B03 = ((A18 << 21) | (A18 >> 43)) & 0xffff_ffff_ffff_ffff B04 = ((A24 << 14) | (A24 >> 50)) & 0xffff_ffff_ffff_ffff B05 = ((A03 << 28) | (A03 >> 36)) & 0xffff_ffff_ffff_ffff B06 = ((A09 << 20) | (A09 >> 44)) & 0xffff_ffff_ffff_ffff B07 = ((A10 << 3) | (A10 >> 61)) & 0xffff_ffff_ffff_ffff B08 = ((A16 << 45) | (A16 >> 19)) & 0xffff_ffff_ffff_ffff B09 = ((A22 << 61) | (A22 >> 3)) & 0xffff_ffff_ffff_ffff B10 = ((A01 << 1) | (A01 >> 63)) & 0xffff_ffff_ffff_ffff B11 = ((A07 << 6) | (A07 >> 58)) & 0xffff_ffff_ffff_ffff B12 = ((A13 << 25) | (A13 >> 39)) & 0xffff_ffff_ffff_ffff B13 = ((A19 << 8) | (A19 >> 56)) & 0xffff_ffff_ffff_ffff B14 = ((A20 << 18) | (A20 >> 46)) & 0xffff_ffff_ffff_ffff B15 = ((A04 << 27) | (A04 >> 37)) & 0xffff_ffff_ffff_ffff B16 = ((A05 << 36) | (A05 >> 28)) & 0xffff_ffff_ffff_ffff B17 = ((A11 << 10) | (A11 >> 54)) & 0xffff_ffff_ffff_ffff B18 = ((A17 << 15) | (A17 >> 49)) & 0xffff_ffff_ffff_ffff B19 = ((A23 << 56) | (A23 >> 8)) & 0xffff_ffff_ffff_ffff B20 = ((A02 << 62) | (A02 >> 2)) & 0xffff_ffff_ffff_ffff B21 = ((A08 << 55) | (A08 >> 9)) & 0xffff_ffff_ffff_ffff B22 = ((A14 << 39) | (A14 >> 25)) & 0xffff_ffff_ffff_ffff B23 = ((A15 << 41) | (A15 >> 23)) & 0xffff_ffff_ffff_ffff B24 = ((A21 << 2) | (A21 >> 62)) & 0xffff_ffff_ffff_ffff # Chi A00 = B00 ^ (~B01 & B02) A01 = B01 ^ (~B02 & B03) A02 = B02 ^ (~B03 & B04) A03 = B03 ^ (~B04 & B00) A04 = B04 ^ (~B00 & B01) A05 = B05 ^ (~B06 & B07) A06 = B06 ^ (~B07 & B08) A07 = B07 ^ (~B08 & B09) A08 = B08 ^ (~B09 & B05) A09 = B09 ^ (~B05 & B06) A10 = B10 ^ (~B11 & B12) A11 = B11 ^ (~B12 & B13) A12 = B12 ^ (~B13 & B14) A13 = B13 ^ (~B14 & B10) A14 = B14 ^ (~B10 & B11) A15 = B15 ^ (~B16 & B17) A16 = B16 ^ (~B17 & B18) A17 = B17 ^ (~B18 & B19) A18 = B18 ^ (~B19 & B15) A19 = B19 ^ (~B15 & B16) A20 = B20 ^ (~B21 & B22) A21 = B21 ^ (~B22 & B23) A22 = B22 ^ (~B23 & B24) A23 = B23 ^ (~B24 & B20) A24 = B24 ^ (~B20 & B21) A00 ^= rc state[0], state[1], state[2], state[3], state[4] = A00, A01, A02, A03, A04 state[5], state[6], state[7], state[8], state[9] = A05, A06, A07, A08, A09 state[10], state[11], state[12], state[13], state[14] = A10, A11, A12, A13, A14 state[15], state[16], state[17], state[18], state[19] = A15, A16, A17, A18, A19 state[20], state[21], state[22], state[23], state[24] = A20, A21, A22, A23, A24 return def left_encode(self, value): if not isinstance(value, int): raise TypeError("value must be int") if value < 0: raise ValueError("value must be >= 0") n = 1 while value >= (1 << (8 * n)): n += 1 return bytes([n]) + value.to_bytes(n, "big") def right_encode(self, value): if not isinstance(value, int): raise TypeError("value must be int") if value < 0: raise ValueError("value must be >= 0") n = 1 while value >= (1 << (8 * n)): n += 1 return value.to_bytes(n, "big") + bytes([n]) def bytepad(self, data, width): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if not isinstance(width, int): raise TypeError("width must be int") if width <= 0: raise ValueError("width must be > 0") data = bytes(data) out = bytearray() out.extend(self.left_encode(width)) out.extend(data) while (len(out) % width) != 0: out.append(0x00) return bytes(out) class ParallelHash128(ParallelHashBase): security_bits = 128 default_digest_bits = 256 inner_digest_bits = 256 xof_mode = False class ParallelHash256(ParallelHashBase): security_bits = 256 default_digest_bits = 512 inner_digest_bits = 512 xof_mode = False class ParallelHashXOF128(ParallelHashBase): security_bits = 128 default_digest_bits = 256 inner_digest_bits = 256 xof_mode = True class ParallelHashXOF256(ParallelHashBase): security_bits = 256 default_digest_bits = 512 inner_digest_bits = 512 xof_mode = True class AbacusBase: block_size = 1 num_blank_rounds = 135 num_absorb_clocks = 1 num_squeeze_clocks = 1 ctr1_mod = 0xe9 # 233 ctr2_mod = 0xef # 239 ctr3_mod = 0xf1 # 241 ctr4_mod = 0xfb # 251 sbox = ( 0xe3, 0x84, 0xf0, 0xd6, 0xf9, 0xf6, 0xbe, 0x90, 0x85, 0x7d, 0x28, 0x43, 0x12, 0xc0, 0xe1, 0xb4, 0x55, 0xc7, 0x8c, 0x87, 0x42, 0xe0, 0xd9, 0x27, 0x78, 0xec, 0xcb, 0x07, 0xaa, 0x95, 0xc1, 0x3f, 0xb2, 0xdc, 0x26, 0xa7, 0x1f, 0xdf, 0xf3, 0x54, 0xd2, 0xe7, 0x24, 0x3e, 0x32, 0xd1, 0x56, 0xc6, 0x35, 0x73, 0xf7, 0x7b, 0x62, 0x29, 0x52, 0x80, 0xa9, 0xba, 0xab, 0xe9, 0x02, 0x53, 0x6a, 0xe4, 0x67, 0xa0, 0x8e, 0xfb, 0x9a, 0x79, 0x4e, 0x8d, 0xe5, 0x4a, 0x41, 0xaf, 0x5a, 0x5c, 0xa6, 0x6b, 0x16, 0x5e, 0xe8, 0x3c, 0x9c, 0x5b, 0x88, 0x76, 0x15, 0xf4, 0x60, 0xbd, 0x83, 0x98, 0x8f, 0xc8, 0x09, 0x68, 0x0d, 0x18, 0x65, 0x45, 0x04, 0xce, 0x7a, 0xf2, 0x39, 0xc5, 0x9e, 0xf1, 0x17, 0xef, 0x38, 0x21, 0x94, 0x86, 0x69, 0x37, 0xf5, 0xed, 0x36, 0x66, 0xcf, 0x3b, 0x63, 0x4b, 0x33, 0xb6, 0xff, 0xbc, 0x11, 0x5d, 0xb3, 0x2b, 0xd3, 0xd0, 0x3a, 0x96, 0x77, 0x7c, 0x1c, 0xc2, 0xfe, 0x0a, 0xc3, 0x25, 0x4d, 0xfc, 0x89, 0xde, 0x30, 0x23, 0x64, 0x81, 0xd5, 0xae, 0x70, 0xdb, 0xe6, 0x7e, 0xb0, 0x6f, 0x0f, 0xd7, 0xbf, 0x9b, 0xc4, 0x74, 0xb7, 0x57, 0x4f, 0x58, 0x10, 0x2d, 0xa4, 0xb9, 0xa2, 0xad, 0x61, 0xeb, 0xac, 0x1a, 0xa3, 0xd8, 0x2c, 0x5f, 0x91, 0x2f, 0x72, 0x31, 0xb1, 0x82, 0x49, 0xda, 0x0c, 0xca, 0x00, 0xa1, 0xb5, 0x75, 0x6e, 0x47, 0x6d, 0x13, 0x19, 0x93, 0x20, 0x05, 0x01, 0x9f, 0x1d, 0x44, 0x8a, 0x1e, 0x50, 0x34, 0xfa, 0x9d, 0xa8, 0x8b, 0x0b, 0x4c, 0xa5, 0x2e, 0x71, 0xf8, 0x40, 0xcd, 0x99, 0xfd, 0x51, 0x59, 0x0e, 0x2a, 0x3d, 0x92, 0x14, 0x48, 0x6c, 0xea, 0x46, 0x22, 0xcc, 0x06, 0xd4, 0x97, 0xe2, 0x1b, 0xdd, 0x7f, 0xbb, 0xc9, 0xb8, 0x03, 0xee, 0x08, ) def __init__(self, data=b"", salt=b""): self.ra = 0 self.rb = [] self.rc = [] self.rd = [] self.ctr1 = 0 self.ctr2 = 0 self.ctr3 = 0 self.ctr4 = 0 self.out = 0 self.msg_len = 0 self.salt = b"" self.is_finalized = False self.digest_buf = b"" if not isinstance(salt, (bytes, bytearray, memoryview)): raise TypeError("salt must be bytes-like") self.salt = bytes(salt) self.init_state() self.absorb_pretrain() if data: self.update(data) return def init_state(self): self.ra = self.sbox[0] self.rb = list(self.sbox[1:6]) self.rc = list(self.sbox[6:43]) self.rd = list(self.sbox[43:132]) self.ctr1 = 0 self.ctr2 = 0 self.ctr3 = 0 self.ctr4 = 0 self.out = 0 self.msg_len = 0 self.is_finalized = False self.digest_buf = b"" return def copy(self): other = self.__class__(salt=self.salt) other.ra = self.ra other.rb = list(self.rb) other.rc = list(self.rc) other.rd = list(self.rd) other.ctr1 = self.ctr1 other.ctr2 = self.ctr2 other.ctr3 = self.ctr3 other.ctr4 = self.ctr4 other.out = self.out other.msg_len = self.msg_len other.is_finalized = self.is_finalized other.digest_buf = bytes(self.digest_buf) return other def update(self, data): if self.is_finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) for b in data: self.absorb_round(b) return self def digest(self): c = self.copy() c.finalize() return bytes(c.digest_buf) def hexdigest(self): return self.digest().hex() def finalize(self): if self.is_finalized: return self for b in self.posttrain(): self.absorb_round(b) for _ in range(self.num_blank_rounds): self.absorb_round(0x00) out = bytearray() for _ in range(self.digest_size): out.append(self.squeeze_round()) self.digest_buf = bytes(out) self.is_finalized = True return self def hash_len_field(self): n = self.digest_size * 8 return n.to_bytes(4, "big") def msg_len_field(self): n = self.msg_len * 8 return n.to_bytes(16, "big") def pretrain(self): # Beta Padding: SALT || HASH_LEN_BITS || NULL_128 return self.salt + self.hash_len_field() + (0).to_bytes(16, "big") def posttrain(self): # Beta Padding: SALT || HASH_LEN_BITS || MSG_LEN_BITS return self.salt + self.hash_len_field() + self.msg_len_field() def absorb_pretrain(self): for b in self.pretrain(): self.absorb_round(b) return def absorb_round(self, msg_byte): for _ in range(self.num_absorb_clocks): self.clock_core(msg_byte, False) return def squeeze_round(self): out_byte = 0 for _ in range(self.num_squeeze_clocks): out_byte = self.clock_core(self.out, True) return out_byte def clock_core(self, in_byte, do_squeeze): s = self.sbox ra = s[self.ra ^ self.rd[58]] ^ self.ctr1 rb0 = s[self.rb[0] ^ self.rc[24]] ^ self.ctr2 rc0 = s[self.rc[0] ^ self.rb[3]] ^ self.ctr3 rd0 = s[self.rd[0] ^ in_byte] ^ self.ctr4 ra, rb0, rc0, rd0 = self.mds4(ra, rb0, rc0, rd0) ra = s[ra] rb0 = s[rb0] rc0 = s[rc0] rd0 = s[rd0] self.ra = ra self.rb[0] = rb0 self.rc[0] = rc0 self.rd[0] = rd0 out_byte = 0 if do_squeeze: self.out = s[self.ra ^ self.rb[0]] ^ s[self.rc[0] ^ self.rd[0]] out_byte = self.out self.rotate_arrays() self.inc_counters() return out_byte def xtime(self, x): y = (x << 1) & 0xff if x & 0x80: y ^= 0x1b return y def mds4(self, x0, x1, x2, x3): a0 = x0 a1 = x1 a2 = x2 a3 = x3 b0 = self.xtime(a0) b1 = self.xtime(a1) b2 = self.xtime(a2) b3 = self.xtime(a3) c0 = b0 ^ a0 c1 = b1 ^ a1 c2 = b2 ^ a2 c3 = b3 ^ a3 r0 = a0 ^ b3 ^ c2 ^ a1 r1 = a1 ^ b0 ^ c3 ^ a2 r2 = a2 ^ b1 ^ c0 ^ a3 r3 = a3 ^ b2 ^ c1 ^ a0 return r0, r1, r2, r3 def rotate_arrays(self): self.rb = self.rb[1:] + self.rb[:1] self.rc = self.rc[1:] + self.rc[:1] self.rd = self.rd[1:] + self.rd[:1] return def inc_counters(self): self.ctr1 = (self.ctr1 + 1) % self.ctr1_mod self.ctr2 = (self.ctr2 + 1) % self.ctr2_mod self.ctr3 = (self.ctr3 + 1) % self.ctr3_mod self.ctr4 = (self.ctr4 + 1) % self.ctr4_mod return class Abacus224(AbacusBase): digest_size = 28 class Abacus256(AbacusBase): digest_size = 32 class Abacus384(AbacusBase): digest_size = 48 class Abacus512(AbacusBase): digest_size = 64 class ARIRANG256Base: block_size = 64 digest_words = 8 digest_size = 32 iv_words = [ 0x6a09_e667, 0xbb67_ae85, 0x3c6e_f372, 0xa54f_f53a, 0x510e_527f, 0x9b05_688c, 0x1f83_d9ab, 0x5be0_cd19, ] k_words = [ 0x517c_c1b7, 0x7651_7cc1, 0xbd76_517c, 0x2dbd_7651, 0x272d_bd76, 0xcb27_2dbd, 0x90cb_272d, 0x0a90_cb27, 0xec0a_90cb, 0x5bec_0a90, 0x9a5b_ec0a, 0xe69a_5bec, 0xb7e6_9a5b, 0xc1b7_e69a, 0x7cc1_b7e6, 0x517c_c1b7, ] round_pairs_1 = [(16, 17), (0, 1), (2, 3), (4, 5), (6, 7), (18, 19), (8, 9), (10, 11), (12, 13), (14, 15)] round_pairs_2 = [(20, 21), (3, 6), (9, 12), (15, 2), (5, 8), (22, 23), (11, 14), (1, 4), (7, 10), (13, 0)] round_pairs_3 = [(24, 25), (12, 5), (14, 7), (0, 9), (2, 11), (26, 27), (4, 13), (6, 15), (8, 1), (10, 3)] round_pairs_4 = [(28, 29), (7, 2), (13, 8), (3, 14), (9, 4), (30, 31), (15, 10), (5, 0), (11, 6), (1, 12)] sbx = None f2 = None f3 = None f4 = None f8 = None f9 = None fa = None def __init__(self, data=b""): self.ensure_tables() self.h = list(self.iv_words) self.buf = bytearray() self.count0 = 0 self.count1 = 0 self.counter0 = 0 self.counter1 = 0 self.finalized = False if data: self.update(data) return @classmethod def ensure_tables(cls): if cls.sbx is not None: return pow_tab = [0] * 256 log_tab = [0] * 256 p = 1 for i in range(256): pow_tab[i] = p & 0xff log_tab[p & 0xff] = i p = p ^ (p << 1) ^ (0x01b if (p & 0x80) else 0) p &= 0xff sbx = [0] * 256 for i in range(256): if i != 0: p = pow_tab[255 - log_tab[i]] else: p = 0 q = p q = ((q >> 7) | (q << 1)) & 0xff p ^= q q = ((q >> 7) | (q << 1)) & 0xff p ^= q q = ((q >> 7) | (q << 1)) & 0xff p ^= q q = ((q >> 7) | (q << 1)) & 0xff p ^= q ^ 0x63 sbx[i] = p & 0xff def ff_mult(a, b): if a == 0 or b == 0: return 0 return pow_tab[(log_tab[a] + log_tab[b]) % 255] f2 = [0] * 256 f3 = [0] * 256 f4 = [0] * 256 f8 = [0] * 256 f9 = [0] * 256 fa = [0] * 256 for i in range(256): f2[i] = ff_mult(i, 2) f3[i] = ff_mult(i, 3) f4[i] = ff_mult(i, 4) f8[i] = ff_mult(i, 8) f9[i] = ff_mult(i, 9) fa[i] = ff_mult(i, 10) cls.sbx = sbx cls.f2 = f2 cls.f3 = f3 cls.f4 = f4 cls.f8 = f8 cls.f9 = f9 cls.fa = fa return def copy(self): other = self.__class__() other.h = list(self.h) other.buf = bytearray(self.buf) other.count0 = self.count0 other.count1 = self.count1 other.counter0 = self.counter0 other.counter1 = self.counter1 other.finalized = self.finalized return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if self.finalized: raise ValueError("hash already finalized") data = bytes(data) add_bits = len(data) << 3 new_count0 = (self.count0 + add_bits) & 0xffff_ffff_ffff_ffff if new_count0 < self.count0: self.count1 = (self.count1 + 1) & 0xffff_ffff_ffff_ffff self.count0 = new_count0 self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def byte(self, x, n): return (x >> (8 * n)) & 0xff def rol32(self, x, n): x &= 0xffff_ffff return ((x << n) | (x >> (32 - n))) & 0xffff_ffff def g256(self, x): sbx = self.sbx f2 = self.f2 f3 = self.f3 t = ( sbx[self.byte(x, 0)] | (sbx[self.byte(x, 1)] << 8) | (sbx[self.byte(x, 2)] << 16) | (sbx[self.byte(x, 3)] << 24) ) b0 = self.byte(t, 0) b1 = self.byte(t, 1) b2 = self.byte(t, 2) b3 = self.byte(t, 3) out = ( (f2[b0] ^ f3[b1] ^ b2 ^ b3) | ((b0 ^ f2[b1] ^ f3[b2] ^ b3) << 8) | ((b0 ^ b1 ^ f2[b2] ^ f3[b3]) << 16) | ((f3[b0] ^ b1 ^ b2 ^ f2[b3]) << 24) ) return out & 0xffff_ffff def step(self, r, m1, m2): r[0] ^= m1 r[4] ^= m2 t1 = self.g256(r[0]) t2 = self.g256(r[4]) r[1] ^= t1 r[2] ^= self.rol32(t1, 13) r[3] ^= self.rol32(t1, 23) r[5] ^= t2 r[6] ^= self.rol32(t2, 29) r[7] ^= self.rol32(t2, 7) t = r[7] r[7] = r[6] r[6] = r[5] r[5] = r[4] r[4] = r[3] r[3] = r[2] r[2] = r[1] r[1] = r[0] r[0] = t return def message_schedule(self, w): k = self.k_words w = list(w) w.append(self.rol32(w[9] ^ w[11] ^ w[13] ^ w[15] ^ k[0], 5)) w.append(self.rol32(w[8] ^ w[10] ^ w[12] ^ w[14] ^ k[1], 11)) w.append(self.rol32(w[1] ^ w[3] ^ w[5] ^ w[7] ^ k[2], 19)) w.append(self.rol32(w[0] ^ w[2] ^ w[4] ^ w[6] ^ k[3], 31)) w.append(self.rol32(w[14] ^ w[4] ^ w[10] ^ w[0] ^ k[4], 5)) w.append(self.rol32(w[11] ^ w[1] ^ w[7] ^ w[13] ^ k[5], 11)) w.append(self.rol32(w[6] ^ w[12] ^ w[2] ^ w[8] ^ k[6], 19)) w.append(self.rol32(w[3] ^ w[9] ^ w[15] ^ w[5] ^ k[7], 31)) w.append(self.rol32(w[13] ^ w[15] ^ w[1] ^ w[3] ^ k[8], 5)) w.append(self.rol32(w[4] ^ w[6] ^ w[8] ^ w[10] ^ k[9], 11)) w.append(self.rol32(w[5] ^ w[7] ^ w[9] ^ w[11] ^ k[10], 19)) w.append(self.rol32(w[12] ^ w[14] ^ w[0] ^ w[2] ^ k[11], 31)) w.append(self.rol32(w[10] ^ w[0] ^ w[6] ^ w[12] ^ k[12], 5)) w.append(self.rol32(w[15] ^ w[5] ^ w[11] ^ w[1] ^ k[13], 11)) w.append(self.rol32(w[2] ^ w[8] ^ w[14] ^ w[4] ^ k[14], 19)) w.append(self.rol32(w[7] ^ w[13] ^ w[3] ^ w[9] ^ k[15], 31)) return w def compress(self, block): self.h[0] ^= (self.counter0 >> 32) & 0xffff_ffff self.h[4] ^= self.counter0 & 0xffff_ffff w0 = struct.unpack(">16I", block) w = self.message_schedule(w0) r = [v & 0xffff_ffff for v in self.h] for i, j in self.round_pairs_1: self.step(r, w[i], w[j]) for i, j in self.round_pairs_2: self.step(r, w[i], w[j]) for i in range(8): r[i] ^= self.h[i] for i, j in self.round_pairs_3: self.step(r, w[i], w[j]) for i, j in self.round_pairs_4: self.step(r, w[i], w[j]) for i in range(8): self.h[i] ^= r[i] self.counter0 = (self.counter0 + 1) & 0xffff_ffff_ffff_ffff if self.counter0 == 0: self.counter1 = (self.counter1 + 1) & 0xffff_ffff_ffff_ffff return def finalize(self): if self.finalized: return buf = bytearray(self.buf) buf.append(0x80) if len(buf) > (self.block_size - 8): while len(buf) < self.block_size: buf.append(0x00) self.compress(bytes(buf)) buf = bytearray() while len(buf) < (self.block_size - 8): buf.append(0x00) buf.extend(struct.pack(">Q", self.count0)) self.counter0 = 0xb7e1_5162_8aed_2a6a self.compress(bytes(buf)) self.buf = bytearray() self.finalized = True return def digest(self): c = self.copy() c.finalize() words = c.h[:self.digest_words] return struct.pack(">" + ("I" * len(words)), *words) def hexdigest(self): return self.digest().hex() class ARIRANG224(ARIRANG256Base): digest_words = 7 digest_size = 28 iv_words = [ 0xcbbb_9d5d, 0x629a_292a, 0x9159_015a, 0x152f_ecd8, 0x6733_2667, 0x8eb4_4a87, 0xdb0c_2e0d, 0x47b5_481d, ] class ARIRANG256(ARIRANG256Base): pass class ARIRANG512Base(ARIRANG256Base): block_size = 128 digest_words = 8 digest_size = 64 iv_words = [ 0x6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1, 0x510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179, ] k_words = [ 0x517c_c1b7_2722_0a94, 0x2db6_517c_c1b7_2722, 0xe695_2db6_517c_c1b7, 0x90cb_e695_2db6_517c, 0x7cca_90cb_e695_2db6, 0xcb23_7cca_90cb_e695, 0x765e_cb23_7cca_90cb, 0xec01_765e_cb23_7cca, 0xb7e9_ec01_765e_cb23, 0xbd7d_b7e9_ec01_765e, 0x9a5f_bd7d_b7e9_ec01, 0x5be8_9a5f_bd7d_b7e9, 0x0a94_5be8_9a5f_bd7d, 0x2722_0a94_5be8_9a5f, 0xc1b7_2722_0a94_5be8, 0x517c_c1b7_2722_0a94, ] def __init__(self, data=b""): self.ensure_tables() self.h = list(self.iv_words) self.buf = bytearray() self.count0 = 0 self.count1 = 0 self.counter0 = 0 self.counter1 = 0 self.finalized = False if data: self.update(data) return def copy(self): other = self.__class__() other.h = list(self.h) other.buf = bytearray(self.buf) other.count0 = self.count0 other.count1 = self.count1 other.counter0 = self.counter0 other.counter1 = self.counter1 other.finalized = self.finalized return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if self.finalized: raise ValueError("hash already finalized") data = bytes(data) add_bits = len(data) << 3 new_count0 = (self.count0 + add_bits) & 0xffff_ffff_ffff_ffff if new_count0 < self.count0: self.count1 = (self.count1 + 1) & 0xffff_ffff_ffff_ffff self.count0 = new_count0 self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def byte(self, x, n): return (x >> (8 * n)) & 0xff def rol64(self, x, n): x &= 0xffff_ffff_ffff_ffff return ((x << n) | (x >> (64 - n))) & 0xffff_ffff_ffff_ffff def g512(self, x): sbx = self.sbx f2 = self.f2 f4 = self.f4 f8 = self.f8 f9 = self.f9 fa = self.fa t = 0 t |= sbx[self.byte(x, 0)] t |= sbx[self.byte(x, 1)] << 8 t |= sbx[self.byte(x, 2)] << 16 t |= sbx[self.byte(x, 3)] << 24 t |= sbx[self.byte(x, 4)] << 32 t |= sbx[self.byte(x, 5)] << 40 t |= sbx[self.byte(x, 6)] << 48 t |= sbx[self.byte(x, 7)] << 56 b0 = self.byte(t, 0) b1 = self.byte(t, 1) b2 = self.byte(t, 2) b3 = self.byte(t, 3) b4 = self.byte(t, 4) b5 = self.byte(t, 5) b6 = self.byte(t, 6) b7 = self.byte(t, 7) out = 0 out |= (b0 ^ f2[b1] ^ fa[b2] ^ f9[b3] ^ f8[b4] ^ b5 ^ f4[b6] ^ b7) out |= (b0 ^ b1 ^ f2[b2] ^ fa[b3] ^ f9[b4] ^ f8[b5] ^ b6 ^ f4[b7]) << 8 out |= (f4[b0] ^ b1 ^ b2 ^ f2[b3] ^ fa[b4] ^ f9[b5] ^ f8[b6] ^ b7) << 16 out |= (b0 ^ f4[b1] ^ b2 ^ b3 ^ f2[b4] ^ fa[b5] ^ f9[b6] ^ f8[b7]) << 24 out |= (f8[b0] ^ b1 ^ f4[b2] ^ b3 ^ b4 ^ f2[b5] ^ fa[b6] ^ f9[b7]) << 32 out |= (f9[b0] ^ f8[b1] ^ b2 ^ f4[b3] ^ b4 ^ b5 ^ f2[b6] ^ fa[b7]) << 40 out |= (fa[b0] ^ f9[b1] ^ f8[b2] ^ b3 ^ f4[b4] ^ b5 ^ b6 ^ f2[b7]) << 48 out |= (f2[b0] ^ fa[b1] ^ f9[b2] ^ f8[b3] ^ b4 ^ f4[b5] ^ b6 ^ b7) << 56 return out & 0xffff_ffff_ffff_ffff def step(self, r, m1, m2): r[0] ^= m1 r[4] ^= m2 t1 = self.g512(r[0]) t2 = self.g512(r[4]) r[1] ^= t1 r[2] ^= self.rol64(t1, 29) r[3] ^= self.rol64(t1, 41) r[5] ^= t2 r[6] ^= self.rol64(t2, 53) r[7] ^= self.rol64(t2, 13) t = r[7] r[7] = r[6] r[6] = r[5] r[5] = r[4] r[4] = r[3] r[3] = r[2] r[2] = r[1] r[1] = r[0] r[0] = t return def message_schedule(self, w): k = self.k_words w = list(w) w.append(self.rol64(w[9] ^ w[11] ^ w[13] ^ w[15] ^ k[0], 11)) w.append(self.rol64(w[8] ^ w[10] ^ w[12] ^ w[14] ^ k[1], 23)) w.append(self.rol64(w[1] ^ w[3] ^ w[5] ^ w[7] ^ k[2], 37)) w.append(self.rol64(w[0] ^ w[2] ^ w[4] ^ w[6] ^ k[3], 59)) w.append(self.rol64(w[14] ^ w[4] ^ w[10] ^ w[0] ^ k[4], 11)) w.append(self.rol64(w[11] ^ w[1] ^ w[7] ^ w[13] ^ k[5], 23)) w.append(self.rol64(w[6] ^ w[12] ^ w[2] ^ w[8] ^ k[6], 37)) w.append(self.rol64(w[3] ^ w[9] ^ w[15] ^ w[5] ^ k[7], 59)) w.append(self.rol64(w[13] ^ w[15] ^ w[1] ^ w[3] ^ k[8], 11)) w.append(self.rol64(w[4] ^ w[6] ^ w[8] ^ w[10] ^ k[9], 23)) w.append(self.rol64(w[5] ^ w[7] ^ w[9] ^ w[11] ^ k[10], 37)) w.append(self.rol64(w[12] ^ w[14] ^ w[0] ^ w[2] ^ k[11], 59)) w.append(self.rol64(w[10] ^ w[0] ^ w[6] ^ w[12] ^ k[12], 11)) w.append(self.rol64(w[15] ^ w[5] ^ w[11] ^ w[1] ^ k[13], 23)) w.append(self.rol64(w[2] ^ w[8] ^ w[14] ^ w[4] ^ k[14], 37)) w.append(self.rol64(w[7] ^ w[13] ^ w[3] ^ w[9] ^ k[15], 59)) return w def compress(self, block): self.h[0] ^= self.counter1 self.h[4] ^= self.counter0 w = struct.unpack(">16Q", block) w = self.message_schedule(w) r = [v & 0xffff_ffff_ffff_ffff for v in self.h] for i, j in self.round_pairs_1: self.step(r, w[i], w[j]) for i, j in self.round_pairs_2: self.step(r, w[i], w[j]) for i in range(8): r[i] ^= self.h[i] for i, j in self.round_pairs_3: self.step(r, w[i], w[j]) for i, j in self.round_pairs_4: self.step(r, w[i], w[j]) for i in range(8): self.h[i] ^= r[i] self.counter0 = (self.counter0 + 1) & 0xffff_ffff_ffff_ffff if self.counter0 == 0: self.counter1 = (self.counter1 + 1) & 0xffff_ffff_ffff_ffff return def finalize(self): if self.finalized: return buf = bytearray(self.buf) buf.append(0x80) if len(buf) > (self.block_size - 16): while len(buf) < self.block_size: buf.append(0x00) self.compress(bytes(buf)) buf = bytearray() while len(buf) < (self.block_size - 16): buf.append(0x00) buf.extend(struct.pack(">Q", self.count1)) buf.extend(struct.pack(">Q", self.count0)) self.counter1 = 0xb7e1_5162_8aed_2a6a self.counter0 = 0xbf71_5880_9cf4_f3c7 self.compress(bytes(buf)) self.buf = bytearray() self.finalized = True return def digest(self): c = self.copy() c.finalize() words = c.h[:self.digest_words] return struct.pack(">" + ("Q" * len(words)), *words) def hexdigest(self): return self.digest().hex() class ARIRANG384(ARIRANG512Base): digest_words = 6 digest_size = 48 iv_words = [ 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4, ] class ARIRANG512(ARIRANG512Base): pass class AURORABase: block_size = 64 digest_size = 0 init_fill = 0x00 con_iv = () con_mask = () mode = "" # "256", "512", "256m" sbox = [ 0xd9, 0xdc, 0xd3, 0x69, 0xbd, 0x00, 0x4d, 0xeb, 0x02, 0x24, 0x57, 0xc2, 0xb8, 0x5d, 0xb7, 0x6d, 0xf5, 0x40, 0x37, 0x4e, 0x19, 0xd8, 0x64, 0x62, 0x9d, 0x34, 0x0f, 0x7c, 0xec, 0xce, 0x94, 0x04, 0xd1, 0x8a, 0x74, 0xfb, 0xe7, 0x87, 0x12, 0x23, 0xb5, 0x5c, 0x1a, 0xbb, 0x42, 0x49, 0x18, 0x85, 0x11, 0x46, 0x0d, 0x71, 0x67, 0x8f, 0xc6, 0x50, 0x58, 0xfd, 0x4b, 0xa4, 0xcd, 0x8e, 0x99, 0x1f, 0xad, 0x63, 0xc9, 0x6b, 0xf7, 0x28, 0x9f, 0x65, 0x2f, 0x5f, 0x61, 0x73, 0x3d, 0x8b, 0x0e, 0x1b, 0x33, 0xe0, 0xac, 0x26, 0xa1, 0xe3, 0xf3, 0x82, 0x83, 0x75, 0x44, 0x90, 0x13, 0xaf, 0xf0, 0x07, 0x96, 0x21, 0xf8, 0x3f, 0xa2, 0x98, 0x9a, 0xa3, 0x91, 0x4c, 0x7f, 0x92, 0x97, 0xea, 0x01, 0x1c, 0x1e, 0x2d, 0x89, 0x39, 0xe6, 0x9c, 0x0a, 0x54, 0x0c, 0x51, 0x6c, 0x43, 0xae, 0xdb, 0x53, 0x59, 0xa6, 0xf4, 0x06, 0xda, 0xe2, 0x78, 0x1d, 0x29, 0x30, 0xe1, 0x35, 0xfc, 0xed, 0xbc, 0x47, 0xd5, 0xc0, 0xab, 0xcc, 0xa8, 0x80, 0x2b, 0x09, 0xb0, 0x93, 0xd4, 0xc5, 0xb3, 0xd0, 0xdf, 0xa9, 0xaa, 0x7a, 0x36, 0x2a, 0xd6, 0xb2, 0xfa, 0xe8, 0xb1, 0xa0, 0x68, 0x5a, 0x81, 0x48, 0x08, 0x17, 0xc7, 0xfe, 0x76, 0xbf, 0xc4, 0xf2, 0x3e, 0x4a, 0x0b, 0x10, 0x14, 0xf1, 0xef, 0xa7, 0x27, 0xe5, 0xc8, 0xde, 0x9b, 0x8d, 0x3c, 0x56, 0xd7, 0x8c, 0x60, 0x6a, 0x79, 0xee, 0xa5, 0x31, 0x2e, 0x77, 0x41, 0xff, 0x95, 0xdd, 0x25, 0x3b, 0x55, 0xca, 0x52, 0x9e, 0x2c, 0x15, 0x4f, 0xe4, 0x16, 0x70, 0x7d, 0x72, 0x3a, 0x7b, 0x84, 0xf6, 0x32, 0x86, 0x03, 0xb4, 0x38, 0x6f, 0xb9, 0xc1, 0x45, 0x88, 0xe9, 0xba, 0xb6, 0x6e, 0x5e, 0xbe, 0x7e, 0x20, 0xf9, 0x22, 0x66, 0x05, 0xd2, 0xcb, 0xc3, 0xcf, 0x5b, ] mat0 = [0x01, 0x02, 0x02, 0x03] mat1 = [0x01, 0x06, 0x08, 0x02] mat2 = [0x03, 0x01, 0x02, 0x02] mat3 = [0x06, 0x08, 0x02, 0x01] pi = [ 4, 29, 22, 15, 8, 9, 10, 11, 12, 5, 30, 23, 16, 17, 18, 19, 20, 13, 6, 31, 24, 25, 26, 27, 28, 21, 14, 7, 0, 1, 2, 3 ] mul_table_0x01 = ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ) mul_table_0x02 = () mul_table_0x03 = () mul_table_0x06 = () mul_table_0x08 = () @classmethod def build_mul_table(cls, y): tbl = [0x00] * 256 x = 0 while x < 256: xy = 0x00 xx = x yy = y i = 0 while i < 4: if yy & 0x01: xy ^= xx yy >>= 1 if xx & 0x80: xx ^= 0x0d xx = ((xx << 1) | (xx >> 7)) & 0xff i += 1 tbl[x] = xy x += 1 return tuple(tbl) @classmethod def init_mul_tables(cls): if cls.mul_table_0x02: return cls.mul_table_0x02 = cls.build_mul_table(0x02) cls.mul_table_0x03 = cls.build_mul_table(0x03) cls.mul_table_0x06 = cls.build_mul_table(0x06) cls.mul_table_0x08 = cls.build_mul_table(0x08) return def __init__(self, data=b""): self.__class__.init_mul_tables() self.h = [self.init_fill] * 64 self.blk_num = [0x00] * 8 self.cnt = 0 self.blk_idx = 0 self.buf = [0x00] * 64 if data: self.update(data) return def get_mul_table(self, v): if v == 0x01: return self.mul_table_0x01 if v == 0x02: return self.mul_table_0x02 if v == 0x03: return self.mul_table_0x03 if v == 0x06: return self.mul_table_0x06 if v == 0x08: return self.mul_table_0x08 raise ValueError("invalid mul table") def byte_cpy(self, dst, dst_ofs, src, src_ofs, bytelen): dst[dst_ofs:dst_ofs + bytelen] = src[src_ofs:src_ofs + bytelen] return def byte_xor(self, dst, dst_ofs, src, src_ofs, bytelen): i = 0 while i < bytelen: dst[dst_ofs + i] ^= src[src_ofs + i] i += 1 return def aurora_f_xor(self, y, y_ofs, x, x_ofs, cirmat): sbox = self.sbox z0 = sbox[x[x_ofs + 0]] z1 = sbox[x[x_ofs + 1]] z2 = sbox[x[x_ofs + 2]] z3 = sbox[x[x_ofs + 3]] t0 = self.get_mul_table(cirmat[0]) t1 = self.get_mul_table(cirmat[1]) t2 = self.get_mul_table(cirmat[2]) t3 = self.get_mul_table(cirmat[3]) y0 = t0[z0] ^ t1[z1] ^ t2[z2] ^ t3[z3] y1 = t3[z0] ^ t0[z1] ^ t1[z2] ^ t2[z3] y2 = t2[z0] ^ t3[z1] ^ t0[z2] ^ t1[z3] y3 = t1[z0] ^ t2[z1] ^ t3[z2] ^ t0[z3] y[y_ofs + 0] = y0 y[y_ofs + 1] = y1 y[y_ofs + 2] = y2 y[y_ofs + 3] = y3 y[y_ofs + 4] = x[x_ofs + 4] ^ y0 y[y_ofs + 5] = x[x_ofs + 5] ^ y1 y[y_ofs + 6] = x[x_ofs + 6] ^ y2 y[y_ofs + 7] = x[x_ofs + 7] ^ y3 return def aurora_bd(self, y, x): pi = self.pi y[0] = x[pi[0]] y[1] = x[pi[1]] y[2] = x[pi[2]] y[3] = x[pi[3]] y[4] = x[pi[4]] y[5] = x[pi[5]] y[6] = x[pi[6]] y[7] = x[pi[7]] y[8] = x[pi[8]] y[9] = x[pi[9]] y[10] = x[pi[10]] y[11] = x[pi[11]] y[12] = x[pi[12]] y[13] = x[pi[13]] y[14] = x[pi[14]] y[15] = x[pi[15]] y[16] = x[pi[16]] y[17] = x[pi[17]] y[18] = x[pi[18]] y[19] = x[pi[19]] y[20] = x[pi[20]] y[21] = x[pi[21]] y[22] = x[pi[22]] y[23] = x[pi[23]] y[24] = x[pi[24]] y[25] = x[pi[25]] y[26] = x[pi[26]] y[27] = x[pi[27]] y[28] = x[pi[28]] y[29] = x[pi[29]] y[30] = x[pi[30]] y[31] = x[pi[31]] return def copy(self): other = self.__class__() other.h = list(self.h) other.blk_num = list(self.blk_num) other.cnt = self.cnt other.blk_idx = self.blk_idx other.buf = list(self.buf) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if self.mode == "256": self.aurora256_update(data) elif self.mode in ("512", "256m"): self.aurora512_update(data) else: raise ValueError("invalid mode") return self def digest(self): c = self.copy() out = c.final_bytes() return out def hexdigest(self): out = self.digest().hex() return out def final_bytes(self): if self.mode == "256": out = self.aurora256_final() elif self.mode == "512": out = self.aurora512_final() elif self.mode == "256m": out = self.aurora256m_final() else: raise ValueError("invalid mode") out = self.truncate_digest(out) return out def truncate_digest(self, out): return out[:self.digest_size] def aurora_one_round(self, inout, cirmat0, cirmat1): # codespell:ignore x = [0x00] * 32 self.aurora_bd(x, inout) # codespell:ignore self.aurora_f_xor(inout, 0, x, 0, cirmat0) # codespell:ignore self.aurora_f_xor(inout, 8, x, 8, cirmat1) # codespell:ignore self.aurora_f_xor(inout, 16, x, 16, cirmat0) # codespell:ignore self.aurora_f_xor(inout, 24, x, 24, cirmat1) # codespell:ignore return def aurora_con_update(self, con, iv, mask): con[0] = (iv[0] ^ mask[0]) & 0xff con[1] = (iv[1] ^ mask[1]) & 0xff con[2] = (~iv[1]) & 0xff con[3] = (~iv[0]) & 0xff con[4] = (iv[2] ^ mask[2]) & 0xff con[5] = (iv[3] ^ mask[3]) & 0xff con[6] = (~iv[3]) & 0xff con[7] = (~iv[2]) & 0xff con[8] = iv[1] & 0xff con[9] = iv[0] & 0xff con[10] = (iv[0] ^ mask[4]) & 0xff con[11] = (iv[1] ^ mask[5]) & 0xff con[12] = ((iv[2] >> 7) | (iv[3] << 1)) & 0xff con[13] = ((iv[3] >> 7) | (iv[2] << 1)) & 0xff con[14] = (iv[2] ^ mask[6]) & 0xff con[15] = (iv[3] ^ mask[7]) & 0xff if iv[0] & 0x80: iv[0] = (iv[0] ^ 0x54) & 0xff iv[1] = (iv[1] ^ 0x18) & 0xff tmp = (iv[0] >> 7) & 0xff iv0 = iv[0] iv1 = iv[1] iv[0] = ((iv0 << 1) | (iv1 >> 7)) & 0xff iv[1] = ((iv1 << 1) | tmp) & 0xff if iv[3] & 0x01: iv[2] = (iv[2] ^ 0xa8) & 0xff iv[3] = (iv[3] ^ 0x30) & 0xff tmp = ((iv[2] << 7) & 0xff) iv2 = iv[2] iv3 = iv[3] iv[2] = ((iv2 >> 1) | (iv3 << 7)) & 0xff iv[3] = ((iv3 >> 1) | tmp) & 0xff return def aurora_rotl_con_xor(self, inout, con, mask, rotval): # codespell:ignore inout[4] = (inout[4] ^ ((con[0] << rotval) ^ (con[1] >> (8 - rotval)))) & 0xff # codespell:ignore inout[5] = (inout[5] ^ ((con[1] << rotval) ^ (con[2] >> (8 - rotval)))) & 0xff # codespell:ignore inout[6] = (inout[6] ^ ((con[2] << rotval) ^ (con[3] >> (8 - rotval)))) & 0xff # codespell:ignore inout[7] = (inout[7] ^ ((con[3] << rotval) ^ (con[0] >> (8 - rotval)))) & 0xff # codespell:ignore inout[12] = (inout[12] ^ ((con[4] << rotval) ^ (con[5] >> (8 - rotval)))) & 0xff # codespell:ignore inout[13] = (inout[13] ^ ((con[5] << rotval) ^ (con[6] >> (8 - rotval)))) & 0xff # codespell:ignore inout[14] = (inout[14] ^ ((con[6] << rotval) ^ (con[7] >> (8 - rotval)))) & 0xff # codespell:ignore inout[15] = (inout[15] ^ ((con[7] << rotval) ^ (con[4] >> (8 - rotval)))) & 0xff # codespell:ignore inout[20] = (inout[20] ^ ((con[8] << rotval) ^ (con[9] >> (8 - rotval)))) & 0xff # codespell:ignore inout[21] = (inout[21] ^ ((con[9] << rotval) ^ (con[10] >> (8 - rotval)))) & 0xff # codespell:ignore inout[22] = (inout[22] ^ ((con[10] << rotval) ^ (con[11] >> (8 - rotval)))) & 0xff # codespell:ignore inout[23] = (inout[23] ^ ((con[11] << rotval) ^ (con[8] >> (8 - rotval)))) & 0xff # codespell:ignore inout[28] = (inout[28] ^ ((con[12] << rotval) ^ (con[13] >> (8 - rotval)) ^ mask)) & 0xff # codespell:ignore inout[29] = (inout[29] ^ ((con[13] << rotval) ^ (con[14] >> (8 - rotval)) ^ mask)) & 0xff # codespell:ignore inout[30] = (inout[30] ^ ((con[14] << rotval) ^ (con[15] >> (8 - rotval)) ^ mask)) & 0xff # codespell:ignore inout[31] = (inout[31] ^ ((con[15] << rotval) ^ (con[12] >> (8 - rotval)) ^ mask)) & 0xff # codespell:ignore return def aurora_rotr_con_xor(self, inout, con, mask, rotval): # codespell:ignore inout[4] = (inout[4] ^ ((con[0] >> rotval) ^ (con[3] << (8 - rotval)))) & 0xff # codespell:ignore inout[5] = (inout[5] ^ ((con[1] >> rotval) ^ (con[0] << (8 - rotval)))) & 0xff # codespell:ignore inout[6] = (inout[6] ^ ((con[2] >> rotval) ^ (con[1] << (8 - rotval)))) & 0xff # codespell:ignore inout[7] = (inout[7] ^ ((con[3] >> rotval) ^ (con[2] << (8 - rotval)))) & 0xff # codespell:ignore inout[12] = (inout[12] ^ ((con[4] >> rotval) ^ (con[7] << (8 - rotval)))) & 0xff # codespell:ignore inout[13] = (inout[13] ^ ((con[5] >> rotval) ^ (con[4] << (8 - rotval)))) & 0xff # codespell:ignore inout[14] = (inout[14] ^ ((con[6] >> rotval) ^ (con[5] << (8 - rotval)))) & 0xff # codespell:ignore inout[15] = (inout[15] ^ ((con[7] >> rotval) ^ (con[6] << (8 - rotval)))) & 0xff # codespell:ignore inout[20] = (inout[20] ^ ((con[8] >> rotval) ^ (con[11] << (8 - rotval)))) & 0xff # codespell:ignore inout[21] = (inout[21] ^ ((con[9] >> rotval) ^ (con[8] << (8 - rotval)))) & 0xff # codespell:ignore inout[22] = (inout[22] ^ ((con[10] >> rotval) ^ (con[9] << (8 - rotval)))) & 0xff # codespell:ignore inout[23] = (inout[23] ^ ((con[11] >> rotval) ^ (con[10] << (8 - rotval)))) & 0xff # codespell:ignore inout[28] = (inout[28] ^ ((con[12] >> rotval) ^ (con[15] << (8 - rotval)) ^ mask)) & 0xff # codespell:ignore inout[29] = (inout[29] ^ ((con[13] >> rotval) ^ (con[12] << (8 - rotval)) ^ mask)) & 0xff # codespell:ignore inout[30] = (inout[30] ^ ((con[14] >> rotval) ^ (con[13] << (8 - rotval)) ^ mask)) & 0xff # codespell:ignore inout[31] = (inout[31] ^ ((con[15] >> rotval) ^ (con[14] << (8 - rotval)) ^ mask)) & 0xff # codespell:ignore return def aurora_protl_xor(self, dst, x): z = [0x00] * 32 self.byte_cpy(z, 0, x, 0, 4) self.byte_cpy(z, 8, x, 8, 4) self.byte_cpy(z, 16, x, 16, 16) z[4] = ((x[4] << 1) | (x[5] >> 7)) & 0xff z[5] = ((x[5] << 1) | (x[6] >> 7)) & 0xff z[6] = ((x[6] << 1) | (x[7] >> 7)) & 0xff z[7] = ((x[7] << 1) | (x[12] >> 7)) & 0xff z[12] = ((x[12] << 1) | (x[13] >> 7)) & 0xff z[13] = ((x[13] << 1) | (x[14] >> 7)) & 0xff z[14] = ((x[14] << 1) | (x[15] >> 7)) & 0xff z[15] = ((x[15] << 1) | (x[4] >> 7)) & 0xff self.byte_xor(dst, 0, z, 0, 32) return def aurora_protr_xor(self, dst, y): z = [0x00] * 32 self.byte_cpy(z, 0, y, 0, 4) self.byte_cpy(z, 8, y, 8, 4) self.byte_cpy(z, 16, y, 16, 16) z[4] = ((y[4] >> 1) | (y[15] << 7)) & 0xff z[5] = ((y[5] >> 1) | (y[4] << 7)) & 0xff z[6] = ((y[6] >> 1) | (y[5] << 7)) & 0xff z[7] = ((y[7] >> 1) | (y[6] << 7)) & 0xff z[12] = ((y[12] >> 1) | (y[7] << 7)) & 0xff z[13] = ((y[13] >> 1) | (y[12] << 7)) & 0xff z[14] = ((y[14] >> 1) | (y[13] << 7)) & 0xff z[15] = ((y[15] >> 1) | (y[14] << 7)) & 0xff self.byte_xor(dst, 0, z, 0, 32) return def aurora256_cf(self, cv, m, mask, con_iv, con_mask): ml = [0x00] * 32 mr = [0x00] * 32 x = [0x00] * 32 t = [0x00] * 4 con = [0x00] * 16 self.byte_cpy(ml, 0, m, 0, 32) self.byte_cpy(mr, 0, m, 32, 32) self.byte_cpy(x, 0, cv, 0, 32) self.byte_cpy(t, 0, con_iv, 0, 4) r = 0 while r < 17: self.aurora_con_update(con, t, con_mask) if (r % 2) == 0: if r < 16: self.aurora_rotl_con_xor(ml, con, 0x00, 1) self.aurora_protl_xor(x, ml) self.aurora_one_round(ml, self.mat0, self.mat1) else: self.aurora_rotr_con_xor(mr, con, 0x00, 1) self.aurora_protr_xor(x, mr) self.aurora_one_round(mr, self.mat2, self.mat3) self.aurora_rotl_con_xor(x, con, mask, 0) self.aurora_one_round(x, self.mat1, self.mat0) r += 1 self.aurora_protr_xor(x, mr) self.byte_xor(cv, 0, x, 0, 32) return def aurora512_cf(self, cv, m, mask, con_iv, con_mask): ml = [0x00] * 32 mr = [0x00] * 32 xl = [0x00] * 32 xr = [0x00] * 32 t = [0x00] * 4 con = [0x00] * 16 self.byte_cpy(ml, 0, m, 0, 32) self.byte_cpy(mr, 0, m, 32, 32) self.byte_cpy(xl, 0, cv, 0, 32) self.byte_cpy(xr, 0, cv, 32, 32) self.byte_cpy(t, 0, con_iv, 0, 4) r = 0 while r < 17: self.aurora_con_update(con, t, con_mask) if (r % 2) == 0: if r < 16: self.aurora_rotl_con_xor(ml, con, 0x00, 1) self.aurora_protl_xor(xl, ml) self.aurora_protl_xor(xr, ml) self.aurora_one_round(ml, self.mat0, self.mat1) else: self.aurora_rotr_con_xor(mr, con, 0x00, 1) self.aurora_protr_xor(xl, mr) self.aurora_protr_xor(xr, mr) self.aurora_one_round(mr, self.mat2, self.mat3) self.aurora_rotl_con_xor(xl, con, mask, 0) self.aurora_one_round(xl, self.mat1, self.mat0) self.aurora_rotl_con_xor(xr, con, mask, 3) self.aurora_one_round(xr, self.mat3, self.mat2) r += 1 self.aurora_protr_xor(xl, mr) self.aurora_protr_xor(xr, mr) self.byte_xor(cv, 0, xl, 0, 32) self.byte_xor(cv, 32, xr, 0, 32) return def aurora512_cfmf(self): if self.blk_idx >= 0x08: self.aurora512_cf(self.h, self.h, self.blk_idx, self.con_iv, self.con_mask) self.blk_idx = 0 self.aurora512_cf(self.h, self.buf, self.blk_idx, self.con_iv, self.con_mask) self.blk_idx = (self.blk_idx + 1) & 0xff return def aurora256_mff(self, cv): mr = [0x00] * 32 x = [0x00] * 32 t = [0x3c, 0x6e, 0xa5, 0x4f] con_mask = [0xb5, 0xc0, 0xe9, 0xb5, 0x61, 0x35, 0x79, 0xcc] con = [0x00] * 16 self.byte_cpy(mr, 0, cv, 32, 32) self.byte_cpy(x, 0, cv, 0, 32) r = 0 while r < 17: self.aurora_con_update(con, t, con_mask) if (r % 2) == 1: self.aurora_rotr_con_xor(mr, con, 0x00, 1) self.aurora_protr_xor(x, mr) self.aurora_one_round(mr, self.mat2, self.mat3) self.aurora_rotl_con_xor(x, con, 0x09, 0) self.aurora_one_round(x, self.mat1, self.mat0) r += 1 self.aurora_protr_xor(x, mr) self.byte_xor(cv, 0, x, 0, 32) return def add_blk(self): i = 7 while i >= 0: self.blk_num[i] = (self.blk_num[i] + 1) & 0xff if self.blk_num[i] != 0: break i -= 1 return def aurora256_update(self, msg): if self.cnt & 0x07: raise ValueError("bit-aligned state is not supported") if (self.cnt < 0) or (self.cnt >= 512): raise ValueError("invalid internal state") byte_cnt = self.cnt // 8 msg_pos = 0 msg_len = len(msg) while msg_pos < msg_len: self.buf[byte_cnt] = msg[msg_pos] & 0xff byte_cnt += 1 msg_pos += 1 self.cnt += 8 if byte_cnt >= 64: self.aurora256_cf(self.h, self.buf, 0x00, self.con_iv, self.con_mask) self.add_blk() byte_cnt = 0 self.cnt = 0 return def aurora512_update(self, msg): if self.cnt & 0x07: raise ValueError("bit-aligned state is not supported") if (self.cnt < 0) or (self.cnt >= 512): raise ValueError("invalid internal state") byte_cnt = self.cnt // 8 msg_pos = 0 msg_len = len(msg) while msg_pos < msg_len: self.buf[byte_cnt] = msg[msg_pos] & 0xff byte_cnt += 1 msg_pos += 1 self.cnt += 8 if byte_cnt >= 64: self.aurora512_cfmf() self.add_blk() byte_cnt = 0 self.cnt = 0 return def aurora256_final(self): byte_cnt = self.cnt // 8 if (self.cnt < 0) or (self.cnt >= 512): return bytes([0x00] * 32) if self.cnt != 0: self.add_blk() if self.cnt & 0x07: raise ValueError("bit-aligned state is not supported") self.buf[byte_cnt] = 0x80 byte_cnt += 1 if self.cnt > 447: while byte_cnt < 64: self.buf[byte_cnt] = 0x00 byte_cnt += 1 byte_cnt = 0 self.aurora256_cf(self.h, self.buf, 0x00, self.con_iv, self.con_mask) while byte_cnt < 56: self.buf[byte_cnt] = 0x00 byte_cnt += 1 self.byte_cpy(self.buf, 56, self.blk_num, 0, 8) self.aurora256_cf(self.h, self.buf, 0x01, self.con_iv, self.con_mask) return bytes(self.h[:32]) def aurora512_final(self): byte_cnt = self.cnt // 8 if (self.cnt < 0) or (self.cnt >= 512): return bytes([0x00] * 64) if self.cnt != 0: self.add_blk() if self.cnt & 0x07: raise ValueError("bit-aligned state is not supported") self.buf[byte_cnt] = 0x80 byte_cnt += 1 if self.cnt > 447: while byte_cnt < 64: self.buf[byte_cnt] = 0x00 byte_cnt += 1 byte_cnt = 0 self.aurora512_cfmf() while byte_cnt < 56: self.buf[byte_cnt] = 0x00 byte_cnt += 1 self.byte_cpy(self.buf, 56, self.blk_num, 0, 8) self.aurora512_cfmf() self.aurora512_cf(self.h, self.h, 0x09, self.con_iv, self.con_mask) return bytes(self.h[:64]) def aurora256m_final(self): byte_cnt = self.cnt // 8 if (self.cnt < 0) or (self.cnt >= 512): return bytes([0x00] * 32) if self.cnt != 0: self.add_blk() if self.cnt & 0x07: raise ValueError("bit-aligned state is not supported") self.buf[byte_cnt] = 0x80 byte_cnt += 1 if self.cnt > 447: while byte_cnt < 64: self.buf[byte_cnt] = 0x00 byte_cnt += 1 byte_cnt = 0 self.aurora512_cfmf() while byte_cnt < 56: self.buf[byte_cnt] = 0x00 byte_cnt += 1 self.byte_cpy(self.buf, 56, self.blk_num, 0, 8) self.aurora512_cfmf() self.aurora256_mff(self.h) return bytes(self.h[:32]) class AURORA256(AURORABase): digest_size = 32 init_fill = 0x00 con_iv = [0x6a, 0x09, 0xbb, 0x67] con_mask = [0x42, 0x8a, 0x71, 0x37, 0x26, 0x11, 0x3e, 0xe8] mode = "256" class AURORA224(AURORA256): digest_size = 28 init_fill = 0xff def truncate_digest(self, out): dst = bytearray() i = 0 while i < 4: base = i * 8 j = 0 while j < 7: dst.append(out[base + j]) j += 1 i += 1 return bytes(dst) class AURORA512(AURORABase): digest_size = 64 init_fill = 0x00 con_iv = [0x51, 0x0e, 0x9b, 0x05] con_mask = [0x39, 0x56, 0x59, 0xf1, 0x9d, 0x8a, 0xab, 0x97] mode = "512" class AURORA384(AURORA512): digest_size = 48 init_fill = 0xff def truncate_digest(self, out): dst = bytearray() i = 0 while i < 8: base = i * 8 j = 0 while j < 6: dst.append(out[base + j]) j += 1 i += 1 return bytes(dst) class AURORA256M(AURORABase): digest_size = 32 init_fill = 0x00 con_iv = [0x3c, 0x6e, 0xa5, 0x4f] con_mask = [0xb5, 0xc0, 0xe9, 0xb5, 0x61, 0x35, 0x79, 0xcc] mode = "256m" class AURORA224M(AURORA256M): digest_size = 28 init_fill = 0xff def truncate_digest(self, out): dst = bytearray() i = 0 while i < 4: base = i * 8 j = 0 while j < 7: dst.append(out[base + j]) j += 1 i += 1 return bytes(dst) class BlenderBase: block_size = 0 digest_size = 0 word_bits = 0 word_count = 0 iv_words = () first_block_size = 13 def __init__(self, data=b""): self.word_bytes = self.word_bits // 8 self.mask = (1 << self.word_bits) - 1 self.word = list(self.iv_words) self.sum_words = [0] * self.word_count self.checksum1 = 0 self.checksum2 = 0 self.carry1 = 0 self.carry2 = 0 self.rounds_to_go = 32 self.block_size_frags = 16 self.buf = bytearray() self.first_block = bytearray() self.msg_bits = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.word = list(self.word) other.sum_words = list(self.sum_words) other.checksum1 = self.checksum1 other.checksum2 = self.checksum2 other.carry1 = self.carry1 other.carry2 = self.carry2 other.rounds_to_go = self.rounds_to_go other.block_size_frags = self.block_size_frags other.buf = bytearray(self.buf) other.first_block = bytearray(self.first_block) other.msg_bits = self.msg_bits return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) if not data: return self self.msg_bits += len(data) * 8 if len(self.first_block) < self.first_block_size: need = self.first_block_size - len(self.first_block) self.first_block.extend(data[:need]) self.buf.extend(data) frag_bytes = self.word_bytes whole = (len(self.buf) // frag_bytes) * frag_bytes if whole > 0: block = bytes(self.buf[:whole]) del self.buf[:whole] self.compress_fragments(block) return self def digest(self): c = self.copy() c.finalize() out = bytearray() for value in c.sum_words: out.extend(c.word_to_big_endian_bytes(value)) return bytes(out) def hexdigest(self): return self.digest().hex() def finalize(self): len_bytes = self.encode_length_le(self.msg_bits) residue = bytearray(self.buf) space_left = (self.rounds_to_go - 2) * self.word_bytes space_needed = len(len_bytes) + 2 + len(residue) while space_left < space_needed: space_left += self.block_size_frags * self.word_bytes fill_size = space_left + 1 - space_needed fill_source = bytearray(self.first_block) if len(fill_source) < self.first_block_size and residue: need = self.first_block_size - len(fill_source) fill_source.extend(residue[:need]) if not fill_source: fill_source = bytearray(b"\x00" * self.first_block_size) work = bytearray(residue) if fill_size > 0: reps = (fill_size + len(fill_source) - 1) // len(fill_source) work.extend((fill_source * reps)[:fill_size]) work.extend(len_bytes) work.append(len(len_bytes)) if len(work) % self.word_bytes != 0: raise ValueError("internal error: pre-checksum tail is not fragment-aligned") if work: self.compress_fragments(bytes(work)) chk = bytearray() chk.extend(self.word_to_little_endian_bytes((~self.checksum1) & self.mask)) chk.extend(self.word_to_little_endian_bytes(self.checksum2)) self.compress_fragments(bytes(chk)) return def compress_fragments(self, data): if not data: return if len(data) % self.word_bytes != 0: raise ValueError("fragment data must be aligned") pos = 0 while pos < len(data): fragment = self.bytes_to_little_endian_word(data[pos:pos + self.word_bytes]) pos += self.word_bytes self.checksum1 = (self.checksum1 + fragment) & self.mask self.checksum2 = (self.checksum2 + ((~fragment) & self.mask)) & self.mask even_left = self.ror(fragment, 8) ^ self.word[0] even_right = self.ror(self.word[2], 8) ^ self.word[4] self.carry2, t2 = self.add_with_carry(even_left, even_right, self.carry2) odd_left = fragment ^ self.word[5] odd_right = self.rol(self.word[3], 8) ^ self.word[1] self.carry1, t1 = self.add_with_carry(odd_left, odd_right, self.carry1) rotate_bits = 8 - (self.carry1 + self.carry2) t1 = self.rol(t1, rotate_bits) t2 = self.ror(t2, rotate_bits) temp = self.ror(self.word[0], 7) old = list(self.word) self.word[0] = old[1] ^ t2 self.word[1] = old[2] ^ t1 self.word[2] = old[3] ^ t2 self.word[3] = old[4] ^ t1 self.word[4] = old[5] ^ t2 if self.word_count == 6: self.word[5] = temp ^ t1 elif self.word_count == 7: self.word[5] = old[6] ^ t1 self.word[6] = temp ^ t2 elif self.word_count == 8: self.word[5] = old[6] ^ t1 self.word[6] = old[7] ^ t2 self.word[7] = temp ^ t1 else: raise ValueError("unsupported word_count") for i in range(self.word_count): self.sum_words[i] = (self.sum_words[i] + self.word[i]) & self.mask self.rounds_to_go -= 1 if self.rounds_to_go == 0: self.rounds_to_go = self.block_size_frags return def add_with_carry(self, x, y, carry_in): total = x + y + carry_in carry_out = 0 if total > self.mask: carry_out = 1 result = total & self.mask return carry_out, result def rol(self, value, count): value &= self.mask count %= self.word_bits if count == 0: return value return ((value << count) | (value >> (self.word_bits - count))) & self.mask def ror(self, value, count): value &= self.mask count %= self.word_bits if count == 0: return value return ((value >> count) | (value << (self.word_bits - count))) & self.mask def encode_length_le(self, value): if value == 0: return b"\x00" out = bytearray() while value > 0: out.append(value & 0xff) value >>= 8 return bytes(out) def bytes_to_little_endian_word(self, chunk): value = 0 shift = 0 for b in chunk: value |= b << shift shift += 8 return value def word_to_little_endian_bytes(self, value): out = bytearray() for i in range(self.word_bytes): out.append((value >> (8 * i)) & 0xff) return bytes(out) def word_to_big_endian_bytes(self, value): out = bytearray() for i in range(self.word_bytes - 1, -1, -1): out.append((value >> (8 * i)) & 0xff) return bytes(out) class Blender224(BlenderBase): block_size = 64 digest_size = 28 word_bits = 32 word_count = 7 iv_words = [ 0xc105_9ed8, 0x367c_d507, 0x3070_dd17, 0xf70e_5939, 0xffc0_0b31, 0x6858_1511, 0x64f9_8fa7, ] class Blender256(BlenderBase): block_size = 64 digest_size = 32 word_bits = 32 word_count = 8 iv_words = [ 0x6a09_e667, 0xbb67_ae85, 0x3c6e_f372, 0xa54f_f53a, 0x510e_527f, 0x9b05_688c, 0x1f83_d9ab, 0x5be0_cd19, ] class Blender384(BlenderBase): # KAT-compatible version of Reference Implementation / BlenderUpdate.zip # The sixth word is set to 0 to accommodate V384 initialization. block_size = 128 digest_size = 48 word_bits = 64 word_count = 6 iv_words = [ 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31, 0x0000_0000_0000_0000, ] class Blender384Spec(BlenderBase): # Version using SHA-384-derived initial values ​​as per the specification block_size = 128 digest_size = 48 word_bits = 64 word_count = 6 iv_words = [ 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511, ] class Blender512(BlenderBase): block_size = 128 digest_size = 64 word_bits = 64 word_count = 8 iv_words = [ 0x6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1, 0x510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179, ] class BOOLEBase: word_size = 64 block_size = 8 state_words = 16 initsum = 0x6996_c53a mask = 0xffff_ffff_ffff_ffff def __init__(self, data=b"", digest_bits=None): if digest_bits is None: if not hasattr(self, "default_digest_bits"): raise ValueError("digest_bits is required") digest_bits = self.default_digest_bits if digest_bits <= 0 or digest_bits > (8 * self.word_size): raise ValueError("invalid digest_bits") if (digest_bits % 8) != 0: raise ValueError("digest_bits must be byte-aligned") self.digest_bits = int(digest_bits) self.digest_size = self.digest_bits // 8 self.hashbitlen = self.digest_bits self.r = [0] * self.state_words self.nbits_total = 0 self.xsum = 0 self.lsum = 0 self.rsum = 0 self.buf = bytearray() self.msg_len = 0 self.init_state() if data: self.update(data) return def copy(self): other = self.__class__(digest_bits=self.digest_bits) other.r = list(self.r) other.nbits_total = self.nbits_total other.xsum = self.xsum other.lsum = self.lsum other.rsum = self.rsum other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.nbits_total = (self.nbits_total + (len(data) * 8)) & self.mask self.buf.extend(data) while len(self.buf) >= self.block_size: word = int.from_bytes(self.buf[:self.block_size], "little") del self.buf[:self.block_size] self.data_cycle(word) return self def digest(self): c = self.copy() c.finalize() return c.generate() def hexdigest(self): return self.digest().hex() def finalize(self): self.finish() self.finish() return def rol(self, value, count): value &= self.mask return ((value << count) | (value >> (self.word_size - count))) & self.mask def ror(self, value, count): value &= self.mask return ((value >> count) | (value << (self.word_size - count))) & self.mask def sbox1(self, value): value &= self.mask value ^= self.initsum value ^= self.rol(value, 34) | self.rol(value, 42) value ^= self.rol(value, 20) | self.rol(value, 55) value ^= (value << 3) | self.rol(value, 60) return value & self.mask def sbox2(self, value): value &= self.mask value ^= self.initsum value ^= self.ror(value, 35) | self.ror(value, 46) value ^= self.ror(value, 27) | self.ror(value, 52) value ^= (value >> 5) | self.ror(value, 55) return value & self.mask def soft_reset(self): self.nbits_total = 0 self.xsum = 0 self.lsum = self.initsum self.rsum = self.rol(self.initsum, 8) return def init_state(self): self.r[0] = self.sbox1(1) i = 1 while i < self.state_words: self.r[i] = self.sbox1(self.r[i - 1]) i += 1 self.soft_reset() return def cycle(self): t = self.r[12] ^ self.r[13] t = self.sbox1(t) ^ self.rol(self.r[0], 1) i = 1 while i < self.state_words: self.r[i - 1] = self.r[i] i += 1 self.r[self.state_words - 1] = t t = self.sbox2(self.r[2] ^ self.r[15]) self.r[0] ^= t return def data_cycle(self, word): self.xsum ^= word self.lsum = self.sbox1(self.lsum) ^ word self.rsum ^= self.lsum self.lsum = self.rol(self.lsum, 1) self.rsum = self.ror(self.rsum, 1) self.r[3] ^= self.lsum self.r[13] ^= self.rsum self.cycle() return def stream_cycle(self): self.cycle() return self.r[0] ^ self.r[8] ^ self.r[12] def diffuse(self): i = 0 while i < self.state_words: self.cycle() i += 1 return def finish(self): if len(self.buf) != 0: # Absorb remaining bytes as a zero-padded little-endian word. tail = bytes(self.buf) word = int.from_bytes(tail.ljust(self.block_size, b"\x00"), "little") self.data_cycle(word) self.buf.clear() # For 64-bit DataLength, only R[0] is xored with the message bit length. self.r[0] ^= self.nbits_total self.r[4] ^= self.hashbitlen i = 4 while i < self.state_words: self.r[i] ^= self.lsum self.r[i + 1] ^= self.xsum self.r[i + 2] ^= self.rsum i += 3 self.diffuse() return def generate(self): out = bytearray() while len(out) < self.digest_size: word = self.stream_cycle() out.extend(word.to_bytes(self.block_size, "little")) return bytes(out[:self.digest_size]) class BOOLE224(BOOLEBase): default_digest_bits = 224 digest_size = 28 class BOOLE256(BOOLEBase): default_digest_bits = 256 digest_size = 32 class BOOLE384(BOOLEBase): default_digest_bits = 384 digest_size = 48 class BOOLE512(BOOLEBase): default_digest_bits = 512 digest_size = 64 class CheetahBase: block_size = 128 reference_stride_bytes = 1 shift_rows4c = [0, 1, 3, 4] shift_rows8c = [0, 1, 2, 3, 4, 5, 6, 7] shift_rows8c_message = [0, 1, 2, 3, 5, 6, 7, 8] mc8 = [ [0x02, 0x0c, 0x06, 0x08, 0x01, 0x04, 0x01, 0x01], [0x01, 0x02, 0x0c, 0x06, 0x08, 0x01, 0x04, 0x01], [0x01, 0x01, 0x02, 0x0c, 0x06, 0x08, 0x01, 0x04], [0x04, 0x01, 0x01, 0x02, 0x0c, 0x06, 0x08, 0x01], [0x01, 0x04, 0x01, 0x01, 0x02, 0x0c, 0x06, 0x08], [0x08, 0x01, 0x04, 0x01, 0x01, 0x02, 0x0c, 0x06], [0x06, 0x08, 0x01, 0x04, 0x01, 0x01, 0x02, 0x0c], [0x0c, 0x06, 0x08, 0x01, 0x04, 0x01, 0x01, 0x02], ] mc64 = [ [0x01, 0x04, 0x01, 0x01, 0x02, 0x0c, 0x06, 0x08], [0x08, 0x01, 0x04, 0x01, 0x01, 0x02, 0x0c, 0x06], [0x06, 0x08, 0x01, 0x04, 0x01, 0x01, 0x02, 0x0c], [0x0c, 0x06, 0x08, 0x01, 0x04, 0x01, 0x01, 0x02], [0x02, 0x0c, 0x06, 0x08, 0x01, 0x04, 0x01, 0x01], [0x01, 0x02, 0x0c, 0x06, 0x08, 0x01, 0x04, 0x01], [0x01, 0x01, 0x02, 0x0c, 0x06, 0x08, 0x01, 0x04], [0x04, 0x01, 0x01, 0x02, 0x0c, 0x06, 0x08, 0x01], ] sbox = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ] mul_identity = list(range(256)) mul_tables = None def __init__(self, data=b""): self.data = bytearray() self.ensure_mul_tables() if data: self.update(data) return @classmethod def ensure_mul_tables(cls): if cls.mul_tables is not None: return mul2 = [0] * 256 mul3 = [0] * 256 mul4 = [0] * 256 mul6 = [0] * 256 mul8 = [0] * 256 mul12 = [0] * 256 for x in range(256): x2 = ((x << 1) & 0xff) ^ (0x1b if (x & 0x80) else 0x00) x4 = ((x2 << 1) & 0xff) ^ (0x1b if (x2 & 0x80) else 0x00) x8 = ((x4 << 1) & 0xff) ^ (0x1b if (x4 & 0x80) else 0x00) mul2[x] = x2 mul3[x] = x2 ^ x mul4[x] = x4 mul6[x] = x4 ^ x2 mul8[x] = x8 mul12[x] = x8 ^ x4 cls.mul2 = mul2 cls.mul3 = mul3 cls.mul4 = mul4 cls.mul6 = mul6 cls.mul8 = mul8 cls.mul12 = mul12 cls.mul_tables = { 0x01: cls.mul_identity, 0x02: cls.mul2, 0x03: cls.mul3, 0x04: cls.mul4, 0x06: cls.mul6, 0x08: cls.mul8, 0x0c: cls.mul12, } return def copy(self): other = self.__class__() other.data = bytearray(self.data) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.data.extend(data) return self def digest(self): c = self.copy() return c.finalize() def hexdigest(self): return self.digest().hex() def xtime(self, a): a = ((a << 1) & 0xff) ^ (0x1b if (a & 0x80) else 0x00) return a def sub_bytes(self, a): sbox = self.sbox for row in a: for j in range(8): row[j] = sbox[row[j]] return def sub_bytes_m(self, message_block): sbox = self.sbox for row in message_block: for j in range(16): row[j] = sbox[row[j]] return def shift_rows4(self, a): shifts = self.shift_rows4c for i in range(4): row = a[i] shift = shifts[i] a[i] = row[shift:] + row[:shift] return def shift_rows64(self, a): shifts = self.shift_rows8c for i in range(8): row = a[i] shift = shifts[i] a[i] = row[shift:] + row[:shift] return def shift_rows8_message(self, message_block): shifts = self.shift_rows8c_message for i in range(8): row = message_block[i] shift = shifts[i] a = row[shift:] b = row[:shift] message_block[i] = a + b return def mix_column4(self, a): mul2 = self.mul2 mul3 = self.mul3 r0 = a[0] r1 = a[1] r2 = a[2] r3 = a[3] t0 = [0] * 8 t1 = [0] * 8 t2 = [0] * 8 t3 = [0] * 8 for j in range(8): x0 = r0[j] x1 = r1[j] x2 = r2[j] x3 = r3[j] t0[j] = mul2[x0] ^ mul3[x1] ^ x2 ^ x3 t1[j] = x0 ^ mul2[x1] ^ mul3[x2] ^ x3 t2[j] = x0 ^ x1 ^ mul2[x2] ^ mul3[x3] t3[j] = mul3[x0] ^ x1 ^ x2 ^ mul2[x3] a[0] = t0 a[1] = t1 a[2] = t2 a[3] = t3 return def mix_column64(self, a): mc = self.mc64 mul_tables = self.mul_tables temp = [[0] * 8 for _ in range(8)] for i in range(8): c0, c1, c2, c3, c4, c5, c6, c7 = mc[i] t0 = mul_tables[c0] t1 = mul_tables[c1] t2 = mul_tables[c2] t3 = mul_tables[c3] t4 = mul_tables[c4] t5 = mul_tables[c5] t6 = mul_tables[c6] t7 = mul_tables[c7] out = temp[i] for j in range(8): out[j] = ( t0[a[0][j]] ^ t1[a[1][j]] ^ t2[a[2][j]] ^ t3[a[3][j]] ^ t4[a[4][j]] ^ t5[a[5][j]] ^ t6[a[6][j]] ^ t7[a[7][j]] ) for i in range(8): a[i] = temp[i] return def mix_column8_message(self, message_block): mc = self.mc8 mul_tables = self.mul_tables temp = [[0] * 16 for _ in range(8)] for i in range(8): c0, c1, c2, c3, c4, c5, c6, c7 = mc[i] t0 = mul_tables[c0] t1 = mul_tables[c1] t2 = mul_tables[c2] t3 = mul_tables[c3] t4 = mul_tables[c4] t5 = mul_tables[c5] t6 = mul_tables[c6] t7 = mul_tables[c7] out = temp[i] for j in range(16): out[j] = ( t0[message_block[0][j]] ^ t1[message_block[1][j]] ^ t2[message_block[2][j]] ^ t3[message_block[3][j]] ^ t4[message_block[4][j]] ^ t5[message_block[5][j]] ^ t6[message_block[6][j]] ^ t7[message_block[7][j]] ) for i in range(8): message_block[i] = temp[i] return def add_round_constant(self, message_block, r): sbox = self.sbox base = 4 * r message_block[0][0] ^= sbox[base + 0] message_block[1][0] ^= sbox[base + 1] message_block[2][0] ^= sbox[base + 2] message_block[3][0] ^= sbox[base + 3] return def internal_round4(self, a): self.sub_bytes(a) self.shift_rows4(a) self.mix_column4(a) return def internal_round64(self, a): self.sub_bytes(a) self.shift_rows64(a) self.mix_column64(a) return def message_expansion(self, block, rounds): message_block = [[0] * 16 for _ in range(8)] expanded_block = [[0] * 16 for _ in range((rounds + 1) * 8)] for j in range(16): base = 8 * j for i in range(8): b = block[base + 7 - i] message_block[i][j] = b expanded_block[i][j] = b for k in range(1, rounds + 1): self.sub_bytes_m(message_block) self.shift_rows8_message(message_block) self.mix_column8_message(message_block) self.add_round_constant(message_block, k) dst = k * 8 for i in range(8): expanded_block[dst + i] = message_block[i][:] return expanded_block def compress32(self, a, block, block_counter): temp = [row[:] for row in a] for i in range(4): a[i][0] ^= (block_counter >> (56 - i * 8)) & 0xff a[i][1] ^= (block_counter >> (24 - i * 8)) & 0xff expanded_block = self.message_expansion(block, 3) for k in range(16): src_row = (k // 2) * 4 src_col = (k % 2) * 8 for i in range(4): row = a[i] exp = expanded_block[src_row + i] for j in range(8): row[j] ^= exp[src_col + j] self.internal_round4(a) for i in range(4): a[i][0] ^= (block_counter >> (56 - i * 8)) & 0xff a[i][1] ^= (block_counter >> (24 - i * 8)) & 0xff for i in range(4): row = a[i] old = temp[i] for j in range(8): row[j] ^= old[j] return def compress64(self, a, block, block_counter): temp = [row[:] for row in a] for i in range(8): a[i][0] ^= (block_counter >> (56 - i * 8)) & 0xff expanded_block = self.message_expansion(block, 5) for k in range(12): src_row = (k // 2) * 8 src_col = (k % 2) * 8 for i in range(8): row = a[i] exp = expanded_block[src_row + i] for j in range(8): row[j] ^= exp[src_col + j] self.internal_round64(a) for i in range(8): a[i][0] ^= (block_counter >> (56 - i * 8)) & 0xff for i in range(8): row = a[i] old = temp[i] for j in range(8): row[j] ^= old[j] return def last_block_permutation(self, a): for i in range(self.n_rows): row = a[i] row[0], row[4] = row[4], row[0] row[1], row[5] = row[5], row[1] row[2], row[6] = row[6], row[2] row[3], row[7] = row[7], row[3] return def set_length_and_digest(self, block, databitlen): for i in range(1, 9): block[self.block_size - 11 + i] = (databitlen >> (64 - 8 * i)) & 0xff block[self.block_size - 2] = (self.hashbitlen >> 8) & 0xff block[self.block_size - 1] = self.hashbitlen & 0xff return def hash_reference_bytes(self, data): if len(data) > ((1 << 64) - 1) // 8: raise ValueError("message too long") databitlen = len(data) * 8 a = [[0] * 8 for _ in range(self.n_rows)] done_length = 0 block_count = databitlen // 1024 while (done_length + 1) <= block_count: start = done_length * self.reference_stride_bytes block = bytes(data[start:start + self.block_size]) if len(block) != self.block_size: raise ValueError("internal block underflow") if self.hashbitlen <= 256: self.compress32(a, block, done_length) else: self.compress64(a, block, done_length) done_length += 1 lrs = databitlen - 1024 * done_length if (lrs % 8) != 0: raise ValueError("only byte-aligned messages are supported") rem_len = lrs // 8 rs = bytearray(self.block_size) for i in range(rem_len): rs[i] = data[done_length + i] if lrs > 0: rs[rem_len] ^= 0x80 if rem_len < self.block_size - 10: self.set_length_and_digest(rs, databitlen) self.last_block_permutation(a) if self.hashbitlen <= 256: self.compress32(a, bytes(rs), done_length) else: self.compress64(a, bytes(rs), done_length) else: if self.hashbitlen <= 256: self.compress32(a, bytes(rs), done_length) else: self.compress64(a, bytes(rs), done_length) rs = bytearray(self.block_size) self.last_block_permutation(a) self.set_length_and_digest(rs, databitlen) if self.hashbitlen <= 256: self.compress32(a, bytes(rs), done_length + 1) else: self.compress64(a, bytes(rs), done_length + 1) else: rs[0] = 0x80 self.set_length_and_digest(rs, databitlen) self.last_block_permutation(a) if self.hashbitlen <= 256: self.compress32(a, bytes(rs), done_length) else: self.compress64(a, bytes(rs), done_length) out = bytearray(self.digest_size) if self.hashbitlen <= 256: for j in range(self.hashbitlen // 32): for i in range(4): out[4 * j + 3 - i] = a[i][j] else: for j in range(self.hashbitlen // 64): for i in range(8): out[8 * j + 7 - i] = a[i][j] return bytes(out) def finalize(self): out = self.hash_reference_bytes(bytes(self.data)) return out class Cheetah224(CheetahBase): digest_size = 28 hashbitlen = 224 n_rows = 4 class Cheetah256(CheetahBase): digest_size = 32 hashbitlen = 256 n_rows = 4 class Cheetah384(CheetahBase): digest_size = 48 hashbitlen = 384 n_rows = 8 class Cheetah512(CheetahBase): digest_size = 64 hashbitlen = 512 n_rows = 8 class CHIBase: K_TABLE = [ 0x9b05_688c_2b3e_6c1f, 0xdbd9_9e6f_f3c9_0bdc, 0x4dbc_6471_2a5b_b168, 0x767e_27c3_cf76_c8e7, 0x21ee_9ac5_ef4c_823a, 0xb36c_cfc1_204a_9bd8, 0x754c_8a7f_b36b_d941, 0xcf20_868f_04a8_25e9, 0xabb3_79e0_a883_8bb0, 0x12d8_b70a_5959_e391, 0xb591_68fa_9f69_e181, 0x15c7_d491_8739_f18f, 0x4b54_7673_ea8d_68e0, 0x3ced_7326_fcd0_ef81, 0x09f1_d773_0999_8460, 0x1af3_9374_15e9_1f32, 0x1f83_d9ab_fb41_bd6b, 0x23c4_654c_2a21_7583, 0x2842_0121_3157_3f2a, 0xa599_16ac_a399_1fca, 0xec1b_7c06_fd19_d256, 0x1ec7_85bc_dc8b_af26, 0x69ca_4e0f_f2e6_bdd8, 0xca25_75ee_6c95_0d0e, 0x5bcf_66d2_fb3d_99f6, 0x9d6d_08c7_bbca_18f3, 0xeef6_4039_f217_5e83, 0x00ed_5aeb_aa2a_b6e0, 0x5040_712f_c29a_d308, 0x6daf_e433_438d_2c43, 0xbd7f_aa3f_06c7_1f15, 0x03b5_aa8c_e9b6_a4dd, 0x5be0_cd19_137e_2179, 0x867f_5e3b_7222_1265, 0x43b6_cbe0_d67f_4a20, 0xdb99_d767_cb0e_4933, 0xdc45_0dbc_4692_48bd, 0xfe1e_5e48_7610_0d6f, 0xb799_d29e_a173_3137, 0x16ea_7abc_f920_53c4, 0xbe3e_ce96_8dba_92ac, 0x1853_8f84_d82c_318b, 0x38d7_9f4e_9c8a_18c0, 0xa8bb_c28f_1271_f1f7, 0x2796_e710_67d2_c8cc, 0xde1b_f233_4edb_3ff6, 0xb094_d782_a857_f9c1, 0x639b_484b_0c1d_aed1, 0xcbbb_9d5d_c105_9ed8, 0xe773_0eaf_f25e_24a3, 0xf367_f2fc_266a_0373, 0xfe7a_4d34_486d_08ae, 0xd416_70a1_3685_1f32, 0x6639_14b6_6b4b_3c23, 0x1b9e_3d77_40a6_0887, 0x63c1_1d86_d446_cb1c, 0xd167_d246_9049_d628, 0xdddb_b606_b9a4_9e38, 0x557f_1c8b_ee68_a7f7, 0xf99d_c58b_50f9_24bd, 0x205a_cc9f_6535_12a5, 0x67c6_6344_e4ba_b193, 0x1802_6e46_7960_d0c8, 0xa2f5_d84d_aeca_8980, 0x629a_292a_367c_d507, 0x98e6_7012_d90c_bb6d, 0xeed7_58d1_d18c_7e35, 0x031c_02e4_437d_c71e, 0x79b6_3d64_8219_8eb7, 0x936a_9d7e_8c9e_4b33, 0xb30c_a682_c3e6_c65d, 0xcc44_2382_ba42_62fa, 0x5117_9ba5_a1d3_7ff6, 0x7202_bde7_a98e_ea51, 0x2b9f_65d1_df9c_610f, 0xf56b_742b_0af1_ce83, 0xf998_9d19_9b75_848b, 0xd142_f19d_8b46_d578, 0x7a75_8051_4d75_ea33, 0xb74f_9690_808e_704d, ] INIT224 = [ 0xbb67_ae85_84ca_a73b, 0x2574_2d70_78b8_3b89, 0x25d8_34cc_53da_4798, 0xc720_a648_6e45_a6e2, 0x490b_cfd9_5ef1_5dbd, 0xa993_0aae_1222_8f87, ] INIT256 = [ 0x3c6e_f372_fe94_f82b, 0xe739_80c0_b9db_9068, 0x2104_4ed7_e744_e4a3, 0xf0d8_d423_a183_1d2a, 0x4ecf_e162_a7a4_f6fe, 0x068e_08b6_b7e3_04fe, ] INIT384 = [ 0xa54f_f53a_5f1d_36f1, 0xcea7_e61f_c37a_20d5, 0x4a77_fe7b_7841_5dfc, 0x8e34_a6fe_8e2d_f92a, 0x4e5b_408c_9c97_d4d8, 0x24a0_5eee_2992_2401, 0x5a81_76cf_fc7c_2224, 0xc3ed_ebda_29be_c4c8, 0x8a07_4c0f_4d99_9610, ] INIT512 = [ 0x510e_527f_ade6_82d1, 0xde49_e330_e42b_4cbb, 0x29ba_5a45_5316_e0c6, 0x5507_cd18_e9e5_1e69, 0x4f9b_11c8_1009_a030, 0xe3d3_775f_1553_85c6, 0x4892_2163_2788_fb30, 0x4192_1db8_feeb_38c2, 0x9af9_4a7c_48bb_d5b6, ] mask32 = 0xffff_ffff mask64 = 0xffff_ffff_ffff_ffff block_size = None digest_size = None hash_bit_len = None def __init__(self, data=b""): self.buf = bytearray() self.msg_len = 0 self.finalized = False self.init_state() if data: self.update(data) return def init_state(self): if self.hash_bit_len == 224: self.state = list(self.INIT224) elif self.hash_bit_len == 256: self.state = list(self.INIT256) elif self.hash_bit_len == 384: self.state = list(self.INIT384) elif self.hash_bit_len == 512: self.state = list(self.INIT512) else: raise ValueError("invalid hash_bit_len") return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.finalized = self.finalized other.state = list(self.state) return other def update(self, data): if self.finalized: raise ValueError("hash already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block, False) return self def digest(self): c = self.copy() c.finalize() return c.output_bytes() def hexdigest(self): return self.digest().hex() def finalize(self): if self.finalized: return bit_len = self.msg_len * 8 if self.hash_bit_len in (224, 256): length_bytes = 8 else: length_bytes = 16 self.buf.append(0x80) while (len(self.buf) % self.block_size) != (self.block_size - length_bytes): self.buf.append(0x00) if length_bytes == 16: high = (bit_len >> 64) & self.mask64 low = bit_len & self.mask64 self.buf.extend(struct.pack(">Q", high)) self.buf.extend(struct.pack(">Q", low)) else: self.buf.extend(struct.pack(">Q", bit_len & self.mask64)) while len(self.buf) > self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block, False) if len(self.buf) != self.block_size: raise AssertionError("invalid final block size") block = bytes(self.buf) self.buf.clear() self.compress(block, True) self.finalized = True return def output_bytes(self): out = bytearray() if self.hash_bit_len == 224: out.extend(struct.pack(">Q", self.state[0])) out.extend(struct.pack(">Q", self.state[1])) out.extend(struct.pack(">Q", self.state[3])) out.extend(struct.pack(">I", (self.state[4] >> 32) & self.mask32)) return bytes(out) if self.hash_bit_len == 256: out.extend(struct.pack(">Q", self.state[0])) out.extend(struct.pack(">Q", self.state[1])) out.extend(struct.pack(">Q", self.state[3])) out.extend(struct.pack(">Q", self.state[4])) return bytes(out) if self.hash_bit_len == 384: i = 0 while i < 6: out.extend(struct.pack(">Q", self.state[i])) i += 1 return bytes(out) if self.hash_bit_len == 512: i = 0 while i < 8: out.extend(struct.pack(">Q", self.state[i])) i += 1 return bytes(out) raise ValueError("invalid hash_bit_len") def rotr32(self, x, r): x &= self.mask32 return ((x >> r) | (x << (32 - r))) & self.mask32 def rotr64(self, x, r): x &= self.mask64 return ((x >> r) | (x << (64 - r))) & self.mask64 def drotr32(self, x, r1, r2): hi = (x >> 32) & self.mask32 lo = x & self.mask32 return ((self.rotr32(hi, r1) << 32) | self.rotr32(lo, r2)) & self.mask64 def swap8(self, x): x &= self.mask64 x = ((x & 0xffff_ffff_0000_0000) >> 32) | ((x & 0x0000_0000_ffff_ffff) << 32) x = ((x & 0xffff_0000_ffff_0000) >> 16) | ((x & 0x0000_ffff_0000_ffff) << 16) x = ((x & 0xff00_ff00_ff00_ff00) >> 8) | ((x & 0x00ff_00ff_00ff_00ff) << 8) return x def swap32(self, x): return self.rotr64(x, 32) def map0(self, r, s, t, u): return ( (r & (~s) & t & u) | ((~s) & (~t) & (~u)) | ((~r) & (~t) & u) | ((~r) & s & t) | (r & (~t) & (~u)) ) & self.mask64 def map1(self, r, s, t, u): return ( (r & s & t & (~u)) | (r & (~s) & (~t) & (~u)) | ((~r) & (~s) & t) | (s & (~t) & u) | ((~r) & u) ) & self.mask64 def map2(self, r, s, t, u): return ( ((~r) & (~s) & t & u) | ((~r) & s & t & (~u)) | (s & (~t) & u) | (r & (~t) & (~u)) | (r & s & u) | (r & (~s) & (~u)) ) & self.mask64 def map_func(self, r, s, t, u, i): m0 = self.map0(r, s, t, u) m1 = self.map1(r, s, t, u) m2 = self.map2(r, s, t, u) m = [m0, m1, m2] x = m[i % 3] y = m[(i + 1) % 3] z = m[(i + 2) % 3] return x, y, z def theta256_0(self, x): return self.drotr32(x, 21, 21) ^ self.drotr32(x, 26, 26) ^ self.drotr32(x, 30, 30) def theta256_1(self, x): return self.drotr32(x, 14, 14) ^ self.drotr32(x, 24, 24) ^ self.drotr32(x, 31, 31) def mu256_0(self, x): return self.rotr64(x, 36) ^ self.rotr64(x, 18) ^ (x >> 1) def mu256_1(self, x): return self.rotr64(x, 59) ^ self.rotr64(x, 37) ^ (x >> 10) def message_expansion256(self, w): i = 8 while i < 40: w[i] = w[i - 8] ^ self.mu256_0(w[i - 7]) ^ self.mu256_1(w[i - 2]) i += 1 return def premix256(self, a, b, d, e): pre_r = self.drotr32(b, 8, 8) ^ self.drotr32(self.swap32(d), 5, 1) pre_s = a ^ self.drotr32(self.swap32(d), 18, 17) pre_t = self.drotr32(self.swap32(a), 7, 26) ^ self.drotr32(d, 14, 22) pre_u = self.drotr32(a, 17, 12) ^ self.drotr32(self.swap32(e), 2, 23) return pre_r, pre_s, pre_t, pre_u def datainput256(self, pre_r, pre_s, pre_t, pre_u, v0, v1): r = pre_r ^ v0 s = pre_s ^ v1 t = pre_t ^ self.theta256_0(v0) u = pre_u ^ self.theta256_1(v1) return r, s, t, u def postmix256(self, x, y, z): xx = x yy = self.swap8(self.drotr32(y, 5, 5)) zz = self.swap32(z) aa = self.rotr64((xx + yy) & self.mask64, 16) dd = self.rotr64((yy + zz) & self.mask64, 48) return xx, yy, zz, aa, dd def theta512_0(self, x): return self.rotr64(x, 5) ^ self.rotr64(x, 6) ^ self.rotr64(x, 43) def theta512_1(self, x): return self.rotr64(x, 20) ^ self.rotr64(x, 30) ^ self.rotr64(x, 49) def mu512_0(self, x): return self.rotr64(x, 36) ^ self.rotr64(x, 18) ^ (x >> 1) def mu512_1(self, x): return self.rotr64(x, 60) ^ self.rotr64(x, 30) ^ (x >> 3) def message_expansion512(self, w): i = 16 while i < 80: w[i] = w[i - 16] ^ w[i - 7] ^ self.mu512_0(w[i - 15]) ^ self.mu512_1(w[i - 2]) i += 1 return def premix512(self, a, b, d, e, g, p): pre_r = self.rotr64(b, 11) ^ self.rotr64(d, 8) ^ self.rotr64(g, 13) pre_s = a ^ self.rotr64(d, 21) ^ self.rotr64(g, 29) pre_t = self.rotr64(a, 11) ^ self.rotr64(d, 38) ^ p pre_u = self.rotr64(a, 26) ^ self.rotr64(e, 40) ^ self.rotr64(g, 50) return pre_r, pre_s, pre_t, pre_u def datainput512(self, pre_r, pre_s, pre_t, pre_u, v0, v1): r = pre_r ^ v0 s = pre_s ^ v1 t = pre_t ^ self.theta512_0(v0) u = pre_u ^ self.theta512_1(v1) return r, s, t, u def postmix512(self, x, y, z): xx = x yy = self.swap8(self.rotr64(y, 31)) zz = self.swap32(z) aa = self.rotr64((xx + yy) & self.mask64, 16) dd = self.rotr64((yy + zz) & self.mask64, 48) gg = (zz + (xx << 1)) & self.mask64 return xx, yy, zz, aa, dd, gg def compress(self, block, final_flag): if self.hash_bit_len in (224, 256): self.compress256(block, final_flag) else: self.compress512(block, final_flag) return def compress256(self, block, final_flag): a, b, c, d, e, f = self.state[:6] if final_flag: a = self.rotr64(a, 1) b = self.rotr64(b, 1) c = self.rotr64(c, 1) d = self.rotr64(d, 1) e = self.rotr64(e, 1) f = self.rotr64(f, 1) w = [0] * 40 i = 0 while i < 8: w[i] = struct.unpack(">Q", block[i * 8:(i + 1) * 8])[0] i += 1 self.message_expansion256(w) i = 0 while i < 20: pre_r, pre_s, pre_t, pre_u = self.premix256(a, b, d, e) v0 = w[2 * i] ^ self.K_TABLE[2 * i] v1 = w[2 * i + 1] ^ self.K_TABLE[2 * i + 1] r, s, t, u = self.datainput256(pre_r, pre_s, pre_t, pre_u, v0, v1) x, y, z = self.map_func(r, s, t, u, i) xx, yy, zz, aa, dd = self.postmix256(x, y, z) new_a = aa ^ f new_d = dd ^ c f = e e = d d = new_d & self.mask64 c = b b = a a = new_a & self.mask64 i += 1 self.state[0] = (self.rotr64(self.state[0], 1) ^ a) & self.mask64 self.state[1] = (self.rotr64(self.state[1], 1) ^ b) & self.mask64 self.state[2] = (self.rotr64(self.state[2], 1) ^ c) & self.mask64 self.state[3] = (self.rotr64(self.state[3], 1) ^ d) & self.mask64 self.state[4] = (self.rotr64(self.state[4], 1) ^ e) & self.mask64 self.state[5] = (self.rotr64(self.state[5], 1) ^ f) & self.mask64 return def compress512(self, block, final_flag): a, b, c, d, e, f, g, p, q = self.state[:9] if final_flag: a = self.rotr64(a, 1) b = self.rotr64(b, 1) c = self.rotr64(c, 1) d = self.rotr64(d, 1) e = self.rotr64(e, 1) f = self.rotr64(f, 1) g = self.rotr64(g, 1) p = self.rotr64(p, 1) q = self.rotr64(q, 1) w = [0] * 80 i = 0 while i < 16: w[i] = struct.unpack(">Q", block[i * 8:(i + 1) * 8])[0] i += 1 self.message_expansion512(w) i = 0 while i < 40: pre_r, pre_s, pre_t, pre_u = self.premix512(a, b, d, e, g, p) v0 = w[2 * i] ^ self.K_TABLE[2 * i] v1 = w[2 * i + 1] ^ self.K_TABLE[2 * i + 1] r, s, t, u = self.datainput512(pre_r, pre_s, pre_t, pre_u, v0, v1) x, y, z = self.map_func(r, s, t, u, i) xx, yy, zz, aa, dd, gg = self.postmix512(x, y, z) new_a = aa ^ q new_d = dd ^ c new_g = gg ^ f q = p p = g g = new_g & self.mask64 f = e e = d d = new_d & self.mask64 c = b b = a a = new_a & self.mask64 i += 1 self.state[0] = (self.rotr64(self.state[0], 1) ^ a) & self.mask64 self.state[1] = (self.rotr64(self.state[1], 1) ^ b) & self.mask64 self.state[2] = (self.rotr64(self.state[2], 1) ^ c) & self.mask64 self.state[3] = (self.rotr64(self.state[3], 1) ^ d) & self.mask64 self.state[4] = (self.rotr64(self.state[4], 1) ^ e) & self.mask64 self.state[5] = (self.rotr64(self.state[5], 1) ^ f) & self.mask64 self.state[6] = (self.rotr64(self.state[6], 1) ^ g) & self.mask64 self.state[7] = (self.rotr64(self.state[7], 1) ^ p) & self.mask64 self.state[8] = (self.rotr64(self.state[8], 1) ^ q) & self.mask64 return class CHI224(CHIBase): block_size = 64 digest_size = 28 hash_bit_len = 224 class CHI256(CHIBase): block_size = 64 digest_size = 32 hash_bit_len = 256 class CHI384(CHIBase): block_size = 128 digest_size = 48 hash_bit_len = 384 class CHI512(CHIBase): block_size = 128 digest_size = 64 hash_bit_len = 512 class DCHBase: block_size = 64 state_size = 64 num_rounds = 4 sbox_table = ( 0x03, 0x02, 0x8d, 0xf7, 0x44, 0xa4, 0x79, 0xb9, 0xae, 0x9e, 0xde, 0x9b, 0x3e, 0xa9, 0x5e, 0x95, 0xdb, 0x71, 0xc3, 0x5b, 0xe3, 0x3d, 0x4f, 0x65, 0x93, 0xdd, 0x56, 0x83, 0xa3, 0x80, 0x48, 0x29, 0x6f, 0xee, 0x3a, 0x52, 0x63, 0x55, 0x2f, 0x89, 0x73, 0xd3, 0x1c, 0x49, 0x25, 0x88, 0x30, 0x6d, 0x4b, 0x8a, 0x6c, 0x2d, 0xa7, 0xc0, 0x43, 0x5d, 0x53, 0x21, 0xcc, 0xaa, 0xa8, 0x0f, 0x16, 0xe2, 0x35, 0x5c, 0xfb, 0xd6, 0x91, 0x4d, 0xa5, 0x07, 0x33, 0x8b, 0x28, 0x1d, 0x15, 0x64, 0x46, 0x90, 0x3b, 0x20, 0x6b, 0x8f, 0x82, 0x19, 0x26, 0x62, 0x10, 0xc2, 0xc8, 0x60, 0x94, 0x0d, 0x34, 0x42, 0x27, 0x54, 0xc9, 0x58, 0xba, 0xc7, 0x14, 0x4e, 0x51, 0x8e, 0xec, 0xb0, 0x23, 0xef, 0x2c, 0x31, 0x2b, 0xd2, 0x12, 0xda, 0xea, 0xf8, 0xd9, 0x7a, 0xd8, 0x74, 0x05, 0xb8, 0x87, 0xce, 0xfd, 0xff, 0x18, 0x57, 0xa2, 0x1e, 0x7f, 0xcf, 0xe7, 0xb3, 0x4a, 0x32, 0x24, 0x2e, 0x50, 0x6a, 0x01, 0xf6, 0x1b, 0xdc, 0x47, 0x4c, 0x98, 0xbf, 0x0c, 0x5f, 0x08, 0xdf, 0xbe, 0x97, 0xaf, 0x0a, 0xc4, 0xa1, 0x1f, 0x81, 0x9c, 0xc5, 0x37, 0xc1, 0x45, 0x06, 0xcd, 0x38, 0x0e, 0x3f, 0x9f, 0x0b, 0xbd, 0xb4, 0x84, 0xe6, 0xed, 0x68, 0xe8, 0xf1, 0xbc, 0xac, 0xc6, 0x67, 0x04, 0x78, 0x96, 0x99, 0xad, 0xb5, 0x11, 0x5a, 0xa6, 0x36, 0x66, 0xbb, 0xa0, 0x9d, 0xd1, 0xf4, 0x61, 0x59, 0x86, 0x7e, 0xab, 0x39, 0x2a, 0x72, 0xcb, 0xf5, 0xfa, 0x40, 0xd4, 0xd5, 0x13, 0x70, 0x75, 0x7b, 0x9a, 0x09, 0x1a, 0x92, 0x17, 0x3c, 0xe5, 0xf3, 0x85, 0xb2, 0xe1, 0xf2, 0xf9, 0x77, 0xf0, 0xb7, 0x6e, 0x22, 0xb1, 0x69, 0xe0, 0xe4, 0xb6, 0xe9, 0x00, 0x8c, 0xd0, 0xca, 0x41, 0xd7, 0xeb, 0x76, 0x7c, 0xfc, 0x7d, 0xfe, ) gf_table = ( 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01, ) gfinv_table = ( 0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71, 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45, 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6, 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88, 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6, 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf, ) transform_table = ( 0x01, 0x02, 0x04, 0x08, 0x99, 0x2f, 0x5e, 0xbc, 0x4f, 0x9e, 0x21, 0x42, 0xdd, 0xa7, 0x53, 0xa6, 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0xd6, 0xb1, 0x7f, 0xfe, 0x92, 0x39, 0x72, 0xe4, 0x45, 0x8a, 0x09, 0x12, 0x22, 0x4e, 0x9c, 0x25, 0x4a, 0x44, 0x88, 0x0d, 0x1a, 0xd7, 0xb3, 0x7b, 0xf6, 0x0b, 0x16, 0x2c, 0x58, 0x6e, 0x0a, 0x14, 0x28, 0x50, 0x93, 0x3b, 0x76, 0xec, 0xdc, 0xa5, 0x57, 0xae, 0xa1, ) tables_ready = False @classmethod def init_tables(cls): if cls.tables_ready: return cls.round_key_ints = tuple( int.from_bytes(bytes(cls.sbox_table[round_index * 64:(round_index + 1) * 64]), "big") for round_index in range(cls.num_rounds) ) gf_table = cls.gf_table gfinv_table = cls.gfinv_table mul_tables = [] for coeff in cls.transform_table: row = bytearray(256) if coeff != 0: coeff_log = gfinv_table[coeff] for value in range(1, 256): row[value] = gf_table[(gfinv_table[value] + coeff_log) % 255] mul_tables.append(bytes(row)) round_tables = [[0] * 256 for i in range(64)] sbox_table = cls.sbox_table for value in range(256): repeated = int.from_bytes(bytes([sbox_table[value]]) * 64, "big") round_tables[0][value] = repeated column = bytearray([sbox_table[value]]) * 64 for pos in range(1, 64): for row_index in range(64): column[row_index] = mul_tables[row_index][column[row_index]] round_tables[pos][value] = int.from_bytes(column, "big") cls.round_tables = tuple(tuple(row) for row in round_tables) cls.tables_ready = True return def __init__(self, data=b"", data_bitlen=None): self.__class__.init_tables() self.hashbitlen = self.digest_size * 8 self.num_unprocessed = 0 self.unprocessed = bytearray(self.block_size) self.curr = bytearray(self.state_size) self.datalen = 0 self.p = [bytearray(64), bytearray(64), bytearray(64)] for i in range(63): self.p[0][i] = 64 - i self.p[1][0] = 64 self.p[2][0] = 64 self.top = [63, 0, 0] self.parity = 1 self.small = 0 self.count = 0 self.move = 0x60 if data: if data_bitlen is None: self.update(data) else: self.update_bits(data, data_bitlen) return def copy(self): other = self.__class__() other.num_unprocessed = self.num_unprocessed other.unprocessed = bytearray(self.unprocessed) other.curr = bytearray(self.curr) other.datalen = self.datalen other.p = [bytearray(self.p[0]), bytearray(self.p[1]), bytearray(self.p[2])] other.top = list(self.top) other.parity = self.parity other.small = self.small other.count = self.count other.move = self.move return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.update_bits(data, len(data) * 8) return self def update_bits(self, data, databitlen): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if databitlen < 0: raise ValueError("databitlen must be >= 0") if databitlen > len(data) * 8: raise ValueError("databitlen exceeds input size") if databitlen == 0: return self if isinstance(data, bytes): pass elif isinstance(data, memoryview): data = data.cast("B") else: data = memoryview(data) full_bits = self.block_size * 8 if self.num_unprocessed == 0: self.set_next_sequence_value() self.num_unprocessed = 8 max_bits = full_bits - self.num_unprocessed if databitlen < max_bits: max_bits = databitlen databitlen -= max_bits self.datalen += max_bits dst = self.num_unprocessed >> 3 nbytes = (max_bits + 7) >> 3 self.unprocessed[dst:dst + nbytes] = data[:nbytes] self.num_unprocessed += max_bits data = data[max_bits >> 3:] while self.num_unprocessed == full_bits: self.hash_one_block() if databitlen > 0: self.set_next_sequence_value() self.num_unprocessed = 8 max_bits = full_bits - 8 if databitlen < max_bits: max_bits = databitlen databitlen -= max_bits self.datalen += max_bits nbytes = (max_bits + 7) >> 3 self.unprocessed[1:1 + nbytes] = data[:nbytes] self.num_unprocessed = 8 + max_bits data = data[max_bits >> 3:] else: self.num_unprocessed = 0 return self def digest(self): copied = self.copy() copied.finalize() return bytes(copied.curr[:self.digest_size]) def hexdigest(self): return self.digest().hex() def finalize(self): if self.num_unprocessed == 0: self.num_unprocessed = 8 self.set_next_sequence_value() idx = self.num_unprocessed >> 3 rem = self.num_unprocessed & 7 self.unprocessed[idx] |= 0x80 >> rem self.unprocessed[idx] &= 0xff ^ ((1 << (7 - rem)) - 1) self.unprocessed[idx + 1:self.block_size] = b"\x00" * (self.block_size - (idx + 1)) self.num_unprocessed += 1 if (self.block_size * 8) - self.num_unprocessed < 64: self.hash_one_block() self.unprocessed[:] = b"\x00" * self.block_size self.set_next_sequence_value() self.unprocessed[self.block_size - 8:self.block_size] = int(self.datalen).to_bytes(8, "big", signed=False) self.unprocessed[0] &= 0x1f self.hash_one_block() return def set_next_sequence_value(self): self.unprocessed[0] = self.move ^ self.count self.count += 1 if self.count == 32: self.count = 0 if self.parity: self.move = (self.small + 3 * ((self.small + 1) % 3)) << 5 self.top[self.small] -= 1 self.small += 1 self.small %= 3 self.top[self.small] += 1 self.p[self.small][self.top[self.small]] = 1 else: a = 1 b = 2 if self.small == 1: a = 0 elif self.small == 2: b = 0 if self.p[b][self.top[b]] > self.p[a][self.top[a]]: b, a = a, b self.move = (b + 3 * a) << 5 self.top[a] += 1 self.p[a][self.top[a]] = self.p[b][self.top[b]] self.top[b] -= 1 self.parity ^= 1 return def hash_one_block(self): unprocessed_int = int.from_bytes(self.unprocessed, "big") block = bytes(self.unprocessed) round_tables = self.round_tables for round_key_int in self.round_key_ints: acc = 0 for pos in range(64): acc ^= round_tables[pos][block[pos]] acc ^= round_key_int block = acc.to_bytes(64, "big") final_int = int.from_bytes(self.curr, "big") ^ unprocessed_int ^ acc self.curr[:] = final_int.to_bytes(64, "big") self.num_unprocessed = 0 return class DCH224(DCHBase): digest_size = 28 class DCH256(DCHBase): digest_size = 32 class DCH384(DCHBase): digest_size = 48 class DCH512(DCHBase): digest_size = 64 class DynamicSHABase: round_count = 48 def __init__(self, data=b""): self.h = list(self.iv) self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.h = list(self.h) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def digest(self): other = self.copy() other.finalize() out = bytearray() for value in other.h: out.extend(struct.pack(self.pack_fmt, value)) return bytes(out[:self.digest_size]) def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 self.buf.append(0x80) if (bit_len % (self.block_size * 8)) > self.length_block_threshold: while len(self.buf) < self.block_size: self.buf.append(0x00) block = bytes(self.buf[:self.block_size]) self.buf.clear() self.compress(block) while len(self.buf) < (self.block_size - self.length_size): self.buf.append(0x00) self.buf.extend(bit_len.to_bytes(self.length_size, "big")) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return def rotr(self, value, count): return ((value >> count) | (value << (self.word_size - count))) & self.word_mask def calc_d(self, index, w, ch): if (index % 4) == 0: value = w[index % 16] + (ch[0] ^ ch[1] ^ ch[2]) + self.tt[index // 16] elif (index % 4) == 1: value = w[index % 16] + ((ch[0] & ch[1]) ^ ch[2]) + self.tt[index // 16] elif (index % 4) == 2: value = ( w[index % 16] + ((~(ch[0] | ch[2])) | (ch[0] & (ch[1] ^ ch[2]))) + self.tt[index // 16] ) else: value = ( w[index % 16] + ((~(ch[0] | (ch[1] ^ ch[2]))) | (ch[0] & (~ch[2]))) + self.tt[index // 16] ) return value & self.word_mask def compress(self, block): w = list(struct.unpack(self.unpack_fmt, block)) ch = list(self.h) for i in range(self.round_count): temp = (ch[0] + ch[1]) & self.word_mask temp = (temp ^ ch[2]) & self.word_mask temp = (temp + ch[3]) & self.word_mask temp = (temp ^ ch[4]) & self.word_mask temp = (temp + ch[5]) & self.word_mask temp = (temp ^ ch[6]) & self.word_mask temp = self.reduce_temp(temp) temp = self.rotr(ch[7], temp) d = self.calc_d(i, w, ch) ch[7] = ch[6] ch[6] = ch[5] ch[5] = ch[4] ch[4] = ch[3] ch[3] = d ch[2] = ch[1] ch[1] = ch[0] ch[0] = temp for i in range(8): self.h[i] = (self.h[i] + ch[i]) & self.word_mask return class DynamicSHA32Base(DynamicSHABase): block_size = 64 word_size = 32 word_mask = 0xffff_ffff length_size = 8 length_block_threshold = 512 - 65 pack_fmt = ">I" unpack_fmt = ">16I" tt = [ 0x5a82_7999, 0x6ed9_eba1, 0x8f1b_bcdc, ] def reduce_temp(self, value): value = ((value >> 17) ^ value) & 0x1f_fff value = ((value >> 10) ^ value) & 0x3ff value = ((value >> 5) ^ value) & 0x1f return value class DynamicSHA64Base(DynamicSHABase): block_size = 128 word_size = 64 word_mask = 0xffff_ffff_ffff_ffff length_size = 16 length_block_threshold = 1024 - 129 pack_fmt = ">Q" unpack_fmt = ">16Q" tt = [ 0x5a82_7999_50a2_8be6, 0x6ed9_eba1_5c4d_d124, 0x8f1b_bcdc_6d70_3ef3, ] def reduce_temp(self, value): value = ((value >> 36) ^ value) & 0xf_ffff_ffff value = ((value >> 18) ^ value) & 0x3f_fff value = ((value >> 12) ^ value) & 0xfff value = ((value >> 6) ^ value) & 0x3f return value class DynamicSHA224(DynamicSHA32Base): digest_size = 28 iv = [ 0xc105_9ed8, 0x367c_d507, 0x3070_dd17, 0xf70e_5939, 0xffc0_0b31, 0x6858_1511, 0x64f9_8fa7, 0xbefa_4fa4, ] class DynamicSHA256(DynamicSHA32Base): digest_size = 32 iv = [ 0x6a09_e667, 0xbb67_ae85, 0x3c6e_f372, 0xa54f_f53a, 0x510e_527f, 0x9b05_688c, 0x1f83_d9ab, 0x5be0_cd19, ] class DynamicSHA384(DynamicSHA64Base): digest_size = 48 iv = [ 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4, ] class DynamicSHA512(DynamicSHA64Base): digest_size = 64 iv = [ 0x6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1, 0x510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179, ] class DynamicSHA2Base: def __init__(self, data=b""): self.state = list(self.iv) self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.state = list(self.state) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def digest(self): c = self.copy() c.finalize() return c.state_to_bytes()[:self.digest_size] def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 self.buf.append(0x80) while (len(self.buf) % self.block_size) != (self.block_size - self.length_size): self.buf.append(0x00) self.buf.extend(bit_len.to_bytes(self.length_size, "big")) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return def rotr(self, x, n): n %= self.word_bits return ((x >> n) | (x << (self.word_bits - n))) & self.mask def state_to_bytes(self): out = bytearray() for value in self.state: out.extend(value.to_bytes(self.word_bytes, "big")) return bytes(out) def g0(self, x, y, z): return x ^ y ^ z def g1(self, x, y, z): return (x & y) ^ z def g2(self, x, y, z): return (~(x | z)) | (x & (y ^ z)) def g3(self, x, y, z): return (~(x | (y ^ z))) | (x & (~z)) def unpack_block(self, block): return list(struct.unpack(self.unpack_fmt, block)) def apply_g(self, selector, x, y, z): if selector == 0: return self.g0(x, y, z) if selector == 1: return self.g1(x, y, z) if selector == 2: return self.g2(x, y, z) return self.g3(x, y, z) def sum1(self, ch): return ((((((ch[0] ^ ch[1]) + ch[2]) ^ ch[3]) + ch[4]) ^ ch[5]) + ch[6]) ^ ch[7] def sum2(self, ch): return (((((ch[0] + ch[1]) ^ ch[2]) + ch[3]) ^ ch[4]) + ch[5]) ^ ch[6] def compress(self, block): w = self.unpack_block(block) ch = list(self.state) init = list(self.state) for i in range(8): for i1 in range(0, 9, 8): word = w[i + i1] r0, r1, r2 = self.rotate_amounts_first(word) temp = self.sum1(ch) & self.mask temp = self.rotr(temp, r0) ch[7] = ch[6] ch[6] = self.update_ch6_first(ch, word) ch[5] = self.update_ch5_first(ch, w, i, i1, word) ch[4] = self.rotr(ch[3], r2) selector = self.selector_first(word, i) ch[3] = (w[((i + 2) % 8) + i1] + self.apply_g(selector, ch[0], ch[1], ch[2])) & self.mask ch[2] = self.update_ch2_first(ch, word) ch[1] = ch[0] ch[0] = (temp + w[((i + 1) % 8) + i1]) & self.mask r3, r4, _r5, r6 = self.rotate_amounts_second(word) temp = self.sum1(ch) & self.mask temp = self.rotr(temp, r3) ch[7] = (ch[6] + w[((i + 7) % 8) + i1]) & self.mask ch[6] = self.rotr(ch[5], r4) ch[5] = self.update_ch5_second(ch, w, i, i1, word) ch[4] = self.rotr(ch[3], r6) selector = self.selector_second(word, i) ch[3] = (w[((i + 5) % 8) + i1] + self.apply_g(selector, ch[0], ch[1], ch[2])) & self.mask ch[2] = (ch[1] + word) & self.mask ch[1] = self.update_ch1_second(ch, word) ch[0] = (temp + w[((i + 4) % 8) + i1]) & self.mask if i == 0: for _ in range(9): ch = self.mix_round(ch) for i in range(8): self.state[i] = (init[i] + ch[i]) & self.mask return class DynamicSHA2_32Base(DynamicSHA2Base): block_size = 64 word_bits = 32 word_bytes = 4 length_size = 8 mask = 0xffff_ffff unpack_fmt = ">16I" def selector_first(self, word, i): return word >> 30 def selector_second(self, word, i): return i % 4 def rotate_amounts_first(self, word): return ( word & 31, (word >> 5) & 31, (word >> 10) & 31, ) def rotate_amounts_second(self, word): return ( (word >> 15) & 31, (word >> 20) & 31, (word >> 25) & 31, (word >> 25) & 31, ) def update_ch6_first(self, ch, word): return self.rotr(ch[5], (word >> 5) & 31) def update_ch5_first(self, ch, w, i, i1, word): return (ch[4] + w[((i + 3) % 8) + i1]) & self.mask def update_ch2_first(self, ch, word): return ch[1] def update_ch5_second(self, ch, w, i, i1, word): return (ch[4] + w[((i + 6) % 8) + i1]) & self.mask def update_ch1_second(self, ch, word): return ch[0] def mix_round(self, ch): temp = self.sum2(ch) & self.mask temp = ((temp >> 17) ^ temp) & 0x1_ffff temp = ((temp >> 10) ^ temp) & 0x3ff temp = ((temp >> 5) ^ temp) & 0x1f temp = self.rotr(ch[7], temp) return [temp, ch[0], ch[1], ch[2], ch[3], ch[4], ch[5], ch[6]] class DynamicSHA2_64Base(DynamicSHA2Base): block_size = 128 word_bits = 64 word_bytes = 8 length_size = 16 mask = 0xffff_ffff_ffff_ffff unpack_fmt = ">16Q" def selector_first(self, word, i): return word >> 62 def selector_second(self, word, i): return (word >> 60) & 0x3 def rotate_amounts_first(self, word): return ( word & 0x3f, (word >> 6) & 0x3f, (word >> 18) & 0x3f, ) def rotate_amounts_second(self, word): return ( (word >> 30) & 0x3f, (word >> 36) & 0x3f, (word >> 48) & 0x3f, (word >> 48) & 0x3f, ) def update_ch6_first(self, ch, word): return self.rotr(ch[5], (word >> 6) & 0x3f) def update_ch5_first(self, ch, w, i, i1, word): return (self.rotr(ch[4], (word >> 12) & 0x3f) + w[((i + 3) % 8) + i1]) & self.mask def update_ch2_first(self, ch, word): return self.rotr(ch[1], (word >> 24) & 0x3f) def update_ch5_second(self, ch, w, i, i1, word): return (self.rotr(ch[4], (word >> 42) & 0x3f) + w[((i + 6) % 8) + i1]) & self.mask def update_ch1_second(self, ch, word): return self.rotr(ch[0], (word >> 54) & 0x3f) def mix_round(self, ch): temp = self.sum2(ch) & self.mask temp = ((temp >> 36) ^ temp) & 0xf_ffff_ffff temp = ((temp >> 18) ^ temp) & 0x3_ffff temp = ((temp >> 12) ^ temp) & 0xfff temp = ((temp >> 6) ^ temp) & 0x3f temp = self.rotr(ch[7], temp) return [temp, ch[0], ch[1], ch[2], ch[3], ch[4], ch[5], ch[6]] class DynamicSHA2_224(DynamicSHA2_32Base): digest_size = 28 iv = ( 0xc105_9ed8, 0x367c_d507, 0x3070_dd17, 0xf70e_5939, 0xffc0_0b31, 0x6858_1511, 0x64f9_8fa7, 0xbefa_4fa4, ) class DynamicSHA2_256(DynamicSHA2_32Base): digest_size = 32 iv = ( 0x6a09_e667, 0xbb67_ae85, 0x3c6e_f372, 0xa54f_f53a, 0x510e_527f, 0x9b05_688c, 0x1f83_d9ab, 0x5be0_cd19, ) class DynamicSHA2_384(DynamicSHA2_64Base): digest_size = 48 iv = ( 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4, ) class DynamicSHA2_512(DynamicSHA2_64Base): digest_size = 64 iv = ( 0x6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1, 0x510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179, ) class ECOHBase: C_TEMPLATE = r""" #include #include #include #define NLIMBS REPLACE_NLIMBS #define GF_M REPLACE_M #define GF_K0 REPLACE_K0 #define GF_K1 REPLACE_K1 #define GF_K2 REPLACE_K2 #define GF_NKS REPLACE_NKS #define HASH_BLOCK_SIZE REPLACE_BLOCK_SIZE #define CLEN_BITS REPLACE_CLEN_BITS #define YP_BIT REPLACE_YP_BIT #define COUNTER_LIMBS REPLACE_COUNTER_LIMBS #define CHECKSUM_LIMBS REPLACE_CHECKSUM_LIMBS typedef uint64_t Limb; typedef Limb GFElem[NLIMBS]; typedef Limb GFElem2[NLIMBS * 2]; static void gfe_zero(GFElem r) { memset(r, 0, sizeof(GFElem)); } static void gfe_copy(GFElem dst, const GFElem src) { memcpy(dst, src, sizeof(GFElem)); } static int gfe_is_zero(const GFElem a) { for (int i = 0; i < NLIMBS; i++) if (a[i]) return 0; return 1; } static int gfe_eq(const GFElem a, const GFElem b) { for (int i = 0; i < NLIMBS; i++) if (a[i] != b[i]) return 0; return 1; } static int gf2_getbit(const Limb *a, int i) { return (int)((a[i >> 6] >> (i & 63)) & 1); } static void limbs_from_bytes_be(Limb *dst, int dst_limbs, const uint8_t *src, int len) { memset(dst, 0, sizeof(Limb) * dst_limbs); for (int bi = 0; bi < len; bi++) { int src_idx = len - 1 - bi; dst[bi >> 3] |= (Limb)src[src_idx] << ((bi & 7) * 8); } } static void limbs_xor_shifted(Limb *dst, int dst_limbs, const Limb *src, int src_limbs, int bit_offset) { int wshift = bit_offset >> 6; int bshift = bit_offset & 63; for (int i = 0; i < src_limbs; i++) { int dst_idx = i + wshift; if (dst_idx >= dst_limbs) { break; } dst[dst_idx] ^= src[i] << bshift; if (bshift != 0 && dst_idx + 1 < dst_limbs) { dst[dst_idx + 1] ^= src[i] >> (64 - bshift); } } } static void limbs_add_small(Limb *a, int n, Limb v) { Limb carry = v; for (int i = 0; i < n && carry; i++) { Limb old = a[i]; a[i] += carry; carry = (a[i] < old) ? 1 : 0; } } static void limbs_rshift1(Limb *dst, const Limb *src, int n) { Limb carry = 0; for (int i = n - 1; i >= 0; i--) { Limb next_carry = src[i] << 63; dst[i] = (src[i] >> 1) | carry; carry = next_carry; } } static void gf_reduce_wide(GFElem r, const GFElem2 x) { Limb t[NLIMBS * 2]; memcpy(t, x, sizeof(t)); for (int bit = (NLIMBS * 2 * 64) - 1; bit >= GF_M; bit--) { if (!gf2_getbit(t, bit)) continue; t[bit >> 6] ^= (Limb)1 << (bit & 63); int d = bit - GF_M; t[d >> 6] ^= (Limb)1 << (d & 63); int pos; pos = d + GF_K0; t[pos >> 6] ^= (Limb)1 << (pos & 63); #if GF_NKS >= 2 pos = d + GF_K1; t[pos >> 6] ^= (Limb)1 << (pos & 63); #endif #if GF_NKS >= 3 pos = d + GF_K2; t[pos >> 6] ^= (Limb)1 << (pos & 63); #endif } memcpy(r, t, sizeof(GFElem)); } void gf_mul(GFElem r, const GFElem a, const GFElem b) { GFElem2 acc; memset(acc, 0, sizeof(acc)); for (int wi = 0; wi < NLIMBS; wi++) { Limb bw = b[wi]; if (!bw) { continue; } for (int bit = 0; bit < 64; bit++) { if (((bw >> bit) & 1) == 0) { continue; } int shift = wi * 64 + bit; int wshift = shift >> 6; int bshift = shift & 63; for (int j = 0; j < NLIMBS; j++) { int dst = j + wshift; if (dst >= NLIMBS * 2) { break; } acc[dst] ^= a[j] << bshift; if (bshift != 0 && dst + 1 < NLIMBS * 2) { acc[dst + 1] ^= a[j] >> (64 - bshift); } } } } gf_reduce_wide(r, acc); } void gf_sqr(GFElem r, const GFElem a) { GFElem2 wide; memset(wide, 0, sizeof(wide)); for (int i = 0; i < NLIMBS; i++) { Limb w = a[i]; Limb lo = 0, hi = 0; for (int b = 0; b < 32; b++) { if ((w >> b) & 1) lo |= (Limb)1 << (b * 2); } for (int b = 0; b < 32; b++) { if ((w >> (b + 32)) & 1) hi |= (Limb)1 << (b * 2); } wide[i * 2] ^= lo; wide[i * 2 + 1] ^= hi; } gf_reduce_wide(r, wide); } void gf_inv(GFElem r, const GFElem a) { if (gfe_is_zero(a)) { gfe_zero(r); return; } Limb mod[NLIMBS + 1]; memset(mod, 0, sizeof(mod)); mod[GF_M >> 6] |= (Limb)1 << (GF_M & 63); { int pos; pos = GF_K0; mod[pos >> 6] |= (Limb)1 << (pos & 63); #if GF_NKS >= 2 pos = GF_K1; mod[pos >> 6] |= (Limb)1 << (pos & 63); #endif #if GF_NKS >= 3 pos = GF_K2; mod[pos >> 6] |= (Limb)1 << (pos & 63); #endif mod[0] |= 1; } Limb u[NLIMBS + 1], v[NLIMBS + 1]; Limb g1[NLIMBS + 1], g2[NLIMBS + 1]; memset(u, 0, sizeof(u)); memcpy(u, a, sizeof(GFElem)); memcpy(v, mod, sizeof(v)); memset(g1, 0, sizeof(g1)); g1[0] = 1; memset(g2, 0, sizeof(g2)); while (1) { int u_is_one = (u[0] == 1); for (int i = 1; i <= NLIMBS; i++) if (u[i]) { u_is_one = 0; break; } if (u_is_one) break; int lu = 0, lv = 0; for (int i = NLIMBS; i >= 0; i--) { if (u[i] && !lu) { Limb w = u[i]; int b = 0; while (w) { b++; w >>= 1; } lu = i * 64 + b; } if (v[i] && !lv) { Limb w = v[i]; int b = 0; while (w) { b++; w >>= 1; } lv = i * 64 + b; } if (lu && lv) break; } int j = lu - lv; if (j < 0) { Limb tmpbuf[NLIMBS + 1]; memcpy(tmpbuf, u, sizeof(tmpbuf)); memcpy(u, v, sizeof(tmpbuf)); memcpy(v, tmpbuf, sizeof(tmpbuf)); memcpy(tmpbuf, g1, sizeof(tmpbuf)); memcpy(g1, g2, sizeof(tmpbuf)); memcpy(g2, tmpbuf, sizeof(tmpbuf)); j = -j; } int wshift = j >> 6, bshift = j & 63; for (int i = NLIMBS; i >= 0; i--) { if (i - wshift >= 0) u[i] ^= v[i - wshift] << bshift; if (bshift && i - wshift - 1 >= 0) u[i] ^= v[i - wshift - 1] >> (64 - bshift); } for (int i = NLIMBS; i >= 0; i--) { if (i - wshift >= 0) g1[i] ^= g2[i - wshift] << bshift; if (bshift && i - wshift - 1 >= 0) g1[i] ^= g2[i - wshift - 1] >> (64 - bshift); } } GFElem2 wide; memset(wide, 0, sizeof(wide)); memcpy(wide, g1, (NLIMBS + 1) * sizeof(Limb)); gf_reduce_wide(r, wide); } void half_trace(GFElem r, const GFElem a) { GFElem z, tmp; gfe_copy(z, a); int limit = (GF_M - 1) / 2; for (int i = 0; i < limit; i++) { gf_sqr(tmp, z); gf_sqr(z, tmp); for (int j = 0; j < NLIMBS; j++) z[j] ^= a[j]; } gfe_copy(r, z); } int point_decompress( GFElem rx, GFElem ry, const GFElem x_in, const GFElem curve_a, const GFElem curve_b, int yp_bit) { GFElem x; gfe_copy(x, x_in); if (gfe_is_zero(x)) return 0; GFElem inv_x, sq_inv_x, b_term, rhs; gf_inv(inv_x, x); gf_sqr(sq_inv_x, inv_x); gf_mul(b_term, curve_b, sq_inv_x); for (int i = 0; i < NLIMBS; i++) rhs[i] = x[i] ^ curve_a[i] ^ b_term[i]; GFElem z; half_trace(z, rhs); GFElem z2, lhs; gf_sqr(z2, z); for (int i = 0; i < NLIMBS; i++) lhs[i] = z2[i] ^ z[i]; if (!gfe_eq(lhs, rhs)) return 0; int yp = gf2_getbit(x, yp_bit) & 1; if ((z[0] & 1) != (Limb)yp) z[0] ^= 1; gfe_copy(rx, x); gf_mul(ry, x, z); return 1; } int point_add( GFElem rx, GFElem ry, const GFElem p1x, const GFElem p1y, int p1inf, const GFElem p2x, const GFElem p2y, int p2inf, const GFElem curve_a) { if (p1inf) { gfe_copy(rx, p2x); gfe_copy(ry, p2y); return p2inf; } if (p2inf) { gfe_copy(rx, p1x); gfe_copy(ry, p1y); return 0; } if (gfe_eq(p1x, p2x)) { if (gfe_eq(p1y, p2y)) { if (gfe_is_zero(p1x)) { gfe_zero(rx); gfe_zero(ry); return 1; } GFElem inv_x1, lam, lam_sq, x3, y3, tmp; gf_inv(inv_x1, p1x); gf_mul(lam, inv_x1, p1y); for (int i = 0; i < NLIMBS; i++) lam[i] ^= p1x[i]; gf_sqr(lam_sq, lam); for (int i = 0; i < NLIMBS; i++) x3[i] = lam_sq[i] ^ lam[i] ^ curve_a[i]; gf_sqr(tmp, p1x); GFElem lam1; gfe_copy(lam1, lam); lam1[0] ^= 1; gf_mul(y3, x3, lam1); for (int i = 0; i < NLIMBS; i++) y3[i] ^= tmp[i]; gfe_copy(rx, x3); gfe_copy(ry, y3); return 0; } gfe_zero(rx); gfe_zero(ry); return 1; } GFElem dx, dy, inv_dx, lam, lam_sq, x3, y3, tmp; for (int i = 0; i < NLIMBS; i++) dx[i] = p1x[i] ^ p2x[i]; for (int i = 0; i < NLIMBS; i++) dy[i] = p1y[i] ^ p2y[i]; gf_inv(inv_dx, dx); gf_mul(lam, inv_dx, dy); gf_sqr(lam_sq, lam); for (int i = 0; i < NLIMBS; i++) x3[i] = lam_sq[i] ^ lam[i] ^ p1x[i] ^ p2x[i] ^ curve_a[i]; GFElem x3_p1x; for (int i = 0; i < NLIMBS; i++) x3_p1x[i] = x3[i] ^ p1x[i]; gf_mul(tmp, lam, x3_p1x); for (int i = 0; i < NLIMBS; i++) y3[i] = tmp[i] ^ x3[i] ^ p1y[i]; gfe_copy(rx, x3); gfe_copy(ry, y3); return 0; } int scalar_mul( GFElem rx, GFElem ry, const uint8_t *kbytes, int klen, const GFElem px, const GFElem py, const GFElem curve_a) { GFElem ax, ay, qx, qy; gfe_copy(ax, px); gfe_copy(ay, py); gfe_zero(qx); gfe_zero(qy); int qinf = 1; int total_bits = klen * 8; for (int i = 0; i < total_bits; i++) { int byte_idx = klen - 1 - (i >> 3); int bit = (kbytes[byte_idx] >> (i & 7)) & 1; if (bit) { GFElem tx, ty; int tinf = point_add(tx, ty, qx, qy, qinf, ax, ay, 0, curve_a); gfe_copy(qx, tx); gfe_copy(qy, ty); qinf = tinf; } if (i + 1 < total_bits) { GFElem tx, ty; int ainf = point_add(tx, ty, ax, ay, 0, ax, ay, 0, curve_a); gfe_copy(ax, tx); gfe_copy(ay, ty); if (ainf) break; } } gfe_copy(rx, qx); gfe_copy(ry, qy); return !qinf; } int scalar_mul_limbs( GFElem rx, GFElem ry, const GFElem k, const GFElem px, const GFElem py, const GFElem curve_a) { GFElem ax, ay, qx, qy; gfe_copy(ax, px); gfe_copy(ay, py); gfe_zero(qx); gfe_zero(qy); int qinf = 1; for (int i = 0; i < NLIMBS * 64; i++) { if (gf2_getbit(k, i)) { GFElem tx, ty; int tinf = point_add(tx, ty, qx, qy, qinf, ax, ay, 0, curve_a); gfe_copy(qx, tx); gfe_copy(qy, ty); qinf = tinf; } if (i + 1 < NLIMBS * 64) { GFElem tx, ty; int ainf = point_add(tx, ty, ax, ay, 0, ax, ay, 0, curve_a); gfe_copy(ax, tx); gfe_copy(ay, ty); if (ainf) break; } } gfe_copy(rx, qx); gfe_copy(ry, qy); return !qinf; } int decompress_search( GFElem rx, GFElem ry, const GFElem x_in, const GFElem curve_a, const GFElem curve_b, int yp_bit) { GFElem x; gfe_copy(x, x_in); while (1) { if (point_decompress(rx, ry, x, curve_a, curve_b, yp_bit)) { return 1; } limbs_add_small(x, NLIMBS, 2); } } void process_block_state( GFElem qx, GFElem qy, int *qinf, Limb *checksum, Limb *counter, const uint8_t *block, const GFElem curve_a, const GFElem curve_b) { Limb blk[CHECKSUM_LIMBS]; GFElem x, px, py; limbs_from_bytes_be(blk, CHECKSUM_LIMBS, block, HASH_BLOCK_SIZE); for (int i = 0; i < CHECKSUM_LIMBS; i++) { checksum[i] ^= blk[i]; } gfe_zero(x); x[0] = 1; limbs_xor_shifted(x, NLIMBS, counter, COUNTER_LIMBS, CLEN_BITS); limbs_xor_shifted(x, NLIMBS, blk, CHECKSUM_LIMBS, CLEN_BITS * 2); decompress_search(px, py, x, curve_a, curve_b, YP_BIT); *qinf = point_add(qx, qy, qx, qy, *qinf, px, py, 0, curve_a); limbs_add_small(counter, COUNTER_LIMBS, 1); } void process_blocks_state( GFElem qx, GFElem qy, int *qinf, Limb *checksum, Limb *counter, const uint8_t *blocks, int nblocks, const GFElem curve_a, const GFElem curve_b) { for (int i = 0; i < nblocks; i++) { process_block_state( qx, qy, qinf, checksum, counter, blocks + (i * HASH_BLOCK_SIZE), curve_a, curve_b ); } } int finalize_hash_state( GFElem qx, GFElem qy, int *qinf, Limb *checksum, Limb *counter, const uint8_t *tail, int tail_len, const Limb *msg_len_bits, const GFElem curve_a, const GFElem curve_b, const GFElem gx, const GFElem gy) { uint8_t block[HASH_BLOCK_SIZE]; GFElem x, px, py, sx, sy, k; memset(block, 0, sizeof(block)); if (tail_len > 0) { memcpy(block, tail, tail_len); } block[tail_len] = 0x80; process_block_state(qx, qy, qinf, checksum, counter, block, curve_a, curve_b); gfe_zero(x); x[0] = 1; limbs_xor_shifted(x, NLIMBS, msg_len_bits, COUNTER_LIMBS, CLEN_BITS); limbs_xor_shifted(x, NLIMBS, checksum, CHECKSUM_LIMBS, CLEN_BITS * 2); decompress_search(px, py, x, curve_a, curve_b, YP_BIT); *qinf = point_add(qx, qy, qx, qy, *qinf, px, py, 0, curve_a); if (*qinf) { return 0; } limbs_rshift1(k, qx, NLIMBS); if (!scalar_mul_limbs(sx, sy, k, gx, gy, curve_a)) { return 0; } *qinf = point_add(qx, qy, qx, qy, *qinf, sx, sy, 0, curve_a); return !*qinf; } """ DEF_TEMPLATE = r""" void gf_mul(uint64_t *r, const uint64_t *a, const uint64_t *b); void gf_sqr(uint64_t *r, const uint64_t *a); void gf_inv(uint64_t *r, const uint64_t *a); void half_trace(uint64_t *r, const uint64_t *a); int point_decompress(uint64_t *rx, uint64_t *ry, const uint64_t *x_in, const uint64_t *curve_a, const uint64_t *curve_b, int yp_bit); int point_add(uint64_t *rx, uint64_t *ry, const uint64_t *p1x, const uint64_t *p1y, int p1inf, const uint64_t *p2x, const uint64_t *p2y, int p2inf, const uint64_t *curve_a); int scalar_mul(uint64_t *rx, uint64_t *ry, const uint8_t *kbytes, int klen, const uint64_t *px, const uint64_t *py, const uint64_t *curve_a); void process_block_state(uint64_t *qx, uint64_t *qy, int *qinf, uint64_t *checksum, uint64_t *counter, const uint8_t *block, const uint64_t *curve_a, const uint64_t *curve_b); void process_blocks_state(uint64_t *qx, uint64_t *qy, int *qinf, uint64_t *checksum, uint64_t *counter, const uint8_t *blocks, int nblocks, const uint64_t *curve_a, const uint64_t *curve_b); int finalize_hash_state(uint64_t *qx, uint64_t *qy, int *qinf, uint64_t *checksum, uint64_t *counter, const uint8_t *tail, int tail_len, const uint64_t *msg_len_bits, const uint64_t *curve_a, const uint64_t *curve_b, const uint64_t *gx, const uint64_t *gy); """ class ECOHCurve: def __init__(self, m, ks, a, b, gx, gy): self.m = m self.ks = tuple(ks) self.a = a self.b = b self.g = (gx, gy) self.modulus = 1 << m for k in self.ks: self.modulus |= 1 << k self.modulus |= 1 self.low_mask = (1 << m) - 1 self.square_table = self.make_square_table() return def make_square_table(self): table = [] for value in range(256): out = 0 for bit in range(8): if value & (1 << bit): out |= 1 << (bit * 2) table.append(out) return table def init_cffi_backend(self): try: import cffi import warnings except ImportError: self.USE_CFFI = False return key = self.__class__ base_class = Hash.ECOHBase if not hasattr(base_class, "cffi_cache"): base_class.cffi_cache = {} # fast return if key in base_class.cffi_cache: self.cffi = base_class.cffi_cache[key] self.USE_CFFI = True return # ffi, lib ks_padded = list(self.curve.ks) + [0] * (3 - len(self.curve.ks)) nlimbs = (self.curve.m + 63) // 64 + 1 # one extra limb for overflow headroom counter_limbs = (self.clen_bits + 63) // 64 checksum_limbs = self.block_size // 8 C_TEMPLATE = base_class.C_TEMPLATE C_TEMPLATE = C_TEMPLATE.replace("REPLACE_NLIMBS", f"({nlimbs})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_M", f"({self.curve.m})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_K0", f"({ks_padded[0]})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_K1", f"({ks_padded[1]})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_K2", f"({ks_padded[2]})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_NKS", f"({len(self.curve.ks)})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_BLOCK_SIZE", f"({self.block_size})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_CLEN_BITS", f"({self.clen_bits})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_YP_BIT", f"({self.yp_bit})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_COUNTER_LIMBS", f"({counter_limbs})") C_TEMPLATE = C_TEMPLATE.replace("REPLACE_CHECKSUM_LIMBS", f"({checksum_limbs})") try: with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=r"reimporting '_cffi__.*' might overwrite older definitions", category=UserWarning, module=r"cffi\.vengine_cpy", ) ffi = cffi.FFI() ffi.cdef(base_class.DEF_TEMPLATE) lib = ffi.verify(C_TEMPLATE, extra_compile_args=["-O2"]) except Exception: self.USE_CFFI = False return # Different for each class def to_limbs(value, size): arr = ffi.new("uint64_t[]", size) for i in range(size): arr[i] = (value >> (64 * i)) & 0xffff_ffff_ffff_ffff return arr try: curve_a = to_limbs(self.curve.a, nlimbs) curve_b = to_limbs(self.curve.b, nlimbs) gx = to_limbs(self.curve.g[0], nlimbs) gy = to_limbs(self.curve.g[1], nlimbs) qx = ffi.new("uint64_t[]", nlimbs) qy = ffi.new("uint64_t[]", nlimbs) except Exception: self.USE_CFFI = False return # add to cache cffi_obj = collections.namedtuple("CFFI", "ffi lib nlimbs counter_limbs checksum_limbs curve_a curve_b gx gy qx qy")( ffi, lib, nlimbs, counter_limbs, checksum_limbs, curve_a, curve_b, gx, gy, qx, qy, ) self.cffi = base_class.cffi_cache[key] = cffi_obj self.USE_CFFI = True return def __init__(self, data=b""): self.buf = bytearray() self.msg_len = 0 self.checksum = 0 self.counter = 0 self.q = None self.curve = self.ECOHCurve(*self.curve_const) self.init_cffi_backend() if self.USE_CFFI: self.cffi_qinf = self.cffi.ffi.new("int *", 1) self.cffi_qinf[0] = 1 self.cffi_checksum = self.cffi.ffi.new("uint64_t[]", self.cffi.checksum_limbs) self.cffi_counter = self.cffi.ffi.new("uint64_t[]", self.cffi.counter_limbs) if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.checksum = self.checksum other.counter = self.counter other.q = None if self.q is None else (self.q[0], self.q[1]) other.USE_CFFI = self.USE_CFFI if other.USE_CFFI: other.cffi = self.cffi other.cffi_qinf = other.cffi.ffi.new("int *", self.cffi_qinf[0]) other.cffi_checksum = other.cffi.ffi.new("uint64_t[]", list(self.cffi_checksum)) other.cffi_counter = other.cffi.ffi.new("uint64_t[]", list(self.cffi_counter)) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) if self.USE_CFFI: full_len = (len(self.buf) // self.block_size) * self.block_size if full_len: blocks = bytes(self.buf[:full_len]) del self.buf[:full_len] block_buf = self.cffi.ffi.new("uint8_t[]", blocks) self.cffi.lib.process_blocks_state( self.cffi.qx, self.cffi.qy, self.cffi_qinf, self.cffi_checksum, self.cffi_counter, block_buf, full_len // self.block_size, self.cffi.curve_a, self.cffi.curve_b, ) self.counter += full_len // self.block_size return self # --- pure Python --- while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.process_block(block) return self def digest(self): c = self.copy() c.finalize() value = c.q[0] >> 1 mask = (1 << (self.digest_size * 8)) - 1 return (value & mask).to_bytes(self.digest_size, "big") def hexdigest(self): return self.digest().hex() def finalize(self): if (self.msg_len * 8) >= (1 << self.clen_bits): raise OverflowError("message too long for ECOH length field") def int_to_limbs(value, nlimbs): arr = self.cffi.ffi.new("uint64_t[]", nlimbs) for i in range(nlimbs): arr[i] = (value >> (64 * i)) & 0xffff_ffff_ffff_ffff return arr def limbs_to_int(arr, nlimbs): result = 0 for i in range(nlimbs - 1, -1, -1): result = (result << 64) | int(arr[i]) return result if self.USE_CFFI: msg_len_bits = int_to_limbs(self.msg_len * 8, self.cffi.counter_limbs) tail_bytes = bytes(self.buf) tail = self.cffi.ffi.new("uint8_t[]", tail_bytes or b"\x00") self.cffi.lib.finalize_hash_state( self.cffi.qx, self.cffi.qy, self.cffi_qinf, self.cffi_checksum, self.cffi_counter, tail, len(tail_bytes), msg_len_bits, self.cffi.curve_a, self.cffi.curve_b, self.cffi.gx, self.cffi.gy, ) self.buf = bytearray() self.counter += 1 if self.cffi_qinf[0]: self.q = None else: self.q = (limbs_to_int(self.cffi.qx, self.cffi.nlimbs), limbs_to_int(self.cffi.qy, self.cffi.nlimbs)) return # --- pure Python --- pad_len = self.block_size - len(self.buf) - 1 block = bytes(self.buf) + b"\x80" + (b"\x00" * pad_len) self.process_block(block) x = 1 x |= (self.msg_len * 8) << self.clen_bits x |= self.checksum << (2 * self.clen_bits) p = self.decompress_search(x) self.q = self.point_add(self.q, p) k = self.q[0] >> 1 self.q = self.point_add(self.q, self.scalar_mul(k, self.curve.g)) return def process_block(self, block): n = int.from_bytes(block, "big") self.checksum ^= n x = 1 x |= self.counter << self.clen_bits x |= n << (2 * self.clen_bits) p = self.decompress_search(x) self.q = self.point_add(self.q, p) self.counter += 1 return def decompress_search(self, x): while True: p = self.point_decompress(x) if p is not None: return p x += 2 return def gf_reduce(self, x): m = self.curve.m ks = self.curve.ks low_mask = self.curve.low_mask while x.bit_length() > m: hi = x >> m x = (x & low_mask) ^ hi for k in ks: x ^= hi << k return x def gf_mul(self, a, b): if a == 0 or b == 0: return 0 if a < b: a, b = b, a a1 = a a2 = a << 1 a4 = a << 2 a8 = a << 3 table = ( 0, a1, a2, a1 ^ a2, a4, a1 ^ a4, a2 ^ a4, a1 ^ a2 ^ a4, a8, a1 ^ a8, a2 ^ a8, a1 ^ a2 ^ a8, a4 ^ a8, a1 ^ a4 ^ a8, a2 ^ a4 ^ a8, a1 ^ a2 ^ a4 ^ a8, ) r = 0 shift = 0 while b: r ^= table[b & 0xf] << shift b >>= 4 shift += 4 return self.gf_reduce(r) def gf_sqr(self, a): table = self.curve.square_table r = 0 shift = 0 while a: r |= table[a & 0xff] << shift a >>= 8 shift += 16 return self.gf_reduce(r) def gf_inv(self, a): if a == 0: return 0 u = a v = self.curve.modulus g1 = 1 g2 = 0 while u != 1: j = u.bit_length() - v.bit_length() if j < 0: u, v, g1, g2, j = v, u, g2, g1, -j u ^= v << j g1 ^= g2 << j return self.gf_reduce(g1) def half_trace(self, a): z = a limit = (self.curve.m - 1) // 2 for _ in range(limit): z = self.gf_sqr(self.gf_sqr(z)) ^ a return z def point_decompress(self, x): x = self.gf_reduce(x) if x == 0: return None rhs = x ^ self.curve.a ^ self.gf_mul(self.curve.b, self.gf_sqr(self.gf_inv(x))) z = self.half_trace(rhs) if (self.gf_sqr(z) ^ z) != rhs: return None yp = (x >> self.yp_bit) & 1 if (z & 1) != yp: z ^= 1 y = self.gf_mul(x, z) return (x, y) def point_double(self, p): if p is None: return None x1, y1 = p if x1 == 0: return None lam = self.gf_mul(self.gf_inv(x1), y1) ^ x1 x3 = self.gf_sqr(lam) ^ lam ^ self.curve.a y3 = self.gf_sqr(x1) ^ self.gf_mul(x3, lam ^ 1) return (x3, y3) def point_add(self, p, q): if p is None: return q if q is None: return p x1, y1 = p x2, y2 = q if x1 == x2: if y1 == y2: return self.point_double(p) return None lam = self.gf_mul(self.gf_inv(x1 ^ x2), y1 ^ y2) x3 = self.gf_sqr(lam) ^ lam ^ x1 ^ x2 ^ self.curve.a y3 = self.gf_mul(lam, x3 ^ x1) ^ x3 ^ y1 return (x3, y3) def scalar_mul(self, k, p): r = None a = p while k: if k & 1: r = self.point_add(r, a) k >>= 1 if k: a = self.point_double(a) return r class ECOH224(ECOHBase): block_size = 16 digest_size = 28 clen_bits = 64 yp_bit = 255 curve_const = ( 283, (12, 7, 5), 0x1, 0x27b_680a_c8b8_596d_a5a4_af8a_19a0_303f_ca97_fd76_4530_9fa2_a581_485a_f626_3e31_3b79_a2f5, 0x5f9_3925_8db7_dd90_e193_4f8c_70b0_dfec_2eed_25b8_557e_ac9c_80e2_e198_f8cd_becd_86b1_2053, 0x367_6854_fe24_141c_b98f_e6d4_b20d_02b4_516f_f702_350e_ddb0_8267_79c8_13f0_df45_be81_12f4, ) class ECOH256(ECOH224): digest_size = 32 class ECOH384(ECOHBase): block_size = 24 digest_size = 48 clen_bits = 64 yp_bit = 319 curve_const = ( 409, (87,), 0x1, int("0x021_a5c2_c8ee_9feb_5c4b_9a75_3b7b_476b_7fd6_422e_f1f3_dd67_4761_fa99_d6ac_27c8_a9a1_97b2" "_7282_2f6c_d57a_55aa_4f50_ae31_7b13_545f", 16), int("0x15d_4860_d088_ddb3_496b_0c60_6475_6260_441c_de4a_f177_1d4d_b01f_fe5b_34e5_9703_dc25_5a86" "_8a11_8051_5603_aeab_6079_4e54_bb79_96a7", 16), int("0x061_b1cf_ab6b_e5f3_2bbf_a783_24ed_106a_7636_b9c5_a7bd_198d_0158_aa4f_5488_d08f_3851_4f1f" "_df4b_4f40_d218_1b36_81c3_64ba_0273_c706", 16), ) class ECOH512(ECOH384): block_size = 32 digest_size = 64 clen_bits = 128 yp_bit = 511 curve_const = ( 571, (10, 5, 2), 0x1, int("0x2f4_0e7e_2221_f295_de29_7117_b7f3_d62f_5c6a_97ff_cb8c_eff1_cd6b_a8ce_4a9a_18ad_84ff_abbd" "_8efa_5933_2be7_ad67_56a6_6e29_4afd_185a_78ff_12aa_520e_4de7_39ba_ca0c_7ffe_ff7f_2955_727a", 16), int("0x303_001d_34b8_5629_6c16_c0d4_0d3c_d775_0a93_d1d2_955f_a80a_a5f4_0fc8_db7b_2abd_bde5_3950" "_f4c0_d293_cdd7_11a3_5b67_fb14_99ae_6003_8614_f139_4abf_a3b4_c850_d927_e1e7_769c_8eec_2d19", 16), int("0x37b_f273_42da_639b_6dcc_fffe_b73d_69d7_8c6c_27a6_009c_bbca_1980_f853_3921_e8a6_8442_3e43" "_bab0_8a57_6291_af8f_461b_b2a8_b353_1d2f_0485_c19b_16e2_f151_6e23_dd3c_1a48_27af_1b8a_c15b", 16), ) class EDONRBase: def __init__(self, data=b""): self.double_pipe = list(self.init_pipe) self.buf = bytearray() self.msg_bits = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.double_pipe = list(self.double_pipe) other.buf = bytearray(self.buf) other.msg_bits = self.msg_bits return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_bits += len(data) * 8 self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def digest(self): other = self.copy() other.finalize() word_count = self.digest_size // (self.word_bits // 8) words = other.double_pipe[self.out_offset_words:self.out_offset_words + word_count] return struct.pack("<" + (self.word_format * word_count), *words) def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_bits self.buf.append(0x80) while (len(self.buf) % self.block_size) != (self.block_size - 8): self.buf.append(0x00) self.buf.extend(struct.pack("= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return def rol(self, x, n): return ((x << n) | (x >> (self.word_bits - n))) & self.word_mask def q(self, x0, x1, x2, x3, x4, x5, x6, x7, y0, y1, y2, y3, y4, y5, y6, y7): t0 = (self.q_const_a + x0 + x1 + x2 + x4 + x7) & self.word_mask t1 = (x0 + x1 + x3 + x4 + x7) & self.word_mask t2 = (x0 + x1 + x4 + x6 + x7) & self.word_mask t3 = (x2 + x3 + x5 + x6 + x7) & self.word_mask t4 = (x1 + x2 + x3 + x5 + x6) & self.word_mask t5 = (x0 + x2 + x3 + x4 + x5) & self.word_mask t6 = (x0 + x1 + x5 + x6 + x7) & self.word_mask t7 = (x2 + x3 + x4 + x5 + x6) & self.word_mask t1 = self.rol(t1, self.rot1[0]) t2 = self.rol(t2, self.rot1[1]) t3 = self.rol(t3, self.rot1[2]) t4 = self.rol(t4, self.rot1[3]) t5 = self.rol(t5, self.rot1[4]) t6 = self.rol(t6, self.rot1[5]) t7 = self.rol(t7, self.rot1[6]) u8 = t3 ^ t5 ^ t6 u9 = t2 ^ t5 ^ t6 u10 = t2 ^ t3 ^ t5 u11 = t0 ^ t1 ^ t4 u12 = t0 ^ t4 ^ t7 u13 = t1 ^ t6 ^ t7 u14 = t2 ^ t3 ^ t4 u15 = t0 ^ t1 ^ t7 s0 = (self.q_const_b + y0 + y1 + y2 + y5 + y7) & self.word_mask s1 = (y0 + y1 + y3 + y4 + y6) & self.word_mask s2 = (y0 + y1 + y2 + y3 + y5) & self.word_mask s3 = (y2 + y3 + y4 + y6 + y7) & self.word_mask s4 = (y0 + y1 + y3 + y4 + y5) & self.word_mask s5 = (y2 + y4 + y5 + y6 + y7) & self.word_mask s6 = (y1 + y2 + y5 + y6 + y7) & self.word_mask s7 = (y0 + y3 + y4 + y6 + y7) & self.word_mask s1 = self.rol(s1, self.rot2[0]) s2 = self.rol(s2, self.rot2[1]) s3 = self.rol(s3, self.rot2[2]) s4 = self.rol(s4, self.rot2[3]) s5 = self.rol(s5, self.rot2[4]) s6 = self.rol(s6, self.rot2[5]) s7 = self.rol(s7, self.rot2[6]) z5 = (u8 + (s3 ^ s4 ^ s6)) & self.word_mask z6 = (u9 + (s2 ^ s5 ^ s7)) & self.word_mask z7 = (u10 + (s4 ^ s6 ^ s7)) & self.word_mask z0 = (u11 + (s0 ^ s1 ^ s5)) & self.word_mask z1 = (u12 + (s2 ^ s6 ^ s7)) & self.word_mask z2 = (u13 + (s0 ^ s1 ^ s3)) & self.word_mask z3 = (u14 + (s0 ^ s3 ^ s4)) & self.word_mask z4 = (u15 + (s1 ^ s2 ^ s5)) & self.word_mask return [z0, z1, z2, z3, z4, z5, z6, z7] def compress(self, block): words = list(struct.unpack("<" + (self.word_format * (self.block_size // (self.word_bits // 8))), block)) p = self.double_pipe z = self.q( words[15], words[14], words[13], words[12], words[11], words[10], words[9], words[8], words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], ) for i in range(8): p[16 + i] = z[i] z = self.q( p[16], p[17], p[18], p[19], p[20], p[21], p[22], p[23], words[8], words[9], words[10], words[11], words[12], words[13], words[14], words[15], ) for i in range(8): p[24 + i] = z[i] z = self.q( p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19], p[20], p[21], p[22], p[23], ) for i in range(8): p[16 + i] = z[i] z = self.q( p[16], p[17], p[18], p[19], p[20], p[21], p[22], p[23], p[24], p[25], p[26], p[27], p[28], p[29], p[30], p[31], ) for i in range(8): p[24 + i] = z[i] z = self.q( p[16], p[17], p[18], p[19], p[20], p[21], p[22], p[23], p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], ) for i in range(8): p[16 + i] = z[i] z = self.q( p[24], p[25], p[26], p[27], p[28], p[29], p[30], p[31], p[16], p[17], p[18], p[19], p[20], p[21], p[22], p[23], ) for i in range(8): p[24 + i] = z[i] z = self.q( words[7], words[6], words[5], words[4], words[3], words[2], words[1], words[0], p[16], p[17], p[18], p[19], p[20], p[21], p[22], p[23], ) for i in range(8): p[i] = z[i] z = self.q( p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[24], p[25], p[26], p[27], p[28], p[29], p[30], p[31], ) for i in range(8): p[8 + i] = z[i] return class EDONR224(EDONRBase): block_size = 64 digest_size = 28 word_bits = 32 word_mask = 0xffff_ffff q_const_a = 0xaaaa_aaaa q_const_b = 0x5555_5555 rot1 = [4, 8, 13, 17, 22, 24, 29] rot2 = [5, 9, 11, 15, 20, 25, 27] word_format = "I" init_pipe = [ 0x0001_0203, 0x0405_0607, 0x0809_0a0b, 0x0c0d_0e0f, 0x1011_1213, 0x1415_1617, 0x1819_1a1b, 0x1c1d_1e1f, 0x2021_2223, 0x2425_2627, 0x2829_2a2b, 0x2c2d_2e2f, 0x3031_3233, 0x2435_3637, 0x3839_3a3b, 0x3c3d_3e3f, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, ] out_offset_words = 9 class EDONR256(EDONR224): digest_size = 32 init_pipe = [ 0x4041_4243, 0x4445_4647, 0x4849_4a4b, 0x4c4d_4e4f, 0x5051_5253, 0x5455_5657, 0x5859_5a5b, 0x5c5d_5e5f, 0x6061_6263, 0x6465_6667, 0x6869_6a6b, 0x6c6d_6e6f, 0x7071_7273, 0x7475_7677, 0x7879_7a7b, 0x7c7d_7e7f, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000, ] out_offset_words = 8 class EDONR384(EDONRBase): block_size = 128 digest_size = 48 word_bits = 64 word_mask = 0xffff_ffff_ffff_ffff q_const_a = 0xaaaa_aaaa_aaaa_aaaa q_const_b = 0x5555_5555_5555_5555 rot1 = [5, 15, 22, 31, 40, 50, 59] rot2 = [10, 19, 29, 36, 44, 48, 55] word_format = "Q" init_pipe = [ 0x0001_0203_0405_0607, 0x0809_0a0b_0c0d_0e0f, 0x1011_1213_1415_1617, 0x1819_1a1b_1c1d_1e1f, 0x2021_2223_2425_2627, 0x2829_2a2b_2c2d_2e2f, 0x3031_3233_2435_3637, 0x3839_3a3b_3c3d_3e3f, 0x4041_4243_4445_4647, 0x4849_4a4b_4c4d_4e4f, 0x5051_5253_5455_5657, 0x5859_5a5b_5c5d_5e5f, 0x6061_6263_6465_6667, 0x6869_6a6b_6c6d_6e6f, 0x7071_7273_7475_7677, 0x7879_7a7b_7c7d_7e7f, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, ] out_offset_words = 10 class EDONR512(EDONR384): digest_size = 64 init_pipe = [ 0x8081_8283_8485_8687, 0x8889_8a8b_8c8d_8e8f, 0x9091_9293_9495_9697, 0x9899_9a9b_9c9d_9e9f, 0xa0a1_a2a3_a4a5_a6a7, 0xa8a9_aaab_acad_aeaf, 0xb0b1_b2b3_b4b5_b6b7, 0xb8b9_babb_bcbd_bebf, 0xc0c1_c2c3_c4c5_c6c7, 0xc8c9_cacb_cccd_cecf, 0xd0d1_d2d3_d4d5_d6d7, 0xd8d9_dadb_dcdd_dedf, 0xe0e1_e2e3_e4e5_e6e7, 0xe8e9_eaeb_eced_eeef, 0xf0f1_f2f3_f4f5_f6f7, 0xf8f9_fafb_fcfd_feff, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, ] out_offset_words = 8 class EnRUPTBase: word_bits = 64 parallelism = 2 security_parameter = 4 def __init__(self, data=b""): self.word_bytes = self.word_bits // 8 self.word_mask = (1 << self.word_bits) - 1 self.state_words = ( ( self.hashbitlen * 2 + self.word_bits * self.parallelism - 1 ) // (self.word_bits * self.parallelism) ) * self.parallelism self.x = [0] * self.state_words self.d = [0] * self.parallelism self.r = 0 self.buf = bytearray() self.finalized = False if data: self.update(data) return def copy(self): other = self.__class__() other.x = self.x[:] other.d = self.d[:] other.r = self.r other.buf = bytearray(self.buf) other.finalized = self.finalized return other def update(self, data): if self.finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.buf.extend(bytes(data)) while len(self.buf) >= self.word_bytes: word = int.from_bytes(self.buf[:self.word_bytes], "big") del self.buf[:self.word_bytes] self.absorb_word(word) return self def digest(self): other = self.copy() other.finalize() return other.squeeze() def hexdigest(self): return self.digest().hex() def rotr(self, x, n): return ((x >> n) | (x << (self.word_bits - n))) & self.word_mask def ir1(self): r = self.r h = self.state_words p = self.parallelism a = (r // p * p + (r + 1) % p) % h b = (r + 2 * p) % h f = self.rotr( ((self.x[a] * 2) ^ self.x[b] ^ self.d[r % p] ^ r) & self.word_mask, self.word_bits // 4, ) f = (f * 9) & self.word_mask self.x[(r + p) % h] ^= f self.d[r % p] ^= f ^ self.x[(h * p // 2 + p % 2 + r) % h] self.r = (r + 1) & self.word_mask return def enr2s(self, word): for _ in range(2 * self.security_parameter): self.ir1() self.d[self.parallelism - 1] ^= word return def absorb_word(self, word): self.enr2s(word) return def finalize(self): if self.finalized: return padded = bytearray(self.buf) padded.append(0x80) while len(padded) % self.word_bytes != 0: padded.append(0x00) for i in range(0, len(padded), self.word_bytes): word = int.from_bytes(padded[i:i + self.word_bytes], "big") self.absorb_word(word) self.absorb_word(self.hashbitlen) for _ in range(self.state_words): self.absorb_word(0) self.buf.clear() self.finalized = True return def squeeze(self): out = bytearray() full_words = self.hashbitlen // self.word_bits remain_bits = self.hashbitlen % self.word_bits for _ in range(full_words): self.absorb_word(0) out.extend(self.d[self.parallelism - 1].to_bytes(self.word_bytes, "big")) if remain_bits: self.absorb_word(0) out.extend( self.d[self.parallelism - 1].to_bytes(self.word_bytes, "big")[ : (remain_bits + 7) // 8 ] ) return bytes(out) class EnRUPT224(EnRUPTBase): digest_size = 28 hashbitlen = 224 class EnRUPT256(EnRUPTBase): digest_size = 32 hashbitlen = 256 class EnRUPT384(EnRUPTBase): digest_size = 48 hashbitlen = 384 class EnRUPT512(EnRUPTBase): digest_size = 64 hashbitlen = 512 class ESSENCEBase: md_block_size = 1_048_576 compress_steps = 32 hash_tree_level = 0 organizational_small_constant = 0xb7e1_5162 organizational_big_constant = 0x8aed_2a6a_bf71_5880 expansion_of_pi_32 = [ 0x85a3_08d3, 0x243f_6a88, 0x0370_7344, 0x1319_8a2e, 0x299f_31d0, 0xa409_3822, 0xec4e_6c89, 0x082e_fa98, ] expansion_of_pi_64 = [ 0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344, 0xa409_3822_299f_31d0, 0x082e_fa98_ec4e_6c89, 0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c, 0xc0ac_29b7_c97c_50dd, 0x3f84_d5b5_b547_0917, ] l32_table = None l64_table = None def __init__(self, data=b""): self.word_bits = 32 if self.block_size == 32 else 64 self.word_mask = (1 << self.word_bits) - 1 self.chunk_size = self.block_size self.running_hash = self.init_running_hash() self.mdbiv_init = self.init_mdbiv() self.chain_vars = [0] * 8 self.buf = bytearray() self.buf_bitlen = 0 self.last_md_block_number = 0xffff_ffff_ffff_ffff self.within_md_block = False self.current_md_block_datalen = 0 self.ensure_tables() if data: self.update(data) return @classmethod def ensure_tables(cls): if cls.l32_table is None: cls.l32_table = cls.generate_l_table(32, 0x814a_3b35) if cls.l64_table is None: cls.l64_table = cls.generate_l_table(64, 0xb0a6_5313_e696_6997) return @classmethod def generate_l_table(cls, bits, poly): mask = (1 << bits) - 1 top = 1 << (bits - 1) shift = bits - 8 table = [] value = 0 while value < 256: x = value << shift i = 0 while i < 8: if x & top: x = ((x << 1) & mask) ^ poly else: x = (x << 1) & mask i += 1 table.append(x) value += 1 return table def init_running_hash(self): if self.word_bits == 64: return list(self.expansion_of_pi_64) return list(self.expansion_of_pi_32) def init_mdbiv(self): if self.word_bits == 64: return [ 0, self.md_block_size, (self.hashbitlen & 0xffff) | (self.compress_steps << 16) | (self.hash_tree_level << 24) | (self.organizational_small_constant << 32), self.organizational_big_constant, self.expansion_of_pi_64[4], self.expansion_of_pi_64[5], self.expansion_of_pi_64[6], self.expansion_of_pi_64[7], ] return [ 0, 0, self.md_block_size & 0xffff_ffff, (self.md_block_size >> 32) & 0xffff_ffff, (self.hashbitlen & 0xffff) | (self.compress_steps << 16) | (self.hash_tree_level << 24), self.organizational_small_constant, self.organizational_big_constant & 0xffff_ffff, (self.organizational_big_constant >> 32) & 0xffff_ffff, ] def copy(self): other = self.__class__() other.running_hash = list(self.running_hash) other.mdbiv_init = list(self.mdbiv_init) other.chain_vars = list(self.chain_vars) other.buf = bytearray(self.buf) other.buf_bitlen = self.buf_bitlen other.last_md_block_number = self.last_md_block_number other.within_md_block = self.within_md_block other.current_md_block_datalen = self.current_md_block_datalen return other def normalize_data(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") return bytes(data) def update(self, data): data = self.normalize_data(data) return self.update_bits(data, len(data) * 8) def update_bits(self, data, databitlen): data = self.normalize_data(data) if databitlen < 0: raise ValueError("databitlen must be >= 0") data_bytelen = (databitlen + 7) >> 3 if data_bytelen > len(data): raise ValueError("databitlen exceeds data length") if databitlen == 0: return self if (self.buf_bitlen % 8) != 0: raise ValueError("cannot append after non-byte-aligned update") chunk_bits = self.chunk_size * 8 offset = 0 if (self.buf_bitlen + databitlen) < chunk_bits: self.append_partial(data[:data_bytelen], databitlen) return self if self.buf_bitlen > 0: needed = self.chunk_size - len(self.buf) self.buf.extend(data[:needed]) offset += needed databitlen -= needed * 8 self.process_chunk(bytes(self.buf)) self.buf.clear() self.buf_bitlen = 0 full_bytes = databitlen >> 3 tail_bits = databitlen & 7 direct_bytes = full_bytes - (full_bytes % self.chunk_size) end = offset + direct_bytes while offset < end: self.process_chunk(data[offset:offset + self.chunk_size]) offset += self.chunk_size remain_bytes = full_bytes - direct_bytes if remain_bytes > 0: self.buf.extend(data[offset:offset + remain_bytes]) self.buf_bitlen = remain_bytes * 8 offset += remain_bytes if tail_bits != 0: self.buf.append(data[offset] & (0xff << (8 - tail_bits))) self.buf_bitlen += tail_bits return self def append_partial(self, data, databitlen): full_bytes = databitlen >> 3 tail_bits = databitlen & 7 if full_bytes > 0: self.buf.extend(data[:full_bytes]) if tail_bits != 0: self.buf.append(data[full_bytes] & (0xff << (8 - tail_bits))) self.buf_bitlen += databitlen return def init_chain_vars(self): block_number = (self.last_md_block_number + 1) & 0xffff_ffff_ffff_ffff if self.word_bits == 64: self.chain_vars[0] = block_number index = 1 while index < 8: self.chain_vars[index] = self.mdbiv_init[index] index += 1 else: self.chain_vars[0] = block_number & 0xffff_ffff self.chain_vars[1] = (block_number >> 32) & 0xffff_ffff index = 2 while index < 8: self.chain_vars[index] = self.mdbiv_init[index] index += 1 self.within_md_block = True self.current_md_block_datalen = 0 return def process_chunk(self, chunk): if not self.within_md_block: self.init_chain_vars() self.compress_words(self.chain_vars, chunk) self.current_md_block_datalen += self.chunk_size * 8 if (self.current_md_block_datalen >> 3) == self.md_block_size: self.merge_tree() return def merge_tree(self): self.last_md_block_number = (self.last_md_block_number + 1) & 0xffff_ffff_ffff_ffff self.within_md_block = False self.current_md_block_datalen = 0 self.compress_words(self.running_hash, self.words_to_bytes(self.chain_vars)) return def words_to_bytes(self, words): out = bytearray() if self.word_bits == 64: for value in words: out.extend(value.to_bytes(8, "little")) else: for value in words: out.extend(value.to_bytes(4, "little")) return bytes(out) def l_func(self, value): if self.word_bits == 64: table = self.l64_table shift = 56 count = 8 else: table = self.l32_table shift = 24 count = 4 i = 0 while i < count: value = table[value >> shift] ^ ((value << 8) & self.word_mask) i += 1 return value def f_func(self, a, b, c, d, e, f, g): m = self.word_mask na = a ^ m nb = b ^ m nc = c ^ m nd = d ^ m # codespell:ignore ne = e ^ m nf = f ^ m ng = g ^ m result = ( (a & nb & c & d & e & f & ng) ^ (a & b & c & e & nf & g) ^ (a & b & d & ne & g) ^ (a & b & e & f & g) ^ (a & nb & d & e & f) ^ (a & c & e & f & g) ^ (a & nc & d & f & ng) ^ (a & d & e & nf & g) ^ (b & c & d & f & g) ^ (b & d & e & nf & g) ^ (c & d & e & f & ng) ^ (a & b & nc & g) ^ (na & b & c & nf) ^ (b & c & ne & g) ^ (b & nc & d & e) ^ (a & b & f) ^ (a & nb & e) ^ (a & c & g) ^ (a & d & ng) ^ (a & e & g) ^ (na & e & f) ^ (b & d & nf) ^ (b & e & g) ^ (nb & f & g) ^ (c & nd & ne) # codespell:ignore ^ (nc & d & f) ^ (d & e & f) ^ (d & ne & g) ^ (d & f & g) ^ na ^ b ^ f ) return result & m def compress_words(self, chain_vars, block): if self.word_bits == 64: k0, k1, k2, k3, k4, k5, k6, k7 = struct.unpack("<8Q", block) else: k0, k1, k2, k3, k4, k5, k6, k7 = struct.unpack("<8I", block) r0, r1, r2, r3, r4, r5, r6, r7 = chain_vars r0_orig = r0 r1_orig = r1 r2_orig = r2 r3_orig = r3 r4_orig = r4 r5_orig = r5 r6_orig = r6 r7_orig = r7 i = 0 while i < self.compress_steps: tmp_r = self.l_func(r0) tmp_k = self.l_func(k0) f_r = self.f_func(r6, r5, r4, r3, r2, r1, r0) f_k = self.f_func(k6, k5, k4, k3, k2, k1, k0) tmp_r ^= f_r ^ r7 ^ k7 tmp_k ^= f_k ^ k7 r7 = r6 k7 = k6 r6 = r5 k6 = k5 r5 = r4 k5 = k4 r4 = r3 k4 = k3 r3 = r2 k3 = k2 r2 = r1 k2 = k1 r1 = r0 k1 = k0 r0 = tmp_r k0 = tmp_k i += 1 chain_vars[0] = (r0 ^ r0_orig) & self.word_mask chain_vars[1] = (r1 ^ r1_orig) & self.word_mask chain_vars[2] = (r2 ^ r2_orig) & self.word_mask chain_vars[3] = (r3 ^ r3_orig) & self.word_mask chain_vars[4] = (r4 ^ r4_orig) & self.word_mask chain_vars[5] = (r5 ^ r5_orig) & self.word_mask chain_vars[6] = (r6 ^ r6_orig) & self.word_mask chain_vars[7] = (r7 ^ r7_orig) & self.word_mask return def finalize(self): if self.buf_bitlen > 0: if not self.within_md_block: self.init_chain_vars() block = bytearray(self.chunk_size) block[:len(self.buf)] = self.buf self.compress_words(self.chain_vars, bytes(block)) self.current_md_block_datalen += self.buf_bitlen self.buf.clear() self.buf_bitlen = 0 last_block_data_bitlen = self.current_md_block_datalen if self.within_md_block: self.merge_tree() final_block = list(self.mdbiv_init) if self.word_bits == 64: final_block[0] = self.last_md_block_number & 0xffff_ffff_ffff_ffff final_block[3] = last_block_data_bitlen & 0xffff_ffff_ffff_ffff else: final_block[0] = self.last_md_block_number & 0xffff_ffff final_block[1] = (self.last_md_block_number >> 32) & 0xffff_ffff final_block[6] = last_block_data_bitlen & 0xffff_ffff final_block[7] = (last_block_data_bitlen >> 32) & 0xffff_ffff self.compress_words(self.running_hash, self.words_to_bytes(final_block)) return def output_bytes(self): num_bytes = (self.hashbitlen + 7) >> 3 out = bytearray() if self.word_bits == 64: for value in self.running_hash: out.extend(value.to_bytes(8, "little")) else: for value in self.running_hash: out.extend(value.to_bytes(4, "little")) out = out[:num_bytes] if (self.hashbitlen & 7) != 0: out[-1] &= 0xff << (8 - (self.hashbitlen & 7)) return bytes(out) def digest(self): other = self.copy() other.finalize() return other.output_bytes() def hexdigest(self): return self.digest().hex() class ESSENCE224(ESSENCEBase): block_size = 32 digest_size = 28 hashbitlen = 224 class ESSENCE256(ESSENCEBase): block_size = 32 digest_size = 32 hashbitlen = 256 class ESSENCE384(ESSENCEBase): block_size = 64 digest_size = 48 hashbitlen = 384 class ESSENCE512(ESSENCEBase): block_size = 64 digest_size = 64 hashbitlen = 512 class Khichidi1Base: def __init__(self, data=b"", data_bit_length=None): self.total_bits = 0 self.num_words = self.digest_size // 4 self.h = [0] * (self.num_words + 1) self.z = [0] * self.num_words self.residue = bytearray(self.block_size) self.residue_bit_length = 0 if data: self.update(data, data_bit_length) return def copy(self): other = self.__class__() other.total_bits = self.total_bits other.h = list(self.h) other.z = list(self.z) other.residue = bytearray(self.residue) other.residue_bit_length = self.residue_bit_length return other def ensure_bytes_like(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") return bytes(data) def shuffling(self, x): x = ((x & 0x0000_ff00) << 8) | ((x >> 8) & 0x0000_ff00) | (x & 0xff00_00ff) x = ((x & 0x00f0_00f0) << 4) | ((x >> 4) & 0x00f0_00f0) | (x & 0xf00f_f00f) x = ((x & 0x0c0c_0c0c) << 2) | ((x >> 2) & 0x0c0c_0c0c) | (x & 0xc3c3_c3c3) x = ((x & 0x2222_2222) << 1) | ((x >> 1) & 0x2222_2222) | (x & 0x9999_9999) return x def t_function(self, n): return (2 * ((n * n) & 0xffff_ffff) + n) & 0xffff_ffff def lfsr(self, x): flag = x & 0x8000_0000 x = (x << 1) & 0xffff_ffff if flag: x ^= 0x04c1_1db7 return x def message_pre_process(self, x): x = self.shuffling(x) x = self.t_function(x) x = self.lfsr(x) return x def words_to_bytes(self, words): out = bytearray() for word in words: out.extend(word.to_bytes(4, "big")) return bytes(out) def copy_bits(self, dest, dest_bit_offset, source, num_bits): if num_bits < 0: raise ValueError("num_bits must be non-negative") if num_bits == 0: return if dest_bit_offset % 8 == 0 and num_bits % 8 == 0: start = dest_bit_offset // 8 end = start + (num_bits // 8) dest[start:end] = source[: num_bits // 8] return for i in range(num_bits): src_byte = source[i // 8] src_mask = 0x80 >> (i % 8) bit = 1 if (src_byte & src_mask) else 0 dest_index = dest_bit_offset + i dest_byte_index = dest_index // 8 dest_mask = 0x80 >> (dest_index % 8) if bit: dest[dest_byte_index] |= dest_mask else: dest[dest_byte_index] &= (~dest_mask) & 0xff return def dopad(self, data, data_bit_length): padded = bytearray(self.block_size) self.copy_bits(padded, 0, data, data_bit_length) if data_bit_length < self.block_size * 8: byte_index = data_bit_length // 8 bit_index = data_bit_length % 8 padded[byte_index] |= 0x80 >> bit_index return bytes(padded) def digest_block(self, block): k = 0 for j in range(self.num_words): mj = int.from_bytes(block[k:k + 4], "big") self.z[j] = self.message_pre_process(mj ^ self.h[j]) self.h[j + 1] = self.z[j] k += 4 self.h[0] = self.h[self.num_words] return def update(self, data, data_bit_length=None): data = self.ensure_bytes_like(data) if data_bit_length is None: data_bit_length = len(data) * 8 if data_bit_length < 0 or data_bit_length > len(data) * 8: raise ValueError("invalid data_bit_length") self.total_bits = (self.total_bits + data_bit_length) & 0xffff_ffff_ffff_ffff block_bits = self.block_size * 8 remaining_bits = data_bit_length source_bit_offset = 0 if self.residue_bit_length > 0: take = min(block_bits - self.residue_bit_length, remaining_bits) self.copy_bits( self.residue, self.residue_bit_length, data[source_bit_offset // 8:], take, ) self.residue_bit_length += take remaining_bits -= take source_bit_offset += take if self.residue_bit_length == block_bits: self.digest_block(bytes(self.residue)) self.residue = bytearray(self.block_size) self.residue_bit_length = 0 while remaining_bits >= block_bits and source_bit_offset % 8 == 0: start = source_bit_offset // 8 block = data[start:start + self.block_size] self.digest_block(block) remaining_bits -= block_bits source_bit_offset += block_bits if remaining_bits > 0: self.copy_bits( self.residue, self.residue_bit_length, data[source_bit_offset // 8:], remaining_bits, ) self.residue_bit_length += remaining_bits return self def finalize(self): if self.residue_bit_length > 0 or self.z[0] == 0: block = self.dopad(self.residue, self.residue_bit_length) self.digest_block(block) self.residue = bytearray(self.block_size) self.residue_bit_length = 0 block = bytearray(self.block_size) block[-8:] = self.total_bits.to_bytes(8, "big") self.digest_block(bytes(block)) for _i in range(5): mdash = self.words_to_bytes(self.z) self.digest_block(mdash) self.digest_block(mdash) return def digest(self): c = self.copy() c.finalize() return c.words_to_bytes(c.z) def hexdigest(self): return self.digest().hex() class Khichidi1_224(Khichidi1Base): block_size = 28 digest_size = 28 class Khichidi1_256(Khichidi1Base): block_size = 32 digest_size = 32 class Khichidi1_384(Khichidi1Base): block_size = 48 digest_size = 48 class Khichidi1_512(Khichidi1Base): block_size = 64 digest_size = 64 class LesamntaBase: rounds = 32 sbox = ( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ) c_table = ( (0x01, 0x00), (0x03, 0x02), (0x05, 0x04), (0x07, 0x06), (0x09, 0x08), (0x0b, 0x0a), (0x0d, 0x0c), (0x0f, 0x0e), (0x11, 0x10), (0x13, 0x12), (0x15, 0x14), (0x17, 0x16), (0x19, 0x18), (0x1b, 0x1a), (0x1d, 0x1c), (0x1f, 0x1e), (0x21, 0x20), (0x23, 0x22), (0x25, 0x24), (0x27, 0x26), (0x29, 0x28), (0x2b, 0x2a), (0x2d, 0x2c), (0x2f, 0x2e), (0x31, 0x30), (0x33, 0x32), (0x35, 0x34), (0x37, 0x36), (0x39, 0x38), (0x3b, 0x3a), (0x3d, 0x3c), (0x3f, 0x3e), ) mul2_table = ( 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05, 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25, 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45, 0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65, 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85, 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5, 0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5, 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5, ) mul4_table = ( 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c, 0x40, 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c, 0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c, 0x80, 0x84, 0x88, 0x8c, 0x90, 0x94, 0x98, 0x9c, 0xa0, 0xa4, 0xa8, 0xac, 0xb0, 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc, 0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf4, 0xf8, 0xfc, 0x1b, 0x1f, 0x13, 0x17, 0x0b, 0x0f, 0x03, 0x07, 0x3b, 0x3f, 0x33, 0x37, 0x2b, 0x2f, 0x23, 0x27, 0x5b, 0x5f, 0x53, 0x57, 0x4b, 0x4f, 0x43, 0x47, 0x7b, 0x7f, 0x73, 0x77, 0x6b, 0x6f, 0x63, 0x67, 0x9b, 0x9f, 0x93, 0x97, 0x8b, 0x8f, 0x83, 0x87, 0xbb, 0xbf, 0xb3, 0xb7, 0xab, 0xaf, 0xa3, 0xa7, 0xdb, 0xdf, 0xd3, 0xd7, 0xcb, 0xcf, 0xc3, 0xc7, 0xfb, 0xff, 0xf3, 0xf7, 0xeb, 0xef, 0xe3, 0xe7, 0x36, 0x32, 0x3e, 0x3a, 0x26, 0x22, 0x2e, 0x2a, 0x16, 0x12, 0x1e, 0x1a, 0x06, 0x02, 0x0e, 0x0a, 0x76, 0x72, 0x7e, 0x7a, 0x66, 0x62, 0x6e, 0x6a, 0x56, 0x52, 0x5e, 0x5a, 0x46, 0x42, 0x4e, 0x4a, 0xb6, 0xb2, 0xbe, 0xba, 0xa6, 0xa2, 0xae, 0xaa, 0x96, 0x92, 0x9e, 0x9a, 0x86, 0x82, 0x8e, 0x8a, 0xf6, 0xf2, 0xfe, 0xfa, 0xe6, 0xe2, 0xee, 0xea, 0xd6, 0xd2, 0xde, 0xda, 0xc6, 0xc2, 0xce, 0xca, 0x2d, 0x29, 0x25, 0x21, 0x3d, 0x39, 0x35, 0x31, 0x0d, 0x09, 0x05, 0x01, 0x1d, 0x19, 0x15, 0x11, 0x6d, 0x69, 0x65, 0x61, 0x7d, 0x79, 0x75, 0x71, 0x4d, 0x49, 0x45, 0x41, 0x5d, 0x59, 0x55, 0x51, 0xad, 0xa9, 0xa5, 0xa1, 0xbd, 0xb9, 0xb5, 0xb1, 0x8d, 0x89, 0x85, 0x81, 0x9d, 0x99, 0x95, 0x91, 0xed, 0xe9, 0xe5, 0xe1, 0xfd, 0xf9, 0xf5, 0xf1, 0xcd, 0xc9, 0xc5, 0xc1, 0xdd, 0xd9, 0xd5, 0xd1, ) mul8_table = ( 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78, 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x1b, 0x13, 0x0b, 0x03, 0x3b, 0x33, 0x2b, 0x23, 0x5b, 0x53, 0x4b, 0x43, 0x7b, 0x73, 0x6b, 0x63, 0x9b, 0x93, 0x8b, 0x83, 0xbb, 0xb3, 0xab, 0xa3, 0xdb, 0xd3, 0xcb, 0xc3, 0xfb, 0xf3, 0xeb, 0xe3, 0x36, 0x3e, 0x26, 0x2e, 0x16, 0x1e, 0x06, 0x0e, 0x76, 0x7e, 0x66, 0x6e, 0x56, 0x5e, 0x46, 0x4e, 0xb6, 0xbe, 0xa6, 0xae, 0x96, 0x9e, 0x86, 0x8e, 0xf6, 0xfe, 0xe6, 0xee, 0xd6, 0xde, 0xc6, 0xce, 0x2d, 0x25, 0x3d, 0x35, 0x0d, 0x05, 0x1d, 0x15, 0x6d, 0x65, 0x7d, 0x75, 0x4d, 0x45, 0x5d, 0x55, 0xad, 0xa5, 0xbd, 0xb5, 0x8d, 0x85, 0x9d, 0x95, 0xed, 0xe5, 0xfd, 0xf5, 0xcd, 0xc5, 0xdd, 0xd5, 0x6c, 0x64, 0x7c, 0x74, 0x4c, 0x44, 0x5c, 0x54, 0x2c, 0x24, 0x3c, 0x34, 0x0c, 0x04, 0x1c, 0x14, 0xec, 0xe4, 0xfc, 0xf4, 0xcc, 0xc4, 0xdc, 0xd4, 0xac, 0xa4, 0xbc, 0xb4, 0x8c, 0x84, 0x9c, 0x94, 0x77, 0x7f, 0x67, 0x6f, 0x57, 0x5f, 0x47, 0x4f, 0x37, 0x3f, 0x27, 0x2f, 0x17, 0x1f, 0x07, 0x0f, 0xf7, 0xff, 0xe7, 0xef, 0xd7, 0xdf, 0xc7, 0xcf, 0xb7, 0xbf, 0xa7, 0xaf, 0x97, 0x9f, 0x87, 0x8f, 0x5a, 0x52, 0x4a, 0x42, 0x7a, 0x72, 0x6a, 0x62, 0x1a, 0x12, 0x0a, 0x02, 0x3a, 0x32, 0x2a, 0x22, 0xda, 0xd2, 0xca, 0xc2, 0xfa, 0xf2, 0xea, 0xe2, 0x9a, 0x92, 0x8a, 0x82, 0xba, 0xb2, 0xaa, 0xa2, 0x41, 0x49, 0x51, 0x59, 0x61, 0x69, 0x71, 0x79, 0x01, 0x09, 0x11, 0x19, 0x21, 0x29, 0x31, 0x39, 0xc1, 0xc9, 0xd1, 0xd9, 0xe1, 0xe9, 0xf1, 0xf9, 0x81, 0x89, 0x91, 0x99, 0xa1, 0xa9, 0xb1, 0xb9, ) mul9_table = ( 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc, 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01, 0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91, 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a, 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa, 0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b, 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b, 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0, 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30, 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed, 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d, 0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6, 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46, ) mul10_table = ( 0x00, 0x0a, 0x14, 0x1e, 0x28, 0x22, 0x3c, 0x36, 0x50, 0x5a, 0x44, 0x4e, 0x78, 0x72, 0x6c, 0x66, 0xa0, 0xaa, 0xb4, 0xbe, 0x88, 0x82, 0x9c, 0x96, 0xf0, 0xfa, 0xe4, 0xee, 0xd8, 0xd2, 0xcc, 0xc6, 0x5b, 0x51, 0x4f, 0x45, 0x73, 0x79, 0x67, 0x6d, 0x0b, 0x01, 0x1f, 0x15, 0x23, 0x29, 0x37, 0x3d, 0xfb, 0xf1, 0xef, 0xe5, 0xd3, 0xd9, 0xc7, 0xcd, 0xab, 0xa1, 0xbf, 0xb5, 0x83, 0x89, 0x97, 0x9d, 0xb6, 0xbc, 0xa2, 0xa8, 0x9e, 0x94, 0x8a, 0x80, 0xe6, 0xec, 0xf2, 0xf8, 0xce, 0xc4, 0xda, 0xd0, 0x16, 0x1c, 0x02, 0x08, 0x3e, 0x34, 0x2a, 0x20, 0x46, 0x4c, 0x52, 0x58, 0x6e, 0x64, 0x7a, 0x70, 0xed, 0xe7, 0xf9, 0xf3, 0xc5, 0xcf, 0xd1, 0xdb, 0xbd, 0xb7, 0xa9, 0xa3, 0x95, 0x9f, 0x81, 0x8b, 0x4d, 0x47, 0x59, 0x53, 0x65, 0x6f, 0x71, 0x7b, 0x1d, 0x17, 0x09, 0x03, 0x35, 0x3f, 0x21, 0x2b, 0x77, 0x7d, 0x63, 0x69, 0x5f, 0x55, 0x4b, 0x41, 0x27, 0x2d, 0x33, 0x39, 0x0f, 0x05, 0x1b, 0x11, 0xd7, 0xdd, 0xc3, 0xc9, 0xff, 0xf5, 0xeb, 0xe1, 0x87, 0x8d, 0x93, 0x99, 0xaf, 0xa5, 0xbb, 0xb1, 0x2c, 0x26, 0x38, 0x32, 0x04, 0x0e, 0x10, 0x1a, 0x7c, 0x76, 0x68, 0x62, 0x54, 0x5e, 0x40, 0x4a, 0x8c, 0x86, 0x98, 0x92, 0xa4, 0xae, 0xb0, 0xba, 0xdc, 0xd6, 0xc8, 0xc2, 0xf4, 0xfe, 0xe0, 0xea, 0xc1, 0xcb, 0xd5, 0xdf, 0xe9, 0xe3, 0xfd, 0xf7, 0x91, 0x9b, 0x85, 0x8f, 0xb9, 0xb3, 0xad, 0xa7, 0x61, 0x6b, 0x75, 0x7f, 0x49, 0x43, 0x5d, 0x57, 0x31, 0x3b, 0x25, 0x2f, 0x19, 0x13, 0x0d, 0x07, 0x9a, 0x90, 0x8e, 0x84, 0xb2, 0xb8, 0xa6, 0xac, 0xca, 0xc0, 0xde, 0xd4, 0xe2, 0xe8, 0xf6, 0xfc, 0x3a, 0x30, 0x2e, 0x24, 0x12, 0x18, 0x06, 0x0c, 0x6a, 0x60, 0x7e, 0x74, 0x42, 0x48, 0x56, 0x5c, ) def __init__(self, data=b""): self.buf = bytearray() self.msg_len = 0 self.hash_words = self.initial_hash_words() if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.hash_words = list(self.hash_words) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = memoryview(data) self.msg_len += len(data) if self.buf: need = self.block_size - len(self.buf) if len(data) < need: self.buf.extend(data) return self self.buf.extend(data[:need]) words = self.load_block_words(self.buf) self.compress(self.hash_words, words) self.buf.clear() data = data[need:] block_size = self.block_size limit = len(data) - (len(data) % block_size) offset = 0 while offset < limit: words = self.load_block_words(data[offset:offset + block_size]) self.compress(self.hash_words, words) offset += block_size if offset != len(data): self.buf.extend(data[offset:]) return self def digest(self): other = self.copy() other.finalize() return other.store_hash_words(other.hash_words)[:self.digest_size] def hexdigest(self): return self.digest().hex() def finalize(self): final_block = bytearray(self.block_size) buf_len = len(self.buf) final_block[:buf_len] = self.buf final_block[buf_len] = 0x80 if buf_len != 0: words = self.load_block_words(final_block) self.compress(self.hash_words, words) final_block = bytearray(self.block_size) words = self.load_block_words(final_block) self.set_length_words(words, self.msg_len * 8) self.output(self.hash_words, words) return class Lesamnta256Base(LesamntaBase): block_size = 32 block_bits = 256 def initial_hash_words(self): return [self.digest_bits] * 8 def load_block_words(self, block): return [ int.from_bytes(block[0:4], "big"), int.from_bytes(block[4:8], "big"), int.from_bytes(block[8:12], "big"), int.from_bytes(block[12:16], "big"), int.from_bytes(block[16:20], "big"), int.from_bytes(block[20:24], "big"), int.from_bytes(block[24:28], "big"), int.from_bytes(block[28:32], "big"), ] def store_hash_words(self, words): return ( words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + words[2].to_bytes(4, "big") + words[3].to_bytes(4, "big") + words[4].to_bytes(4, "big") + words[5].to_bytes(4, "big") + words[6].to_bytes(4, "big") + words[7].to_bytes(4, "big") ) def set_length_words(self, words, bit_len): words[6] = (bit_len >> 32) & 0xffff_ffff words[7] = bit_len & 0xffff_ffff return def f_func(self, words): s = self.sbox m2 = self.mul2_table b0 = (words[0] >> 24) & 0xff b1 = (words[0] >> 16) & 0xff b2 = (words[0] >> 8) & 0xff b3 = words[0] & 0xff b4 = (words[1] >> 24) & 0xff b5 = (words[1] >> 16) & 0xff b6 = (words[1] >> 8) & 0xff b7 = words[1] & 0xff for _round_index in range(4): b0 = s[b0] b1 = s[b1] b2 = s[b2] b3 = s[b3] b4 = s[b4] b5 = s[b5] b6 = s[b6] b7 = s[b7] b0, b1, b2, b3, b4, b5, b6, b7 = b0, b3, b2, b5, b4, b7, b6, b1 a0 = b0 a1 = b1 b0 = m2[a0] ^ a1 b1 = a0 ^ m2[a1] a0 = b2 a1 = b3 b2 = m2[a0] ^ a1 b3 = a0 ^ m2[a1] a0 = b4 a1 = b5 b4 = m2[a0] ^ a1 b5 = a0 ^ m2[a1] a0 = b6 a1 = b7 b6 = m2[a0] ^ a1 b7 = a0 ^ m2[a1] return [ (b0 << 24) | (b1 << 16) | (b2 << 8) | b3, (b4 << 24) | (b5 << 16) | (b6 << 8) | b7, ] def sub_words(self, w0, w1): s = self.sbox return ( (s[(w0 >> 24) & 0xff] << 24) | (s[(w0 >> 16) & 0xff] << 16) | (s[(w0 >> 8) & 0xff] << 8) | s[w0 & 0xff], (s[(w1 >> 24) & 0xff] << 24) | (s[(w1 >> 16) & 0xff] << 16) | (s[(w1 >> 8) & 0xff] << 8) | s[w1 & 0xff], ) def key_linear_words(self, w0, w1): m2 = self.mul2_table a0 = (w0 >> 24) & 0xff a1 = (w0 >> 16) & 0xff a2 = (w0 >> 8) & 0xff a3 = w0 & 0xff x0 = m2[a0] ^ m2[a1] ^ a1 ^ a2 ^ a3 x1 = a0 ^ m2[a1] ^ m2[a2] ^ a2 ^ a3 x2 = a0 ^ a1 ^ m2[a2] ^ m2[a3] ^ a3 x3 = m2[a0] ^ a0 ^ a1 ^ a2 ^ m2[a3] b0 = (w1 >> 24) & 0xff b1 = (w1 >> 16) & 0xff b2 = (w1 >> 8) & 0xff b3 = w1 & 0xff y0 = m2[b0] ^ m2[b1] ^ b1 ^ b2 ^ b3 y1 = b0 ^ m2[b1] ^ m2[b2] ^ b2 ^ b3 y2 = b0 ^ b1 ^ m2[b2] ^ m2[b3] ^ b3 y3 = m2[b0] ^ b0 ^ b1 ^ b2 ^ m2[b3] return ( (y0 << 24) | (y1 << 16) | (x2 << 8) | x3, (x0 << 24) | (x1 << 16) | (y2 << 8) | y3, ) def compress(self, chain, mb): x0, x1, x2, x3, x4, x5, x6, x7 = mb k0, k1, k2, k3, k4, k5, k6, k7 = chain f_func = self.f_func c_table = self.c_table for round_index in range(self.rounds): t0, t1 = self.sub_words(k4 ^ c_table[round_index][0], k5 ^ c_table[round_index][1]) t0, t1 = self.key_linear_words(t0, t1) k6 ^= t0 k7 ^= t1 k0, k1, k2, k3, k4, k5, k6, k7 = k6, k7, k0, k1, k2, k3, k4, k5 sub0, sub1 = f_func([x4 ^ k2, x5 ^ k3]) x6 ^= sub0 x7 ^= sub1 x0, x1, x2, x3, x4, x5, x6, x7 = x6, x7, x0, x1, x2, x3, x4, x5 chain[0] = x0 ^ mb[0] chain[1] = x1 ^ mb[1] chain[2] = x2 ^ mb[2] chain[3] = x3 ^ mb[3] chain[4] = x4 ^ mb[4] chain[5] = x5 ^ mb[5] chain[6] = x6 ^ mb[6] chain[7] = x7 ^ mb[7] return def output(self, chain, mb): x0, x1, x2, x3, x4, x5, x6, x7 = mb k0, k1, k2, k3, k4, k5, k6, k7 = chain f_func = self.f_func c_table = self.c_table for round_index in range(self.rounds): sub0, sub1 = f_func([k4 ^ c_table[round_index][0], k5 ^ c_table[round_index][1]]) k6 ^= sub0 k7 ^= sub1 k0, k1, k2, k3, k4, k5, k6, k7 = k6, k7, k0, k1, k2, k3, k4, k5 sub0, sub1 = f_func([x4 ^ k2, x5 ^ k3]) x6 ^= sub0 x7 ^= sub1 x0, x1, x2, x3, x4, x5, x6, x7 = x6, x7, x0, x1, x2, x3, x4, x5 chain[0] = x0 ^ mb[0] chain[1] = x1 ^ mb[1] chain[2] = x2 ^ mb[2] chain[3] = x3 ^ mb[3] chain[4] = x4 ^ mb[4] chain[5] = x5 ^ mb[5] chain[6] = x6 ^ mb[6] chain[7] = x7 ^ mb[7] return class Lesamnta512Base(LesamntaBase): block_size = 64 block_bits = 512 def initial_hash_words(self): return [self.digest_bits] * 8 def load_block_words(self, block): return [ int.from_bytes(block[0:8], "big"), int.from_bytes(block[8:16], "big"), int.from_bytes(block[16:24], "big"), int.from_bytes(block[24:32], "big"), int.from_bytes(block[32:40], "big"), int.from_bytes(block[40:48], "big"), int.from_bytes(block[48:56], "big"), int.from_bytes(block[56:64], "big"), ] def store_hash_words(self, words): return ( words[0].to_bytes(8, "big") + words[1].to_bytes(8, "big") + words[2].to_bytes(8, "big") + words[3].to_bytes(8, "big") + words[4].to_bytes(8, "big") + words[5].to_bytes(8, "big") + words[6].to_bytes(8, "big") + words[7].to_bytes(8, "big") ) def set_length_words(self, words, bit_len): words[6] = 0 words[7] = bit_len return def f_func(self, words): s = self.sbox m2 = self.mul2_table b0 = (words[0] >> 56) & 0xff b1 = (words[0] >> 48) & 0xff b2 = (words[0] >> 40) & 0xff b3 = (words[0] >> 32) & 0xff b4 = (words[0] >> 24) & 0xff b5 = (words[0] >> 16) & 0xff b6 = (words[0] >> 8) & 0xff b7 = words[0] & 0xff b8 = (words[1] >> 56) & 0xff b9 = (words[1] >> 48) & 0xff b10 = (words[1] >> 40) & 0xff b11 = (words[1] >> 32) & 0xff b12 = (words[1] >> 24) & 0xff b13 = (words[1] >> 16) & 0xff b14 = (words[1] >> 8) & 0xff b15 = words[1] & 0xff for _round_index in range(4): b0 = s[b0] b1 = s[b1] b2 = s[b2] b3 = s[b3] b4 = s[b4] b5 = s[b5] b6 = s[b6] b7 = s[b7] b8 = s[b8] b9 = s[b9] b10 = s[b10] b11 = s[b11] b12 = s[b12] b13 = s[b13] b14 = s[b14] b15 = s[b15] b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 = ( b0, b5, b10, b15, b4, b9, b14, b3, b8, b13, b2, b7, b12, b1, b6, b11 ) a0 = b0 a1 = b1 a2 = b2 a3 = b3 b0 = m2[a0] ^ m2[a1] ^ a1 ^ a2 ^ a3 b1 = a0 ^ m2[a1] ^ m2[a2] ^ a2 ^ a3 b2 = a0 ^ a1 ^ m2[a2] ^ m2[a3] ^ a3 b3 = m2[a0] ^ a0 ^ a1 ^ a2 ^ m2[a3] a0 = b4 a1 = b5 a2 = b6 a3 = b7 b4 = m2[a0] ^ m2[a1] ^ a1 ^ a2 ^ a3 b5 = a0 ^ m2[a1] ^ m2[a2] ^ a2 ^ a3 b6 = a0 ^ a1 ^ m2[a2] ^ m2[a3] ^ a3 b7 = m2[a0] ^ a0 ^ a1 ^ a2 ^ m2[a3] a0 = b8 a1 = b9 a2 = b10 a3 = b11 b8 = m2[a0] ^ m2[a1] ^ a1 ^ a2 ^ a3 b9 = a0 ^ m2[a1] ^ m2[a2] ^ a2 ^ a3 b10 = a0 ^ a1 ^ m2[a2] ^ m2[a3] ^ a3 b11 = m2[a0] ^ a0 ^ a1 ^ a2 ^ m2[a3] a0 = b12 a1 = b13 a2 = b14 a3 = b15 b12 = m2[a0] ^ m2[a1] ^ a1 ^ a2 ^ a3 b13 = a0 ^ m2[a1] ^ m2[a2] ^ a2 ^ a3 b14 = a0 ^ a1 ^ m2[a2] ^ m2[a3] ^ a3 b15 = m2[a0] ^ a0 ^ a1 ^ a2 ^ m2[a3] return [ (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7, (b8 << 56) | (b9 << 48) | (b10 << 40) | (b11 << 32) | (b12 << 24) | (b13 << 16) | (b14 << 8) | b15, ] def sub_words(self, w0, w1): s = self.sbox return ( (s[(w0 >> 56) & 0xff] << 56) | (s[(w0 >> 48) & 0xff] << 48) | (s[(w0 >> 40) & 0xff] << 40) | (s[(w0 >> 32) & 0xff] << 32) | (s[(w0 >> 24) & 0xff] << 24) | (s[(w0 >> 16) & 0xff] << 16) | (s[(w0 >> 8) & 0xff] << 8) | s[w0 & 0xff], (s[(w1 >> 56) & 0xff] << 56) | (s[(w1 >> 48) & 0xff] << 48) | (s[(w1 >> 40) & 0xff] << 40) | (s[(w1 >> 32) & 0xff] << 32) | (s[(w1 >> 24) & 0xff] << 24) | (s[(w1 >> 16) & 0xff] << 16) | (s[(w1 >> 8) & 0xff] << 8) | s[w1 & 0xff], ) def key_linear_words(self, w0, w1): m2 = self.mul2_table m4 = self.mul4_table m8 = self.mul8_table m9 = self.mul9_table m10 = self.mul10_table src = [ (w0 >> 56) & 0xff, (w0 >> 48) & 0xff, (w0 >> 40) & 0xff, (w0 >> 32) & 0xff, (w0 >> 24) & 0xff, (w0 >> 16) & 0xff, (w0 >> 8) & 0xff, w0 & 0xff, (w1 >> 56) & 0xff, (w1 >> 48) & 0xff, (w1 >> 40) & 0xff, (w1 >> 32) & 0xff, (w1 >> 24) & 0xff, (w1 >> 16) & 0xff, (w1 >> 8) & 0xff, w1 & 0xff, ] out = [0] * 16 for base in (0, 8): a0 = src[base + 0] a1 = src[base + 1] a2 = src[base + 2] a3 = src[base + 3] a4 = src[base + 4] a5 = src[base + 5] a6 = src[base + 6] a7 = src[base + 7] out[base + 0] = a0 ^ a1 ^ m2[a2] ^ m10[a3] ^ m9[a4] ^ m8[a5] ^ a6 ^ m4[a7] out[base + 1] = m4[a0] ^ a1 ^ a2 ^ m2[a3] ^ m10[a4] ^ m9[a5] ^ m8[a6] ^ a7 out[base + 2] = a0 ^ m4[a1] ^ a2 ^ a3 ^ m2[a4] ^ m10[a5] ^ m9[a6] ^ m8[a7] out[base + 3] = m8[a0] ^ a1 ^ m4[a2] ^ a3 ^ a4 ^ m2[a5] ^ m10[a6] ^ m9[a7] out[base + 4] = m9[a0] ^ m8[a1] ^ a2 ^ m4[a3] ^ a4 ^ a5 ^ m2[a6] ^ m10[a7] out[base + 5] = m10[a0] ^ m9[a1] ^ m8[a2] ^ a3 ^ m4[a4] ^ a5 ^ a6 ^ m2[a7] out[base + 6] = m2[a0] ^ m10[a1] ^ m9[a2] ^ m8[a3] ^ a4 ^ m4[a5] ^ a6 ^ a7 out[base + 7] = a0 ^ m2[a1] ^ m10[a2] ^ m9[a3] ^ m8[a4] ^ a5 ^ m4[a6] ^ a7 return ( (out[8] << 56) | (out[9] << 48) | (out[10] << 40) | (out[11] << 32) | (out[4] << 24) | (out[5] << 16) | (out[6] << 8) | out[7], (out[0] << 56) | (out[1] << 48) | (out[2] << 40) | (out[3] << 32) | (out[12] << 24) | (out[13] << 16) | (out[14] << 8) | out[15], ) def compress(self, chain, mb): x0, x1, x2, x3, x4, x5, x6, x7 = mb k0, k1, k2, k3, k4, k5, k6, k7 = chain f_func = self.f_func c_table = self.c_table for round_index in range(self.rounds): t0, t1 = self.sub_words(k4 ^ c_table[round_index][0], k5 ^ c_table[round_index][1]) t0, t1 = self.key_linear_words(t0, t1) k6 ^= t0 k7 ^= t1 k0, k1, k2, k3, k4, k5, k6, k7 = k6, k7, k0, k1, k2, k3, k4, k5 sub0, sub1 = f_func([x4 ^ k2, x5 ^ k3]) x6 ^= sub0 x7 ^= sub1 x0, x1, x2, x3, x4, x5, x6, x7 = x6, x7, x0, x1, x2, x3, x4, x5 chain[0] = x0 ^ mb[0] chain[1] = x1 ^ mb[1] chain[2] = x2 ^ mb[2] chain[3] = x3 ^ mb[3] chain[4] = x4 ^ mb[4] chain[5] = x5 ^ mb[5] chain[6] = x6 ^ mb[6] chain[7] = x7 ^ mb[7] return def output(self, chain, mb): x0, x1, x2, x3, x4, x5, x6, x7 = mb k0, k1, k2, k3, k4, k5, k6, k7 = chain f_func = self.f_func c_table = self.c_table for round_index in range(self.rounds): sub0, sub1 = f_func([k4 ^ c_table[round_index][0], k5 ^ c_table[round_index][1]]) k6 ^= sub0 k7 ^= sub1 k0, k1, k2, k3, k4, k5, k6, k7 = k6, k7, k0, k1, k2, k3, k4, k5 sub0, sub1 = f_func([x4 ^ k2, x5 ^ k3]) x6 ^= sub0 x7 ^= sub1 x0, x1, x2, x3, x4, x5, x6, x7 = x6, x7, x0, x1, x2, x3, x4, x5 chain[0] = x0 ^ mb[0] chain[1] = x1 ^ mb[1] chain[2] = x2 ^ mb[2] chain[3] = x3 ^ mb[3] chain[4] = x4 ^ mb[4] chain[5] = x5 ^ mb[5] chain[6] = x6 ^ mb[6] chain[7] = x7 ^ mb[7] return class Lesamnta224(Lesamnta256Base): digest_bits = 0x0000_0224 digest_size = 28 class Lesamnta256(Lesamnta256Base): digest_bits = 0x0000_0256 digest_size = 32 class Lesamnta384(Lesamnta512Base): digest_bits = 0x0000_0000_0000_0384 digest_size = 48 class Lesamnta512(Lesamnta512Base): digest_bits = 0x0000_0000_0000_0512 digest_size = 64 class LUXBase: block_size = None digest_size = None hashbitlen = None mrows = None SBOX = bytes([ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ]) SHIFT_ROWS_4 = (0, 1, 3, 4) SHIFT_ROWS_8 = (0, 1, 2, 3, 4, 5, 6, 7) ADD_CONSTANT = (0x2a, 0xd0, 0x1c, 0x64) MUL1 = bytes(range(256)) MUL2 = None MUL3 = None MUL4 = None MUL6 = None MUL8 = None MUL12 = None @classmethod def initTables(cls): if cls.MUL2 is not None: return def xtime(x): y = x << 1 if x & 0x80: y ^= 0x1b return y & 0xff mul2 = bytearray(256) mul3 = bytearray(256) mul4 = bytearray(256) mul6 = bytearray(256) mul8 = bytearray(256) mul12 = bytearray(256) for x in range(256): x2 = xtime(x) x4 = xtime(x2) x8 = xtime(x4) mul2[x] = x2 mul3[x] = x2 ^ x mul4[x] = x4 mul6[x] = x4 ^ x2 mul8[x] = x8 mul12[x] = x8 ^ x4 cls.MUL2 = bytes(mul2) cls.MUL3 = bytes(mul3) cls.MUL4 = bytes(mul4) cls.MUL6 = bytes(mul6) cls.MUL8 = bytes(mul8) cls.MUL12 = bytes(mul12) return def __init__(self, data=b""): self.__class__.initTables() self.cr = [bytearray(8) for _ in range(self.mrows)] self.bf = [bytearray(16) for _ in range(self.mrows)] self.buf = bytearray() self.total_bits = 0 self.partial_bits = 0 self.partial_byte = 0 self.finalized = False if data: self.update(data) return def copy(self): other = self.__class__() other.cr = [bytearray(row) for row in self.cr] other.bf = [bytearray(row) for row in self.bf] other.buf = bytearray(self.buf) other.total_bits = self.total_bits other.partial_bits = self.partial_bits other.partial_byte = self.partial_byte other.finalized = self.finalized return other def update(self, data): if self.finalized: raise ValueError("hash object already finalized") if self.partial_bits: raise ValueError("cannot update after partial-bit input") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.total_bits += len(data) * 8 self.buf.extend(data) self.processBuffer() return self def processBuffer(self): block_size = self.mrows compress = self.compress buf = self.buf while len(buf) >= block_size: block = bytes(buf[:block_size]) del buf[:block_size] compress(block) return def subBytes(self): sbox = self.SBOX for i in range(self.mrows): row = self.cr[i] for j in range(8): row[j] = sbox[row[j]] return def shiftRows(self): if self.mrows == 4: shifts = self.SHIFT_ROWS_4 else: shifts = self.SHIFT_ROWS_8 for i, shift in enumerate(shifts): if shift: row = self.cr[i] self.cr[i] = row[shift:] + row[:shift] return def mixColumns4(self): m1 = self.MUL1 m2 = self.MUL2 m3 = self.MUL3 cr0 = self.cr[0] cr1 = self.cr[1] cr2 = self.cr[2] cr3 = self.cr[3] out0 = bytearray(8) out1 = bytearray(8) out2 = bytearray(8) out3 = bytearray(8) for j in range(8): a0 = cr0[j] a1 = cr1[j] a2 = cr2[j] a3 = cr3[j] out0[j] = m2[a0] ^ m3[a1] ^ m1[a2] ^ m1[a3] out1[j] = m1[a0] ^ m2[a1] ^ m3[a2] ^ m1[a3] out2[j] = m1[a0] ^ m1[a1] ^ m2[a2] ^ m3[a3] out3[j] = m3[a0] ^ m1[a1] ^ m1[a2] ^ m2[a3] self.cr[0] = out0 self.cr[1] = out1 self.cr[2] = out2 self.cr[3] = out3 return def mixColumns8(self): m1 = self.MUL1 m2 = self.MUL2 m4 = self.MUL4 m6 = self.MUL6 m8 = self.MUL8 m12 = self.MUL12 cr = self.cr out = [bytearray(8) for _ in range(8)] for j in range(8): a0 = cr[0][j] a1 = cr[1][j] a2 = cr[2][j] a3 = cr[3][j] a4 = cr[4][j] a5 = cr[5][j] a6 = cr[6][j] a7 = cr[7][j] out[0][j] = m1[a0] ^ m4[a1] ^ m1[a2] ^ m1[a3] ^ m2[a4] ^ m12[a5] ^ m6[a6] ^ m8[a7] out[1][j] = m8[a0] ^ m1[a1] ^ m4[a2] ^ m1[a3] ^ m1[a4] ^ m2[a5] ^ m12[a6] ^ m6[a7] out[2][j] = m6[a0] ^ m8[a1] ^ m1[a2] ^ m4[a3] ^ m1[a4] ^ m1[a5] ^ m2[a6] ^ m12[a7] out[3][j] = m12[a0] ^ m6[a1] ^ m8[a2] ^ m1[a3] ^ m4[a4] ^ m1[a5] ^ m1[a6] ^ m2[a7] out[4][j] = m2[a0] ^ m12[a1] ^ m6[a2] ^ m8[a3] ^ m1[a4] ^ m4[a5] ^ m1[a6] ^ m1[a7] out[5][j] = m1[a0] ^ m2[a1] ^ m12[a2] ^ m6[a3] ^ m8[a4] ^ m1[a5] ^ m4[a6] ^ m1[a7] out[6][j] = m1[a0] ^ m1[a1] ^ m2[a2] ^ m12[a3] ^ m6[a4] ^ m8[a5] ^ m1[a6] ^ m4[a7] out[7][j] = m4[a0] ^ m1[a1] ^ m1[a2] ^ m2[a3] ^ m12[a4] ^ m6[a5] ^ m8[a6] ^ m1[a7] self.cr = out return def addConstant(self): if self.mrows == 4: for i, val in enumerate(self.ADD_CONSTANT): self.cr[i][0] ^= val else: for i, val in enumerate(self.ADD_CONSTANT): self.cr[i + 4][0] ^= val return def functionG(self): self.subBytes() self.shiftRows() if self.mrows == 4: self.mixColumns4() else: self.mixColumns8() self.addConstant() return def functionF(self): bf = self.bf temp = [bf[i][15] for i in range(self.mrows)] for j in range(15, 0, -1): for i in range(self.mrows): bf[i][j] = bf[i][j - 1] for i in range(self.mrows): bf[i][0] = temp[i] return def compress(self, block): for i in range(self.mrows): x = block[self.mrows - 1 - i] self.bf[i][0] ^= x self.cr[i][0] ^= x self.functionG() self.functionF() for j in range(8): for i in range(self.mrows): self.bf[i][j + 4] ^= self.cr[i][j] for i in range(self.mrows): self.cr[i][7] ^= self.bf[i][15] return def finalBytes(self): rem = bytearray(self.mrows) used = len(self.buf) rem[:used] = self.buf if self.partial_bits: idx = used rem[idx] = self.partial_byte & (0xff << (8 - self.partial_bits)) rem[idx] ^= 0x01 << (7 - self.partial_bits) elif used: rem[used] = 0x80 else: rem[0] = 0x80 return rem def finalize(self): if self.finalized: return rem = self.finalBytes() self.compress(rem) length_block = self.total_bits.to_bytes(8, "big") if self.hashbitlen <= 256: self.compress(length_block[:4]) self.compress(length_block[4:]) else: self.compress(length_block) zero = bytes(self.mrows) for _ in range(16): self.compress(zero) out = bytearray() if self.hashbitlen <= 256: for _ in range(self.hashbitlen // 32): self.compress(zero) out.extend((self.cr[3][3], self.cr[2][3], self.cr[1][3], self.cr[0][3])) else: for _ in range(self.hashbitlen // 64): self.compress(zero) out.extend(( self.cr[7][3], self.cr[6][3], self.cr[5][3], self.cr[4][3], self.cr[3][3], self.cr[2][3], self.cr[1][3], self.cr[0][3], )) self.result = bytes(out) self.finalized = True return def digest(self): other = self.copy() other.finalize() return other.result def hexdigest(self): return self.digest().hex() class LUX224(LUXBase): block_size = 4 digest_size = 28 hashbitlen = 224 mrows = 4 class LUX256(LUXBase): block_size = 4 digest_size = 32 hashbitlen = 256 mrows = 4 class LUX384(LUXBase): block_size = 8 digest_size = 48 hashbitlen = 384 mrows = 8 class LUX512(LUXBase): block_size = 8 digest_size = 64 hashbitlen = 512 mrows = 8 class MCSSHA3Base: block_size = 1 sbox = ( 0x30, 0x60, 0x67, 0xb5, 0x43, 0xea, 0x93, 0x25, 0x48, 0x0d, 0x18, 0x6f, 0x28, 0x7a, 0xfe, 0xb6, 0xd5, 0x9c, 0x23, 0x86, 0x52, 0x42, 0xf7, 0xfd, 0xf6, 0x9b, 0xee, 0x99, 0x91, 0xbc, 0x2a, 0x63, 0xa1, 0xa0, 0x57, 0x3c, 0x39, 0xd2, 0xec, 0x71, 0x45, 0xcb, 0x41, 0xdc, 0x0b, 0x5b, 0xc2, 0x36, 0x01, 0x55, 0x7d, 0xfb, 0xed, 0x83, 0x8f, 0x31, 0xc0, 0x4c, 0x08, 0xe3, 0x9d, 0xc1, 0xd3, 0xe9, 0xb8, 0xbd, 0xae, 0x0f, 0xe7, 0x70, 0x5a, 0xeb, 0x4d, 0x29, 0xf9, 0xa9, 0x3d, 0x26, 0x46, 0x06, 0xd0, 0x50, 0xa5, 0xbe, 0x66, 0x90, 0xf4, 0x20, 0xe4, 0x33, 0x27, 0xe2, 0xab, 0xef, 0x68, 0x54, 0x37, 0x6a, 0xdb, 0xbb, 0xd8, 0x7b, 0x69, 0xc4, 0xf2, 0xbf, 0x85, 0xc7, 0xa6, 0xb4, 0x9a, 0xdd, 0x72, 0x34, 0xe8, 0xfc, 0xd6, 0x21, 0x98, 0x96, 0x32, 0xca, 0x49, 0xb3, 0xf3, 0x97, 0x8e, 0x2f, 0x00, 0xb0, 0x10, 0x1a, 0x77, 0x38, 0xcf, 0x51, 0xba, 0x1f, 0x22, 0xac, 0x62, 0x89, 0x76, 0xc3, 0x02, 0x6e, 0x2c, 0x47, 0x3a, 0x5c, 0x1b, 0x56, 0x8a, 0x5d, 0x03, 0x16, 0x74, 0x58, 0x79, 0x09, 0xd7, 0xf5, 0x0a, 0x92, 0x4f, 0x87, 0xcd, 0xda, 0x8c, 0xc9, 0x9e, 0x3b, 0x12, 0x6b, 0x53, 0xff, 0x80, 0xb7, 0xf8, 0xd9, 0xf1, 0x5e, 0xaf, 0xe0, 0x05, 0xa4, 0x14, 0x2b, 0xa3, 0xcc, 0x6c, 0x7c, 0x78, 0xaa, 0x95, 0x84, 0x61, 0xa8, 0xce, 0x13, 0x88, 0xfa, 0x59, 0x4e, 0xb9, 0xc8, 0x4b, 0x24, 0xd1, 0x07, 0x94, 0x2e, 0xdf, 0xb1, 0x17, 0xa2, 0x1d, 0x4a, 0xc6, 0xad, 0x15, 0x19, 0x35, 0x7f, 0x81, 0x44, 0x0c, 0x9f, 0x75, 0x7e, 0xd4, 0x82, 0xde, 0xe6, 0xe1, 0x2d, 0x3e, 0x73, 0x11, 0x8b, 0xc5, 0xa7, 0xf0, 0x6d, 0x1c, 0x64, 0x0e, 0x04, 0x40, 0x1e, 0x8d, 0xe5, 0x3f, 0xb2, 0x65, 0x5f, ) def __init__(self, data=b"", bit_length=None): if self.hashbitlen not in (224, 256, 384, 512): raise ValueError("invalid hashbitlen") n = (self.hashbitlen >> 3) - 1 self.points = [0, 1, n - 3, n] self.partial_bits = 0 self.partial_byte = 0 self.state = bytearray(range(n + 1)) if data: if bit_length is None: self.update(data) else: self.update_bits(data, bit_length) return def copy(self): other = self.__class__() other.points = self.points[:] other.partial_bits = self.partial_bits other.partial_byte = self.partial_byte other.state = bytearray(self.state) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") self.update_bits(data, len(data) * 8) return self def update_bits(self, data, bit_length): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if isinstance(data, memoryview): data = data.tobytes() else: data = bytes(data) if bit_length < 0: raise ValueError("bit_length must be non-negative") if bit_length > len(data) * 8: raise ValueError("bit_length exceeds input size") x1 = self.points[0] x2 = self.points[1] x3 = self.points[2] x4 = self.points[3] bits = self.partial_bits last = self.partial_byte n = (self.hashbitlen >> 3) - 8 state_len = n + 8 full_bytes = (bit_length + bits) >> 3 state = self.state sbox = self.sbox index = 0 while index < full_bytes: value = data[index] if bits != 0: mixed = last ^ (value >> bits) last = (value << (8 - bits)) & 0xff state[x1] = ( sbox[(state[x1] - state[x2] - state[x3] + state[x4]) & 0xff] + mixed ) & 0xff else: state[x1] = ( sbox[(state[x1] - state[x2] - state[x3] + state[x4]) & 0xff] + value ) & 0xff x1 += 1 x2 += 1 x3 += 1 x4 += 1 if x1 == state_len: x1 = 0 if x2 == state_len: x2 = 0 if x3 == state_len: x3 = 0 if x4 == state_len: x4 = 0 state[x1] = sbox[(state[x1] - state[x2] - state[x3] + state[x4]) & 0xff] x1 += 1 x2 += 1 x3 += 1 x4 += 1 if x1 == state_len: x1 = 0 if x2 == state_len: x2 = 0 if x3 == state_len: x3 = 0 if x4 == state_len: x4 = 0 state[x1] = sbox[(state[x1] - state[x2] - state[x3] + state[x4]) & 0xff] x1 += 1 x2 += 1 x3 += 1 x4 += 1 if x1 == state_len: x1 = 0 if x2 == state_len: x2 = 0 if x3 == state_len: x3 = 0 if x4 == state_len: x4 = 0 state[x1] = sbox[(state[x1] - state[x2] - state[x3] + state[x4]) & 0xff] x1 += 1 x2 += 1 x3 += 1 x4 += 1 if x1 == state_len: x1 = 0 if x2 == state_len: x2 = 0 if x3 == state_len: x3 = 0 if x4 == state_len: x4 = 0 index += 1 if bits != 0: self.partial_bits = bit_length + bits - (index << 3) if self.partial_bits != 0: if index != 0: self.partial_byte = ((last >> (8 - self.partial_bits)) << (8 - self.partial_bits)) & 0xff else: mixed = last ^ (data[index] >> bits) self.partial_byte = ((mixed >> (8 - self.partial_bits)) << (8 - self.partial_bits)) & 0xff else: self.partial_byte = 0 else: self.partial_bits = bit_length - ((bit_length >> 3) << 3) if self.partial_bits != 0: self.partial_byte = ((data[index] >> (8 - self.partial_bits)) << (8 - self.partial_bits)) & 0xff else: self.partial_byte = 0 self.points[0] = x1 self.points[1] = x2 self.points[2] = x3 self.points[3] = x4 return self def finalize(self): x1 = self.points[0] x2 = self.points[1] x3 = self.points[2] x4 = self.points[3] bits = self.partial_bits last = self.partial_byte n = (self.hashbitlen >> 3) - 8 state_len = n + 8 final_len = self.hashbitlen >> 1 state = self.state sbox = self.sbox repeated = bytes(state[:state_len]) * 4 index = 0 while index < final_len: value = repeated[index] if bits != 0: mixed = last ^ (value >> bits) last = (value << (8 - bits)) & 0xff state[x1] = ( sbox[(state[x1] - state[x2] - state[x3] + state[x4]) & 0xff] + mixed ) & 0xff else: state[x1] = ( sbox[(state[x1] - state[x2] - state[x3] + state[x4]) & 0xff] + value ) & 0xff x1 += 1 x2 += 1 x3 += 1 x4 += 1 if x1 == state_len: x1 = 0 if x2 == state_len: x2 = 0 if x3 == state_len: x3 = 0 if x4 == state_len: x4 = 0 index += 1 self.points[0] = x1 self.points[1] = x2 self.points[2] = x3 self.points[3] = x4 self.partial_bits = bits self.partial_byte = last return bytes(state[:state_len]) def digest(self): other = self.copy() result = other.finalize() return result def hexdigest(self): result = self.digest().hex() return result class MCSSHA3_224(MCSSHA3Base): digest_size = 28 hashbitlen = 224 class MCSSHA3_256(MCSSHA3Base): digest_size = 32 hashbitlen = 256 class MCSSHA3_384(MCSSHA3Base): digest_size = 48 hashbitlen = 384 class MCSSHA3_512(MCSSHA3Base): digest_size = 64 hashbitlen = 512 class MeshHashBase: word_size = 8 counter_length = 4 min_number_of_pipes = 4 max_number_of_pipes = 256 number_of_extra_pipes = 1 def __init__(self, data=b""): if self.hashbitlen is None or self.digest_size is None: raise ValueError("hashbitlen and digest_size must be defined") self.number_of_pipes = self.compute_number_of_pipes(self.hashbitlen) self.block_size = self.number_of_pipes * self.word_size self.pipe = [0] * (self.number_of_pipes + 1) self.bit_counter = [0] * self.counter_length self.block_counter = [0] * self.counter_length self.block_round_counter = 0 self.data_buffer = bytearray() self.msg_len = 0 self.squeezing = False if data: self.update(data) return def copy(self): other = self.__class__() other.number_of_pipes = self.number_of_pipes other.block_size = self.block_size other.pipe = self.pipe[:] other.bit_counter = self.bit_counter[:] other.block_counter = self.block_counter[:] other.block_round_counter = self.block_round_counter other.data_buffer = bytearray(self.data_buffer) other.msg_len = self.msg_len other.squeezing = self.squeezing return other def compute_number_of_pipes(self, hashbitlen): number_of_pipes = (hashbitlen + 63) // 64 number_of_pipes += self.number_of_extra_pipes if number_of_pipes < self.min_number_of_pipes: number_of_pipes = self.min_number_of_pipes if number_of_pipes > self.max_number_of_pipes: number_of_pipes = self.max_number_of_pipes return number_of_pipes def rot64(self, word, bits): bits &= 63 if bits == 0: return word return ((word >> bits) | ((word << (64 - bits)) & 0xffff_ffff_ffff_ffff)) & 0xffff_ffff_ffff_ffff def sbox(self, word): word = (word * 0x9e37_79b9_7f4a_7bb9 + 0x5e2d_58d8_b3bc_def7) & 0xffff_ffff_ffff_ffff word = self.rot64(word, 37) word = (word * 0x9e37_79b9_7f4a_7bb9 + 0x5e2d_58d8_b3bc_def7) & 0xffff_ffff_ffff_ffff word = self.rot64(word, 37) return word def add_to_counter(self, counter, to_add): carry = 0 for i in range(self.counter_length): temp = ((to_add & 0xffff_ffff_ffff_ffff) + carry) & 0xffff_ffff_ffff_ffff counter[i] = (counter[i] + temp) & 0xffff_ffff_ffff_ffff if carry == 1 and temp == 0: carry = 1 elif counter[i] < temp: carry = 1 else: carry = 0 to_add >>= 64 return def normal_round(self, data_word): pipe = self.pipe number_of_pipes = self.number_of_pipes rot64 = self.rot64 sbox = self.sbox xor_step = 0x0101_0101_0101_0101 pipe[number_of_pipes] = pipe[0] for i in range(number_of_pipes): value = pipe[i] ^ (i * xor_step) ^ data_word value = rot64(value, (i * 37) & 63) value = sbox(value) pipe[i] = (value + pipe[i + 1]) & 0xffff_ffff_ffff_ffff self.block_round_counter += 1 return def final_block_round(self): pipe = self.pipe number_of_pipes = self.number_of_pipes sbox = self.sbox block_counter = self.block_counter self.block_round_counter = 0 for i in range(number_of_pipes): pipe[i] = sbox(pipe[i] ^ block_counter[i % self.counter_length]) self.add_to_counter(block_counter, 1) return def process_word(self, data_word): self.normal_round(data_word) if self.block_round_counter == self.number_of_pipes: self.final_block_round() return def update(self, data): if self.squeezing: raise ValueError("cannot update after finalize") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") mv = memoryview(data).cast("B") data_len = len(mv) if data_len == 0: return self self.msg_len += data_len self.add_to_counter(self.bit_counter, data_len * 8) offset = 0 if self.data_buffer: needed = 8 - len(self.data_buffer) take = needed if take > data_len: take = data_len self.data_buffer.extend(mv[:take]) offset += take if len(self.data_buffer) == 8: self.process_word(int.from_bytes(self.data_buffer, "big")) self.data_buffer.clear() remaining = data_len - offset full_len = remaining & ~7 end = offset + full_len while offset < end: self.process_word(int.from_bytes(mv[offset:offset + 8], "big")) offset += 8 if offset < data_len: self.data_buffer.extend(mv[offset:data_len]) return self def finalize(self): if self.squeezing: return if self.data_buffer: padded = bytes(self.data_buffer) + (b"\x00" * (8 - len(self.data_buffer))) self.process_word(int.from_bytes(padded, "big")) self.data_buffer.clear() if self.block_round_counter != 0: while self.block_round_counter < self.number_of_pipes: self.normal_round(0) self.final_block_round() pipe = self.pipe sbox = self.sbox xor_step = 0x0101_0101_0101_0101 for counter_word in self.bit_counter: for pipe_index in range(self.number_of_pipes): pipe[pipe_index] = sbox(pipe[pipe_index] ^ counter_word ^ (pipe_index * xor_step)) for pipe_index in range(self.number_of_pipes): pipe[pipe_index] = sbox(pipe[pipe_index] ^ self.hashbitlen ^ (pipe_index * xor_step)) self.squeezing = True return def squeeze_bytes(self, size): out = bytearray(size) pipe = self.pipe for i in range(size): self.normal_round(0) temp = 0 for j in range(0, self.number_of_pipes, 2): temp ^= pipe[j] out[i] = temp & 0xff if self.block_round_counter == self.number_of_pipes: self.final_block_round() return bytes(out) def digest(self): other = self.copy() other.finalize() return other.squeeze_bytes(other.digest_size) def hexdigest(self): return self.digest().hex() class MeshHash224(MeshHashBase): digest_size = 28 hashbitlen = 224 class MeshHash256(MeshHashBase): digest_size = 32 hashbitlen = 256 class MeshHash384(MeshHashBase): digest_size = 48 hashbitlen = 384 class MeshHash512(MeshHashBase): digest_size = 64 hashbitlen = 512 class NaSHABase: sbox = ( 0x8c, 0x90, 0xd9, 0xc1, 0x46, 0x63, 0x53, 0xf1, 0x61, 0x32, 0x15, 0x3e, 0x26, 0x9a, 0x97, 0x2e, 0xd8, 0xa0, 0x99, 0x9e, 0xc0, 0x95, 0x67, 0xb7, 0x6d, 0xe0, 0xf3, 0x28, 0x20, 0x86, 0xb6, 0xef, 0x4b, 0x31, 0xb5, 0xd2, 0x13, 0x39, 0x6c, 0xa5, 0x03, 0x3f, 0x4d, 0x34, 0xf9, 0xec, 0x8e, 0x17, 0xc5, 0x25, 0x3c, 0x89, 0xc9, 0x2b, 0x3a, 0xc2, 0x6e, 0xc6, 0xaa, 0x91, 0x49, 0x18, 0x93, 0xde, 0x0d, 0x6f, 0x65, 0xaf, 0x92, 0xa7, 0xf6, 0xa6, 0x40, 0xb9, 0xed, 0xb0, 0xc3, 0xd7, 0x7d, 0x7c, 0x54, 0x59, 0xdf, 0x2f, 0xda, 0xa4, 0x05, 0x94, 0x9b, 0x72, 0x01, 0x74, 0xa9, 0xf7, 0x81, 0xe9, 0x1f, 0xb3, 0xeb, 0xcf, 0xe8, 0x47, 0x52, 0x36, 0xbc, 0x16, 0x29, 0x76, 0x12, 0xfa, 0x9c, 0x8a, 0x5b, 0xa8, 0x43, 0xd1, 0x79, 0x85, 0x42, 0x82, 0xc7, 0xa1, 0x78, 0x4f, 0xe2, 0x35, 0xea, 0xad, 0xdc, 0x0e, 0xd3, 0x2d, 0x6a, 0x5a, 0x44, 0xab, 0xc8, 0xe5, 0x37, 0x0a, 0x6b, 0x51, 0xe3, 0x14, 0xcd, 0x56, 0x4a, 0xd6, 0x08, 0x83, 0xbb, 0x33, 0xe1, 0x30, 0x4e, 0x24, 0x5e, 0xb4, 0x00, 0x48, 0x5f, 0x22, 0x0b, 0x50, 0x3d, 0x80, 0x1a, 0xbf, 0xcc, 0xff, 0x64, 0x87, 0x1b, 0xc4, 0x07, 0xf8, 0x0c, 0xd4, 0xac, 0x02, 0x10, 0x84, 0x7e, 0x69, 0x70, 0x60, 0x55, 0x2a, 0x21, 0x57, 0x23, 0x66, 0x62, 0x73, 0xcb, 0x41, 0x58, 0x71, 0x77, 0x1c, 0x7b, 0x8f, 0x9f, 0x9d, 0xa3, 0xb1, 0x7f, 0x5d, 0xf4, 0x06, 0xae, 0xd5, 0xe6, 0x3b, 0xba, 0xfe, 0x96, 0xe7, 0x0f, 0x45, 0x2c, 0xf0, 0xfc, 0xbd, 0xe4, 0x98, 0xfb, 0xca, 0x11, 0xf5, 0xdd, 0x7a, 0x5c, 0xfd, 0xce, 0x88, 0xd0, 0x68, 0x8d, 0x4c, 0xbe, 0x04, 0x38, 0x1d, 0x1e, 0xf2, 0x27, 0x19, 0xb2, 0x75, 0xa2, 0xee, 0xdb, 0xb8, 0x09, 0x8b, ) def __init__(self, data=b""): self.h = list(self.h_init) self.hash_words = list(self.hash_init) self.buf = bytearray() self.msg_len = 0 if data: self.update(data) return def copy(self): other = self.__class__() other.h = list(self.h) other.hash_words = list(self.hash_words) other.buf = bytearray(self.buf) other.msg_len = self.msg_len return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def digest(self): c = self.copy() c.finalize() out = bytearray() for word in c.hash_words: out.extend(word.to_bytes(8, "big")) return bytes(out[:self.digest_size]) def hexdigest(self): return self.digest().hex() def u64_to_le_list(self, x): return list(x.to_bytes(8, "little")) def u64_from_le_list(self, values): return int.from_bytes(bytes(values), "little") def get_f16(self, x, a, b, c): return [x[1] ^ a, x[0] ^ b ^ self.sbox[x[1] ^ c]] def get_f32(self, x, a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama): y0 = x[2] ^ ((alpha >> 8) & 0xff) y1 = x[3] ^ (alpha & 0xff) z = [ x[2] ^ ((gama >> 8) & 0xff), x[3] ^ (gama & 0xff), ] f = self.get_f16(z, a3, b3, c3) z = self.get_f16(f, a2, b2, c2) f = self.get_f16(z, a1, b1, c1) return [ y0, y1, x[0] ^ ((beta >> 8) & 0xff) ^ f[0], x[1] ^ (beta & 0xff) ^ f[1], ] def get_f64(self, x, a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama, a_word, b_word, c_word): y = [ x[4] ^ ((a_word >> 24) & 0xff), x[5] ^ ((a_word >> 16) & 0xff), x[6] ^ ((a_word >> 8) & 0xff), x[7] ^ (a_word & 0xff), ] z = [ x[4] ^ ((c_word >> 24) & 0xff), x[5] ^ ((c_word >> 16) & 0xff), x[6] ^ ((c_word >> 8) & 0xff), x[7] ^ (c_word & 0xff), ] f = self.get_f32(z, a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama) return y + [ x[0] ^ ((b_word >> 24) & 0xff) ^ f[0], x[1] ^ ((b_word >> 16) & 0xff) ^ f[1], x[2] ^ ((b_word >> 8) & 0xff) ^ f[2], x[3] ^ (b_word & 0xff) ^ f[3], ] def get_q64(self, x, y, a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama, a_word, b_word, c_word): z = [xb ^ yb for xb, yb in zip(x, y)] fp = self.get_f64( z[::-1], a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama, a_word, b_word, c_word, ) fz = fp[::-1] return [fb ^ yb for fb, yb in zip(fz, y)] def ae(self, leader, values, a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama, a_word, b_word, c_word): out = [0] * len(values) m1 = leader for i, value in enumerate(values): p = (value + m1) & 0xffff_ffff_ffff_ffff m1 = value q = self.get_q64( self.u64_to_le_list(p), self.u64_to_le_list(m1), a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama, a_word, b_word, c_word, ) m1 = self.u64_from_le_list(q) out[i] = m1 return out def rae(self, leader, values, a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama, a_word, b_word, c_word): out = [0] * len(values) p = leader for i in range(len(values) - 1, -1, -1): p = (p + values[i]) & 0xffff_ffff_ffff_ffff q = self.get_q64( self.u64_to_le_list(values[i]), self.u64_to_le_list(p), a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha, beta, gama, a_word, b_word, c_word, ) p = self.u64_from_le_list(q) out[i] = p return out def lintr(self, values): values = list(values) last = len(values) - 1 t0, t1, t2, t3 = self.lintr_taps for _ in range(self.lintr_rounds): pom = values[t0] ^ values[t1] ^ values[t2] ^ values[t3] for i in range(last, 0, -1): values[i] = values[i - 1] values[0] = pom return values def swap_64_halves(self, values): return [((x & 0xffff_ffff) << 32) | (x >> 32) for x in values] def compile_words(self, message_words): chain_words = len(self.h) x = [0] * (chain_words * 4) for k in range(chain_words): x[k << 2] = message_words[k << 1] x[(k << 2) + 1] = self.h[k] x[(k << 2) + 2] = message_words[(k << 1) + 1] x[(k << 2) + 3] = self.hash_words[k] x = self.lintr(x) l1 = (x[0] + x[1]) & 0xffff_ffff_ffff_ffff l2 = (x[2] + x[3]) & 0xffff_ffff_ffff_ffff tmp = (x[4] + x[5]) & 0xffff_ffff_ffff_ffff a1 = (tmp >> 56) & 0xff b1 = (tmp >> 48) & 0xff c1 = (tmp >> 40) & 0xff a2 = (tmp >> 32) & 0xff b2 = (tmp >> 24) & 0xff c2 = (tmp >> 16) & 0xff a3 = (tmp >> 8) & 0xff b3 = tmp & 0xff c3 = a1 tmp = (x[6] + x[7]) & 0xffff_ffff_ffff_ffff alpha1 = (tmp >> 48) & 0xffff beta1 = (tmp >> 32) & 0xffff gama1 = (tmp >> 16) & 0xffff alpha2 = tmp & 0xffff tmp = (x[8] + x[9]) & 0xffff_ffff_ffff_ffff beta2 = (tmp >> 48) & 0xffff gama2 = (tmp >> 32) & 0xffff tmp = (x[10] + x[11]) & 0xffff_ffff_ffff_ffff a_word1 = (tmp >> 32) & 0xffff_ffff b_word1 = tmp & 0xffff_ffff tmp = (x[12] + x[13]) & 0xffff_ffff_ffff_ffff c_word1 = (tmp >> 32) & 0xffff_ffff a_word2 = tmp & 0xffff_ffff tmp = (x[14] + x[15]) & 0xffff_ffff_ffff_ffff b_word2 = (tmp >> 32) & 0xffff_ffff c_word2 = tmp & 0xffff_ffff y = self.ae( l2, x, a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha1, beta1, gama1, a_word1, b_word1, c_word1, ) y = self.swap_64_halves(y) x = self.rae( l1, y, a1, b1, c1, a2, b2, c2, a3, b3, c3, alpha2, beta2, gama2, a_word2, b_word2, c_word2, ) for i in range(chain_words): self.h[i] = x[(i << 2) + 1] self.hash_words[i] = x[(i << 2) + 3] return def compress(self, block): message_words = [ int.from_bytes(block[i:i + 8], "little") for i in range(0, self.block_size, 8) ] self.compile_words(message_words) return def finalize(self): total_bits = self.msg_len * 8 word_count = self.block_size // 8 buf = bytearray(self.buf) buf.extend(b"\x00" * (self.block_size - len(buf))) words = [ int.from_bytes(buf[i:i + 8], "little") for i in range(0, self.block_size, 8) ] i = len(self.buf) bit_mod_64 = total_bits % 64 word_index = i >> 3 if bit_mod_64 != 0: words[word_index] &= 0xffff_ffff_ffff_fffe >> (64 - bit_mod_64) else: words[word_index] = 0 words[word_index] |= 1 << bit_mod_64 if i > self.block_size - 17: if i < self.block_size - 8: words[word_count - 1] = 0 self.compile_words(words) i = 0 words = [0] * word_count else: i = (i >> 3) + 1 while i < word_count - 2: words[i] = 0 i += 1 words[word_count - 2] = 0 words[word_count - 1] = total_bits self.compile_words(words) return class NaSHA224(NaSHABase): name = "nasha224" block_size = 64 digest_size = 28 lintr_rounds = 16 lintr_taps = (15, 9, 6, 3) h_init = ( 0x6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1, ) hash_init = ( 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, ) class NaSHA256(NaSHABase): name = "nasha256" block_size = 64 digest_size = 32 lintr_rounds = 16 lintr_taps = (15, 9, 6, 3) h_init = ( 0x510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179, ) hash_init = ( 0x6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4, ) class NaSHA384(NaSHABase): name = "nasha384" block_size = 128 digest_size = 48 lintr_rounds = 32 lintr_taps = (31, 24, 14, 6) h_init = ( 0x6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1, 0x510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179, ) hash_init = ( 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4, ) class NaSHA512(NaSHABase): name = "nasha512" block_size = 128 digest_size = 64 lintr_rounds = 32 lintr_taps = (31, 24, 14, 6) h_init = ( 0x2dd8_a09a_3c4e_3efb, 0x061a_77a0_6094_8dcd, 0x8a47_ea18_8055_9ce6, 0x9f22_535b_2646_07a8, 0x2547_d84e_9ccd_e59d, 0x9486_eb50_c7d8_037f, 0xc0f9_05d7_41c9_cb74, 0xad0d_1e41_a985_e51e, ) hash_init = ( 0xe076_88dc_6f16_6b73, 0x0c34_aa2a_315e_01d5, 0xc785_f436_4a0b_98f4, 0x53a8_c8ca_56e1_288c, 0x3c15_63a9_317c_57a1, 0x7734_1eda_d21e_9a40, 0xd648_813e_4512_1dbb, 0x4cf7_68fc_7df1_1b00, ) class SANDstorm256Base: block_size = 64 extra_rounds = 0 mask64 = 0xffff_ffff_ffff_ffff aconst_256 = 0xa611_186b bconst_256 = 0xbee8_390d cconst_256 = 0x6135_f68d_4c0c_bb6f dconst_256 = 0x79cc_4519_5cf5_b7a4 j3 = 0x3333_3333_3333_3333 j5 = 0x5555_5555_5555_5555 j6 = 0x6666_6666_6666_6666 ms_rot_bits = 27 bitmix_rot_bits = 19 r_rot_bits = 25 b = [ 0x428a_2f98_7137_4491, 0xb5c0_fbcf_e9b5_dba5, 0x3956_c25b_59f1_11f1, 0x923f_82a4_ab1c_5ed5, 0xd807_aa98_1283_5b01, 0x2431_85be_550c_7dc3, 0x72be_5d74_80de_b1fe, 0x9bdc_06a7_c19b_f174, 0xe49b_69c1_efbe_4786, 0x0fc1_9dc6_240c_a1cc, 0x2de9_2c6f_4a74_84aa, 0x5cb0_a9dc_76f9_88da, 0x983e_5152_a831_c66d, 0xb003_27c8_bf59_7fc7, 0xc6e0_0bf3_d5a7_9147, 0x06ca_6351_1429_2967, 0x27b7_0a85_2e1b_2138, 0x4d2c_6dfc_5338_0d13, 0x650a_7354_766a_0abb, 0x81c2_c92e_9272_2c85, 0xa2bf_e8a1_a81a_664b, 0xc24b_8b70_c76c_51a3, 0xd192_e819_d699_0624, 0xf40e_3585_106a_a070, 0x19a4_c116_1e37_6c08, ] fsbox = [ 0x63, 0x7d, 0x75, 0x78, 0xf6, 0x6e, 0x69, 0xc2, 0x38, 0x08, 0x6d, 0x20, 0xf2, 0xda, 0xa5, 0x79, 0xda, 0x93, 0xdb, 0x6e, 0xee, 0x4c, 0x51, 0xe7, 0xb5, 0xcd, 0xb8, 0xb4, 0x80, 0xb9, 0x6c, 0xdf, 0x97, 0xdc, 0xb1, 0x05, 0x12, 0x1a, 0xd1, 0xeb, 0x1c, 0x8c, 0xcf, 0xda, 0x5d, 0xf5, 0x1f, 0x3a, 0x34, 0xf6, 0x11, 0xf0, 0x2c, 0xa3, 0x33, 0xad, 0x3f, 0x2b, 0xba, 0xd9, 0xd7, 0x1a, 0x8c, 0x4a, 0x49, 0xc2, 0x6e, 0x59, 0x5f, 0x2b, 0x1c, 0xe7, 0x1a, 0x72, 0x9c, 0xf8, 0x65, 0xae, 0x61, 0xcb, 0x03, 0x80, 0x52, 0xbe, 0x74, 0xa9, 0xe7, 0x0c, 0x32, 0x92, 0xe4, 0x62, 0x16, 0x11, 0x06, 0x90, 0xb0, 0x8e, 0xc8, 0x98, 0x27, 0x28, 0x55, 0xe2, 0x2d, 0x90, 0x68, 0x14, 0x3c, 0x51, 0xf1, 0xc7, 0x21, 0xd2, 0x32, 0xfc, 0xe6, 0xe8, 0x4e, 0x82, 0xc4, 0xcf, 0xa0, 0x5a, 0x6c, 0x82, 0x8d, 0xad, 0x4d, 0x8d, 0x91, 0x6f, 0xdb, 0x12, 0xc2, 0x90, 0x4c, 0x2e, 0xf4, 0xb6, 0xe8, 0xd0, 0x97, 0xfc, 0xf0, 0x10, 0xdd, 0x4f, 0xb6, 0xbf, 0x06, 0x1f, 0xde, 0x77, 0x22, 0x8f, 0x42, 0xc3, 0x95, 0x44, 0x40, 0x93, 0x98, 0xa9, 0xed, 0xa3, 0x82, 0xfb, 0x6a, 0x7a, 0x06, 0xc9, 0x3d, 0x38, 0x4a, 0xd6, 0x57, 0x79, 0x85, 0xde, 0x39, 0x60, 0xf8, 0x1e, 0xd4, 0xef, 0x4e, 0x51, 0xd9, 0xc7, 0x10, 0xb7, 0x7a, 0xb9, 0xe7, 0xed, 0xd8, 0x63, 0x72, 0x01, 0x20, 0x14, 0xbe, 0xd4, 0x87, 0x70, 0x45, 0x45, 0xa0, 0xef, 0x67, 0xb5, 0x9c, 0xd6, 0x20, 0xd9, 0xb9, 0xec, 0x8d, 0x62, 0x5a, 0x1c, 0xc3, 0x41, 0x01, 0x19, 0x7a, 0xf2, 0x8d, 0x3c, 0x68, 0x73, 0x73, 0xf7, 0x6d, 0x02, 0x22, 0xb8, 0xc6, 0x30, 0x7c, 0x50, 0x7b, 0xfe, 0x4b, 0x13, 0xb4, 0x9f, 0xb9, 0x60, 0xd7, 0xf4, 0x4c, 0xa9, 0x45, 0xe9, ] constants_224 = [ [0xc105_9ed8_367c_d507, 0x3070_dd17_f70e_5939, 0xffc0_0b31_6858_1511, 0x64f9_8fa7_befa_4fa4], [0x367c_d507_3070_dd17, 0xf70e_5939_ffc0_0b31, 0x6858_1511_64f9_8fa7, 0xbefa_4fa4_c105_9ed8], [0x3070_dd17_f70e_5939, 0xffc0_0b31_6858_1511, 0x64f9_8fa7_befa_4fa4, 0xc105_9ed8_367c_d507], [0xf70e_5939_ffc0_0b31, 0x6858_1511_64f9_8fa7, 0xbefa_4fa4_c105_9ed8, 0x367c_d507_3070_dd17], [0xffc0_0b31_6858_1511, 0x64f9_8fa7_befa_4fa4, 0xc105_9ed8_367c_d507, 0x3070_dd17_f70e_5939], ] constants_256 = [ [0x6a09_e667_bb67_ae85, 0x3c6e_f372_a54f_f53a, 0x510e_527f_9b05_688c, 0x1f83_d9ab_5be0_cd19], [0xbb67_ae85_3c6e_f372, 0xa54f_f53a_510e_527f, 0x9b05_688c_1f83_d9ab, 0x5be0_cd19_6a09_e667], [0x3c6e_f372_a54f_f53a, 0x510e_527f_9b05_688c, 0x1f83_d9ab_5be0_cd19, 0x6a09_e667_bb67_ae85], [0xa54f_f53a_510e_527f, 0x9b05_688c_1f83_d9ab, 0x5be0_cd19_6a09_e667, 0xbb67_ae85_3c6e_f372], [0x510e_527f_9b05_688c, 0x1f83_d9ab_5be0_cd19, 0x6a09_e667_bb67_ae85, 0x3c6e_f372_a54f_f53a], ] def __init__(self, data=b""): self.buf = bytearray() self.msg_len = 0 self.piped_bits = 0 self.init_compress_flag = 0 self.block_iters = [0, 0, 0] self.queued_data = bytearray(64) self.prev_block = [[[0, 0, 0, 0] for _ in range(5)] for _ in range(3)] self.level_one_to_four_const = [[[0, 0, 0, 0] for _ in range(5)] for _ in range(4)] if self.hashbitlen == 224: base = self.constants_224 elif self.hashbitlen == 256: base = self.constants_256 else: raise ValueError("invalid hash bit length") self.main_constant_input_words = [row[:] for row in base] self.initial_vector_temp_words = [row[:] for row in base] if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.piped_bits = self.piped_bits other.init_compress_flag = self.init_compress_flag other.block_iters = self.block_iters[:] other.queued_data = bytearray(self.queued_data) other.prev_block = [[row[:] for row in level] for level in self.prev_block] other.level_one_to_four_const = [[row[:] for row in level] for level in self.level_one_to_four_const] other.main_constant_input_words = [row[:] for row in self.main_constant_input_words] other.initial_vector_temp_words = [row[:] for row in self.initial_vector_temp_words] return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 64: self.queued_data[:] = self.buf[:64] del self.buf[:64] self.piped_bits = 512 if self.init_compress_flag == 0: self.init_compress() else: self.compress() self.piped_bits = len(self.buf) * 8 self.queued_data[:len(self.buf)] = self.buf return self def digest(self): other = self.copy() return other.finalize() def hexdigest(self): return self.digest().hex() def finalize(self): last = len(self.buf) self.queued_data[:last] = self.buf self.queued_data[last] = 0x80 for i in range(last + 1, 64): self.queued_data[i] = 0 if self.init_compress_flag == 0: self.init_compress() message_words = self.create_message_from_block(self.initial_vector_temp_words) message_length = self.piped_bits else: message_length = (self.block_iters[0] + 1) * 512 + self.piped_bits self.compress() message_words = self.create_message_from_block(self.prev_block[0]) if self.block_iters[1] > 0: self.do_block_mod_mix_ref(message_words, self.prev_block[1]) message_words = self.create_message_from_block(self.prev_block[1]) if self.block_iters[2] > 0: self.do_block_mod_mix_ref(message_words, self.prev_block[2]) message_words = self.create_message_from_block(self.prev_block[2]) for i in range(5): for j in range(1, 4, 2): self.level_one_to_four_const[3][i][j] ^= message_length self.do_block_mod_mix_ref(message_words, self.level_one_to_four_const[3]) out_words = [] for i in range(4): out_words.append( self.level_one_to_four_const[3][1][i] ^ self.level_one_to_four_const[3][2][i] ^ self.level_one_to_four_const[3][3][i] ^ self.level_one_to_four_const[3][4][i] ) raw = b"".join(word.to_bytes(8, "big") for word in out_words) return raw[:self.digest_size] def init_compress(self): self.init_compress_flag = 1 message_words = self.create_message_from_queue() self.do_block_mod_mix_ref(message_words, self.initial_vector_temp_words) for i in range(4): self.level_one_to_four_const[0][0][i] = self.main_constant_input_words[0][i] ^ self.initial_vector_temp_words[4][i] self.level_one_to_four_const[0][1][i] = self.main_constant_input_words[1][i] ^ self.initial_vector_temp_words[1][i] self.level_one_to_four_const[0][2][i] = self.main_constant_input_words[2][i] ^ self.initial_vector_temp_words[2][i] self.level_one_to_four_const[0][3][i] = self.main_constant_input_words[3][i] ^ self.initial_vector_temp_words[3][i] self.level_one_to_four_const[0][4][i] = self.main_constant_input_words[4][i] ^ self.initial_vector_temp_words[4][i] for i in range(5): for j in range(4): self.level_one_to_four_const[1][i][j] = self.level_one_to_four_const[0][i][j] self.level_one_to_four_const[2][i][j] = self.level_one_to_four_const[0][i][j] self.level_one_to_four_const[3][i][j] = self.main_constant_input_words[i][j] for i in range(5): self.level_one_to_four_const[1][i][3] ^= self.cconst_256 self.level_one_to_four_const[2][i][3] ^= self.dconst_256 self.level_one_to_four_const[3][i][3] ^= self.dconst_256 self.level_one_to_four_const[3][i][0] = (~self.level_one_to_four_const[3][i][0]) & self.mask64 self.level_one_to_four_const[3][i][1] = (~self.level_one_to_four_const[3][i][1]) & self.mask64 for i in range(3): for j in range(5): for k in range(4): self.prev_block[i][j][k] = self.level_one_to_four_const[i][j][k] for i in range(2): for j in range(5): for k in range(1, 4, 2): self.prev_block[i][j][k] ^= 1 return def compress(self): message_l1 = self.create_message_from_queue() if (self.block_iters[0] % 10) == 0 and self.block_iters[0] > 1: message_l2 = self.create_message_from_block(self.prev_block[0]) self.do_block_mod_mix_ref(message_l2, self.prev_block[1]) level_one_superblock = self.block_iters[0] // 10 + 1 for i in range(5): for j in range(4): self.prev_block[0][i][j] = self.level_one_to_four_const[0][i][j] ^ (level_one_superblock if (j & 1) else 0) self.block_iters[1] += 1 if (self.block_iters[1] % 100) == 0 and self.block_iters[1] > 1: message_l3 = self.create_message_from_block(self.prev_block[1]) self.do_block_mod_mix_ref(message_l3, self.prev_block[2]) level_two_superblock = self.block_iters[1] // 100 + 1 for i in range(5): for j in range(4): self.prev_block[1][i][j] = self.level_one_to_four_const[1][i][j] ^ (level_two_superblock if (j & 1) else 0) self.block_iters[2] += 1 self.do_block_mod_mix_ref(message_l1, self.prev_block[0]) self.block_iters[0] += 1 return def create_message_from_queue(self): words = [] for i in range(8): words.append(int.from_bytes(self.queued_data[i * 8:(i + 1) * 8], "big")) return words def create_message_from_block(self, state_words): return [ state_words[1][0] ^ state_words[3][0], state_words[1][1] ^ state_words[3][1], state_words[1][2] ^ state_words[3][2], state_words[1][3] ^ state_words[3][3], state_words[2][0] ^ state_words[4][0], state_words[2][1] ^ state_words[4][1], state_words[2][2] ^ state_words[4][2], state_words[2][3] ^ state_words[4][3], ] def do_block_mod_mix_ref(self, data_input, prev_block_arr): msd = data_input[:] + [0] * 25 for i in range(8, 33): value = ( msd[i - 8] + self.b[i - 8] + self.g(msd[i - 1]) + self.ch(msd[i - 1], msd[i - 2], msd[i - 3]) + msd[i - 4] ) & self.mask64 msd[i] = self.rol64(self.sb(value), self.ms_rot_bits) ws = [self.rol64(msd[i], self.bitmix_rot_bits) ^ msd[i + 4] for i in range(4)] ws = self.bit_mix(ws) ws = [ws[i] ^ prev_block_arr[0][i] for i in range(4)] ws = self.do_round(ws, 0) ws = self.bit_mix(ws) prev_block_arr[1] = [(prev_block_arr[1][i] ^ ws[i]) & self.mask64 for i in range(4)] ws = [prev_block_arr[1][i] ^ msd[14 + i] for i in range(4)] ws = self.do_round(ws, 1) ws = self.bit_mix(ws) prev_block_arr[1] = [(prev_block_arr[2][i] ^ ws[i]) & self.mask64 for i in range(4)] ws = [prev_block_arr[1][i] ^ msd[19 + i] for i in range(4)] ws = self.do_round(ws, 2) ws = self.bit_mix(ws) prev_block_arr[2] = [(prev_block_arr[3][i] ^ ws[i]) & self.mask64 for i in range(4)] ws = [prev_block_arr[2][i] ^ msd[24 + i] for i in range(4)] ws = self.do_round(ws, 3) ws = self.bit_mix(ws) prev_block_arr[3] = [(prev_block_arr[4][i] ^ ws[i]) & self.mask64 for i in range(4)] ws = [prev_block_arr[3][i] ^ msd[29 + i] for i in range(4)] ws = self.do_round(ws, 4) ws = self.bit_mix(ws) for _ in range(self.extra_rounds): ws = self.do_round(ws, 4) ws = self.bit_mix(ws) prev_block_arr[4] = [value & self.mask64 for value in ws] return def bit_mix(self, ws): ws = ws[:] value = (ws[0] ^ ws[2]) & self.j6 ws[0] ^= value ws[2] ^= value value = (ws[1] ^ ws[3]) & self.j3 ws[1] ^= value ws[3] ^= value value = (ws[0] ^ ws[1]) & self.j5 ws[0] ^= value ws[1] ^= value value = (ws[2] ^ ws[3]) & self.j5 ws[2] ^= value ws[3] ^= value return ws def do_round(self, ws, round_index): ws = ws[:] ws[0] = self.rol64( self.sb((ws[0] + self.f(ws[3]) + self.ch(ws[3], ws[2], ws[1]) + self.b[24 - (4 * round_index)]) & self.mask64), self.r_rot_bits, ) ws[1] = self.rol64( self.sb((ws[1] + self.f(ws[0]) + self.ch(ws[0], ws[3], ws[2]) + self.b[23 - (4 * round_index)]) & self.mask64), self.r_rot_bits, ) ws[2] = self.rol64( self.sb((ws[2] + self.f(ws[1]) + self.ch(ws[1], ws[0], ws[3]) + self.b[22 - (4 * round_index)]) & self.mask64), self.r_rot_bits, ) ws[3] = self.rol64( self.sb((ws[3] + self.f(ws[2]) + self.ch(ws[2], ws[1], ws[0]) + self.b[21 - (4 * round_index)]) & self.mask64), self.r_rot_bits, ) return ws def rol64(self, value, count): value &= self.mask64 return ((value << count) | (value >> (64 - count))) & self.mask64 def rot32(self, value): value &= self.mask64 return ((value << 32) | (value >> 32)) & self.mask64 def f(self, value): upper = (value >> 32) & 0xffff_ffff lower = value & 0xffff_ffff return (upper * upper + lower * lower) & self.mask64 def g(self, value): upper = (value >> 32) & 0xffff_ffff lower = value & 0xffff_ffff return ( upper * upper + lower * lower + self.rot32((((upper + self.aconst_256) & 0xffff_ffff) * ((lower + self.bconst_256) & 0xffff_ffff)) & self.mask64) ) & self.mask64 def ch(self, a, b, c): return ((a & b) ^ ((~a) & c)) & self.mask64 def sb(self, value): value &= self.mask64 return value ^ self.fsbox[value & 0xff] class SANDstorm224(SANDstorm256Base): digest_size = 28 hashbitlen = 224 class SANDstorm256(SANDstorm256Base): digest_size = 32 hashbitlen = 256 class SANDstorm512Base(SANDstorm256Base): block_size = 128 extra_rounds = 0 mask64 = 0xffff_ffff_ffff_ffff mask128 = 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff aconst_512 = 0xa611_186b_ae67_496b bconst_512 = 0xbee8_390d_4395_5aed sandwblkc = 0x6135_f68d_4c0c_bb6f_b43b_47a2_4577_8989 sandwblkd = 0x79cc_4519_5cf5_b7a4_aec4_e749_6801_dbb9 j3_128 = 0x3333_3333_3333_3333_3333_3333_3333_3333 j5_128 = 0x5555_5555_5555_5555_5555_5555_5555_5555 j6_128 = 0x6666_6666_6666_6666_6666_6666_6666_6666 sandwmspick = [4, 14, 19, 24, 29] sandwlvlc384 = [ 0xcbbb_9d5d_c105_9ed8_629a_292a_367c_d507, 0x9159_015a_3070_dd17_152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31_8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7_47b5_481d_befa_4fa4, 0x629a_292a_367c_d507_9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939_6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511_db0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4_cbbb_9d5d_c105_9ed8, 0x9159_015a_3070_dd17_152f_ecd8_f70e_5939, 0x6733_2667_ffc0_0b31_8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7_47b5_481d_befa_4fa4, 0xcbbb_9d5d_c105_9ed8_629a_292a_367c_d507, 0x152f_ecd8_f70e_5939_6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511_db0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4_cbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507_9159_015a_3070_dd17, 0x6733_2667_ffc0_0b31_8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7_47b5_481d_befa_4fa4, 0xcbbb_9d5d_c105_9ed8_629a_292a_367c_d507, 0x9159_015a_3070_dd17_152f_ecd8_f70e_5939, ] sandwlvlc512 = [ 0x6a09_e667_f3bc_c908_bb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b_a54f_f53a_5f1d_36f1, 0x510e_527f_ade6_82d1_9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b_5be0_cd19_137e_2179, 0xbb67_ae85_84ca_a73b_3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1_510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f_1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179_6a09_e667_f3bc_c908, 0x3c6e_f372_fe94_f82b_a54f_f53a_5f1d_36f1, 0x510e_527f_ade6_82d1_9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b_5be0_cd19_137e_2179, 0x6a09_e667_f3bc_c908_bb67_ae85_84ca_a73b, 0xa54f_f53a_5f1d_36f1_510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f_1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179_6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b_3c6e_f372_fe94_f82b, 0x510e_527f_ade6_82d1_9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b_5be0_cd19_137e_2179, 0x6a09_e667_f3bc_c908_bb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b_a54f_f53a_5f1d_36f1, ] sandwmsc = [ 0x428a_2f98_d728_ae22_7137_4491_23ef_65cd, 0xb5c0_fbcf_ec4d_3b2f_e9b5_dba5_8189_dbbc, 0x3956_c25b_f348_b538_59f1_11f1_b605_d019, 0x923f_82a4_af19_4f9b_ab1c_5ed5_da6d_8118, 0xd807_aa98_a303_0242_1283_5b01_4570_6fbe, 0x2431_85be_4ee4_b28c_550c_7dc3_d5ff_b4e2, 0x72be_5d74_f27b_896f_80de_b1fe_3b16_96b1, 0x9bdc_06a7_25c7_1235_c19b_f174_cf69_2694, 0xe49b_69c1_9ef1_4ad2_efbe_4786_384f_25e3, 0x0fc1_9dc6_8b8c_d5b5_240c_a1cc_77ac_9c65, 0x2de9_2c6f_592b_0275_4a74_84aa_6ea6_e483, 0x5cb0_a9dc_bd41_fbd4_76f9_88da_8311_53b5, 0x983e_5152_ee66_dfab_a831_c66d_2db4_3210, 0xb003_27c8_98fb_213f_bf59_7fc7_beef_0ee4, 0xc6e0_0bf3_3da8_8fc2_d5a7_9147_930a_a725, 0x06ca_6351_e003_826f_1429_2967_0a0e_6e70, 0x27b7_0a85_46d2_2ffc_2e1b_2138_5c26_c926, 0x4d2c_6dfc_5ac4_2aed_5338_0d13_9d95_b3df, 0x650a_7354_8baf_63de_766a_0abb_3c77_b2a8, 0x81c2_c92e_47ed_aee6_9272_2c85_1482_353b, 0xa2bf_e8a1_4cf1_0364_a81a_664b_bc42_3001, 0xc24b_8b70_d0f8_9791_c76c_51a3_0654_be30, 0xd192_e819_d6ef_5218_d699_0624_5565_a910, 0xf40e_3585_5771_202a_106a_a070_32bb_d1b8, 0x19a4_c116_b8d2_d0c8_1e37_6c08_5141_ab53, ] def __init__(self, data=b""): self.buf = bytearray() self.msg_len = 0 self.msglen_bits = 0 self.mbcnt = 0 self.msgbytes = bytearray(128) self.msg = [0] * 8 self.blkn1 = 1 self.blkn2 = 1 self.lvl0flag = 0 self.lvl1cnt = 0 self.lvl2cnt = 0 self.lvl3flag = 0 self.prev = [0] * 100 if self.hashbitlen == 384: self.lvlc = self.sandwlvlc384 elif self.hashbitlen == 512: self.lvlc = self.sandwlvlc512 else: raise ValueError("invalid hash bit length") for i in range(20): self.prev[i] = self.lvlc[i] if data: self.update(data) return def copy(self): other = self.__class__() other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.msglen_bits = self.msglen_bits other.mbcnt = self.mbcnt other.msgbytes = bytearray(self.msgbytes) other.msg = self.msg[:] other.blkn1 = self.blkn1 other.blkn2 = self.blkn2 other.lvl0flag = self.lvl0flag other.lvl1cnt = self.lvl1cnt other.lvl2cnt = self.lvl2cnt other.lvl3flag = self.lvl3flag other.prev = self.prev[:] return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.msglen_bits = (self.msglen_bits + (len(data) * 8)) & self.mask128 self.buf.extend(data) while len(self.buf) >= 128: self.msgbytes[:] = self.buf[:128] del self.buf[:128] self.mbcnt = 128 self.sandwdoit() return self def digest(self): other = self.copy() return other.finalize() def hexdigest(self): return self.digest().hex() def finalize(self): self.msgbytes[:len(self.buf)] = self.buf self.mbcnt = len(self.buf) for i in range(self.mbcnt, 128): self.msgbytes[i] = 0 self.mbcnt += 1 self.msgbytes[self.mbcnt - 1] |= 0x80 self.sandwdoit() if not (self.lvl2cnt == 0 and self.blkn2 == 1): if self.lvl1cnt: if self.lvl2cnt == 100: for i in range(8): self.msg[i] = self.x128(self.prev[44 + i], self.prev[52 + i]) if not self.lvl3flag: for i in range(20): self.prev[60 + i] = self.x128(self.lvlc[i], self.prev[i + 16 if i < 4 else i]) for i in range(0, 20, 4): self.prev[63 + i] = self.x128(self.prev[63 + i], self.sandwblkd) self.sandwcmprs(self.msg, self.prev, 3, self.extra_rounds) self.lvl2cnt = 0 self.blkn2 = self.a128(self.blkn2, 1) self.lvl3flag = 1 if not self.lvl2cnt: for i in range(20): self.prev[40 + i] = self.x128(self.lvlc[i], self.prev[i + 16 if i < 4 else i]) for i in range(0, 20, 4): self.prev[43 + i] = self.x128(self.prev[43 + i], self.sandwblkc) for i in range(0, 20, 2): self.prev[41 + i] = self.x128(self.prev[41 + i], self.blkn2) for i in range(8): self.msg[i] = self.x128(self.prev[24 + i], self.prev[32 + i]) self.sandwcmprs(self.msg, self.prev, 2, self.extra_rounds) self.lvl1cnt = 0 self.blkn1 = self.a128(self.blkn1, 1) self.lvl2cnt += 1 if self.lvl3flag and self.lvl2cnt: for i in range(8): self.msg[i] = self.x128(self.prev[44 + i], self.prev[52 + i]) self.sandwcmprs(self.msg, self.prev, 3, self.extra_rounds) self.lvl2cnt = 0 self.blkn2 = self.a128(self.blkn2, 1) for i in range(20): self.prev[80 + i] = self.lvlc[i] for i in range(0, 20, 4): self.prev[83 + i] = self.x128(self.prev[83 + i], self.sandwblkd) for i in range(0, 20, 2): self.prev[81 + i] = self.x128(self.prev[81 + i], self.msglen_bits) for i in range(20): if not (i & 2): self.prev[80 + i] = self.c128(self.prev[80 + i]) if self.lvl1cnt == 0 and self.blkn1 == 1: for i in range(8): self.msg[i] = self.x128(self.prev[4 + i], self.prev[12 + i]) elif self.lvl2cnt == 0 and self.blkn2 == 1: for i in range(8): self.msg[i] = self.x128(self.prev[24 + i], self.prev[32 + i]) elif self.lvl3flag == 0: for i in range(8): self.msg[i] = self.x128(self.prev[44 + i], self.prev[52 + i]) else: for i in range(8): self.msg[i] = self.x128(self.prev[64 + i], self.prev[72 + i]) self.sandwcmprs(self.msg, self.prev, 4, self.extra_rounds) for i in range(4): self.prev[96 + i] = self.x128( self.prev[84 + i], self.x128( self.prev[88 + i], self.x128(self.prev[92 + i], self.prev[96 + i]), ), ) raw = b"".join(word.to_bytes(16, "big") for word in self.prev[96:100]) return raw[:self.digest_size] def sandwdoit(self): if self.lvl1cnt == 10: if self.lvl2cnt == 100: for i in range(8): self.msg[i] = self.x128(self.prev[44 + i], self.prev[52 + i]) if not self.lvl3flag: for i in range(20): self.prev[60 + i] = self.x128(self.lvlc[i], self.prev[i + 16 if i < 4 else i]) for i in range(0, 20, 4): self.prev[63 + i] = self.x128(self.prev[63 + i], self.sandwblkd) self.sandwcmprs(self.msg, self.prev, 3, self.extra_rounds) self.lvl2cnt = 0 self.blkn2 = self.a128(self.blkn2, 1) self.lvl3flag = 1 if not self.lvl2cnt: for i in range(20): self.prev[40 + i] = self.x128(self.lvlc[i], self.prev[i + 16 if i < 4 else i]) for i in range(0, 20, 4): self.prev[43 + i] = self.x128(self.prev[43 + i], self.sandwblkc) for i in range(0, 20, 2): self.prev[41 + i] = self.x128(self.prev[41 + i], self.blkn2) for i in range(8): self.msg[i] = self.x128(self.prev[24 + i], self.prev[32 + i]) self.sandwcmprs(self.msg, self.prev, 2, self.extra_rounds) self.lvl1cnt = 0 self.blkn1 = self.a128(self.blkn1, 1) self.lvl2cnt += 1 for i in range(8): self.msg[i] = 0 for i in range(128): self.msg[i >> 4] ^= self.msgbytes[i] << (120 - ((i & 15) << 3)) self.mbcnt = 0 if self.lvl0flag == 0: self.sandwcmprs(self.msg, self.prev, 0, self.extra_rounds) self.lvl0flag = 1 else: if not self.lvl1cnt: for i in range(20): self.prev[20 + i] = self.x128(self.lvlc[i], self.prev[i + 16 if i < 4 else i]) for i in range(0, 20, 2): self.prev[21 + i] = self.x128(self.prev[21 + i], self.blkn1) self.sandwcmprs(self.msg, self.prev, 1, self.extra_rounds) self.lvl1cnt += 1 return def sandwcmprs(self, msg, prev, level, xrnds): ms = msg[:] + [0] * 25 for i in range(8, 33): ms[i] = self.r128( self.sb128( self.a128( ms[i - 8], self.a128( self.sandwmsc[i - 8], self.a128( self.sandwg(ms[i - 1]), self.a128( self.x128( self.b128(ms[i - 1], ms[i - 2]), self.b128(self.c128(ms[i - 1]), ms[i - 3]), ), ms[i - 4], ), ), ), ), ), 59, ) for i in range(4): ms[i + 4] = self.x128(self.r128(ms[i], 37), ms[i + 4]) temp = self.b128(self.x128(ms[4], ms[6]), self.j6_128) ms[4] = self.x128(ms[4], temp) ms[6] = self.x128(ms[6], temp) temp = self.b128(self.x128(ms[5], ms[7]), self.j3_128) ms[5] = self.x128(ms[5], temp) ms[7] = self.x128(ms[7], temp) temp = self.b128(self.x128(ms[4], ms[5]), self.j5_128) ms[4] = self.x128(ms[4], temp) ms[5] = self.x128(ms[5], temp) temp = self.b128(self.x128(ms[6], ms[7]), self.j5_128) ms[6] = self.x128(ms[6], temp) ms[7] = self.x128(ms[7], temp) w = [prev[20 * level + i] for i in range(4)] for rnd in range(5): for i in range(4): w[i] = self.x128(w[i], ms[self.sandwmspick[rnd] + i]) rounds = 1 if rnd < 4 else (xrnds + 1) for _ in range(rounds): for i in range(4): w[i] = self.r128( self.sb128( self.a128( w[i], self.a128( self.sandwf(w[(i + 3) & 3]), self.a128( self.x128( self.b128(w[(i + 3) & 3], w[i ^ 2]), self.b128(self.c128(w[(i + 3) & 3]), w[(i + 1) & 3]), ), self.sandwmsc[24 - i - (rnd << 2)], ), ), ), ), 57, ) temp = self.b128(self.x128(w[0], w[2]), self.j6_128) w[0] = self.x128(w[0], temp) w[2] = self.x128(w[2], temp) temp = self.b128(self.x128(w[1], w[3]), self.j3_128) w[1] = self.x128(w[1], temp) w[3] = self.x128(w[3], temp) temp = self.b128(self.x128(w[0], w[1]), self.j5_128) w[0] = self.x128(w[0], temp) w[1] = self.x128(w[1], temp) temp = self.b128(self.x128(w[2], w[3]), self.j5_128) w[2] = self.x128(w[2], temp) w[3] = self.x128(w[3], temp) if rnd < 4: for i in range(4): w[i] = self.x128(w[i], prev[20 * level + 4 * rnd + 4 + i]) if rnd: for i in range(4): prev[20 * level + 4 * rnd + i] = w[i] return def a128(self, a, b): return (a + b) & self.mask128 def x128(self, a, b): return (a ^ b) & self.mask128 def c128(self, value): return (~value) & self.mask128 def b128(self, a, b): return a & b def r128(self, value, count): count &= 127 if count == 0: return value & self.mask128 return ((value << count) | (value >> (128 - count))) & self.mask128 def m128(self, a, b): return (a * b) & self.mask128 def sandwf(self, value): upper = (value >> 64) & self.mask64 lower = value & self.mask64 return self.a128(self.m128(upper, upper), self.m128(lower, lower)) def sandwg(self, value): upper = (value >> 64) & self.mask64 lower = value & self.mask64 return self.a128( self.m128(upper, upper), self.a128( self.m128(lower, lower), self.r128( self.m128((upper + self.aconst_512) & self.mask64, (lower + self.bconst_512) & self.mask64), 64, ), ), ) def sb128(self, value): value &= self.mask128 return value ^ self.fsbox[value & 0xff] class SANDstorm384(SANDstorm512Base): digest_size = 48 hashbitlen = 384 class SANDstorm512(SANDstorm512Base): digest_size = 64 hashbitlen = 512 class SarmalBase: block_size = 128 hashbitlen = None digest_size = None MASK64 = 0xffff_ffff_ffff_ffff S = ( 0x3a, 0x5b, 0xf2, 0xf, 0xe4, 0xad, 0x29, 0x91, 0xc5, 0x47, 0xb8, 0x63, 0x8c, 0x10, 0xde, 0x76, 0x2c, 0x75, 0x89, 0x40, 0xa3, 0xe1, 0x32, 0x6d, 0xbb, 0xe, 0xc6, 0x94, 0xfa, 0xdf, 0x17, 0x58, 0x61, 0xd0, 0xa4, 0xb5, 0x82, 0xfc, 0x93, 0x2a, 0x4f, 0xc8, 0x7, 0x39, 0xed, 0x7b, 0x56, 0x1e, 0xe7, 0x44, 0x90, 0x79, 0x3b, 0x26, 0xaf, 0xf8, 0xd3, 0x5a, 0x11, 0x85, 0x6e, 0xb2, 0xcc, 0xd, 0x45, 0xec, 0x16, 0x21, 0x5e, 0x70, 0x8, 0xbf, 0x6a, 0x33, 0x99, 0xc7, 0xdb, 0xfd, 0x84, 0xa2, 0xf6, 0xb9, 0x35, 0xd4, 0x9f, 0x67, 0x8b, 0xee, 0x72, 0x1d, 0x5c, 0xa0, 0x28, 0x43, 0x1, 0xca, 0xd9, 0x66, 0xc, 0xf7, 0xcd, 0xb4, 0x1a, 0x73, 0xe8, 0x8f, 0xa5, 0x51, 0x42, 0x2e, 0x30, 0x9b, 0x9d, 0x1f, 0xe3, 0xcb, 0xf9, 0x8a, 0x64, 0x3c, 0x0, 0xb6, 0x4e, 0x22, 0xa1, 0x55, 0x78, 0xd7, 0x12, 0x98, 0x4a, 0x8e, 0xb1, 0xc3, 0xdc, 0x54, 0xa6, 0xf0, 0xeb, 0x7d, 0x9, 0x37, 0x2f, 0x65, 0x88, 0xc2, 0x2b, 0x13, 0x60, 0x9e, 0xf5, 0xa7, 0x59, 0xd1, 0x7a, 0xef, 0x36, 0x4, 0x4d, 0xbc, 0x53, 0x3e, 0xbd, 0xa8, 0x4c, 0x2, 0x71, 0x19, 0x87, 0xe5, 0xff, 0xda, 0xc4, 0x96, 0x6b, 0x20, 0x74, 0x27, 0xc1, 0xe6, 0xa, 0x49, 0x5d, 0xd2, 0xfe, 0xab, 0x80, 0x1c, 0xb3, 0x68, 0x95, 0x3f, 0xcf, 0x8d, 0x7e, 0x9a, 0xd6, 0x1b, 0xb7, 0x5, 0x31, 0x69, 0x23, 0x48, 0x50, 0xac, 0xe2, 0xf4, 0xae, 0x3, 0x6f, 0x52, 0x25, 0x38, 0xe0, 0x86, 0x14, 0x7c, 0xdd, 0xfb, 0x97, 0xc9, 0xba, 0x41, 0xb0, 0xf1, 0x57, 0x6c, 0x18, 0xd5, 0xce, 0x4b, 0x2d, 0x92, 0x34, 0x6, 0x7f, 0xea, 0xa9, 0x83, 0xb, 0xaa, 0xd8, 0x3d, 0x77, 0x5f, 0x46, 0xc0, 0x9c, 0x24, 0x62, 0xbe, 0x15, 0x81, 0xf3, 0xe9, ) MDS = ( (1, 6, 8, 9, 6, 9, 5, 1), (1, 1, 6, 8, 9, 6, 9, 5), (5, 1, 1, 6, 8, 9, 6, 9), (9, 5, 1, 1, 6, 8, 9, 6), (6, 9, 5, 1, 1, 6, 8, 9), (9, 6, 9, 5, 1, 1, 6, 8), (8, 9, 6, 9, 5, 1, 1, 6), (6, 8, 9, 6, 9, 5, 1, 1), ) SIGMA0 = ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ) SIGMA1 = ( 1, 14, 15, 10, 12, 2, 7, 4, 13, 8, 3, 9, 11, 5, 0, 6, ) SIGMA2 = ( 11, 4, 10, 7, 14, 9, 13, 1, 6, 5, 8, 2, 3, 15, 12, 0, ) SIGMA3 = ( 8, 2, 0, 5, 10, 3, 14, 13, 12, 7, 1, 15, 9, 4, 6, 11, ) SIGMA4 = ( 2, 8, 5, 7, 11, 1, 12, 4, 6, 14, 15, 10, 0, 13, 9, 3, ) SIGMA5 = ( 13, 14, 2, 1, 10, 12, 11, 7, 5, 3, 9, 15, 8, 4, 0, 6, ) SIGMA6 = ( 3, 13, 4, 0, 5, 6, 2, 10, 9, 8, 7, 11, 12, 15, 1, 14, ) SIGMA7 = ( 6, 3, 11, 14, 4, 0, 5, 8, 7, 13, 2, 12, 10, 1, 15, 9, ) SIGMA3_5 = ( 13, 10, 3, 2, 8, 11, 1, 5, 9, 12, 0, 4, 15, 6, 7, 14, ) SIGMA7_5 = ( 15, 7, 9, 12, 3, 13, 10, 0, 4, 6, 1, 14, 2, 5, 8, 11, ) LEFT_SIGMAS_COMMON = (SIGMA0, SIGMA1, SIGMA2, SIGMA3) RIGHT_SIGMAS_COMMON = (SIGMA4, SIGMA5, SIGMA6, SIGMA7) G_TABLES = None INIT_H = () INIT_C = () INIT_S = (0, 0, 0, 0) def __init__(self, data=b""): if self.hashbitlen is None or self.digest_size is None: raise TypeError("SarmalBase must be subclassed") self.ensure_tables() self.h = list(self.INIT_H) self.c = list(self.INIT_C) self.s = list(self.INIT_S) self.t = 0 self.msg_len = 0 self.buf = bytearray() if data: self.update(data) return @classmethod def ensure_tables(cls): if cls.G_TABLES is not None: return constants = (1, 5, 6, 8, 9) mul_tables = {} for constant in constants: table = [] for value in range(256): table.append(cls.gf_multiply(value, constant)) mul_tables[constant] = tuple(table) tables = [] for pos in range(8): pos_table = [] for byte_value in range(256): sboxed = cls.S[byte_value] word = 0 for row in range(8): out_byte = mul_tables[cls.MDS[row][pos]][sboxed] word = (word << 8) | out_byte pos_table.append(word) tables.append(tuple(pos_table)) cls.G_TABLES = tuple(tables) return @classmethod def gf_multiply(cls, a, b): result = 0 while b: if b & 1: result ^= a a <<= 1 if a & 0x100: a ^= 0x11d b >>= 1 return result def copy(self): other = self.__class__() other.h = list(self.h) other.c = list(self.c) other.s = list(self.s) other.t = self.t other.msg_len = self.msg_len other.buf = bytearray(self.buf) return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") chunk = bytes(data) self.msg_len += len(chunk) self.buf.extend(chunk) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.compress(block) return self def digest(self): clone = self.copy() clone.finalize() return clone.output_digest() def hexdigest(self): return self.digest().hex() def output_digest(self): state_bytes = b"".join(word.to_bytes(8, "big") for word in self.h) if self.hashbitlen == 224: return state_bytes[-28:] return state_bytes[:self.digest_size] def finalize(self): messagebitlen = self.msg_len * 8 block = bytearray(self.buf) if len(block) <= 118: block.append(0x80) while len(block) < 119: block.append(0x00) else: block.append(0x80) while len(block) < 128: block.append(0x00) self.compress(bytes(block)) block = bytearray(119) if self.hashbitlen == 224: block.append(0xe0) elif self.hashbitlen == 256: block[118] |= 0x01 block.append(0x00) elif self.hashbitlen == 384: block[118] |= 0x01 block.append(0x80) elif self.hashbitlen == 512: block[118] |= 0x02 block.append(0x00) else: raise ValueError("unsupported hashbitlen") block.extend(messagebitlen.to_bytes(8, "big")) self.compress(bytes(block)) self.buf.clear() return def g_func(self, value): tables = self.G_TABLES result = ( tables[0][(value >> 56) & 0xff] ^ tables[1][(value >> 48) & 0xff] ^ tables[2][(value >> 40) & 0xff] ^ tables[3][(value >> 32) & 0xff] ^ tables[4][(value >> 24) & 0xff] ^ tables[5][(value >> 16) & 0xff] ^ tables[6][(value >> 8) & 0xff] ^ tables[7][value & 0xff] ) return result def mix(self, state, a, b, c, d): out1 = state[0] ^ a temp1 = self.g_func(out1) out2 = temp1 ^ state[1] temp2 = state[2] ^ b out3 = (temp1 + temp2) & self.MASK64 out4 = (state[3] - temp1) & self.MASK64 out5 = state[4] ^ c temp2 = self.g_func(out5) out6 = temp2 ^ state[5] temp1 = state[6] ^ d out7 = (temp2 + temp1) & self.MASK64 out0 = (state[7] - temp2) & self.MASK64 return [out0, out1, out2, out3, out4, out5, out6, out7] def apply_sigmas(self, state, message_words, sigmas): current = state for sigma in sigmas: current = self.mix(current, message_words[sigma[0]], message_words[sigma[1]], message_words[sigma[2]], message_words[sigma[3]]) current = self.mix(current, message_words[sigma[4]], message_words[sigma[5]], message_words[sigma[6]], message_words[sigma[7]]) current = self.mix(current, message_words[sigma[8]], message_words[sigma[9]], message_words[sigma[10]], message_words[sigma[11]]) current = self.mix(current, message_words[sigma[12]], message_words[sigma[13]], message_words[sigma[14]], message_words[sigma[15]]) return current def compress(self, block): message_words = [int.from_bytes(block[i * 8:(i + 1) * 8], "big") for i in range(16)] left = [self.h[0], self.h[1], self.h[2], self.h[3], self.s[0], self.s[1], self.c[0], self.t] right = [self.h[4], self.h[5], self.h[6], self.h[7], self.s[2], self.s[3], self.c[1], self.t] left = self.apply_sigmas(left, message_words, self.LEFT_SIGMAS_COMMON) right = self.apply_sigmas(right, message_words, self.RIGHT_SIGMAS_COMMON) if self.hashbitlen == 384 or self.hashbitlen == 512: left = self.apply_sigmas(left, message_words, (self.SIGMA3_5,)) right = self.apply_sigmas(right, message_words, (self.SIGMA7_5,)) for i in range(8): right[i] ^= left[i] for i in range(8): self.h[i] ^= right[i] self.t += 1024 return class Sarmal224(SarmalBase): hashbitlen = 224 digest_size = 28 INIT_H = ( 0xbb67_ae85_84ca_a73b, 0x2574_2d70_78b8_3b89, 0x25d8_34cc_53da_4798, 0xc720_a648_6e45_a6e2, 0x490b_cfd9_5ef1_5dbd, 0xa993_0aae_1222_8f87, 0xcc4c_f24d_a3a1_ec68, 0xd0cd_33a0_1ad9_a383, ) INIT_C = ( 0xb9e1_22e6_138c_3ae6, 0xde5e_de3b_d42d_b730, ) INIT_S = (0, 0, 0, 0) class Sarmal256(SarmalBase): hashbitlen = 256 digest_size = 32 INIT_H = ( 0x9e37_79b9_7f4a_7c15, 0xf39c_c060_5ced_c834, 0x1082_276b_f3a2_7251, 0xf86c_6a11_d0c1_8e95, 0x2767_f0b1_53d2_7b7f, 0x0347_045b_5bf1_827f, 0x0188_6f09_2840_3002, 0xc1d6_4ba4_0f33_5e36, ) INIT_C = ( 0xf06a_d7ae_9717_877e, 0x8583_9d6e_ffbd_7dc6, ) INIT_S = (0, 0, 0, 0) class Sarmal384(SarmalBase): hashbitlen = 384 digest_size = 48 INIT_H = ( 0x3c6e_f372_fe94_f82b, 0xe739_80c0_b9db_9068, 0x2104_4ed7_e744_e4a3, 0xf0d8_d423_a183_1d2a, 0x4ecf_e162_a7a4_f6fe, 0x068e_08b6_b7e3_04fe, 0x0310_de12_5080_6005, 0x83ac_9748_1e66_bc6d, ) INIT_C = ( 0xe0d5_af5d_2e2f_0efd, 0xb07_3add_ff7a_fb8c, ) INIT_S = (0, 0, 0, 0) class Sarmal512(SarmalBase): hashbitlen = 512 digest_size = 64 INIT_H = ( 0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344, 0xa409_3822_299f_31d0, 0x082e_fa98_ec4e_6c89, 0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c, 0xc0ac_29b7_c97c_50dd, 0x3f84_d5b5_b547_0917, ) INIT_C = ( 0x9216_d5d9_8979_fb1b, 0xd131_0ba6_98df_b5ac, ) INIT_S = (0, 0, 0, 0) class SgailBase: block_size = 512 digest_size = None word_modulus_64 = 0xffff_ffff_ffff_ffff centre_rounds_for_digest_bits = { 224: 4, 256: 4, 384: 4, 512: 4, 768: 6, 1024: 6, 1536: 8, 2048: 8, } sbox_0 = ( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ) rc_u64 = ( 0x25d479d8f6e8def7, 0xe3fe501ab6794c3b, 0x976ce0bd04c006ba, 0xc1a94fb6409f60c4, 0x5e5c9ec2196a2463, 0x68fb6faf3e6c53b5, 0x1339b2eb3b52ec6f, 0x6dfc511f9b30952c, 0xcc814544af5ebd09, 0xbee3d004de334afd, 0x660f2807192e4bb3, 0xc0cba85745c8740f, 0xd20b5f39b9d3fbdb, 0x5579c0bd1a60320a, 0xd6a100c6402c7279, 0x679f25fefb1fa3cc, 0x8ea5e9f8db3222f8, 0x3c7516dffd616b15, 0x2f501ec8ad0552ab, 0x323db5fafd238760, 0x53317b483e00df82, 0x9e5c57bbca6f8ca0, 0x1a87562edf1769db, 0xd542a8f6287effc3, 0xac6732c68c4f5573, 0x695b27b0bbca58c8, 0xe1ffa35db8f011a0, 0x10fa3d98fd2183b8, 0x4afcb56c2dd1d35b, 0x9a53e479b6f84565, 0xd28e49bc4bfb9790, 0xe1ddf2daa4cb7e33, 0x62fb1341cee4c6e8, 0xef20cada36774c01, 0xd07e9efe2bf11fb4, 0x95dbda4dae909198, 0xeaad8e716b93d5a0, 0xd08ed1d0afc725e0, 0x8e3c5b2f8e7594b7, 0x8ff6e2fbf2122b64, 0x8888b812900df01c, 0x4fad5ea0688fc31c, 0xd1cff191b3a8c1ad, 0x2f2f2218be0e1777, 0xea752dfe8b021fa1, 0xe5a0cc0fb56f74e8, 0x18acf3d6ce89e299, 0xb4a84fe0fd13e0b7, 0x7cc43b81d2ada8d9, 0x165fa26680957705, 0x93cc7314211a1477, 0xe6ad206577b5fa86, 0xc75442f5fb9d35cf, 0xebcdaf0c7b3e89a0, 0xd6411bd3ae1e7e49, 0x00250e2d2071b35e, 0x226800bb57b8e0af, 0x2464369bf009b91e, 0x5563911d59dfa6aa, 0x78c14389d95a537f, 0x207d5ba202e5b9c5, 0x832603766295cfa9, 0x11c819684e734a41, 0xb3472dca7b14a94a, ) mds_8x8s_0 = ( (0x21f3791f6e74254d, 0x65083159466497f2, 0xebbac2d6e7de8c60, 0x513280cab631cae9, 0xb402d78f03fd68cf, 0x61c19b2e9c642ecc, 0x3184c30c965fb1e0, 0xceebe6d38d5e4e14, 0xee6ce63f1e17c43f, 0xa9202656ca5611cf, 0x2c7df780d2d4d7e9, 0x07147e9900dbeae4, 0x605cb5bec88f29ed, 0xe63d0a871295c268, 0x715dfc34c09c006f, 0x0a274035b99b0985, 0x7b5036e2bbb36ab5, 0x60b426e1c677f9a4, 0x77b26e8f83a27fe6, 0x5e3da4d8860c2da6, 0x476450ec12fc23b6, 0xcf2e58f0a3ff7afc, 0x6bf7cb63af3090f7, 0x53503ecc28971c5d, 0x50efa48c68894c0f, 0x21276ffb7e03ec7e, 0x372f7d214c3fe162, 0x6c634f672f56f0c8, 0x67f188214acdcced, 0xef99dbec81bb786c, 0x616a57ff8ac27eed, 0xfe802d2d3817ef96, 0x74a28f1f190082b8, 0x7e7be032ac97f1aa, 0x44f5245f45efc65f, 0xe1c3cd519ba07dab, 0x5bfe706df793e511, 0xb910dae79f48f386, 0x7239c69f4a218fe8, 0x4ecd8a4e2b95aade, 0xb1558e8ac2f7d83e, 0x8c17f6c38f2edfd6, 0xfe37b22b2d9c06ac, 0x380a01d6f908ef75, 0x5d15e0e4659474a5, 0x36a1856da14e741d, 0xae922a766eb0a93a, 0x35aa2d82f26b4ee7, 0xac36134d4ba9d4b6, 0xf25e058bdc65429d, 0xe251099f3d981cb1, 0xbc0df1764c130a4b, 0x075db9351525409f, 0x5bc4fc17f9f00e9d, 0xb940b5dcf96857a6, 0x8ab14b29d7fa3c69, 0xea42c5cb26f1e037, 0x3480c96e087cf784, 0x16bdd12c8f643c6f, 0x30e78286dac34073, 0x6d57e9d4df1927c4, 0x8adb91d4860855e4, 0x5b59dce1c9664403, 0xcc8d057ff042edf4, 0xb01cbaffe8588c6e, 0xa4d714124e015e31, 0xc5ddfba4eb9e2762, 0xe3fbd520af654293, 0x0328af0adf11b7a2, 0x78f6b1b345a65894, 0xa515ee0cde825010, 0xdca20fe794a68bef, 0x75b0d2bf8c8ef5f0, 0x7864d323d0ad7c53, 0x4b539a49b480e144, 0x5d3dca5622aafb83, 0xa317c4a77bda7917, 0x18868a55f0eb4386, 0x6c64bbc24b4f8458, 0xc48c0dcb99b1f90c, 0x9b0f7d66e0916b0c, 0xd324c75c0041f761, 0x5c7670aa6a3d173b, 0x7c4c450c4275ce6b, 0x9f51c0cad5b5c5bf, 0xa010226e0422300e, 0x32a4ce7654f2682c, 0xcda0e222be738184, 0xcef92650daec813f, 0xff778dcd494aaa89, 0x5bf7b44c7ee4841f, 0x5a899183ea22a7b0, 0x11a5b5fd8406ad15, 0xf6b1725894d4dbf6, 0x6830726bfe407f32, 0x1100379ea17ec920, 0x5d47318e9482db5d, 0xcfe2b57e2600af8e, 0x72c4c4256e76f2e7, 0xee4cd440ac9cb1e2, 0x6a120c87e333e3e4, 0x844a1a4ca7186864, 0xad2f614908fa97cf, 0x66fece73d2cb6d54, 0xb4ce520e8581f6af, 0xd3601ca7ab92fa2c, 0xae6c2011029658e4, 0x0611ec2543e18ad1, 0x37b54b8c6e4533ea, 0x760123eb043cd51f, 0x32531a0db5d37eda, 0x7c64d6a4af08a48b, 0x52239f2b32ef79a8, 0x56aeb5bbc1e96cbb, 0xfc457eb98ee2cf93, 0x0bd31ca600b763d6, 0x325e4f50eba43149, 0xe1951aa52284973a, 0xc41668d43e36dce3, 0xc9cfb9fa6f7982aa, 0x0e5a0c0c602fac12, 0x883b542dec0fca15, 0x7a70cf819b87bc4e, 0x00df4ee43c516fc9, 0xb1070711f15d2d8b, 0xda41212584b5175b, 0xb8b273f21bf1d1a2, 0xb1520d55bd80458d, 0x7920b85bf8b5be6d, 0xd35ad650af0b5049, 0xeee8a083e0fe19e8, 0x065a5c18896fc495, 0x5b5ca657f4d88e7c, 0x582120469e9037d0, 0x7df58b1e8c146893, 0x04dbb1f88857c3b0, 0x18f21d0093d63eef, 0xa177c815cbe3c0d6, 0x7cc6fe256b1b7b28, 0x40692155492087b6, 0xc87834216f2e16b6, 0x7925980df31868f7, 0x8e12ec81843758fb, 0xfca01a9f48ffa29c, 0x7241902e5da5aa7f, 0x4852f7f2c20cd822, 0x961e2ba62f6dfa4b, 0xe8d0d26e769d6dad, 0xc41501027b7fa884, 0x60b289a4cc1529c5, 0xaab85bc1dbf380fe, 0xed33b1d8ad5bf89b, 0xb3e5019ee75a174d, 0x56c61c074f26da3e, 0x3e26af01150e3e24, 0xc49efd72b5735f9d, 0x1c66cf44a8a4f796, 0xe552af9256b1ce72, 0x4f1bdcacfc07eec4, 0x976132ec70a11038, 0x4a83987ebc2dba57, 0x5d7ae5115e5b1518, 0x21c4d5df5bc52e7e, 0xc945fd885b3a9302, 0x1aa4ca3b9e1ee122, 0x977615f72712d37f, 0x8713a03542001bd9, 0x5cf5e6e8cf8a18bd, 0xc7c272ec46289eec, 0xeee34382c5e49963, 0x1997bc57ed7577ae, 0x1e07e060cd4a52ec, 0x7659464225af0fe6, 0xc0ce187ec139bb47, 0xa217d13e6ab9e4cb, 0x8efcd447b91db0b1, 0xc2822e9a84fa42ef, 0xf703b30e47b1da74, 0x6bb14918144ce4fe, 0x96e12a35810ecccf, 0xb7474ef25ea347c9, 0x4754b76b75307779, 0xf899d2965c0399c2, 0xed41912eda8628e3, 0x56d409090d5ed502, 0x07e639c1f924a888, 0xef0b1d738d8bc09b, 0x94d6c0d2a20ee4a5, 0xde90cb8a3dcad71a, 0x772ce398d92f7a55, 0xe63b8856ef0387dc, 0x19ad7d4c4e966874, 0x1621352df9eb959b, 0xb4f8d49283e4c5a2, 0x6bc1dbc70868fefa, 0xa75e67369c9d249c, 0x3bc0a04d73522039, 0x3cec35af85c0d546, 0x74c98bdf7d114b64, 0x25a26761dd9d52ed, 0x49723301bee41188, 0xd3495ca282518cfe, 0x4a3fe0ba68a796f2, 0xc5b599ec5f9b07ca, 0xae1c258959732fd8, 0xeb009224ecec0c9d, 0xb686b9e4a258f586, 0x4c907cd0804a6488, 0x69c1cf18ffd3b0ef, 0x8adabe44cf07d077, 0xc6176ec7eae507c8, 0xdb6b1334bc167a53, 0xbb3562eea27aaa7a, 0xbeaa84312a2f21b5, 0x057742b444f3aac5, 0xb6bf7386ba4bf63b, 0x318a61e1c34d715b, 0xcd067629b45b0f00, 0xe6b443e4b29d00b5, 0x72b9cd07571e057d, 0x8ce8c0e3626bcf39, 0x81afa416a6d96439, 0xf301c73f60ba0b44, 0x5d4d11a5d642a95f, 0x36e15c7cbff6eeaf, 0x76f0da52833f4f6b, 0xff30598e9cbbdd24, 0x14fd71a418f90e83, 0x815bb77a998bb6ad, 0xf4861ceb44304b6d, 0xe1ed2ad43ebf71a2, 0xde6f95c75fc6c08f, 0xce3f746cb3034c1c, 0x5e48e14255eb8044, 0x50a13cc9e7e1cc32, 0x83f740ecc9e12301, 0xcde57a05fdefc3ec, 0x015c974d6f1c729d, 0xdaa4aceaa16f587c, 0x93549daf41217d03, 0xded35d6c27c06574, 0x0094d41d56e9a9bb, 0xfee3fb2188338bec, 0xa3772fa8c48a8ed8, 0x21752c765a77f540, 0xc583af8841c17651, 0x98116c6de1bad52e, 0x4e3f759981c52d6e, 0x6824805d690ca418, 0xbc294852cad3bf1a, 0xf2e2bd8bd57c54c5, 0xbf53852b20f500f2, 0x5b0a8d1ed91673a5, 0x71c01e9b9fae4637, 0xa164f6d009afccad, 0x3f7fc13f651eee41), (0xdc091e1e1d023bfe, 0x4ba4a72d0799ccb5, 0x24e704a59cbe0b4b, 0x2c10e33aab958ee1, 0x4378e752d54f2d21, 0xe4eb240c05e00236, 0x09986d3c0bce468a, 0xc26f668145402464, 0x3036a3780701631b, 0xb27d42c9e6b49a98, 0x1a55fa108e7e0a78, 0x340f263d1f225e97, 0xb0b9c98aead9a623, 0xe2434d3f9643cb1f, 0x0c7dbda3509c8594, 0x4cbaceb8b929dc65, 0x011483b83edffe58, 0x18e845b0ec2fdc68, 0xcc41533175993aa6, 0xe40c4e8be4643f3e, 0x9a6fe95bef10770f, 0x6cea1ef7464cdb95, 0x5244f741db60e1d1, 0xdbc5dad3b2a32b30, 0x1f1b8e7a0da47118, 0x038edf7e2fd3e535, 0xbe7b6c4d078bce3c, 0xcbea7df622b651e2, 0xdd5b383a43c9cb63, 0xff4a8a6cdebe88c3, 0xc28c706a50feb325, 0x7bf6277e82810665, 0x19d0a306eb8d5090, 0xafdddb90427220b6, 0x8dab6924b88ee2df, 0xb22157159e2fb7be, 0x5ffc62c30f24c8a9, 0x6089d1415777373d, 0x6143dc9fb6a44780, 0x889ca6a4c720c6df, 0x40d9941ab8add8cb, 0x008e12cf81165f75, 0x086dc34c228bc30d, 0x8da5c0ace780969e, 0xb97f8ae86f60aad0, 0xcf4220c9ce2f6499, 0x67abc058a10ede51, 0x2eaac0bd9416c506, 0xf516100d80e7a4e6, 0x758efcc910e9616f, 0xe6f8d60d9c4d4064, 0xffe9f4e2d319edf3, 0xbc1c318414913110, 0xb2add38f9d882931, 0x570589b178ac64f8, 0xe8f0a26224fd1518, 0x6d232cde74efa9de, 0x5e989657e9b28144, 0x06eb0f262a5d0052, 0xa58f0e9859245615, 0xf6048851218296a3, 0x347a584c533f7a67, 0x3de874a5c2a25c3e, 0x9c6617a5b2597931, 0x589b9b6406bd53c3, 0x57aca310cdc06602, 0x10a385ac53fc7182, 0x6c8ce921d1ec0038, 0xa907a82ff590eed5, 0x1bb22106445fddb2, 0x1f2acb543ce5efa0, 0x124cb0552ca677fd, 0x9ff9208c0c1c89c3, 0x7a8c58cb55c5f03f, 0x1fc9b6b01dc3673c, 0xdad68ffc443493b8, 0x8620b92cdf84922b, 0xaff805c359ddfb9c, 0x5a41a1d0389c9913, 0xfab8cae7154ef6d0, 0x5a6a1d79233abcef, 0xe1a117aaaf7438dd, 0xcb9a49b05b32b8bf, 0x1f30edc90442f0d8, 0x32b7ef28af1abe32, 0x5f55bdd408d8b201, 0xec068397051b73bd, 0x410b6bac13692c63, 0x2a2edd4e719cd7cb, 0x192825af198240dd, 0x1657ce87cb77542f, 0x500abe5bd7cf0634, 0x404619f4af0ed79d, 0x89176b015eb5f086, 0x07ea227976265253, 0x34f9fad354e8edb5, 0xe11ef1d4b823b43f, 0xb65ed0fb00f475be, 0x8c6b0a5b49dfd571, 0x6bf410af1db4635d, 0x1450532ec0cd88e0, 0xcac1306301394aef, 0x0844a66443d7cabf, 0x4c90012669f711eb, 0xe81d1fed80397c92, 0x1e71c65ca2f696ba, 0xfcb93caa371299a9, 0xc85e4fb5bdc32358, 0x150db782c7176d39, 0x1270e6236884cfee, 0x2a1c5f901328484a, 0x2dab1a3a35febb2a, 0x5e88b568ba756f1f, 0x16bbe70cf5799498, 0xd6f8ef4a314e87df, 0xbc1725c5522e242c, 0xfc906d65465cc6cc, 0x3406eeed6808c920, 0x35a629fc7417429d, 0x4f542cea74b450ee, 0xd4059725677962e0, 0x18c69f86117122cd, 0xb8c83cb49bd38987, 0x6e47475eb76d579c, 0xb9b163211e1e93e3, 0x77998487c06cad25, 0x486982e7b1d0adaa, 0x5dd12cd66abd279e, 0xa87b32fbc195021a, 0xdc5f80293482cc2d, 0x71ac9099084d0e56, 0xb51b37eb74dd82eb, 0x90a8aa773e10a96b, 0x70f053ff4b345bd9, 0xfbef716a1d8356ef, 0x97ceea53d407d793, 0x1e91ca10ed42bb74, 0x2300897cb7132dc3, 0xd4955b43c58b9987, 0x39a8e1b597e932ce, 0xf79788817ec28db0, 0xf11e11fec040ec1e, 0xbcf15a69450ee4f0, 0x5a4404f9a4f667d0, 0x584c917416edb1d4, 0x5f9150ecf2281a6a, 0x577dbfac9717108d, 0xf6a62765c79d9b3b, 0x3c5aa30a0530e1dd, 0x84bee8eeb0a8ceb5, 0xfb92258e5f71514d, 0xd0be1fbed8d9b11a, 0x62e0f653af400e3f, 0xaa49ce8ca1b387f2, 0xa15de9dc0850e528, 0x6fd10cc750795971, 0xf28d86dd3fee6a9a, 0xf7461c419790be7b, 0xbe8d426ef4adf74d, 0xf2cecbf2fcca7166, 0x467a1a77f45da2f5, 0xa7a6f8a4e97c7e6d, 0x1cbec5a3a7321764, 0x57940d75fe419b3f, 0x92513e3783cb714d, 0x11bdfbeaa04c83eb, 0xf8437e7997e477fc, 0x0b1f1432115802ad, 0xada1d17336d6a30c, 0xd64f714ccf6aa1db, 0x0c85519533ac9b04, 0x89bae859a94b485a, 0xe732ba981d1568e6, 0x29bc7cc6c985b2e2, 0xe20ea4ef8f05de22, 0x6455abba1354b87c, 0x5b81b7ca7531939d, 0x4e3a18520218f6f6, 0x6461a304949ee14c, 0x2ce9bfb2c76f9c60, 0xa57016c08f27951c, 0x1a6d369e8f9b3e06, 0x926de9443a25fc3f, 0x14ce9e8b918329a2, 0xf4ca6ca3fa6014d9, 0xe43bd7c9a700e89a, 0xc27676f2120260ea, 0xda76cf9fa2b6bffe, 0xb623b6fca48addc2, 0xc03e599e619f230c, 0xae7f0d21059d9807, 0x3fb7fa8ca0d95039, 0xca1865b67160264d, 0xacbfc8b57dc56cda, 0x8a33149bdfd844c7, 0x00500f8618a23e46, 0xd243786cab62df28, 0x186e4f7728c7da03, 0xe265d7e031823801, 0xbe24f3699ce37b2c, 0x48c09cfc8c6000c2, 0xdd6571045568b463, 0xd8796bd32c020b33, 0xb680ace772940105, 0xa4fa2a00a2a027fd, 0x222a84d177d802b7, 0x09fa5cde5e181c65, 0xb728a521a327f2d3, 0x0c418db1618a011a, 0xbb071237f78565d1, 0x0050601b181d372c, 0xd4e494cceac1af80, 0xdb98ad49ac4a87d7, 0xcc8f4ac5e2207577, 0x65ecbed93522c314, 0x1086bbfc6e4a01b0, 0x9e5c2de01137e8a4, 0xc850bb4af5b264c8, 0x19b9de7a83f944af, 0x2759e635aaa85dc4, 0x7b65179fe2b4021f, 0x107b4b77da9ef8b8, 0xa02c787f53820a9c, 0xd4bbf90e4a9d9a16, 0x2b0eef38ccda3f2b, 0x6cab81c64ae294e9, 0xfa98db5b314d3608, 0x1fb42b7a7e57f4f9, 0xcf9bbef39b904124, 0x132d79e0d9a94354, 0xa40905540d015215, 0x10ed7f391c8fa48f, 0xea468a92dcaaaaa7, 0x91cb638a0fe8a332, 0x5be87a98659f0aa4, 0x1ddecac735d60f1e, 0xe98803429c3c0525, 0x4161f33aa3dff348, 0x6336a552801eded4, 0x977166fab047d040, 0xba4fb2b5c5f5195f, 0xba7bce67fc1d9c67, 0x7d632ecb7b166d73, 0xe402efd7d0ddb9ed, 0x9854459563cb850e, 0x87efdcda596efa78, 0xe99327a5e82ab714, 0x0fe1bcb6bb3ab9c4, 0x1eaeb88b76e12beb, 0xc8dd40d8dbfb7053, 0x83634df0130a84bb, 0xffa148456b8e1042, 0x52b02acdb4d97590, 0x114ce02d320c305e, 0xc1ca2426e305f42b, 0xce37f7ba18e9fef9), (0x6fc2921faa1ec7e1, 0xcf0a9e62c89b454a, 0xc73737b9139da0de, 0x35195652d47556fe, 0x9b3d3573731f86fa, 0x4e513e1bf71ba05b, 0x3bd53496128c7ef3, 0x710d8995f84c14c9, 0xb1056a6a826689d0, 0x15566e2e56245f1b, 0x2d6dbcc52f92e839, 0x7f775aafa8562d1b, 0x1cac474ea5f8f500, 0x962fa97cd3b15f8c, 0x0201ba9d812d2e5c, 0x7168ea0ad0f624e3, 0x610630b7e3375abc, 0x8500f408c9426dbe, 0x288c018df3d115ab, 0x10d1742abbddfc2d, 0xd2423f92b6989520, 0x94f5b47c22a5f4fb, 0x8811d2737a82a761, 0xb14a4c5be803f6dc, 0x05055d40eb223512, 0xb61c400fd3709159, 0x1b3fd7ab9defc967, 0x07742a2e1f3457c2, 0x14391707f58e33a8, 0xc1a85c9e1a13c718, 0xbef1cf581aa3b8a1, 0x3dfc1980a8483590, 0xa2b44d765687d989, 0xbd965be8a4c41d2f, 0x54a658c71a5c7763, 0x15bae8b276522421, 0xcdc6389d7ce3491d, 0x7328d9681e8e489b, 0x6a8b3be699d35676, 0xf0db501cad9e0269, 0x6e375102e3fcd321, 0x7a934b182542584a, 0x9b96f3c665ab4e09, 0xccb483f284efa029, 0x84121cf66c59dd4c, 0xb4716184d26973a3, 0x6cd4bae9fedee8b5, 0x24e8712a312fc48e, 0x88fe2f56fe48b126, 0x648eae87f1591a8e, 0xda390e5c152fc2b3, 0x4f83712782e86113, 0x61cb133276af80eb, 0xf9347396ab8def81, 0xae86ee61c95288f5, 0x5a8bbcaf7a95bbfa, 0xd3ed9f0f96c09105, 0x04ac5f967200aa0e, 0xd1d8e4af6519d655, 0x850da707d5bf14b7, 0x1437883293021475, 0x57e4b5ffca53a9c6, 0xc647c4f719e27786, 0xe37c2d1114732b25, 0x9322fb562bc5cac4, 0x04f86c13f09d9751, 0x99328883584556be, 0x14837f107f91f39e, 0x93b2306f3aa31130, 0x030f8a770f21d1cb, 0xc6755916e70e0fc7, 0x51569e3a1786705a, 0xcd69d10a4059cff6, 0x9658d1b6da68c8fe, 0x52e4491249da1c86, 0x8049528948964763, 0xc2a3f2c4298bd646, 0xb3a142a446ba1ea4, 0x73726fd229d6e67b, 0xdecc158d7a432861, 0x3ee9cb6b425a2121, 0x881124c4108153af, 0x26fc1238520f6dc5, 0x16d9428fd259a41b, 0x491aa8a225d338ba, 0xb09259c43fb59aca, 0x5e9953f496e03d24, 0xea16eba7ff46618c, 0x3f7f2c4f3a92bae6, 0xf795c5efa084bcd0, 0x8b34e0b742940e22, 0xea053edc5b9401d7, 0x4da30e78f6aba21f, 0xc45b12f93254f7ec, 0xc82d9af7e5af31f9, 0x803f44cfefcf6762, 0xfc5f43b7ff9ceb31, 0x245e11c8bfa9ae96, 0x87138003b5a70762, 0x8eae1c6d9eb9646c, 0x30bfe90a8523d4eb, 0x2b21ca0cccdc2bcb, 0xccc6fc2ac14b7b90, 0xb27a14b9a7dd59ad, 0x560348e135944074, 0x0fd0bab3cf9e5f9c, 0x04a726cebdf6618f, 0x7e7d01bf93c27681, 0x56de16d1609f93c2, 0xe6804b157ea2330f, 0x7e5e77472a9220d4, 0xb1b50564d8ed2724, 0x40ffc0c34f1f1fdd, 0x7a05271122771783, 0x09314566f84c5028, 0x9b8e16c5136e4025, 0xefe5a7f790482842, 0xa84c2fbc12839178, 0xfc3ae0a8770e89d2, 0x3ab916bba4436059, 0xbe7ee94c80409bba, 0x35a23e5bd9c2d7b5, 0xcda360f286c14deb, 0x73884457b0943e2c, 0xc7a38a644e9c92c0, 0x95068f09744dac34, 0xf226fc06b5260ec4, 0xe2cb738fd6ffa3b7, 0xb3a96750be44ad25, 0x49bf9aec78f65184, 0x7a6516d7b4dcf139, 0x2211a40eb076d9a7, 0x7c6149dba8e4954e, 0x2927e1787c670b87, 0xf230d3876e76f154, 0x3acdd218b066b3d5, 0xc069fb118f94edc7, 0xa2e24d0c5c52e3da, 0x5208baa0893da38a, 0x7710da3bc36e9191, 0xc0faa7e41744c1b1, 0xd70aeeafae86ce75, 0xf0adc601049b479c, 0x28e8d822b182cc00, 0xcd0000de4bc9f9c4, 0x80766278a7e4c83d, 0xb82d37f661c09b6b, 0x35b1dc3e8173475e, 0xed93c2260643e355, 0xcc708e966c2c12c0, 0x9c1db2b40a709ed1, 0x2eaa70477c4fd12c, 0x76ba2c1b325ab188, 0x7e0a02e76bbeaf5c, 0xdde2006c1460d65b, 0x5f14c8c0971189bc, 0x513fb32a4107f164, 0xd8f92245ed49a64e, 0xf068866975378586, 0x5d73310153a8dd31, 0x7756f2ed68d78b18, 0x0546640ad87ba696, 0x61c858c82af10ee3, 0x06ecbc2c1c3234d1, 0xfcae9d38643f4d7b, 0xca955a6d5ae3a20d, 0x09785f07208a685f, 0xe2ea26ee65633158, 0x0f012cb2d0b450c8, 0x05f545f09cc4e05b, 0xa19a5b4233a20d90, 0xbe4f3e431ba657ca, 0xeebeca5ed4f804ce, 0x1fc8f99713eabd4a, 0x7f17d9462830826d, 0xc237d64e0625bded, 0xe1e3cfc40a257690, 0x73ef0c5f8e5cd3bc, 0x839f399fc0b245dd, 0x80ffe759bef2934b, 0xb96e031ba369d5fd, 0x987c7c9e86ef3bb5, 0x5898cefac2bd2444, 0x1f54e50a7bd1dfca, 0x62c8dd9335e3707e, 0x999f75cf455bd4df, 0xd92f511fc8d8b264, 0x8b5f097285d4fe8b, 0x74b7751291e59e14, 0x9a71c7c34e271355, 0x2a24e67a4b03c156, 0x0993ce03fdb92065, 0x6de9f7c6e3eba322, 0xe7356d25f9f50f99, 0xa9f81071465c6f83, 0x8c051d9450b289f2, 0xc9c4a69d2fad67b6, 0x3e1053f48999de06, 0x9a9d9c24723e7f31, 0x8424e4a64e569c3c, 0x1b21a5aae192973c, 0xc6400ecd16f2a49f, 0xbc7b1ab12e912977, 0xfa499a609600f634, 0x6fd0ff5396ca4f81, 0xfa9b21a8c7640229, 0x1494699d108c2271, 0x3d3e75404a63c076, 0x9aafc2f222f7c421, 0x22aedd01fbaa9139, 0x6798896f3caa504f, 0xdedd11c68572a6e6, 0x8180ac4303b5f3d9, 0xfaed46bab8b52d62, 0x52629eed8527d67f, 0xed6266764922a5a8, 0xf2d60d27fff18d2d, 0xe8eadc8f0208e7b0, 0x3d4c2bd20e0d9c1d, 0x13aa5d311910ccc2, 0x1b3def068a9028bf, 0xba3b44ff43f788f0, 0x73e763653b6800f1, 0x7ab61a083ddf22c3, 0xdb13b8d1f3e68058, 0x24589016be4c96f7, 0xe0c6fddefa1f17b9, 0x359f0d0ea86c7d5b, 0xf0f3d3ab9931f575, 0xf4464e679edda4a8, 0xd3a6b45c24638b04, 0x4e18095682a0176c, 0xfbbe8ff951705af0, 0x9892087a2577de2a, 0x34d2e62ca52c5789, 0x8988436cfadda4a4, 0x6029b6e8dbb404ab, 0x196493da7159ce0c, 0x42a3193795f63b07, 0x4e4326e213d2caba, 0x469f53640ef1ad92, 0x2ae7f9d6daf4e66b, 0xca15a21745e12dbc, 0x5a5383f0e7a2d6c7, 0x07bf7c8b3be63925, 0xd0ede6034e8091f6, 0x00ef4b8df501f843, 0x886f0a4f9de457e9, 0x7b8f83d781958bb7, 0x12f028dd476b34d7, 0xb84f9785716d4670, 0xca2ac68d47146f6b, 0x3ab3679a1444c6a0, 0xc487e9812f64f9dd, 0xf0a2734fa4a6e1c0, 0xaa754d7fb92ec887), (0xa3ca19ec1e3ca6eb, 0x2e25f30dfe4b0e7b, 0x815b95cf08923bc3, 0x5d28901f342a9599, 0x961da80c30b2f165, 0x1f57fa4b35cc45da, 0x8f14fc7485a5e929, 0x79c57797c7b94aad, 0xe93d05152d016624, 0x74aeffe9d10adfdc, 0x854f4da2e30827ef, 0xde423da15adc16e4, 0x266438856b264893, 0xeb2b53f775faee28, 0x0ab4b46b3a30650e, 0xfaa29d29bc040ee4, 0x595fc4d774b3df57, 0xa795b06360fdf052, 0xc587f3b4c0ae0406, 0xd6598de1ede5eea0, 0x3339146dd11e1726, 0xd48afe5c5da12d8e, 0x71969f8173addd66, 0xdf337ffc2048dbe5, 0x7f24932642c982f5, 0x2031bd7764e95978, 0x5c2612876c58eccd, 0x112a96bf57d7706f, 0x335a15b8048dfa3a, 0xc5272cd47b68a522, 0xad41d3b51501e4d6, 0xa30cf0af8ed8ec8a, 0xc23822d52e495a72, 0xfe7d98845c78d88b, 0xb836d976335de314, 0x8bc79bdbb0c1cd04, 0xc946a130e133e9ae, 0xae3f49b9f4d35916, 0xc78b8802d62c590c, 0x1084edf5e01a7e54, 0x5d8ce54d5f790050, 0x83c88f86a452983e, 0xec4c43119b477ecf, 0x2f57ea47d1fd8075, 0x789ccabff179d6ff, 0xbe9fb89821c12aa2, 0xe77ed152761fdfc6, 0x97cc276528b34979, 0x65a248b1f17cedbc, 0x8dbf35ef43260d31, 0xe02533ac974c85dc, 0xd401983bd8ec9e38, 0x7718aa14381c592d, 0xfe77b5d1b7958d22, 0x7797a6cd7ec443e1, 0x8193c434a22acbb3, 0x7bdb85c6c297913c, 0x079db7a761d4f9e2, 0x135f8174a60b35e2, 0x6b2bcc4acb9eed59, 0x9e95091b2c38dff6, 0x39d1add602be929f, 0xed13bc4880ee0c7c, 0x08e2d82df32218b3, 0xc564d3b8308ce5c6, 0xc116ca73ad21ddc5, 0xcd3bc3ad80409ce5, 0x36d2f9fa97cf5f64, 0xdb2dc405185bb023, 0x8815c4a9c7becd81, 0x9be0a7777cc099cf, 0xb86b50bebd78ce14, 0xa2485a77d6f50a36, 0x895862e5da79f7f1, 0x43c9842774740222, 0xae46271ed74c5673, 0x4dfcb4c033b5f5c4, 0x7dca7e6e1867a40d, 0x280d4bde5f1900d7, 0x24d6f7728e3e93c7, 0xd2d4c6facee851bb, 0x5ccc0a29313aaa2b, 0xf30b52ce9b388a47, 0xd1f230f8425fa119, 0x9cfe2d0ee96945dd, 0x2e3ef8073bdea4a4, 0x27db8488d65d6140, 0x74bfb16131f635ab, 0xbd13e420fe06d82a, 0x2f20409c75929657, 0xd751cd00bb238621, 0x20379ff2ede8b3cf, 0x6e86922ccfcc621a, 0x5bd6ecf6eb062cc9, 0xe83b2415c506d796, 0x05f2d71c5e54d9a1, 0xc1b3ee7e955d7a96, 0xe82b94a20697dacf, 0x229213c92a5110e3, 0x27daa40e6f5dcb68, 0x542b2f04876f14e9, 0x2f2f7d3d3b9914a0, 0xaf0551997558e665, 0xee87e9860bccbc77, 0xac93c788a03ebbe2, 0xa5ed2a87a9d52551, 0xecf74835238f33f6, 0x780166a1a4983305, 0xe60f1cc5fd0d7821, 0xce0053e457cba5cb, 0xf81cd730084787fb, 0x7b912f19c88cc763, 0x1ad1fcd7c7dd58dc, 0x62fb274fc49638c4, 0x1d04bb94918c6113, 0xf48569b4a682b961, 0x72e23fba4b779e9a, 0x8f5a0021a4f4b419, 0x6c2e4d175927a596, 0x4d1a5c14ffbc8b00, 0xde6cb81c9470ba72, 0x27f48bf3a412c183, 0x493bedc2603e4338, 0xc02b5d2a4b101b49, 0x6c34a8ccabd89c7c, 0xe0cb6e111762dc5e, 0x555a980a74964861, 0x006f580772c42609, 0xaa9985f4987d44e0, 0xb7b01602ab7d8c85, 0x123a743accf0fb1a, 0x485b37773133e5ee, 0x4d999afe8e0b5245, 0x8812b124c77707f6, 0x152aaf972e193431, 0xa96b2f29ead1d806, 0xec06e460aed8468f, 0x44d40bc26b65a863, 0x54fb1dcffc940b8e, 0x9c388700a2852fdb, 0xfd3cb77d1b932bf7, 0xad60cb1ab7dc9d91, 0x1ad523744716b853, 0xab4dc63320513247, 0x6103df6a7a2bf98a, 0x2e631531271eefe5, 0x716768e6ec3192c9, 0xeee65b6a66203ee8, 0x370534dc2516ad03, 0x165f189803b99554, 0xdc1fcb2587d48ee1, 0x32a73ca97d1e50a6, 0xbc0b6f5ed42fc7cb, 0xe50dca6be90b44fe, 0x259f6c3ca08ed641, 0x1c5d97abd62d56ec, 0x6fe91c3fb5660148, 0xe5a57f06d5d8f5a8, 0xdf73245628549b47, 0xd0b670b94d5f1706, 0xec9c2361e043c623, 0xcce4fb07ab7b0598, 0x34f38b4cb52a35c5, 0xfd713cfd148a2322, 0x88ea3eb59e3bd966, 0x4aa869c428e79a86, 0xade3f5039b57b638, 0x7a5265c502faf057, 0xea0b006876a3a69f, 0x52b3a10e40b1227a, 0xc6128c55de2cbf6d, 0xc5b07128955a1bb3, 0xc938e9436f040e15, 0x1b2e118c59cdc572, 0xdb22bc695a0f8ccf, 0x3d37e3f05d7377fe, 0xea5b65f3ac49be38, 0x8cfaf5d7853a7afb, 0x082d27b3131ab7d2, 0x97cb4fbe33949c18, 0x49968fd355bfd4e4, 0x2db9a0882df435f9, 0x4d47dc4ad738199c, 0xba24998c920691b7, 0x0a33c25618efa701, 0x4e5d05db4d54f348, 0xfca73fc54424ffba, 0x67aff86329dd2770, 0x5d71f93eccebb6a7, 0x3fad49398279e50a, 0x06e04ba86b0d355c, 0x9ef68c04a1197a36, 0xfe00040dad7afd39, 0x24bf6e64f06b971a, 0x9ab14f3f9892cb8d, 0x5d4750883be0221f, 0x56ac5f3aa998cce5, 0x03460fd360a35bad, 0x24ff9e17cf1f4061, 0x5fe446cf9e90e24d, 0x77007c9debd43f24, 0x45d6d05b6e208e1e, 0xc61df9aea48e3429, 0xb3d64fef69d8fe6b, 0x993fbb95a9e7ff96, 0x05a49c472f5d9c97, 0x61d96d955deaf40a, 0x7e2bae19322dd6ed, 0x80ad697bf18e24d9, 0x342181f209a19bbb, 0x2e3087c230736ccc, 0x095c0f102c38f6d0, 0xdf15cb750b93c3e0, 0x5e3f597b1e63680c, 0xbc8837790d3944cf, 0x3d9b7c48ad083eb4, 0xb28ca478d7ffcc0a, 0x63a3c2b1289fea71, 0xc179c66c0a6cd6f7, 0xcaf2519d755bc083, 0x373c7cd588b8f3ec, 0x63ccb2ab654d239f, 0x58a8732f397e3240, 0xd1cd00a6c7bbfec1, 0x4c93e032a920f6e1, 0xdbee63f01f1f0e1f, 0xb53a78e0184837c4, 0x1d9453204e5cc1fe, 0x2c540ec359fbf161, 0x6e362ac2e2e62dc3, 0xc4e63e50fd7f341d, 0xf11c336f5272f81e, 0x34238e63e76e284a, 0x51eded1099809558, 0x774cb1dfd9b8be9b, 0xf51814f1062a280a, 0x393080e9aa8d8a16, 0x1f364188899d3178, 0xf886f4969f051fc9, 0x44c3c5c1881526c7, 0x2794613a617338c8, 0x4f9e59684b59c991, 0xb9ca306caee057dd, 0xa52d955a448b49ec, 0x7930a284317397d3, 0xf0d8fcc2370475e7, 0x6ea39e430b10c570, 0x78777fe74748ffb0, 0xaa1b523d998c7add, 0x8d4ade5f2ff3ef9e, 0xb1b26bdb813ba4b9, 0x7cdb5d99dfbfb35b, 0xdf0e6fce209acf9c, 0x4952ceb5c82de0e1, 0x329444a3db47384f, 0xa1c1c32077699a43), (0xa5daec4ce582914a, 0x1ae3f05b47766548, 0xfd1bc8244a03db45, 0x2f54549832392e52, 0xae74d0679accbc12, 0x063498af58705d9b, 0xf5b6a2a7e1755581, 0x9d82bbe31327eb8a, 0x673c1556cdf45fa3, 0x5580500eb04546f7, 0x5f2e4f7733539709, 0x61b3f016effbfe0a, 0x484a81e10d60f00f, 0xf2032db84d0903da, 0xcc48da35fc5c822c, 0x9dd6694429b8cda3, 0x0f9f658d944135ea, 0xb540d8274ecd2f4e, 0x4e0609e158d69823, 0x0661f2b74ed746dc, 0x8721d7e2e3cc4b64, 0x897ac591c0eeff0b, 0x4189bc954d7bfefe, 0xba39a7270033a81a, 0xc73de2f4184cfe46, 0x50f4a244e28ca10c, 0x344692a57f2db8b7, 0x5f2ff61663c8c916, 0x37c8c746a6fc4222, 0x80c80eae7f9ac3f4, 0xfe07d8e40da76b79, 0xe97a9c248b66f90c, 0x5ce57815285f2c62, 0x250a5a18ed616226, 0x9c6c74726d5fd611, 0x622bc9a192079ec7, 0x4bb7db981fbbc0a9, 0xafeedcc11a515fed, 0x33e3c32905fa57ea, 0x311b3472718ab6e4, 0x45283d91f0d22677, 0xc79d3ea67969a731, 0x5702a377d314cba0, 0xb806d57ea8bba6aa, 0xc410d73d6db21c2a, 0x20c2fc5290917709, 0x32bbb4e681c951b5, 0xb9bcedbacc1a965f, 0x17265684ba1ababd, 0x600765f047755b01, 0x0ff2a521d94afd7d, 0xf9fbea73f2d66288, 0x3120002da0cd80b1, 0xe0f0fb44e63bcb23, 0x4dd311e73b06b9e6, 0x4d11f5a04c8aed66, 0x3a1dd43fdd79a938, 0x198e14e2f1ec334a, 0x5e9b9f713b948b77, 0x49bcf3aa56f4efe2, 0x797da070ec4790a1, 0xa45ca04556c04515, 0xef33f0ad319f5a4a, 0xc1107ae7a3a14d6a, 0xcceafabc98fe319b, 0x1eca2ae214c4c0e4, 0xa905c0a1029a4450, 0xcb58c4b78853a814, 0x23f6f2b348928709, 0x3397e5abc266e60b, 0xd32ea59fc4f2c040, 0x6c043424b77ba9f5, 0xc08f3a34e3ec4e53, 0xc06caa6f3619b3c6, 0xb7ae8ccd0ac989a6, 0x54b42cb72e84b477, 0xf13bc82f118188d7, 0x063b17c07faadca7, 0x7b87abde3340839e, 0xdf621bab0b8d77d2, 0x399776027ad914c1, 0x7f8817bf7a7c537d, 0x72a3d492d5c0b250, 0x70fcdf20e8bac20e, 0x5bc87c0b0041479c, 0x05f23cf2d300fcc2, 0x2f7b94ef197388c4, 0x2079f9c9661376eb, 0x418df4e48879057e, 0xbaf446d92d3cdda0, 0x0eacad7c53b1ba95, 0x0d792b6858d3cf83, 0xfee291fd1c4c831d, 0x6f9f41e6999617e3, 0x987101655bb124b6, 0x54126e930282baf5, 0x6e417532581d6fed, 0x7e6ab0ce3bcbe4cc, 0x269cebfa68d5c2f5, 0x2435aeaf9c7d6b4d, 0x7ca0ef313d53153d, 0xe7c978f8e0fdddb6, 0x9063131cdf5a8e61, 0x11e5c333376ccacc, 0x4f36772c844b22eb, 0x19a969df2c0331c6, 0x500fedf381935c11, 0x9991ada5c6618792, 0x550ccb311555bfd7, 0x46548fe65a4b6851, 0x5b48c281e95a6f1a, 0x65e8f82010ea2d2e, 0xf12a55cdc62152bd, 0xf5dfb060dd95743a, 0x8baad19e277f8205, 0xf6973b40c50f93c7, 0x74b7917854316e88, 0x7c595148b4b49ee9, 0xbae2701ae2aa8e08, 0x9b55f382fe8aa224, 0x1614bb005cd2e535, 0x4c9d8afad853e45d, 0x21511c6493a67fb2, 0xc267a19e50851e83, 0xc877ecc70e6f3982, 0x41d6ed94789c963c, 0x9bc64c5a3d7e98aa, 0x871ff7c0e603a089, 0x5b91eed23c932721, 0xdb35036ba977ec8e, 0x87d8eab2117cce97, 0x57082a1b706722bf, 0xdbe562311d1eb28a, 0xbd295115a2ecaab9, 0xc8feef1ca4310d80, 0xcf2693bb05f3d4a2, 0x8fc7c27dd80a4995, 0x88ec7398f7fbde3e, 0x230d9bb8e1ed9c25, 0xa9274dddf97a2cbd, 0x3f862ebc92949816, 0xb049c0d47607bf45, 0xcab5f4ef79c5b391, 0xd31bd5d2bb466a2c, 0x21b3c4e0917f7f1e, 0x7c15db64fe22bb66, 0x10da4d86de7123f5, 0x0eafd342aad59ba6, 0x5e32e0f61d5c248c, 0x8806946a6714bbd2, 0x318ea2cb7c12abe6, 0xca4d5215b479967f, 0x5dd9a9196a16cd45, 0xaebec5e6f2ac150f, 0x25931ecdaf19fd9f, 0x382d0c78eb7979a4, 0xdbfd6ffe347bd3af, 0xb4a15fc472f1ebb0, 0xe5356b6b3299ab86, 0x8347890b40d394c0, 0x2b41c163ae114a92, 0x24810db0f09ca44f, 0xbaa1a2eb17cccc8f, 0xe523179d2e258380, 0xf460de015c91c40c, 0xe60b34fe32d7453a, 0x18614b84dfcf1f73, 0x2661937a4ead4888, 0xe5bbd5f56b33a284, 0x4b0d4aa517846dd3, 0x40b66c437413fff9, 0xdd2b98d3221a5c85, 0x933dcedf5f038372, 0x4cd7ca53c5735e34, 0xf731711ba40fc8c0, 0x2be521ff7b9da3d2, 0xaebdf2fb45f69ead, 0xe6e7f7dfd8f4b9f2, 0x29ca222b3e6bc338, 0xfe180e15d8f082cb, 0x1d6e2ceb98d82d62, 0xcc3696f12f970320, 0x9c941bfc6924052c, 0xddba42a781f4e64d, 0xa4c6d6014211e565, 0x1bd66cf7e3491250, 0x9a1643f38265e94a, 0x5f2393f46f49f7bd, 0x6a7cb1e085d76c43, 0xe37904967177a763, 0xc0a9103c3008b8a3, 0x61e760d9555f5335, 0xec9706bee23bfba0, 0x58b11226b8690bb5, 0xd59f3738acd66082, 0x9ae530cd12c92fd6, 0x19c8b9e6ff8dc17a, 0xaa1494c28fb68e47, 0x32311345e68921a6, 0x81392725d49faf19, 0x444d33efa0f93ff8, 0x6fe9cec5d131a7c6, 0x0074b5ac1ff048a3, 0x24ed1f10b9b21d75, 0x8fad8c390fa7130e, 0xe3be3019cc752162, 0xd469ed0540fca872, 0xd55717c5222cf580, 0x1d7d09899f1d4318, 0xe0b11bb3dab19128, 0xd37faca902421bd9, 0xb9dd739e04b95103, 0xc3947adc47fd3bb9, 0xfb32bba8f91e1009, 0xb23dbd9ec74d4d98, 0x5583f5356a6fba27, 0xcf4c75e4f54ae2c8, 0x312060b7e3984e24, 0x69e66624dff478bc, 0x7990bd7818927c4a, 0x97dfce046da1bb81, 0xe76aca33e2b99fdc, 0x51902b4fa46c7d79, 0x241fd2d55a511276, 0xa415f14709fae7f2, 0xdf474c6d89afc348, 0x5f902e9e091d5e6a, 0xfc693f1460891819, 0x676c5bbedf905477, 0x510543d996ba526e, 0x6c52fd07e5acde39, 0x591cf6f44d159f62, 0x95d2b917e50ab11b, 0x532b5cc642b72c73, 0x50d302cb317e8301, 0x28d5ad2937ada84d, 0xa83f103c100e180c, 0x623a54c893076220, 0x2cb6694bf352762e, 0xc68f2291976b88fc, 0xc7133026c6e71286, 0x30ef07c1d2538084, 0x3c0ecf4283899ca7, 0x2012eaab9a82d628, 0x8c9bf190d310cf64, 0x932e28936eeb7c4d, 0x8755f869af2a72f5, 0xd05500b95e49e133, 0xd2b84540a4717c39, 0x1be854429117928c, 0x29c5a06464b5f11a, 0x1310cead18a1261e, 0x996a62adb9380e4c, 0x45203ed71265146e, 0x125823dc566005d6, 0x6f85749dc2386470), (0xea1820faababa06a, 0x37bf94578615faaf, 0x7104b0e9398310df, 0x17c94d198ec6760a, 0x5619e12da9480b0f, 0x4be16249d4513f46, 0x396ae0569965ea09, 0xea4c3eb900e39435, 0x7d311f2ea4be5a58, 0x38b3be5c2a742030, 0x439cb1646f85137e, 0x0d8eaad1936ea70e, 0xb0cf831ef3ffc0f3, 0x1c18372980a53fff, 0x287c4d0cc9d451d1, 0x26d5d675e7eee994, 0xfb7a34e486141a94, 0x5aaa60b4caa091ea, 0xda38847aa9145809, 0x962eae22d9bdbe50, 0x93b389a3042d11f2, 0x047f9a3727886c6b, 0x78f92dfe11aeb59d, 0x98211732e77a88fd, 0xde89b0f0baf471bb, 0x1955a7a1afa3dc22, 0xc1c3cb44fa37cf0d, 0xfdedd8b9150cd134, 0xdaec7e0134bff79f, 0xe2c4788f4d359268, 0x8c98962ff42193ca, 0x6cb8718740072552, 0xe38b56b943e48bef, 0x07660c435199ee62, 0x7ea585982525a864, 0x0281d59c065ef478, 0x30f01820e375e4d5, 0xab27955c402fbc88, 0x0762623922847fe8, 0x14d6d77e56e81c82, 0x759b5301ab612ca3, 0x1f2257d7e80e6128, 0x07f86242de00ca89, 0xd6bd4f3310b70b09, 0xc1e5db3588612553, 0x18b64b49262f893d, 0x803898f352a41ef8, 0xab3e35bb62373b3d, 0xc8ce1f60497b630a, 0x4d64ff887802a894, 0xb8d5f05a48bf3267, 0x7bf0f94d3d648902, 0x63d92b04a206d965, 0x8a7744d8e6633f0a, 0xd7e71d808c3a5243, 0xc398d4d94cd6a745, 0x69f4134b3d4b0075, 0xe24cb50c57a10486, 0x6ff65bedd0ab6784, 0xf53e8056ed8252c4, 0x8a3ae4286ae27476, 0x82213f97170cca58, 0xe0ea23125af3b2c4, 0x1f53c9a4dd9a7ddb, 0x64e5f7b00299f3b5, 0xa77a6fcba39339e5, 0x5a049181ab91c0eb, 0xb31790f8e0b5d75a, 0xb8a89114b5b65f21, 0xde412ae12888e8ce, 0xf86a27952e4bbbb3, 0x1a618f4ee01ee329, 0x231e1fe614f8d3c7, 0x2f91770294b77cce, 0x2a5b0aced5f187f8, 0x0182c58068e165f8, 0xbac72501fa5578db, 0x0fcdf5c2069587a3, 0x91561111e81b91a0, 0x742f35f8edb78689, 0xb203985224d0130f, 0x2cb6f04e77283268, 0xfdd9309a1a4250ba, 0xadb4bbdddda26cb7, 0xbcbf15b3b0717cc9, 0x1f72e0bfec2404ea, 0xbc7b7f8f2253896c, 0x0953955d8f9f62ce, 0xf6084345282cdcd9, 0x687c7b2372651219, 0x381e2bf7146e5dfa, 0xd986ea990822dadf, 0x8ac4b4dd465d71c3, 0x5eea61025cd6a83a, 0x52fd32423c10bdf9, 0xf6436ab633194adb, 0xb6881d554b0dae9e, 0x95b5400162992436, 0xea6dae4dece5f22b, 0xfc29e90d21027c10, 0x4e0a27478a6d8b42, 0x28d51365483cd03d, 0x491eaa7cbe627461, 0x265bb947b13427dd, 0x0e22e6115dbc0a63, 0x0cd245bfbcbcdab5, 0xf7ce62829dac7b74, 0x8f2feb8859be75ae, 0x9907c2a0d2ce3013, 0xba992a98985b37ac, 0x8988ac4a085157bf, 0x772e605dc326f157, 0x7924b50712e85d2b, 0x607c14c4ac3ca7f7, 0xf2f85e22785bdd24, 0xfc895eff247afd30, 0xd3a300154639d5c5, 0xd222106bedb08157, 0x7b14f640d1657740, 0x369b9073a7975b1a, 0xe2e9179f0875a71b, 0x5b6c3d9ef2904598, 0xa9d7d7168f08c241, 0x4c85e8ad846e499b, 0x41dac7e1a37662e1, 0x0e0d9466d2c20584, 0x428ac02de66a1811, 0x8593111323323280, 0x9796fbd510ed0fc9, 0x4b6f052d054e0944, 0xdfae1400042b908f, 0x0d581ba48bf675a9, 0x7b015f1d69b01564, 0x4b4f8b2bda9e226b, 0xbd3cb9ccae2ca007, 0x3d7d98807088a197, 0x6726e6935cfee9b5, 0x59124f2b902d792e, 0xd0fbacc7231370a6, 0xe622408dd421b0fe, 0x23f45d0bce31c3b2, 0xb4eb15f8dee27654, 0xfe100c7edbc6b17d, 0xb96590acb9f5fbf1, 0x92869a6e784c9303, 0x9e5e2f392ef3f4bb, 0xe69189b660077e91, 0x1b87e65cbe259875, 0x8585a5978cc26228, 0xa986ce26b4f3df44, 0xe1978f4a620529ce, 0xde2842b7b0394b4f, 0x4562dcc1b81a4db9, 0x04004ff3a90b0c46, 0x0e09c8eb43366b9a, 0xae59a0e996ef0025, 0x1b39116133c551e2, 0x17f8924c2e52559f, 0x6876416ac33a304b, 0x4721be8300dc7405, 0x5d34c86e15c6f811, 0xd4fec6d4af59abe2, 0xcbbac114afb48500, 0xf3d7160ebc9d1747, 0x6d73de07f3128d76, 0xc310f2aa96db7c1c, 0xb0ed9d284bca961e, 0x4f8b9dbfd506f006, 0xef2b5609a4462524, 0x880d6d31077afc15, 0x6d097e373d7a8097, 0xcc83208328ed155f, 0xc5c261dfe2246979, 0x5e921d0ddad3fd1e, 0x7d545406d2c995a5, 0x9a32e809010e6bf3, 0x775e9a9427f3a68b, 0xf88f5924ec9bdd77, 0x437c005541aaad6d, 0x631a0ed5bd345003, 0x25783f92a044a6dd, 0x8047d063a85a8356, 0xb3a76b129161ce96, 0xda77eefb5cf1d599, 0x745236451cc5dbd9, 0x14480fd8644f5289, 0x52136fcb3af7e5da, 0x7c0ac7cbffbdaf58, 0xfae05363fe0f8bd4, 0xd52e7958e48f18c0, 0x32946ce8698f62e4, 0x2728ad2a0c144122, 0x419a7580009e2797, 0x8ab4d484632b5ac7, 0x58ce650ed0f109a7, 0x6b32c93dfc56a0b9, 0x300f52f912212aca, 0x0d46bda30fcef7f9, 0xe63383705da4ff19, 0x7fec0863896689d6, 0xb28b77af043a4b32, 0x5bef9cbedde0525c, 0x5b3d772273c772cc, 0xd98d7b9b9037b94d, 0x446e9dc575aebd84, 0x21b1b813f231652a, 0x2d38d2e4723193aa, 0x83cbc98a72f45ca3, 0x8d22bdc091155dcb, 0xfbee83d2d50704c2, 0x64f4d9b093018e9d, 0xbbcd16344fc66c17, 0x193363c9d64fb14f, 0x7028ccc5ab0d037f, 0x92e1a0e5121e67ef, 0xeb71208dfa06ba59, 0x05a20670c4e64c64, 0xb37bef73ebf5a2ca, 0xddf11a686dcc061e, 0x657598376a5f7936, 0xcd97ddd2ca69b32c, 0x28b547f60347dc78, 0x35909922b46c96f5, 0xe8e98df56a1da7d7, 0x4fb0d7d29c395740, 0x99f1a255b520d66d, 0x6cc9eab7702b33d4, 0xe897bdfdcddab2ed, 0x9f58fd73cb4627a4, 0xeb424dc2e3eec1e0, 0xaec2481d4d6d4a17, 0x2eb50a735a55b3d0, 0x4438e790e967883d, 0xe90aac02303c32a4, 0x5aa971da2a801f35, 0x32c0f896f7795a8d, 0xa76bfe45898b07ac, 0xb2e49a4c129c41dc, 0x935d176c7b1d9d7b, 0x81f161c651f05552, 0xf508522f646067e3, 0x70696aa97a1d031d, 0xaf6fb94208bf4d94, 0x2e11d1fb785a84b6, 0x3969aa886f535d57, 0xe6f2fda831186ba3, 0x7ec67bc20bf2d666, 0xc75f25fba87df5af, 0x0791a7c07e0a0f5b, 0x872aedae9af315dc, 0x0041853bc93e5e42, 0x398eaa185bcf04a5, 0xbca3aff3ea5c213a, 0xcb74ecbcbd6c5eaa, 0xde57e3064abf0a18, 0x1564c089bd75aa22), (0x4fe9223573cef9e1, 0xdc2db2504d791da0, 0xe0648b31d5bb7c7a, 0xbbf1255bcd12c2b3, 0xe0a91917f3de1205, 0xec24e86552e64f24, 0x398aec43cd1e45fb, 0x72ae3697a5c31dd4, 0x474870e554883334, 0x3aba1e122f625f6e, 0x51e411ba06bb1686, 0x2bfb998241fa2641, 0x63da9ec083d3edf5, 0x5458104ebfe93a70, 0x3fd587643da2c9e2, 0x65091e81cd6722d7, 0xb95ab72f17c96d13, 0x0da07d461ada93b1, 0x993be3498581ecb9, 0xf45720620b9fd89d, 0x10af7c2c7a22ad94, 0x04e9c83e4a111179, 0x70994988f38422db, 0x6684d7ebc7d8adca, 0xe62cf69170f634d3, 0xfeeb986687186a11, 0xccfd746024296cc0, 0xff971961b6e3fc94, 0xecf30c35c7630b92, 0xd074faabe5d468ee, 0x5df7b94816e48e50, 0xc9e9a195972bd78f, 0x717d7ea0bc8b8408, 0x4bef65694779e7ed, 0x62c567ddaa1e98c7, 0x542dfecc0923f7a0, 0x4dc5ca2f482d809c, 0x3cd589e39b51c6f7, 0x92208762d018c78f, 0x2ec938209ec2cf0a, 0x9009029773f80a0c, 0x0ceee884138db420, 0x136475ca483e7b50, 0x396eea0744b5212d, 0x70e72af9db4f8a1e, 0x21d8f2fabbb57cea, 0xdaf01f5961a65cfe, 0x65e88a07314d4104, 0x49aa19f1c7f4c265, 0x969d1ac1e40b1741, 0xfff10827ee93f3f5, 0x828b7217182f6135, 0x47b8d04f80653970, 0xe3b17bbef8b2cd34, 0x4f69c889c71049fa, 0xee78f6df3e7c7275, 0x6e7873c197aa1fef, 0xc7e2a26726069746, 0x0e99cfc4939229a0, 0xf5b677aed45fd196, 0x7114035775e0670a, 0xaabc81a12d63dfea, 0xd53e94d10824a119, 0x8e88f6e782509bfd, 0x56710b2c063106d3, 0xd3a042cf642ceb23, 0xc888f8777532eb87, 0x02a5d450049ba9fb, 0x04da0ca115592e87, 0x47d30bc0efaeef8f, 0x347c53517c236d12, 0x6abf2e683de4a293, 0xc30afa1d0e6d8dfc, 0x2651be7e7134654f, 0xfce45ecd8afaf4db, 0xa7cf0da1decf86d8, 0x1008a67a75382b25, 0xad24f101cb2c1aa0, 0x1bc9505588e83395, 0xb197dca2dd0a3e6a, 0xc75f250fbb455424, 0x990cf33143679b2e, 0x4e1670c05403d48a, 0xca9f3b6c2343996e, 0x8d6e249b740a7946, 0x118fab845ad9976a, 0x52ce5e3d87861297, 0x2e2285498bf7aedb, 0xbc80cd7e6fffce8b, 0x768584e4dddeb38f, 0xce2d3f7c650c9410, 0x7af84a290690110c, 0xbc758b080883fbd4, 0xfe2220b28e8d08ab, 0x94df21dfaab30740, 0xb01bedf338073103, 0x9b901eca0a499843, 0x7f27f4b601b7604b, 0x1afb2cb14f7d1e2d, 0xf3c670facf4b6d9f, 0x62fc0aa6efe46407, 0xf27e1970f2051844, 0xe7cd0b117bb5e15d, 0x05ac5342036b14e0, 0xfbad37627bc5ba7a, 0x0c027cb3a12e1c3b, 0x4e5b88c78425534c, 0x467e5aa65dd2f5a6, 0x57bba572fbde2777, 0x667926acfb6f4f11, 0x41ef7b24ccd74415, 0xd97689863a1a4e7f, 0x2db4215af3579bc6, 0xe94634f67b826a73, 0xef09663ffdc2feee, 0x4bad9de7990ab3f2, 0xf558e0d80136218c, 0x6b1dc6e4647d9c72, 0xabfd05ba0e4da05a, 0xf5470e7b72266636, 0x0c23ac04b10e7666, 0x655760f35f195b04, 0x94742ff7098a185c, 0xf10606620251d4b8, 0x103bce40960547b4, 0x04889a8c82e9a346, 0xd3eafd5154dd9cc1, 0x9dc42dddc280f0c0, 0x1a68c523e500797a, 0x9aafb414792f7199, 0x82b4d1cfe765089e, 0x6962953773ca6f3e, 0xfd089f7e05bc85bd, 0x7af82ee70dec3e23, 0x607d36156564de0c, 0xd34e79832b4baf91, 0x4b4d87f1f022979a, 0xd6c489c17fc99d2d, 0x559471a1e69484d4, 0x4a23f806b56a2efa, 0x31a0946759ca97ff, 0x949f24f8233b8310, 0xc209331d025b6598, 0xa02db491600040a6, 0x94f2424db3c9196c, 0x7939359dd36d0483, 0x46894627e893283b, 0xf49be7836203de5a, 0x0822b1803fbd371c, 0x4383e1df82adac3a, 0x10a88ad141344d9f, 0x4cd5a654a892e210, 0x2aea8eb8b0049584, 0xa9bb0ff8a75ac388, 0xfe057eabb89f179c, 0xfae806bb3e8cce92, 0x048672ecd6e9cf49, 0xb644a1f52980c3cc, 0x9fe9cabc6e27849d, 0x25e4423e7fefd2e2, 0x558d3ae63281a4e6, 0xae54be2603a538c9, 0x4991f38a0548bee6, 0x148118c9a82e3ab9, 0xa0c6ce214b89b67a, 0x58b522ae9b9e310c, 0x446badcfae4e5aa2, 0xc4d2f4ccf9e289e0, 0x639a476c0c7f1dc1, 0x7c52ac5287fca418, 0x7d38f439bd3487ef, 0x1a8811de56eb771a, 0x29470828b42f4194, 0x4bcc71612b894f1f, 0x9cea0b9e2aa83da6, 0xa532098bd2b43f49, 0xf6d39dda5cf86a4e, 0x23f442043f3a0b18, 0x6a3e113cf2864d57, 0xf0a23e5181ea3aeb, 0xd4fa49275b579b1d, 0x9cd8383036ee8305, 0xe8c385fdb92c17eb, 0x6965d47ccefc911f, 0xdbebd532d03362a5, 0x2c73a11f13cbaa48, 0x75e559d0a28c58fa, 0x620b7e5a5a734d01, 0x6f72c52fbf66c3e2, 0x7c212dd39735685c, 0xb7b541cb38cacd2d, 0xa1d334b51665a499, 0xedc0476d0b8fc403, 0xde14968d0af0636a, 0x3dce73b681f2743e, 0xa735af736b210711, 0x8bc62e557495cb6d, 0x7a53a7f6deef14c7, 0x473a11f54b02fafd, 0x7576e318c4f91d59, 0x2f173241961049d2, 0x552a848c1fe2262e, 0x071b7cf087868f6a, 0xd3ded8e7f3116853, 0x0f6960f027e11eac, 0xdfa3acb9c5162f3f, 0xb2523fe79ff256c3, 0x3fa4493b0f3e0e49, 0x0c62568a888733cc, 0x1030724ae338a7d0, 0xc646ad78381d6156, 0xf26c976af252900a, 0x0e592ef7f0663c82, 0xfaa9782484405705, 0x61dc1236c994ceed, 0x104677a7846ba603, 0x6265afabbb386dcf, 0xa7141b1fa4f7dd88, 0x48fedcf223cf90f0, 0x27c179d056a2f0ed, 0x1c7d66cacf3cd369, 0x0c797405d536a0be, 0x5ab355326e547020, 0x526e813f9b9828e8, 0xa089c999711b40db, 0x306b9c81f0cf972b, 0x0b0232b7d186ba26, 0x087c89513a8d1a98, 0x17c1b5b9b5d5fb00, 0x8d3f8b1da4451a38, 0xc0aee3b11764d28f, 0xd224166d6686aa2c, 0x36da9bc84707b5e0, 0x3befb3a302431c50, 0x438eb0430edbb686, 0xa88dee8867393071, 0x6a42fdd5ac14b59d, 0x485128f6b7fba95c, 0xa60c2aa1c61112dd, 0x630ae474ac90d7f3, 0x0ab2c73e78e5050e, 0x5570af1aa2ba9c5a, 0x7a274265aa7e7a87, 0x22b63934f604177f, 0x4c181cc2bf375fb6, 0x930a4dde1d7040dd, 0x14136117e8491a7d, 0x520cc3001872eabe, 0x3aee2e1f638fdaec, 0xf194325f3c54fd57, 0xac7176a266b7895e, 0x275cf37ca133bec3, 0xeb9814e3aa11869d, 0xbeb498a63acf6064, 0xe88bf26c61a23502, 0xeb2856cde85bd136), (0x61698ecc0b410e2f, 0xaa843a4bacdee7a5, 0x589d9c72a5b3e063, 0x328be82c53b308d1, 0xb03fce5b8c3f9757, 0x4ab5e0a0e8e4dd89, 0xda541464509c2e25, 0xd6c177c2fb345b93, 0xfe8cea71d04b6665, 0xd618d8f745b339ca, 0xd51239494474b8cc, 0x8d626aee9750768a, 0x1fa9f838b32fbe3d, 0x044facde9613551f, 0x04523ecf9780249e, 0xd08d8d7db1c15e79, 0x76edb6c6b8eb20e3, 0x75f6ec2250e2ff09, 0x1b4311feb33a06cd, 0x0c9d217c2ea74f35, 0xb555b02dbe37e987, 0x676334981eb1be4f, 0xfe67cdedeabaa089, 0x40a1bc016acab55e, 0x49daf1634142f1fa, 0xed4c8258a94bde78, 0xdb82ef1ad14fbed3, 0x36c2ee25c2ea9981, 0x458926464f9bcbfb, 0x2ac83b84383cb3d3, 0xc6f563e46df7f28c, 0x0343a07a3ff91e8d, 0x7279c71961b961e8, 0xd666caa46cea72dc, 0x9ac6654f293c6aaa, 0xc383dffeb4bce54c, 0xb312e645c613e8de, 0x1e6ca35a26cb7bbd, 0xdf8db2d455f0f543, 0x55e182ead2864772, 0x45d48eb8ad897752, 0x9da3bd97c7174794, 0x0ae5c75f86a43120, 0x39b68f84e7846fd8, 0x485a445ec3771dbc, 0x0e70f83fa86a5783, 0xd603896425099de5, 0x17f9094055c11799, 0x4641374a178b8b85, 0x00044011f22cf1f8, 0x12159aad165be495, 0x09f925790e59cccb, 0xa00d2f1261837929, 0x841416a1c23e85cb, 0x9a9328a4f4047167, 0x001774feba43eddf, 0x2fa8e108ba980515, 0x8254a29236598a78, 0xe04a77e707de5769, 0xb70b3da72f36fd58, 0x533c6be732253d96, 0x1028294c7b55b565, 0xb25041e79c8e9716, 0x8eb725311d8dacf9, 0x48f1412ebe8495cd, 0x39fd60e6a8dc9437, 0xa85091a1b2f4d68f, 0x73aece74e438e865, 0xd6dcda536611c848, 0x3393c9f4bbda8064, 0x9e40b8513fd2c7eb, 0x46fd5da36da24c9c, 0xc1e4a25326f8f115, 0x569049dfef71e131, 0x6f3262783ccd4ac6, 0x8949a6ca8ff285fe, 0xc668d77d4dcf334f, 0xbbb987f5ee2177cd, 0x2e1e3e437997cb85, 0x84c2256b84d55b3e, 0x9597acee89e05592, 0xaec2f80f0f9cdaaf, 0x91505378a9b697a7, 0xb587bdde07b047fb, 0xf485710385b7ddf8, 0xedf0c41ed01c61c3, 0xc409cb15d629ac20, 0xced9e0850b9d00c9, 0x26c145efe67c9c5f, 0x6a2cbcf078ddb3e0, 0x4469e57fe5303e09, 0xfd3a7a701bfa4cde, 0x5c2631d0a211f2ec, 0x2ec499503ac95926, 0xefaac8fc319f879e, 0x360f519481b5ce90, 0xdf5800b0850b6172, 0xc43616ded1b1fe1f, 0xf8864b965452313b, 0x9d0fdf10c8c7b595, 0xd75094d921f660d8, 0x7a7e6766c2c746a1, 0x39df51220cf09377, 0xdd1dc4120a66428f, 0xc6a2f2774b16349f, 0x75cfb9ef644da261, 0xaf0036839ac3eb83, 0x4a5b3a87fee45354, 0xad8aa0bb156d69cc, 0x2f3eaac853f259cf, 0x156ac3a99f3f0730, 0x765d23bea3de6205, 0x1740544128020806, 0x49ad80e4f4170acf, 0x5239cd82b25dd862, 0xbdb41a9fe35a0acf, 0x641eca75af89c1ac, 0xf015ed710d267ff6, 0xd013c82bc5d33f7b, 0xb9c9bd9ce25734f7, 0x5cdef3fff51c1bbf, 0x11eb5b74fddae426, 0xecf2c44e0e2f204e, 0xc3eddacb3bdaa03f, 0xc5969e37f684195e, 0x31208d229e9dfe0e, 0x8c842cc2b4e40ed2, 0x1f11cb9485d5dd7c, 0x48124ee486d78d79, 0xcb0e85ac56dd14bf, 0xcc44b711424b8961, 0x4d8259b930aaaa6b, 0x0f7948318d38b90c, 0x2e79d9ac55272400, 0x878e6b21ffe2d8c7, 0xc2ab611e66a1486f, 0xe48c2aed202b0824, 0xc2292bac4d7cb2d5, 0x8e379315adf29867, 0xfb27e067b2d7dcb1, 0x73610d676753f88e, 0x77a8d81ce35037c6, 0x2c17d3e22482ea01, 0x000b847d4ea9a755, 0xd5fad1dc2c1f8e96, 0x098f00d21f956e31, 0x1ad020e4eb6dea5d, 0xba2460c56ab8e36c, 0xbd8f1169572d8615, 0x9744fbb204c45603, 0xa53284bf759b387a, 0x27b7e39728c3d937, 0xcaaa28303f12c4b0, 0x946f09477f6d95bf, 0xd1adbb897b4a331a, 0x1b05cf48258409f1, 0xeaa659a12d7e6197, 0x1fcbb9144238de96, 0x916cdbd80d6ac900, 0x6261bca36f01eb6f, 0x38961d8005074365, 0x9e068c1f157f2653, 0x6b9eb9a5ac7c8e4e, 0x5c764306b4b133e7, 0xb289fe139478f359, 0x4bdbfbd80378d7b6, 0xc5691f342db79ae2, 0xe343b745ecec30e3, 0xb1b0b9ed41f2a7c5, 0x00978a6ecb90350c, 0x08f907fed341892f, 0xb24a2016c926d332, 0xfdc42aaa9c6920de, 0x4be1cedb5e6cb7cf, 0x646534eff2418124, 0x4361f3be0fdc1578, 0xfe70eeee37014983, 0x474e94b07438fd72, 0xaa43cd148321a576, 0xbe6c345193a90761, 0x5a0fd48621fe9a9a, 0xcde086690236c1cd, 0xa6c7055dc8bc64a7, 0x52a637b8e547ebfe, 0xa0a907235741a910, 0xb7bb4184dbcda2c3, 0x5f51f93a2d6f9224, 0x8c5c772ab0169e1c, 0x7ab7da3577c21b9d, 0x2109af27e79d2189, 0x34bc3f66388dfebb, 0x0bfa3839e88e1668, 0xd0a4f9457346e9cc, 0x33a0a6b64175e03d, 0x793be1ac92f76898, 0x9cc2898c830646cd, 0xd987d1099f78a888, 0xcf008b1f0d236304, 0xd6ee552e69fcb13b, 0xe1224168a2a071b4, 0xa206a30317140094, 0x0bac8c13e1fcad56, 0x163255138fc963e4, 0xcc1a5894afe42d68, 0x50ea9f01d9d42c9f, 0x6b1dfbf54d6342f6, 0x5f25841da2d7cbe6, 0x90ede1b0b2bc04cd, 0xa6ae21ffb93ec5ce, 0x5e445c805cdd66a0, 0x8ce5c65e9c30c2be, 0x921ff634e367f2b4, 0xa44808637e62b23d, 0x8e6a851ebafeda33, 0xe3a4cad58c62dff3, 0x82b37968c5335c0b, 0x717ba94877ae4c7e, 0xcbbe4c3e4db91cfe, 0xc83a3555b890b894, 0x9e814c80706d702d, 0x2998f8698b6d9339, 0x30c4a7b6c4cdca5d, 0x6de0179badfca2e3, 0x072c68829e3fca28, 0xacbd1d90cd367157, 0x4cf7eab61ff31022, 0x433df610bb2fccc3, 0x39099e1d1ec23bc4, 0xd435c0d14e1d9e15, 0x6b235431322eadc6, 0xa82229cf66e6767e, 0x0ed907e46d6bff6e, 0x54ed9c2ef1f1d207, 0xde503a213ab653b3, 0xbc29c4ca82cfb84c, 0x26af3ac87ae99921, 0x4cb53d9991097554, 0xb267fee3b07f13c9, 0x7e184d295a4aec2c, 0x69a6168f72575d59, 0x407e3d6abe966c36, 0x8639d5ed35d08225, 0x48c6243e6d8ff3df, 0x2ff3be0cba88a74a, 0x844726a536674bc5, 0x8352542c7d81fa71, 0x82119748a8549586, 0x5ea2e48c0a7c3d70, 0xcacce4767c334662, 0x8dfb47b59af4df48, 0x4eb53072abb55d06, 0x756fe829401dcdd2, 0xb17038e549e16423, 0x26cb8f101f4882bc, 0xd3c06cdade80e990, 0x42e771622ee651e6), ) mds_16x8s_lhs_0 = ( (0x5b276bb177f7aa23, 0x97fdd3eb617c344b, 0xd87e1412d8315e60, 0x45c97c76da29fc38, 0x087d41fdd8df20b2, 0x5ae48d27490b50bb, 0x05276d8424c9d053, 0x5f42d683c4956fe3, 0x0216f52eb64e0d48, 0x332d69e967e266a0, 0x52c8587aac5799c2, 0x3621ce9475c4123a, 0xbb00ac4a2b7f8e8e, 0x63da4e062202ef0d, 0x1da8d81f261f497f, 0xad5b0b36ccef1f37, 0xe22536d1f7f1dd57, 0xd62f76a8a6199b84, 0x47ac1873a688a608, 0x81e0f9aa9b9a5e8f, 0x2aecbf39c9675f1d, 0xcb2a32d27b86ece5, 0xbe49656f9dc3fc66, 0xc10c29f228829893, 0x1360b6024a5eadcf, 0x820306da3a5bb435, 0xce644ac3a9f019f3, 0x5fb563c6a48c016a, 0x51aaeea2ddddba10, 0x3f238a069e1a36d8, 0xb0c369bb8f933ceb, 0x1d17d9f31af24b1a, 0xc24d9a85b62ac4a7, 0x163736d82d4233c0, 0xca36b4882546d0c4, 0x9466bb6e9ea86e1c, 0xb14c183c9b10497e, 0xc4a33559edc63ef6, 0x13e4a99ba2536c29, 0x3737bf5505aca7f0, 0xb83d5975f389b636, 0x89685a529d35c30c, 0x0e8ed7684c4551d6, 0xba59d762a5a42fd8, 0x81a0cf8261f1db56, 0x29d28214f3d160ad, 0x18828df91cd89f7a, 0xc8f5cb05e23b1473, 0x6e6359406046bc59, 0xb53fed1a78739df0, 0x3a5eed44f79e0f2a, 0x9ece39113efc0f51, 0x5b16da83dd21466d, 0xc59b1efb53c2dff3, 0x0ad5cb075b43e207, 0xb88e3bb7aa9acd42, 0x1d87bf28b9d501b2, 0xb67e943ecc8736b3, 0x1a73e9c7de6894dd, 0x5420c47ac5e0e5c9, 0x94c9060ed3198ff4, 0xebaa9bc9c2311754, 0x7d2c20d6d7eb0f64, 0xf38e9bf618bbad5a, 0x5f7d3138bbd981dc, 0xb227a7e251f75f6b, 0x0b752033e9f0f521, 0xdd07e29ec97c34dd, 0x48bcf9ca9aef68ab, 0xf4bc60b6c512295f, 0x4e947af2c14d9d92, 0x77c0cf739a78a7ed, 0xde0ecde3223b28e2, 0xf86d6ebb7845707c, 0xce61176499be6856, 0xfb8ea4bd187b92ba, 0x935cce8688bdfe57, 0xd80c94af663f0519, 0x8b515c795881adaf, 0xe26b2a60e177280a, 0x0f266cd554d0b24d, 0x815d023b96f94f41, 0x56fb1fd74c20e418, 0xf706c9172a173dfb, 0x42c30c4101ad7f91, 0x6dcd99dd92fc0748, 0xceff0a31439db601, 0xec76baaeb41506a2, 0x8831ecf2b12044a6, 0xa1269481322ed32a, 0x38fda335614eecf0, 0x319d1a143714426c, 0x1ba32e9342d4d691, 0xe2962de985051f8a, 0xabfbbb1facb05713, 0x02718fb4732033d8, 0x2ad714af6aa8f53f, 0x8be8878da9508ebd, 0x38bdbff1ca0a3319, 0xbd90882ba2b8e02e, 0x4994a97931e4c32e, 0x530bfe622a306489, 0xf3d2263872d31bc4, 0xdfe69676697a4d53, 0xe56c22686975eb1f, 0x5cdf77693880ce35, 0xbc1ccc3b81120ee1, 0xfed5662972bb3d65, 0xcdb1924fe85cc36d, 0xc5f1fa3a11f0ab59, 0x1d78275d0626c179, 0x94512929f0f6f5a5, 0x225c7738d15722ed, 0x3b0e4569adfebf88, 0x9b722a3ac2de1fcf, 0xc3f3a69bdbb3df2f, 0xe12262685382f900, 0x854eb3f3fa0e9db2, 0x6f822b1a6dc42a44, 0x1b7051e9904de2a7, 0xd146b6aa9df7e3e5, 0xb5a718d73ec4509c, 0x333dab5fb79c8c43, 0x9a516df37e1638c0, 0xdc46ac35f913aee4, 0xa8930daa3d547c92, 0x985bc568adf734cf, 0x57ed4cdd5833a53b, 0x7bbd19da397400dd, 0xd224122d2fd35c66, 0x9f4a7e7ee8960f02, 0x03bf0d4fe6164ca3, 0x98f633d94ec4fe7b, 0xa5ab360e247b2b66, 0xc80080b2c9abe941, 0x83103a7a51db0571, 0xb1856df93fba45c0, 0xb1e7a81b96bc0b7f, 0xec6113b612b367d2, 0xb2272d54455323ad, 0xb6f24bfd608b568c, 0x8a923dd69359e97f, 0x18846dc1c72eaa7f, 0x42936c704499dc84, 0x12c6b2ec9c90abf3, 0xbcda0d82a88c8530, 0x332321e168eb3462, 0x63f03537e699cb7a, 0xcef3cbabce427acb, 0x60b8039806848c3c, 0xcd6613f5455b3443, 0xd8e4338e476900bd, 0x390628ecc77e779f, 0x09c419237648622f, 0x78ebc0f40a5be029, 0x17170669a938dc9a, 0xae38e864a18a9a6d, 0x567f76e8c6234633, 0xb4290fceeeaad95b, 0x658ed71f5573a8d0, 0x56f4fc6e4ee52c0f, 0x4f6b2be72bbeb9c7, 0x9531f0e46fbf0ba2, 0xb9ad0dde6b923e42, 0xfc98af76c5f6493d, 0x5f54ffad3ed35c1b, 0xa0410bb38d2d842e, 0xccfd0762a324be86, 0xa10fc10ab42d40ad, 0xb55b6e9c5bdec366, 0x7eceaa7150a24529, 0xb860171d6221fcdc, 0x80ef858ec3f37bea, 0x676d08e4871036b7, 0x9666055126f738e2, 0x1e51513a131c79f4, 0x4bbedd71f206ab0f, 0x5c9fd0099fcd972a, 0x45aa63b76d0ee944, 0xe0d353a4df77a6f9, 0x85a7fc055d2da5f1, 0xe2e0ad2dc1c93b1b, 0x3c8561ef6d6f4594, 0x5a40a503c38adc97, 0x00c99df9d2148fbe, 0xe3206e2310749bfa, 0x1671f4e6fd88f37b, 0xc7a2d8abb88d1f36, 0xa9fa5150a77aae6f, 0xc2dacc4848b183f2, 0xdd898b1fa5584767, 0xa86b5dde6e0d7a27, 0x56fed89f4a9fc2c1, 0x9c1a5a4dc203e62f, 0x0ca3416304a44368, 0x9b20d29bccaed2ee, 0x51eddbd0b91cb40f, 0x755c4e86217fb461, 0xc3c016ea561ae9a3, 0xdba70a7a4583892f, 0xc01906fbe53d770f, 0xa05dec47be434027, 0xb4b65e68b3577c09, 0xa33275757875a18c, 0xddf5b8443a8735cc, 0x12c0ecb2ab8484f3, 0xfa86ea50c70a3d41, 0xca6732e94e209834, 0xfa5f11506cf5a00f, 0x71e2f42fab3eac1b, 0x9c462507c65b018a, 0x52e41152fc8ca99b, 0xb4fdcfdc4abf84db, 0x445f29a14b784e6f, 0x04f93e1287919d56, 0xa3c029fd38705d2a, 0xebbbdf123dd596e6, 0xffde71ac44f8baaf, 0x3054b83c0c64fc87, 0x8dfcada11cf45283, 0xc2ead825abdc550e, 0x15c4807c4088be54, 0x1ecb77aed0f958d7, 0x039323cf1a5a35d1, 0x9b0f1289307f949f, 0x41f8f48eda94a298, 0x48276dc18bba9008, 0x5bca991f8503050d, 0xabab452614bcbf7b, 0x7a6f79dd51d350d2, 0x0d01e234c4902a83, 0x4f47adc9d2ce0952, 0x3e5fdcfe279406a0, 0x4f29fba6f38bb7f1, 0x316d12891b0f8d04, 0xb21e8fa9b11bb81c, 0xa5f971e8c575fc10, 0x8fbc44d8dffafd77, 0x3159e65e97782150, 0x0a59903136773e42, 0xa5aa9b6dcd7d3b5f, 0x5be222f0fd738b6b, 0x4f309c9be70c189c, 0x7d669cc5ca96009c, 0xc79e34e415d4d80c, 0x00d1b203c4562140, 0x9fa1c6204bf512dd, 0x2f9100ad7527d0c2, 0x4085f44b0bae98c5, 0xbdddf2221e9282eb, 0x33add18617550312, 0xee61e1a655910c53, 0x4a24fe12b65aece4, 0xae5ef6fca874d715, 0x7a5225afce794b07, 0x8cd1f53aaa1d211c), (0xc7c9e602055516cc, 0x8ae9ed37dd727599, 0x68a84e00686bc858, 0xc825038ffbb43b07, 0xaf8775746baa2296, 0x419216e1f1ee1b70, 0xd09817bbcde34127, 0xabd16ea3943409d1, 0x0150a5014c6840f4, 0xe2d8b8fee7a5da59, 0x87c66fd5f413d23d, 0xd7dfb544a4159ee6, 0x8260ca622535dab5, 0xf567205a0453ae3a, 0x53c9b36765dbdc0e, 0x1efcabb640d5737f, 0x3e9dceb66ed8654c, 0x33a1c6085f53e1ee, 0x47931e90d0af4ddc, 0xe561de2931e16ee5, 0x87c1577cae032dc5, 0x15f2d3e085818b6d, 0xf99a29a420c67746, 0x05ffa7fae8de0c8f, 0xb6f34e4408d699d5, 0xb361276c50702b68, 0xca4c9e30c12ef781, 0x6443a5bf9a6ec039, 0xa745eb4155339344, 0xeadd4a77222d35d4, 0x923fe821ffc87182, 0x15da5db5d08f1acd, 0x9727e26368eb71d2, 0xe4030b649d63befb, 0x4d0cb6ba5db32b24, 0x75d3505358ad5d45, 0x0ce82cea2832b671, 0x3a70bcbc2fc2fbf9, 0x3074f1bc1ec6071c, 0xafc57a122ab0bc44, 0xf6e7eb8240d97976, 0x6274cf60d7416f01, 0x23c971b5c86a794d, 0xc4bcfcc083155f17, 0x54b79b61a462208d, 0xd29af1121f6efb35, 0xddafb7414ee63adc, 0xe9ecfb926fdce226, 0x56902281b502aa81, 0x3b7aa31c80bca094, 0xa71d7d539c92ccee, 0x3f01c564e152f62a, 0x9dc68324859a4f72, 0xe311005b6901de16, 0x8fdc961818969ae4, 0xf11fe2046a766b7a, 0x3ec1c162529dead9, 0xe6f15b746381541b, 0xe7bc1128d29628cb, 0x42978378ee9d4457, 0xb51eb756749540b1, 0xd4f9d1d4a0b3f4d7, 0x20144e30638723f0, 0x8d692623bf824ade, 0x3b32c5a0c332f598, 0x29fe43344522677d, 0x3dcf67c0a90cd62a, 0xb6ee805813a51f1c, 0x06d573ce00f3eb92, 0x8bf4ddd41f67721c, 0x451a3b3b3ae44233, 0xfeffd7df4f597e19, 0x3f2883aa4af3fce8, 0x545dff727a8495ae, 0x9f27a0566e12638a, 0x452a8bd9e867248e, 0xb70cc312b0c4576b, 0x8ee46e5878d4acc5, 0xe31f046c9fff9d82, 0x2cbe13eb0fc43727, 0x2a3160be528827bf, 0x526c721ab71e0707, 0x5e74823eff6b65ae, 0xd38623c9e41d234d, 0xd1c1b2137bc2e15b, 0x35e3d0d9ef34fce5, 0x8ad49de492f1849c, 0x62999422a19f3c08, 0xa42d4eb1819cc0ae, 0x274c34ec27caa7b7, 0xe8f21e9274bebf76, 0x7b34c874b320b8a6, 0x8425c978006553ec, 0x128dd9e0e67681a3, 0xcfaccd8705a7ccee, 0x553911c4f90d8b2e, 0xed8b046cc9805419, 0x726bdbb64814f3a5, 0x3d3382597f6fc0e1, 0x7eb32e49ccd280c7, 0x123d7ade0a516d5c, 0xf315d48637610275, 0x2e5578a58eb266f3, 0xf8987ebd88a5584c, 0xe302fdb63c147085, 0xfe3490f39d6060e5, 0x3880ae0b22f7cc51, 0x6655f8be98751101, 0x9f0af3e376b4a7b5, 0xcbfd501de2ce2a93, 0x7d4ebfba3f792f90, 0x46c1e9759c7176dc, 0xb9cf946d99bb80ed, 0x5f33a4299a944e25, 0x20a87d893fcccf52, 0x234af0145ac3a8da, 0xc364ddc09e359f28, 0xe1530254c4937b46, 0x28eff146eddd840b, 0x8149318b88797251, 0x8536e0bfd3a4dbc7, 0x65dcdf194240db27, 0x1f32e2f3fb5e847d, 0x929fc25661897d11, 0x3a91b28c95cf420a, 0x84a84732163268c7, 0xe01ba3d3ec288d9e, 0xc44325c7ca0d8ece, 0x3edaf422eaa86c75, 0xbcb089e1446a5487, 0x6ca810e59822bf7c, 0x74392427bd9e8948, 0xbb72be6637e723f6, 0xef3944f951b6d33f, 0x4b544cc4a266580e, 0xfa058a4cc999f1ab, 0xd488a6235f1c4085, 0x3180ec4620c60abc, 0x99560a1705ebf3dd, 0xc8ed9a572867ba24, 0x9645a758c7ba5899, 0x645c6bf0800ca106, 0x3181cf0ab92e1f8c, 0xcd1046ba2dc6c24f, 0xe2609817de3771a6, 0x99d7d94d1803d069, 0x9dcb3447f9bd9e55, 0x32b2fc502f513fae, 0xbb15107e843698e6, 0x8e89e30c01cc9b33, 0xb8b3f3344b26d1da, 0xa513a07927bbd1aa, 0xdd18af3783f99d86, 0xfac806c4f1dbb8c4, 0x1f3c424909275ece, 0x15da430764e6ee30, 0x42ed3b55e4632911, 0xe48034b27e797b67, 0xb06a16b9642228fa, 0x2de1738350b1c99a, 0x7123c382c4074914, 0x8e69a1ad8e661f79, 0x4039da225c31e1c7, 0xc39d48bd1d6c8c77, 0xffdae66bd21a2910, 0x8a43714dbad17875, 0x85f6af0e635a340e, 0xb78b9e7df137a667, 0x0525213a1517331b, 0x12995ffaf4052c09, 0x7a06448c6838663c, 0xdf5c0ec6d6be0d0f, 0xd14f48cc13c79692, 0x50fa0d5f07379ce6, 0x67f8d589b8303684, 0x29db7460ad6e6b27, 0xf33c651d2e297440, 0xa27b353fb82522f3, 0xfc6f3edaf0e55b5c, 0x58efa75d919ed888, 0xf45dce0815e39d8a, 0x4d84eafe3a9b7be5, 0x2dff78a4b4a54b5d, 0xb88bcba5540093f5, 0xd9d26ead9743b328, 0x86aa59cd4b719c4d, 0xa6d884059d516757, 0x8ef87c455a804895, 0xe9e478de0c5c03fd, 0x5b09ab6c081a2f54, 0x3d40a49e6b883dcc, 0x18fc871aa22b1f4c, 0x4970de355b969478, 0x79b6ebeba927ae08, 0x2c5975a59ec3fe95, 0x9f321ace264204c4, 0xc072850fafa556f8, 0x20eb4bb9d504ea6b, 0x2cb26a8d67b332ee, 0xd17fe579d0fe57c0, 0x834d794b4803f786, 0x13368d5e2951fee0, 0xb5a5403e047404c4, 0x7669dd1f6fb4c2b0, 0x35bddf4aef2f7f5d, 0xeff034d789473247, 0xe11340bc2596c2cd, 0x9902c93fb8ef63f5, 0xd8b17cf313f4e815, 0x8bcee52ce3b185fe, 0xefc190f51d02cd5f, 0xd185f3a812f33276, 0xc3979ea407a22542, 0x93e01728c797fdd7, 0xb105f008df36b821, 0xbd60e25925b6c8ef, 0x4e09ead00ea81315, 0x9d3770b63117765c, 0xc1391e54d1c7f301, 0x5ac6df4f3c9a5d54, 0x999d997fc50216b2, 0x66b98a68a0520990, 0xa3ba1862d8cd6f37, 0x586319ef809055de, 0xd9d4c852edce800b, 0xe4163f8a535c50c9, 0x84943b9f0b32c78f, 0x5b4a9467cbaa4d92, 0x2c55bc536c7dc3a9, 0xbe4549b4bf422d51, 0xa9101eb156ae5050, 0x66569c427401d64e, 0x1ec0edc80b63b2a0, 0xc701edef40ae4aca, 0x4a90bad9224655ff, 0x34972cb95e263fca, 0xf1cb90fdf64b68b7, 0x919711400c17f679, 0x06b0f7d9dc59e833, 0x6a51d84f00969d49, 0xbbe5ea97a494abb2, 0xa8ff16f51ab94545, 0xa9d3d2397162ae97, 0x08fb9ad3fc8d16b1, 0x3e46e53f98a081c5, 0x23bc7e91f40c66f5, 0xfd8a1ecd9e50a32d, 0x239cbed6ebde0d79, 0xd67874cfa0fbcd0d, 0x9722408f30ed6c9d, 0x42beacbbc19eeda5, 0xdfa4437b459dc67f, 0xcff6869f99d44396, 0xedaeee53acf50d53, 0x72c6be3c3dc0e879, 0xce315862fb2b7335), (0xe50ddbb67e550e7d, 0x6538923077c214a7, 0x2278e2ce29ea8a65, 0x56888384a5637423, 0xfdb056bdfd7c4e77, 0x093e09ac6a0952b4, 0x13f1ba76a5bbfb7f, 0x579b5076912f562a, 0x35c23e453060801d, 0x89237fcdfd96b2b0, 0xced1ed7cbc0e610b, 0xb81edac5f7144d8b, 0x0464c803ab1767ba, 0xfc8fb11843932809, 0x9e2b9fdfb99dbb3c, 0xf7edc0c7e651b95d, 0x491abb65feb39179, 0xa505458569adee61, 0x65f012a33274f11d, 0x9602da1c5c35c601, 0x656a1620440adea6, 0xb75556ff246f457c, 0x7bf2d7483c6ee0a7, 0xca141160b91a44ee, 0x500a00e15ec31574, 0x51b2045ac4331e49, 0x755caa1084fb2a35, 0x67b166c2cb34719a, 0xfb25e35ea5e5fa2f, 0x0e2e13fc7b819197, 0xd1d97ad6763f596a, 0x1b9aed15c659548d, 0x77b434f79a3a125f, 0x42328e4bf70fefb0, 0x579da2202b4a8b76, 0xf85bd2efe53f3207, 0x889963c9676f45b8, 0xd54e375ff001560b, 0xe6d5bb5b5be5ab9f, 0x8efcd682405b448c, 0x48ba944dc56c9517, 0x6a724c11d9bb6cde, 0x534dd62a9a8780f8, 0x35dbc136af58aa06, 0x77df82abd0e3f939, 0xd5a3b0f0f71956fc, 0x0b5bbabf12e6f7ff, 0x52472b1998c65124, 0x9a8858f678ac2c7e, 0x83235a21b4d59196, 0x624ac35f06177460, 0x9a59d904c6cd8eef, 0x006be640ce7e8130, 0xdba37cc1c045481a, 0x2a9603d3ec51b33b, 0x5f515df4a766ac21, 0x73d04e3e55d1236c, 0x4da5a317fc44f745, 0x434f95fc4c3b4856, 0xdccb9a84711521f1, 0x9fec6b85b812c233, 0x18c3905a934dd717, 0x522a5df673bdde66, 0xedb5f440b173501b, 0x3f567bc884e40b18, 0xcdd5decb6a20efa4, 0x57a32783adb0be65, 0xefd8d1d6cd921447, 0xbb8e74a4b776039a, 0xa80927ba7940fd75, 0x7383155e8c84ef27, 0x6b94593e576a0e9d, 0xd31a4fbce2ec322b, 0x33bb3801e3b9f1e0, 0x253b15e1fb3686e9, 0xdadd9c2011d2fbf7, 0x526ac430a871fd41, 0x505ff7cfb92164ab, 0x638d2fc60101b7e3, 0xe46611cc7bb9e270, 0xc018880311f86e44, 0xc212fbccedd5c6c2, 0xb457011e6f359fe1, 0x9140ed1e113dd5aa, 0xe6012908e5c5c529, 0xd7566d87872e2b49, 0xfe6a45d23bde1ef2, 0x7878e9e41da9ecd2, 0x11877c095c6913f9, 0xa53304358fb3440d, 0x10605aab83cc32a9, 0x62061f8493ba796a, 0xb4d7604c1b7df474, 0xa61fd1060bf6aead, 0xb321d1c69166392e, 0xad0539e63506c69a, 0xb5e02c3cbe953b50, 0x5d3d92870e9f253d, 0x5adc0a1afeb4b84c, 0x1d05a70ce6f54615, 0xbabc1377ef6082af, 0xc7510d57d21d3360, 0xbe954bf5b4f8d104, 0x78f6bf06b56c5e3d, 0x21059663d6ab86bf, 0x291f33818ca52559, 0x34079bc4b5572976, 0x34870b4697b8a590, 0xc4e77442bad8331d, 0x31bc4fd0faa45562, 0x694831eb3b1ba03c, 0x38e14c79b5b351af, 0x5032dcc005deecb9, 0x52847c0fcb4f40b0, 0xa33ddc4d88b1134f, 0xf91cae4c8212ac6e, 0x46018797220a2a11, 0x980b94e0488e0243, 0xba532ad0a633b7c1, 0x5f881da6f65e540f, 0x644afdd2a460e634, 0x517ed026b815d8eb, 0xe8c08cb2868e59f5, 0x3de744f5772ec38a, 0xd8a589b05a3f30eb, 0x65aa5f3dd2facd96, 0xc2f43381a0ee6044, 0x60150c46c348aeb5, 0x95b00248a6834a60, 0x82922cf19da216a5, 0xe4580fb2c8176939, 0xb3c6b5b1ed84e516, 0x597154d958679273, 0x73c793df53439f90, 0x01c57d4400a3b15b, 0x78813fe7d80d03ec, 0x30d897586affcb1a, 0x05a4fe7c063475b5, 0xe230932465eb0d92, 0x3bc99ebbd541d7ea, 0x23c581617035c1a6, 0xfe55f664cd5aec33, 0x4fa02270ab805006, 0x216b7f5ea82c2467, 0x71cd7a2c9c28a8f9, 0x312605eeb3f3b2f1, 0x2b3e1d1bc6ae7c0e, 0x164703b9187adc56, 0x424ced27ae0f3655, 0x3f43c43bb7e1ce47, 0x1f9a260b0c89fbdf, 0xa9c23970d155ad7a, 0xc9a1b2a193b0afce, 0x5d8e6548db0049c5, 0xb318132a7a62c6e3, 0x94e68aede3ad01dd, 0x3399ae5c71c06735, 0x93e4c328085e6bd6, 0x3d6a351a36a1884a, 0xbdb1e218765aee7a, 0x956019116e633d54, 0xb0a44016d3966f3e, 0xa8f1bbfde65281d0, 0xb798aeb9ab5963bb, 0x8d343fd4a2182436, 0x20e7443245deacf1, 0x88b9003e4fcface9, 0xedf063979b3c9838, 0x07b15d3997964dc7, 0x9b89ca73fb968821, 0x8e1fc130db624b13, 0x8da6a7526f33051b, 0xcf4eaf34cb6fbd8e, 0x8894dca0b2ffdafe, 0x75f420e6cda494b6, 0x3ff06a3dd13a80c6, 0x31ef52fa7ee9da1a, 0xdd7bb9ae42c3a21f, 0xc78d90cadb109e70, 0x412f6196b34abea6, 0x51d4728993ca3dc7, 0x24705b4b1cfb3897, 0x99e5b825ed32f460, 0x57d20822c3327c00, 0xe906992a45e4fc02, 0x3ae5bdd5c19bb975, 0xcf9727eb1d4ad21b, 0x52de427972a54709, 0xce4f3b05033e770f, 0xf699dfd35e1e9d42, 0x8c6860dd8719a6ba, 0x61e5f5ba9722b70f, 0x25f53315758051a8, 0x2ec3eb3bcbcc1531, 0xe5479a51eb32fa4f, 0x6fd3fa1f9115e436, 0x28368821ed272f49, 0xe44ae8f577cef695, 0xb438837184498ba7, 0x74cba444804186e4, 0xda4976d631a3dfc0, 0x2a8ee20bb66e48be, 0xfddd892b8db87c9b, 0x2f86d2b9c635739f, 0x7c15209816d5edf9, 0xe3838ae73fbd8577, 0x1641727f03f5875f, 0x1f81d296f21ee09b, 0xcf83a61e19c892ca, 0x95fe976410836f15, 0x2446161dab029ec8, 0x380519dd7b6be8a1, 0x1f164d5a8dfeffd5, 0x1ac4ee24164e7780, 0xdaed5dbafa7e171f, 0x9da75356b71ec5d2, 0xfaa3cf90aa8fc567, 0x485081ed8a8e1808, 0xa05eb49e11dfc51a, 0xecda446e5e9f49ad, 0xfb72e7cdf9fc1eeb, 0x81fedd7ad04b6523, 0xba35e20bfbaff9a2, 0x6f90b17db7160f26, 0x9e17f956c7fac402, 0x7fa23235e2c058b7, 0x876ba8bf25d48135, 0x920b72419abbf268, 0xdb39cf422c5ad6a9, 0x29eb8f82788b93eb, 0x7c096f392c75231d, 0xd8a9fbf69db1c918, 0x4fe7ef7a5cef24b8, 0xb0349d2e6eb27eff, 0x8cb1928fe8cb1800, 0x61ee944f48f6ff38, 0x3529e1dd2c5aec0d, 0xf432215eaf75c7bf, 0xb5c479d8a0934bb3, 0x0268e6861c1241dc, 0x963696dcb5732737, 0x880393513eaf59cc, 0x37346a8f0944ebb9, 0x89619a38e2d7c835, 0x4fcd75566401122f, 0x4586e0dbdb874612, 0x3d4958561abb2dbb, 0x824f76fb5397a95a, 0x37e95a8e05c4cc08, 0x79d01a7d624fc8c3, 0x49add57a52d657c8, 0x6ebec80fe02e4e70, 0x99ab4b4822fc68c3, 0x9ad2c1423adb6818, 0xe5d32082b5720c81, 0x5f83070377d68c5f), (0x0864c63a477c4fe7, 0x329dbc21f6a02e8f, 0xdcaec6e2f3f31c80, 0xe8f61cdef50a059d, 0xdadb7f00178325f9, 0xc63a0fdd5f84720a, 0x1902fd15ed18e1ac, 0x43759fd3f2186bd3, 0xcbdf1dbbc11dc459, 0x1f9bf867a3eb5e0e, 0x2e75bac7c3afbe5b, 0x30dd15b276f2926c, 0x2e204d2cf1dcbd44, 0x7e119407e3bd4a47, 0xcd33bad3afa7db9c, 0x2363ab0c6dcc094a, 0xe7ab0e22e06822e8, 0x0c9dc9e506bb14db, 0x75a352124bc7ca52, 0xf08dd0f54c2ac8e4, 0x4a5de0466d94c0b8, 0x4170b1e958d38d74, 0x64daa0eb42a11988, 0xffaa4d71fc9f1fb6, 0xb04951224f26cbdd, 0x50a4f6939333020f, 0xa2dbdf601f91afe7, 0xc02f3eef802f9fea, 0xd990c4e67607f722, 0x1f703c3ef675eb8b, 0xc84e10c18d3187c9, 0x114e79680ef08cd7, 0xa45a92b912437cad, 0x8f8da5444487be7c, 0xb4ab4a1c3885c9c2, 0x0955f6925e2fc795, 0xb2d098f7c5251c57, 0x2f853f915d8e161c, 0x8c46571fcbd68e71, 0x66823618e74555b5, 0xdce394e240a98fc1, 0x71a17cdd96801099, 0xc3ffa102820e029c, 0xeb5cddd16540b826, 0x77db92ba6513d94d, 0x8373320a21225b18, 0xa33e571c8e22df60, 0xacf9a8a08d4b22e9, 0x5340acba497e2c50, 0xfb4b473bd888d638, 0x5a5b1ac9f61755d2, 0x415f6130a248c3f3, 0xfd70079d464e8f9f, 0x113a5626ce3ea54f, 0x68affcd89877f74a, 0x233bcace630b67bf, 0x61fd237e50fde112, 0xa13b5b6ec1dc1f2c, 0x2c2052b4aedc20f6, 0xfb8051b9042ee107, 0xb644f332a90ed9a0, 0x78f9d7e70f05aeb5, 0x4a4f727acc547172, 0xe900093f384c4762, 0x84fe2812385099af, 0x380679c83b3b4eb3, 0x7d7bab21314ded5a, 0x819dd47cf4a9c375, 0xeca9b6c6a431aa3a, 0x4e31bba80bb3f14c, 0x2a45a8fb7f015c06, 0x1a1302edb18cba85, 0xa49b9341441a0a73, 0xead30b0e999cc05c, 0x2b50bb69603be131, 0xde6d6f6e2a4c73da, 0x6daf0479a03f00b6, 0xa6eac9326ab14916, 0xe94335ac5e6f5847, 0x7403de1ad163e40b, 0x8a4627f93a4e1607, 0x7efeba6d469bc490, 0x7d0f7b4ea8b946ea, 0x994b575867042448, 0x4f7ff0ff215cf099, 0x7ee977269c958278, 0xd480b6ddde9dad99, 0x2ba2cd31819599d9, 0x8814579e414de045, 0x5ec222338a9a189b, 0x4a28ff08754824a1, 0x781bfb38c1c8a1a2, 0xb37fb9977bf2afd3, 0x454174b9103d2ea6, 0x739ca459e0f9af01, 0x9ae9cc48c06f8746, 0x80ffcbcffb57440f, 0x73814eddb785e54a, 0xa2af3330a11da149, 0xc0fe0f581ba55b73, 0x274621158f331ef7, 0x781468eff65c5e85, 0xa88faaeb95f0a672, 0xc683a2deddafbffb, 0x4cde600274480261, 0x526fa9d0f6e073ff, 0x469239794eb1ecaa, 0x1d9d85362c91a5de, 0x7cc1c669b0fc3a02, 0xb40ef06869de5168, 0x3cd1d981290f5758, 0x9ba1876b89dbb616, 0xa20294b4694afc56, 0x2cbb897b23f4b619, 0x9be1c6d6615264e0, 0x0e0d8081635eea9e, 0xe20dbacc0251c240, 0x4e37ff04b93cdd5d, 0xec2b14de869b5c70, 0x5e2dfdb347ad600c, 0xbc2600be1fa6679c, 0xd3232d4e4338f1da, 0x3559c365910c61c0, 0xd9d662f7ac53a8d5, 0x0c2f2ace1e286e69, 0x8296b6cf710867c8, 0x262032dd65961468, 0x6edf9e0bd10f2aa7, 0x93c96083ce425867, 0x7b182b2250513938, 0x54d4813698a51f89, 0xe497c309b1c82cd0, 0x143e2ea940029634, 0x98e227ad5d1c46a8, 0xeca8135a316b81c2, 0xa127a15a7db82bcc, 0x9742a33611c2f53a, 0x5d529a3fdf1b23af, 0x17c414c2a7053255, 0x84d8f724c6b41f88, 0x72a063c64703bcae, 0x5f50bb1240d9dfd0, 0x6500a3a053f93807, 0x84a501df50d0df6f, 0x44cc72dbd212ce92, 0x4b9745977957269c, 0x602cf7f843ef7d29, 0xb988467c27f0ef14, 0x934ff4ddff15666e, 0x83cf8ebeb5211a4f, 0x607e912ca286695a, 0xecef2898e2fcaabf, 0x8bee9b3baf91afe9, 0xff06bd30ee927d0b, 0x9b06f72dddd941ba, 0xcea277b3e8de791e, 0xf3f621791b825b3f, 0xeeeab9911874f32f, 0x0cd53f087a17cd03, 0x4ec085b36f05a972, 0x1614a1e0bfa139cb, 0x03c07ca5fd299e78, 0xb1378ed006edbc8c, 0x2f10b3e443070a38, 0x242b58a5218120de, 0x02ca74c4a7727d5e, 0xdd06bf04d4e36105, 0xb836a1be48827e92, 0x815d64c9224377a2, 0xf5461ed3fe683571, 0xdb9a3e38dd163ac4, 0xf42cd57415c3b42b, 0xf72479c550704102, 0x1c49612a3c58f20f, 0xaf588165aef1e885, 0xb6532e858b71fd7c, 0x4f490583860c2242, 0x582a7b26aefbc94a, 0x337d5972a029c80a, 0x7eb384cd5e574351, 0x582412382ce780f9, 0x31379cf034a6b749, 0x0979ca7369c97af4, 0x98a0f540715f1c43, 0x6ebf025e3736afe3, 0x3e7171fb069e394f, 0xe89d05dc46e0121d, 0x3d061a1588c382da, 0x9db9cea5e1fbd830, 0x3f13771386818cf0, 0x5580fa6c261aab06, 0x0c251cad7716ce13, 0x8fe1558f053f9fb4, 0x55806d4140ff3fa4, 0xad641f13ba474ac0, 0xe556f7c6aeb3de71, 0x6007665fce1ad636, 0xf4274e0f6ec05099, 0x72a221ab588605fa, 0x249dd0a5494a3c5f, 0x3362a5c388e68781, 0xc24b681f6eb6fb0a, 0x6f038e67a70c263d, 0xf3616a85e8268413, 0x88f2c515ad4ae79f, 0x0d4e4b5aaa4e1e97, 0x0becf567921efc72, 0x5e036be6c40c4b12, 0xa943297a064c48ac, 0xf5dad19be84da2f1, 0x173a466c3b594156, 0x43be2364e3a56322, 0xd2f9f1c58a92a7bd, 0x53bb9a0036b591c3, 0xe7627ecb06338e3a, 0xa636baccde7ba9ef, 0xb66a54819dcc5a0e, 0x66caae6705479fa5, 0xb80c697a1f031160, 0x34d634cf2e46606c, 0xbe5b63a65f170f9e, 0xd97979befee8fa96, 0x21e7c1169acbba24, 0x8be940d32a05001e, 0x552e1e4e38f056b9, 0x3f8ddcf4c06ff237, 0xaf8da21f2698fe3d, 0xfcb7213fd99fe780, 0x5aab9692633ad9e3, 0x0200e3c8ce036f9c, 0x09c88aebb19e9af4, 0x4f5684a0827552ae, 0x7628ee96d38856c0, 0x7a733e762d94c106, 0x61e381c3e63fc7c3, 0xcb71ce510aae6ce8, 0x93a12291c424aa51, 0x3942bb48fa1e3d8c, 0x6c81bc449f82b79e, 0xf439c0b07d8a25ac, 0x1bc3411886ed840e, 0x7304ca5e74ac0af8, 0xe93897b4bce6714f, 0x21192811b616158e, 0xb91f4df015498070, 0x8b065d4cdaca17ce, 0x9b5111137173fbc2, 0x15deccab063f893d, 0xc770d05cd7eaa9bd, 0x2634de2fe0bb2c2f, 0x769648116cec0fcc, 0x736e532c7baef4d0, 0x82a5cd7cb52a6056, 0xd7963218dc0de396, 0x920ba6092c2834cc, 0xc3c2528b135c32c9), (0x158329b7ddc168f2, 0x9a29db39a1a73b44, 0xa62bac228045d323, 0x4a8183728ce4493c, 0x4109f5333c2e08a1, 0x4123dd36585dd530, 0x16ea82cd0400ec4c, 0x867a9d946b8f532d, 0x2c394a7475961b4c, 0x77d1bee5c2ae1bb9, 0x58a578148aa872b9, 0x96904e25fa9c4633, 0xfdf609d139134141, 0x4bf27836ea24fad1, 0x8091d789410f377d, 0x412004fc1c4c8b7d, 0xbca572cee5daef29, 0x10294fff9010d730, 0xd1212b64970ee17a, 0x0e8cb8eec0e219b6, 0xd499fad1b4d4af53, 0x4d9e55b1a6034ac3, 0xdf11c0fd3fe0165c, 0xef3a195c6de72065, 0xdb547cae86df668f, 0x52aef9d7d4eff1e8, 0xc09e4c9bbb90a07c, 0xc4f59832959f8825, 0x6670406a73c6e660, 0xa69a76aa556496d8, 0x93313c2f8fa36abf, 0xa4fe558da314a7ef, 0xeb6e47c204bc4809, 0x931bbb65b1c23712, 0xf8f8c72f9adf74a8, 0xdcc2f95b1276e9c2, 0x415fecb3cf9cb1be, 0xb35f9ec92445ae33, 0x32294ad822b81614, 0x234413c551f82a1f, 0x1c32074ef02e945a, 0xd1475057e8e07e0a, 0x59b01890de402019, 0x6c40430c78263f07, 0xc1603d826f2bf734, 0x0482d309f6b612e5, 0x808e6e8d5cb202ea, 0xb4f8d02eb84dbaf2, 0x67f1454e945a56db, 0xdcb94cc5e576a379, 0x60fb90008da10254, 0xe96c5c49aafd337c, 0xa080b5c7400e9742, 0x56d4afbd2941a4b2, 0x25ba1c5ef3c020f7, 0x534e932e7596fa20, 0xcf5513a852d9ea51, 0x609fdc4f1dceffe5, 0x358f653015c22c69, 0xf7c71a7f5962265c, 0xfe822beebfdeb865, 0xe8931e5ce698cef7, 0xdc05ac7d1e815385, 0x766020d776796c11, 0x928e66e4b821d91b, 0x918affb0ee42a5bc, 0x0fe07c0e90ad6a44, 0x1f618a6121f1c6f7, 0x8988b2c4dea2433f, 0xd342557fbf6e9179, 0x3c01f5378afc336d, 0xb14da623545f4f83, 0xb42e2a460e030dff, 0x2467aa4154be1607, 0x4e51899925d2573f, 0xf6622fc11111e2d5, 0x90be99ff8ccaaaf8, 0xd872b982ddfa1e4f, 0xbe1e243aa0fb37e7, 0xc866044b3d6b4fef, 0x04b5c59d097ac786, 0x17bbe6962ba64d79, 0x3145d00e7f3068eb, 0x450a003a16dfad6a, 0xa9f7b61ba01fe2df, 0x5b6e82ae590987cb, 0xb520f8c5f10e8c11, 0xbb3007af20fb90ba, 0x89e56655c53482a4, 0x4f51853a53c948a8, 0xbc8d5ca5e29835a3, 0x11a74e5cbb64d71b, 0xea1edc5d02512107, 0x49b67602b335b035, 0xdaed0ba9cea631ee, 0xc89ae5e6d3c7c31c, 0xe2b6ab11f96fe0d6, 0x42af0e2d06c72466, 0x14b0961809b52ca7, 0x20353337a881af6d, 0x33a2291e0343015f, 0x188c370ddb2ad070, 0xd446fb74a8052285, 0xffea8966553c7ff4, 0x99ff8687e155b344, 0x3ba9c74864e615c2, 0xc88bc9c338752e23, 0x8af03b613a15743a, 0x70d87f62c0e08510, 0xa0fdd22bfdf86478, 0x6d61357f1390950c, 0x2844b5ebfff29ba7, 0x182fc712ae14bf36, 0x8a6336afccc50eca, 0xc4eb6bacb81206f9, 0xd2ed7b2351d874b2, 0x4a8ec0a6ca354ea7, 0x3927723d3756c953, 0xfaf900606a0a4cb0, 0xe6da7cd49c0ffed7, 0xbd644ba20d67b844, 0x251bdcffa140ecf7, 0x77bcd76b4b73ed2f, 0xd5e94f0d9322ed2b, 0xe75a524381dd3393, 0xfa2c1299c3a727c3, 0xe3cf454d27ac9521, 0x48da42c508df27e5, 0xd1fea8b547ae7337, 0xa2496c5930f50d9d, 0x52db267274670a86, 0xa326c9d692d0c0b8, 0x88f50efc2f20aeaf, 0x543a22066b9f79c2, 0x1124ec03cb31dab1, 0x553bca154e6f9bce, 0x161b0c219cdc22a0, 0xab095b0ce8a54cad, 0xfb321696f8916fc4, 0x11dec6859839be5c, 0x8293023c249cdef8, 0x0733f17b90f456a8, 0x3afe5c7179007bee, 0xc62e06234f8f59bf, 0xb97314e5a1fd3703, 0xe4f9eefee802fe80, 0x17d466b931c05c6a, 0xa454870c4cfc43be, 0xf9675c128c8ac4d1, 0x8dd10ffdd641cc72, 0x46f5347d9206a632, 0x0243881f2805d572, 0x520409c5fda169f5, 0xbff79449c2d10661, 0xa2dde8ba98650a0b, 0xcf2127509d9136ed, 0xf6b111c646f0d2e9, 0x4c2c43add86998e9, 0xe9d1a6f43df2c08b, 0x5dc7b52066f2c155, 0xcf8b0cb9e008eb7c, 0x0dd8222e22c0f212, 0xfadf8615792037b7, 0x972caa8d2b8b49c2, 0x038bdd23ebb8dbab, 0x7fdd88f8eb2fdc93, 0x322805754197ab36, 0xd905f39876d99bda, 0xa49234e4374533fe, 0x5cd72aac1f65b549, 0x10a7fba50254ec9a, 0xbb9c2ee7afd35e47, 0x1c85e5177810e263, 0xe9b517bab717617b, 0xc9926302e8ee239b, 0xe130c636e3910d58, 0x0edfb38db790f325, 0xc6740f789866cb41, 0x347ec07d3fc45e94, 0x40c0a3ed943bf8b4, 0xe804b9c492a45236, 0x580fcf5f6d475e9b, 0xf573e59c8f1e3c31, 0x5e5e3502d63865ef, 0xdf00b10ed0376f17, 0x4e10b0be18a10753, 0x87128c42ffe84d8c, 0xbd6c0d0bd06ed70a, 0x718f7e7760eeaf34, 0x1ba3ff5327ecdab7, 0x0cabf22c29411b42, 0x9473cdc4598e4f8f, 0x3ddfb8602ace5b26, 0xe670abebb619a4e8, 0x0697536b7786de6d, 0xb544d14debc292ba, 0xbe2c8342d78528e6, 0xcf1dbd64adce6259, 0xcbc5c0d0f43ca6a7, 0x5218342b8b9092c7, 0x99cb63345f49f111, 0x499b094dc0ce8f77, 0xb3da92592b98e310, 0xe1d053736620f1e2, 0x9711030e03f9daa4, 0xe69fa65f716be04c, 0xb29444afc2bdd575, 0x57d5403d32b22f74, 0xd447c62dda27c651, 0xe5635cb8756351d9, 0x50c07efb9775a4bd, 0x2e96adf4e64ac761, 0xfb9599ac8d0c82ba, 0x54470836022ebb92, 0xb40ca24595af851f, 0x3b180b2623095b25, 0xdf477d223355df00, 0x9021c2b0cab7cd02, 0x56a85c0b89cd05d2, 0x6baa3ef6b3400e3e, 0xabc407f1ff246151, 0xf64d8d654a999958, 0xaeb79aea2fdef02a, 0x79f6bc2feafe4978, 0x1bb0d1b273f48005, 0x867a8da519dde6b4, 0xab07f45f2fe777b8, 0x914299fefeff52c0, 0xb97d1f3204431bc4, 0x688210fc33bdfda2, 0xdee44438d22d1bab, 0xe2b622a7183d6856, 0x2f4b4b70718ec67e, 0x5d0ad020a8a55648, 0xb2ef08f2444f473a, 0x6a3c0cd732cb529e, 0xc12d5fd10b932932, 0x78d420376eeda2a5, 0xfa8ec6b7e1e1db2b, 0x814c3ca59c2fa7a7, 0x09ef51dc1e3b79e3, 0xc0d19dc33b99c80e, 0x871211f57e39d2ff, 0x050088c170055ebf, 0x572caa212ea105ae, 0xd7d2121883a1d580, 0x9586ea76b944a292, 0xc13392358c498769, 0xdcad1a588111d441, 0x9ba1e6661afd8aa3, 0x0294ce211bd52e4e, 0xe8ee419a16a7bd03, 0xe733869d0229d45a, 0xffa0e393fef2deb5, 0x6e483f23fb94b4c5, 0x3a7768c6ae46403c), (0xa65850663cbd2d0b, 0x92ac42b2313bb4ec, 0xf04c9441a5feb192, 0xab16b01dfa1416db, 0x6201841824208267, 0xb0e7b3359a5e47c7, 0xc5ab90c82cd2f20c, 0xd5c03f031e44a4e4, 0x77284d0e657b7665, 0xe49de00423ac4153, 0x4f344778777229e3, 0x15321d353ca604d5, 0x2cc5b0bdb42ebb87, 0x47a7a9edd8a972d0, 0x8535d69828a7e772, 0x6b18c86d304d682b, 0xaf8089dd5101d5dc, 0xc0e130409752c1ac, 0xc49f5c1297c22574, 0x5a036cf302e32765, 0xa2672380c789970b, 0x0e31dcb103a71340, 0xdf087c357ccf4730, 0x3db6868ef6e5d62f, 0x3db44a689b742817, 0x6136842d40dd069f, 0x93cff4006f6b60af, 0x9cc58428da61f94d, 0x5969c872a07d015c, 0xaa962a93edd682c6, 0xaf3918eeca49daf3, 0xd3b89780c2a39ac9, 0x6ab5567fb5bdafcd, 0x6e12380dc1c9d53d, 0xe22f782061fa511b, 0xec430b5c5ed61d79, 0x6a1b3e15b3a0acba, 0x3abb2baa5a0f5af0, 0x107a5c9a12837ca6, 0xf3f650e23e7c1e87, 0x26b716b7809c7cc0, 0x682a519aebbf45a4, 0x74cddf1c4e6292ae, 0xc429b29e9dfa45d3, 0xe9cc57b5d4825d1d, 0x67e9b7a7db675a8b, 0x79d415cb519bc290, 0x100f0d170087b9e5, 0xd124fb3572f0a481, 0xcfbd2b55038c5d17, 0xf5d2304b99e65e5c, 0x99db5062f9ae6b5d, 0x4898bb88c1f4e1e3, 0xc6de8e9706d41596, 0xd6fc5f8d95695d56, 0xb657021f3d3b2f32, 0x7103da4f095cfac5, 0xcbe66dfeefb7438b, 0x6313646095623092, 0xd46a351d79b25d0a, 0x89f2d9aa50d675f4, 0x5aadaebf6f2589ad, 0x9e74854c69e8dcc6, 0xa30038bf41f58a1f, 0xedf97bd9a1fdebcd, 0x3e643189c2215141, 0xa118dec949e0fd35, 0xf60399f71ec3c651, 0x1dd9ff3a8ef9fdc0, 0x2ddc8a9a3d7ea814, 0x5ed6345a343a9249, 0x1ec3592ba55b6247, 0x06cb412f61c878b7, 0x3a670934a5955d94, 0xf31156e2729a7e3c, 0x5ba200451899ba6c, 0xea3f8d99b5ab8ac6, 0x66521f699b979f95, 0x60b2776b9970b934, 0x445dba57bedb6316, 0x1cbe4da4c85b4f4c, 0xed65fdfdc6c5b1d2, 0x4a35df2299669798, 0x341250fbb1653330, 0xc197c94473f1d7f6, 0x3d332e477d432064, 0xe1eb258f1364268c, 0x278de37acbd0ccc5, 0x02ab2430e7c20f51, 0xcab8e22a49b61767, 0x18a8c15cdea3517a, 0x98bffa2b398689fc, 0x112294ae22d8b80f, 0xc6c10aa7d2b654fa, 0x098368cd28920936, 0x707c145dc7c4ab78, 0x06482aa618370c72, 0x4196ad65eeedd4fb, 0xe08467f31bf94308, 0x9af09876f3eacb5a, 0x43321974434d13c7, 0xcc342c29bc5fc175, 0x4cda0dab066f9ec8, 0x01b50012dc9d3898, 0x38a5caafe8f5609e, 0x74419758f30ec920, 0x7869d23ffd77f2b1, 0x041d40b0ebcfe66c, 0xeda4ae6dc5c72460, 0xac68d5d3dedaf197, 0x5b5b18ca1b65c988, 0x289a5d3d91f387b0, 0x6e26a275fab345c9, 0x85fd30bce408edd4, 0x1d37d21e09dceda5, 0x2c9f04cd95650856, 0x351f4af5ac810729, 0xc574b1e639e126f5, 0x193ed68dd638d4d9, 0x921803aac14d8df0, 0x9586c03c2cd92d04, 0xe800608b5beb6f5e, 0x8c6111caed704c11, 0x739def0541ed2e61, 0x20adecbc4579d4c3, 0xcf40eb63dc0b5156, 0x7b14281fdf5e3f43, 0xdd88822b49c4a734, 0xb639a20071fc2222, 0x81e4f611b16f10b3, 0x3cbfd55b9d8be9ab, 0x1d244a4f12434bef, 0x4e04c135ecee937f, 0x27031d4f9e9d3800, 0xfa0a2744693da7d2, 0x083da176071ac3e7, 0x2ff5e5e23b63553e, 0x8fefc158137fe2ac, 0x5d6989cd7d656822, 0xda50ee9d7c4d5a02, 0x9b095f13d27a8b37, 0x33b5464eb98d2ce1, 0x131b8daccec1640f, 0x57c741da838d7b3f, 0x18dab6477fbfc635, 0x362fcdd0d3625fc1, 0x59df8c8d84ef1e29, 0xc80d9e81b455fca7, 0x44503d1f84fbad24, 0x2a90bdc621a88a58, 0x7fd90d9d9366b383, 0x241414856e7f63b3, 0x6ccdce14c1e76850, 0x8bee351614a3797d, 0x35cda9d9fc7036d0, 0x32489e588070d276, 0xdbcf94d24cc10f3e, 0x89cac832aecc5f6f, 0xf766f6d6519c1240, 0x10062a406d23170e, 0xfffa75bbd0788bd7, 0x9b4b9ffb2933e948, 0x72ad6fa5fd02ce4d, 0xb595ece7b985050f, 0x60916663a6c9dd6e, 0xf2825ab3907baf6a, 0xfebfe22e89febc02, 0x7f4bdba450ab2ecb, 0x82d2991639c6679a, 0x2bdd540651c6c950, 0x25f432e34d876344, 0x36ad28fe7afa28f7, 0x5906e88520a4a20a, 0xf37af17541bff5c9, 0xe340bba410e5c568, 0x3dcb19623d8490bc, 0xc51d5ab1ceb083b3, 0x09e5282ca95510a2, 0xca493d9ecf923752, 0x768bb84df7b5c82b, 0xfc1a1c4c78e0f81c, 0xfe3b01ea18ecf888, 0x472c346d4ba8d811, 0x32b627b2e71c3fa4, 0xe14f434856e1a5b2, 0xcfdf5ebec350bfb0, 0xa7337198ecd82767, 0xe166885b17502336, 0x0e1f8cc5073bc9ae, 0x9ad12a3eb3f8e9b0, 0x3aad6eb7330fad2f, 0x31f7e5907bbb5f97, 0xf7480644a17f9fe0, 0xb9ca40a188d9b349, 0x46c1a5f4c5252cf7, 0x5df18ca5b4eeabd4, 0x72585a1b7dc3ba28, 0x2acaa3125cad7ebe, 0x086dfceb84ec2e75, 0xce9ebd22f0a411d9, 0x858ff8e5c3a1cb42, 0xa7c30b8a4638d63a, 0x0f43a6e98205a164, 0x5a196153601da126, 0x3cfe5eb94522fda1, 0x773858cc9fe6c554, 0x75faa585b7ef1056, 0x1ded6ef3755ba188, 0x328501f39f2f5fe7, 0xafd7914fd3adc4a7, 0x50c0545caf6f3504, 0x0290a30aab47a0cc, 0x0b8b774c0ffcb772, 0x4b9068f513406a8a, 0xe7f2bddb2c58f0d0, 0xdfed6aaa0f89e29f, 0x03fde8b9ed724784, 0xd834718d545cdde7, 0x445d2b319e34cab2, 0xa8fa84085fcb8bcb, 0xb1fe8a738084f3ed, 0xbe64f25ab4253604, 0x527ad2479eada413, 0x050d1e6a091c9012, 0xf4937c5bed7c16a4, 0x830b711032508fe3, 0xdf3578fb52d77639, 0x1abc82f642879291, 0x2e7a237c855d044e, 0xbb3b71ccf421fbdc, 0xacf7d6200e9c4ec6, 0x8b5abf534882e53c, 0xa9cfac9c85f3f2e8, 0x8b62f803c3c22edf, 0x25adb123b1d27bef, 0xe5cc3f5744a2ce0a, 0xc5500fe753e3061a, 0x9a8e4832bc785d58, 0xdcaa2d3f03ff49cc, 0xc31e90a4f02c494f, 0x478d1bdb4de0f0ce, 0x9d8881f32c878d69, 0x34157e74e7de3fcc, 0x87986971b1faf5dc, 0xf9f6a2b87a202751, 0x509748b0da4b2314, 0x00bf78fb242e59e2, 0xf1a0eab991c78bae, 0x136e098667381770, 0xa883cefb0d9a722a, 0x510ba99a1c91be41, 0x961232669cb24524, 0x7bad2866e952a507, 0xafdb048d2557dd80, 0xe43afc0a775267c9, 0xde97438dabcb38c8), (0x0d9e7046371c5cb2, 0xa0908b504c0a40c6, 0x34e191f0efa205c6, 0x8a73e4e6c52e0e0a, 0x01f8773842356e0f, 0xdc7c3e507adff346, 0x5d3e5ca6e5b52709, 0x8395e405d46b098b, 0xd85f970fbc54e02d, 0xd4964113aa7cd2b4, 0x06a66165cb5053f5, 0x92ede07f24f149a8, 0x1eaa664ab6cee154, 0xcc88f7da36a5cab4, 0x23159179e7b584c4, 0x3edec9387626d7b7, 0xb5bb7159c869dd39, 0x0b17038b24deb415, 0x5050ed67d1f51e51, 0x849267aa2135b33d, 0xbfec86e553048c0a, 0xc85f3ad3fd438124, 0xeae313f8564bd266, 0x275395334dce49bd, 0xafcd51322b5047e6, 0x3c7e811ab2e77a42, 0xb989b7aa908cae8e, 0x718822cf66c45730, 0xaa475fdf35398f50, 0xe1cdd1e15e4c14f8, 0x52bc140835fdc83e, 0xb65b61a10ff4b96c, 0xf59f2f5db911c4c3, 0x8c16bd346fdfd8a6, 0xf8f495a5f47b308c, 0x6ee545ea5b94f454, 0xbc72173222523eac, 0xc8a99ab0e40e9632, 0x7ffb6e837e94f850, 0x4906eaae576d9643, 0x4a39d3a6d981d699, 0x47f0d97f1c41cbe1, 0x6c0893fbcff46836, 0x2659e9cfb396d94b, 0xea6c4cbd1747f055, 0x1d7d4414f0149cc9, 0x7ac1a75054f90464, 0xf1806ad0e73e761c, 0x07957e253cb0d845, 0x01992ccc663e7574, 0xa3b7e26c765e2ae3, 0x35dc01301b8510bb, 0x3fd0d84cc3d95cb0, 0x2c18f4b964ed1c5b, 0x250cee825443dd97, 0xa8760d95c9e76ff8, 0x64112191e7f15482, 0x98a7856245608feb, 0xb0537fb054a3cbc2, 0x7d672cf7bc554412, 0x0a3819988e07aa1d, 0x3d53365bfa2e8e8e, 0x8797e73406bf7a78, 0x7015c06329cbc63c, 0xc9169d2c1b7f7cdf, 0xc1fb1239d029109d, 0x37b55142a5af178f, 0xac5d9d8132d6c720, 0x376299b1ecd7ae2d, 0xb0994ea307385db1, 0x8d26ea7cc4b5bdfd, 0x86dc68c380044767, 0x9ee0c08a56e73e99, 0x32ef8e18bc008c66, 0x922f378383b5de11, 0x7140806b147029ae, 0x9d117da80412262b, 0xc5fe632679803cd7, 0xd483535b1df302d3, 0x344b6ec6f9c84f5f, 0x4a13b17040000a4b, 0x88009b8b080be6d8, 0xfbf84751f1af5edf, 0xf4b079da38f6cc19, 0x78a85e00505ba48e, 0xbf5a790acb8d07a3, 0x09facc77e0a617cb, 0x2208e33df106504b, 0x28c6b4941135ec96, 0x0ae53e432557ffd5, 0x27f4e5189563f470, 0x97ffa2c7854cc06b, 0x807dcc092239da77, 0x118fab2d88e7b6ad, 0x8a7f118ee3c0377e, 0x6190043d18be7f27, 0x035a90c364df04a1, 0xbf172ed4c82825dc, 0x7dc4317d210fd8cf, 0xa3fcbb8924c3d544, 0x100ecdf2f7c137bc, 0x91c6abfe23b70d4c, 0x5f2b4c3c72f97ab7, 0x6aec488f5aef6fdf, 0xa9dab2606c727990, 0xb42e27dd9b30899a, 0x925f54bc1188567a, 0x6717be210d2f121f, 0xd183b8009c66bfd7, 0x8f879334c9c508fe, 0x46c7a6e9d15d0971, 0x47b83c3a445ccc39, 0x70ba48e45caa2646, 0x076b26e3d197532c, 0x97a431e1e8182b57, 0x8d0142223ff4fea2, 0x2347dbc8005afbec, 0x6f2e8965bdbac6d2, 0xf0134f2468adc15d, 0x2f04ea064e489973, 0x5e471ed748c65ea9, 0x24d6d85218fe1ced, 0x55c185130c1baaba, 0xc0ae37927b06c7b2, 0xbf94d703a0a15ca7, 0x44a58aa07efdf8e0, 0xf29750435d67391c, 0x8ffa287cbbcfa7a1, 0xa2cd97f20b2695b7, 0xf83ecc1e2f0d4bed, 0x053cd71d01e25d22, 0x1f87e05df664514f, 0xfc3bcac18ddde865, 0x70da5ba2021d2d9d, 0xda053919fe0606b9, 0x742b736aef4f095d, 0xf5cad7fbe8ba862a, 0x0a09064bb875407e, 0xedcbbd423a31059f, 0x880dd37d36ee0bae, 0x39ab62111295cc63, 0xd450af148a12bb9b, 0x2b0b90eac45ce87c, 0x965ace6f4e9e5123, 0x526669f89efdd07a, 0x507e04a02bc7489c, 0x9d488a3c0ca801d4, 0xfe90223ba34c50fb, 0xd4422a8cd506c9d9, 0xb5ebb40d2123734d, 0xd5d55b097060b362, 0x491fd251865bd6be, 0xc828f72c2c9217ec, 0x8ba6673b60dc0b06, 0xe9e13ad568652524, 0xdb9765515e87ef2e, 0xff285a1fdfd79503, 0x97aef67290ed6fbd, 0x43643a8132488225, 0xcec574e3cf14d171, 0x06e958827a1b4efb, 0xda9523e416483d12, 0xd6c63a96ccb06a42, 0x9f5f84cdc7a5a00f, 0x06253705a78fc8fe, 0x2666a69b2e473729, 0x1e010e599be77e7f, 0x09d5dc4fed5d3df4, 0x31f318800b4c9c5c, 0x04775ba46b01ca07, 0x4d445630aaa236d5, 0x398c81be08bc53e3, 0xd6220673878aaa26, 0x17d2ad01fc6b69a4, 0xd378e4ea6ebba24d, 0x6830c5fdc48d4458, 0xaa1c1fc69c6fea73, 0x6368f377f248509f, 0x492c55f14d1a6c72, 0x02eb8f697c97cf4b, 0xdd164d1e9b91997f, 0x162f96234cb17dc0, 0x270978cb10774e24, 0x6e9e70a8bd3601df, 0xa1cb5736f41a8977, 0x37c7f5d659603112, 0xf72885750f419d53, 0x5b371956f853c4ad, 0x0cd782a67bc8181a, 0xac621ab73b58e4bd, 0x9fc5221bf92b83f2, 0xaa687041abe8dba7, 0x8390425b5426a0be, 0x7b83101a9824fb79, 0xbe5f00e906863cc0, 0x54891371dd1c9ddc, 0x69273ca3cd89864f, 0x15fe07a12d04432d, 0x169a4627df882e70, 0x4323eaa8fc936ba2, 0x733389bade07006c, 0xc42b861c99dfbb8e, 0x3f7b42372e04d488, 0x51945398d0c26cf2, 0x463fe79710bb1fde, 0x731e6cca3cd89174, 0x266cf53cde804136, 0xa2719720400226b8, 0x7b728ad16d7dea89, 0x7e82326de6ea4a8c, 0x058e178994224d7e, 0x6261d6e99a39bef9, 0x06953d9f318e7e2b, 0xd505fb992f1ea217, 0x964901292d736edf, 0xd4945e4ddca616e4, 0x59365bbd4ca9ceae, 0x2730e41e91ee8189, 0xbadabe39130f05e6, 0xd3e0895b59d3e355, 0x12452310d406298d, 0x468e58c63bd06d6d, 0xd7c99ecd75b230ce, 0x618304cba3443104, 0xd921afba32628b33, 0x99eaab39394ac3b9, 0xb4fad51f4ff0e792, 0x32d3684d62fea5d6, 0xb841b59154d1e65a, 0x570ffc8c05d7054d, 0x71ce15736202b959, 0x5b9fd05a1a72187a, 0x2566994a48d5b46d, 0x42967c0623d164df, 0x681a461c371e860a, 0xf36282e23e1c6568, 0x25d1c4e21dfa607d, 0xf3e605d27a04aecc, 0xc4ec6ee848aa636e, 0x1ef2bb0555759bc4, 0x3fda4e63db9782e7, 0x3bfcedb42401e817, 0xdbd5a53e205c7655, 0xfa32288e7fb5404b, 0xb4f67558f0102c12, 0x7dea02041aeabe16, 0x2cecbf5a725f9812, 0x3d92793e7cd2ed57, 0x2e5739dfcc33246f, 0x050ffef08cc6e405, 0x6d064066f43969c2, 0x9385fcc7ba584ee7, 0xebcbb6430a98676e, 0xe3907164d71dd20a, 0xec985e6e39086368, 0x213896661e50792b), (0x725ffe2cc58e8106, 0x6f880b22cbfab97a, 0x993575e4c2531f43, 0x6f5998b9b27eaf8b, 0x66e9037121263202, 0x43ce9c552168b3b2, 0x7d5844ac378bf4a1, 0xa110181dc762c7e6, 0x9c80db42ae701df8, 0xe8bf6002fa27b1d8, 0x56c8129166e1a98d, 0x869b30861391743d, 0xd6effeb87b316207, 0x541400e2a3534a55, 0x51337f56d2df5472, 0xc974cdc0e23747b8, 0xa541f6714edef161, 0x343df3ef066b5339, 0x92f913586d91d231, 0x2cc2e866b8560ea8, 0xd98183b6fd4d3bd9, 0x527a27c0c99bdf06, 0xb440add62c07b41a, 0xcee7b93ccd37afc0, 0xa81e54f79577dd4a, 0xf8e00c3fff3f0137, 0x2a4ee347bd0f4ebd, 0x3e2922e65c7a790d, 0xebe7395b88c022f7, 0x3b9ba79d5f233109, 0x706491fd330e7c65, 0x925739223178936e, 0xed489ccac4ba3966, 0xd0c70cc3d7430fad, 0x7b2c81983afbcb90, 0x381014917b0125d6, 0x07261a645f8f3a80, 0x956c878079a7b4e3, 0x5f6ecebbace86267, 0x2708e4fe4dc1050e, 0x4d6aa0b716d73008, 0xc20eca6bdc95ea9d, 0xf75c5a3f67b8ef23, 0xe09041510fa3e0ab, 0x2789b829efe74ac9, 0x54dbcded40a5ea3c, 0x5ad8e5b13607f791, 0x5af02564a4a9953e, 0x3afc483c42001eed, 0x3d7731b527bce3ae, 0xaaa0955e6239ec92, 0xbc5be05823b59dbc, 0x5932e5f6aed68d8d, 0x8f54198fb5590459, 0x20045e57bfc2b49a, 0x8239f25a5b86037a, 0x8fbf1f003abfa819, 0x47fb1b24b84498fa, 0x09a7dcec148c56c5, 0x33e3fc7beaf03b8a, 0x01627bd917afe9db, 0x9d86a7150115e57a, 0x52b1a8e4179588e1, 0x3f47db1ece86c737, 0x2b1f9bd9f964faa9, 0x2eeccd787f6b4103, 0x8ef76c8cb01eda53, 0xe7329b4ad51277bd, 0x81c79b07ff3300d0, 0xfbc76908fb2f481c, 0x8196b60141936b3e, 0x0e9e71660e163f63, 0x1de7e36b259a18b6, 0x56d4be28214d104f, 0x590ce8786214e050, 0xb58daf0ca94b4134, 0x615d3c93c6ab3174, 0x48a75bbd76e6beeb, 0xc3f9e1ff3f0cbeff, 0x877191bd748e9e30, 0xfdb05717569710c2, 0x1bc4156433ff908a, 0x12c4aabcfeb142d3, 0x7e09388785e8d72d, 0xe4cfdc5751ed2a5f, 0x77c84b32aa8f8b29, 0x23b7bb777bcca77b, 0xffafba8685cf0332, 0x356f5db76a358126, 0x4323ecec5a190705, 0x3dabdafed4b51c48, 0x690bf7bea38bef0b, 0x7c6c7f00fe954a53, 0xd7252ffd1e3f6c61, 0x7124cf32d60c012a, 0x8fbb751e186a909f, 0x561e1cf31349a148, 0x4776b09d36795fac, 0x804703bc2293bc2f, 0xed55ca996d97aca3, 0x9f8b75c4d2b3a2cc, 0x0c94228b565bac27, 0x12c5621b5f9ae497, 0x4720e252c52faeac, 0x2a1f475cc91c198b, 0x4d036790fbd3287a, 0x16f5e48082852918, 0x6e9e7ac3bf9c88ca, 0x6f5e9dca5c77a486, 0xfe16ebcc4e7a7028, 0x238e4578039eaf26, 0x646c6d8350284a7b, 0xc5c32f72d3e169d2, 0x0a91aadee749032d, 0x905b33825cc4dc5b, 0x73bd4475950ca52e, 0x9cbc38d158bc2e30, 0x2f1e8560fb258ac8, 0x9c2d317134e9dabb, 0x4cec4a0e97b8633d, 0x6ea7ccddafa9a8b6, 0x46a4914faf4f172a, 0x433587275fa740e6, 0x08831adb2be55c05, 0xd6dc72e7c99edcdb, 0xd81f00b9f7a7e04c, 0xab81f69a20eaf7af, 0x0ba2a1de8e139a8b, 0x4c4553382a717a5b, 0xcbfda0715441ae7d, 0x52907020c01b74cd, 0x62a113863db06ecd, 0x139745508d7bb9bd, 0x200b50758e0789e3, 0x772cdd614c8f354e, 0x6398929e895a5fd8, 0xebe7724ba9343ed8, 0x332fdc1f91d4f9e7, 0xab6c82738adbce9c, 0x5b0066c09c9a60cd, 0x470b6ecd2e0a4e04, 0x677f4cb52f08aa72, 0x733be4e49ccf478d, 0x351b24b2f4d84344, 0x4bedd0be76d15312, 0xf8ac622d91a86377, 0xea60e19dc478a643, 0xce1b3c82270a0da9, 0x225294923c8d1de5, 0x0b69886750fe3d7e, 0xe6a9f3b449b4c8f2, 0x3ddc4c933d66cd57, 0x655dd68fe480320e, 0xef43295021e4453a, 0x3852a0a4117e5029, 0xe8261998930180fe, 0x7f9a55acc776f07c, 0x1eea948da5f71066, 0xad33a2e936125f86, 0x42bc77c6e7408308, 0xccb22238fb7be50b, 0xe60a500635032ccf, 0xc5a904679bdb5b65, 0x46434239436884c6, 0xe325768674ed5506, 0x113e74d5580749e0, 0xf87a5d6dd5b06897, 0x7f26de90784e0c8b, 0x593127ba13241a1b, 0xb09d483e8d1c7e18, 0x2d3a15781574e77b, 0x68ac6f5eb7658d21, 0xd62428206df84bbd, 0xe2ffb5f819b72262, 0x4ef63a52f6f6a585, 0x523dbfaafcebcf7f, 0xb4d50af8ac913d1d, 0xa80c5b2bbf588788, 0xf03843efbcbc7ab2, 0xe389589d99fc8f7c, 0x54894f427202064b, 0xa3188f13cb7783fc, 0xfa81ca5315b502d9, 0x77b48264bcae0597, 0x2b62e5a962f06c1a, 0x6cafebac0ac19afb, 0x1552e5e87923a58c, 0x0aff35cf413ed7b3, 0x0f7b6954262f283e, 0x850a6b1ff0597356, 0xdafae61427dab01e, 0x6d99010e2383667c, 0x64acdb9bd9a41777, 0xe134513d13de9bab, 0x62e702b80775fedc, 0xacf427cdc7a0bb6e, 0x0255b582d91c3087, 0x79851e814befc30d, 0x1b90bee867f95e2e, 0xcec8651ffc750518, 0xcda664f9b34b0d39, 0x1554a3eb766973d2, 0xcbb8e342f7574332, 0x9eb116b5a4553522, 0x418ed189069985f3, 0x4556e2d098ff5d69, 0x3ffcf4128fd0f62f, 0xc521a024a3d49912, 0xcf2c5788c9d36887, 0x65704c58cfa20e80, 0x33aefc936114d352, 0x19478ea2689c47a8, 0xa022bf86d1216aa6, 0xb634b85c8dce42f8, 0x5b95973a394a7030, 0x69c5601e852aabe0, 0xd0089b31191e4c03, 0xc34b895ed3f68c3c, 0x57c17b2fe89dd759, 0x3f18d2c8348e63c5, 0xe21bb9fcab1805a3, 0x7a549f2b5e910e3d, 0x931bea5d5c684d94, 0x8b11d6c5ccae457a, 0x3045c6b62660dd7f, 0xb77f50e5fb6d9768, 0xd1b71462f4b7ca24, 0x51292fedd704e58a, 0xa733c3b4127709b6, 0x76dea7810d72ff70, 0x09b2e1654233b15c, 0x997b27b6b262a38e, 0xa3661097a71e2b4f, 0x2d65cd8b8081d1a9, 0x64e1ab39e699c549, 0xa44e2262e644f818, 0x56de50b8a57c77ad, 0xf2073c48a4d84c11, 0x314ba65cb199bb6d, 0xff89d0c92af4ebe8, 0x6be1f4821a505e9f, 0xc39ea0aab848b48f, 0xadd48b5ca93e5286, 0xbb9d9dcdf95d580d, 0x3339c1bd1554ddd0, 0x2eb0e43e9e4d0a6b, 0xe2e6b6b9c3e3c3c6, 0x01b9742fe9259023, 0x83f86dce5e527502, 0x5fe08901ea3511e7, 0x634bd1825d9ac552, 0x6969be2c4e944b48, 0x1d085e469d4e659f, 0xcc269be706b70cb2, 0x166d27179f3a95f0, 0xd04ee650820d2d57), (0x1ecb6bd0db2dc6b9, 0x35312b3ef14d8dae, 0xac7bf539455b9511, 0x606335e93a7cb84b, 0xeaead6b2639fbdd5, 0x145c070d8a385c56, 0x6f746abad2043bb6, 0x774279469aa61ad9, 0x3116969e351297f3, 0xea5fc0363cd13c97, 0xa7ebcb2d6a5efa91, 0x720972171091debb, 0x6684c079067ad878, 0x01e033add02f1081, 0x0d20a9f6aeb393ca, 0xfea92a7e573636c1, 0x4f90e645555a600e, 0xd4f27c5b181b1f44, 0x4729b1909f7c13e3, 0x149b8d38ba60072b, 0x46d40ec6af11bc5d, 0xe04d0698c5fc4e84, 0xfb65a17105ef5d53, 0xbf480e073dafe19f, 0xc98a8950ce31e61f, 0x67d64a4a80ccfc45, 0x9a5e88d632f29fa9, 0xf350ebebb79f1178, 0xd9c89a2a60d0b66b, 0xc86435beeae31dee, 0xa4fc4fcd15d63bc3, 0xba9d3b196739a9da, 0xee34d03beaf0d0c8, 0x08cb6660e58cc402, 0x8541350b42d40e34, 0x802e97a9859431b9, 0xc978d5965ee32cb8, 0x0facfdf8a397688b, 0x5a5c11324c351bf3, 0xe932fc8453d4564d, 0x74f302463deca67e, 0x1187df661dbf40a4, 0x7f40ae35af2eaec3, 0xef8ce73ca44cbf15, 0x893bfcdf980b8569, 0x27ca0499b0663ac5, 0x8ba9279447473ca4, 0x608787a7e005dbbe, 0xe43745fe25c890e9, 0x5eef2881e37b5dfa, 0x6447a21d68d61490, 0x86f2db0a3d5bab61, 0x7eee64a5525ae136, 0x6255b3d40dba17b4, 0x083aac88161486c8, 0xf77fe0274d28aeff, 0xe7913275c4ee724d, 0x017437b28d495b55, 0x99628b134b8a96d9, 0x364dd772f7f1fed5, 0x0565a4b0585e5088, 0x9b951ea1fe794bee, 0x882af59d2c5039df, 0x998b89655e97d48f, 0x92ef581a4876573a, 0xb8169f43a9c59e97, 0x3d9c9b345cc96b5c, 0xf334aa73b827315e, 0xe02e835c2b1b361b, 0xbedb71d2c9000f81, 0x53d70e503308eb78, 0x41e6406f1c0e7b9a, 0xf022687309afb711, 0xd0172714529fb085, 0x63518020c4273a87, 0x97c2021567af223d, 0x153fbb14257a8a05, 0xe26fb6f0ed1208d8, 0x9fa3a569076c2350, 0x2c7a0a38514bb7e1, 0x55b17b21e64376c8, 0xaf3127af530748b4, 0xdb9778b61e9b38f5, 0x4c8a7f4064a4b0f2, 0x1a8f5f9182c62d31, 0x131bfa6bc1d36e10, 0x165d04333320df9e, 0x0bcd2b28cc74cd1f, 0x34dc620de1054b56, 0xc3c0c5e3512566b3, 0x764340e996dc7df2, 0xba4e5a81d9e81325, 0x6f1b55b3513df3b6, 0x184176721300614f, 0x2923fa379bfef061, 0xdd37d7de4bc8e2a3, 0xa0697ffe5b76b0df, 0xda931af9954f1dab, 0x807e42f6835e757b, 0xdf1645155f424d50, 0x1ba7c2cc1adfe7ee, 0x941705d14db037d3, 0xdc08998a458ed148, 0x61d87476e04ab218, 0xb01654377beb82ee, 0x008bcdbb48cef32e, 0xf7903cb4a481d00b, 0x628ce2f5141d538b, 0xe021a9d4c2d51f9c, 0x3371b6610d72b82f, 0xe3f5cb27cd34b256, 0x029dc5696b213822, 0xef731a3c82318a20, 0x5385d813bff15749, 0xcf7f5b790ca11847, 0xc393368cebc71184, 0x6836e47a290b58fb, 0x32a8743b039c27ba, 0xe53cfec86d8c495c, 0xccc43a0e55e118ed, 0xfbf6c937f7c95e6f, 0x16367ce822619b59, 0x7c114149b1ca3210, 0x79ca5a079086c320, 0xcf68cadbeb5cf9e9, 0x860ffc9e1b0b7d4f, 0x481134a841aa272b, 0x983faddb191e4e29, 0xfbc1bdfb8b540b7a, 0x6f36004dad65f724, 0xf1e2fd6dcf3fc753, 0x32af6ae0226257c0, 0x044cf7e53bd7d4e0, 0xdd61470e04e7d082, 0x27d982e1e847ce56, 0xf72a8f24e4f721b8, 0x9b8bb1d6e6ea501e, 0xeaa7cd0509b25ca2, 0xb842536363b0faba, 0xe4da3f8b3bfcd887, 0xbd932d361065ba06, 0x9eca370313fc2da2, 0x4c83e396f1dd0021, 0x736f7994e9c8f6d0, 0x3ffb5a3d61c968d4, 0x08d8e5f5b52290a1, 0xfb74685303c44c42, 0xb0d224ca8f822196, 0xa1a3b5020fb15f10, 0x997487e4d5d80427, 0x3f70da528d56df59, 0x2d57f6db434b4052, 0x4dc42d63d2427595, 0x103e2ed0fb8e50bb, 0x0372caa074ae44ce, 0x1a1e47dbe2b61f80, 0x309598d72d73d863, 0xca8907ae8837fbd6, 0x5488d21567c40b4a, 0x706aefb716dee3bd, 0x80c2b581d4e65c9f, 0xd3ca44bbb4d91f3b, 0x8812961439a9b487, 0xff7006ad825722d9, 0x929ee7f4f08a9cca, 0x34f157c18f24e75e, 0xaebbb550107fda89, 0x87b6f7b6a92527bb, 0x357c5bdc994eaa23, 0x0928ee03d41cf874, 0xe0c781e8c4beac5d, 0xb1ae862eaac489ff, 0x38dcba54b51bd681, 0xed5da05961518eba, 0x5563a0892e5a1fa3, 0x1a049af5d30e1a4c, 0x764bde3be98f32bf, 0xe0d9075aee0ce335, 0x5f535b5659e256fe, 0x873974b222f1ea00, 0xd888a081be707cf0, 0xf1215d46c44e17d2, 0x4cf419680bc5bb61, 0x972dde1709a3e021, 0xe82c9109c3c19d6a, 0xe3a4416cbebc7b5d, 0x9d6043e3a9ba7f0d, 0x032fda0e9b1d8e2b, 0x56346632cc3b81f7, 0x5367d31f1a722d65, 0x6f4ca4df5b3a0879, 0x7006f93e7b028207, 0xb79d657cccc05e75, 0x00c190a6761e2d93, 0xf9ebe11920c957ea, 0x7db980a6fe125411, 0x8786c1da5493b722, 0x24e42205feeec034, 0x6023b4789c9a4981, 0xad1398e15da32925, 0x8d06d8d9d0014014, 0x58d84d1e7ad7376d, 0x63b03da2ca89a3c0, 0xaa469b13d1cc1fe2, 0xd5eea376b24c5c7a, 0x153958ed22c5940d, 0x7d967fd8e9d29d20, 0x72c645cd51e94f86, 0xe0b8d321c2c51b57, 0x6bdca21dd5527536, 0x6f58402f0183b1ce, 0xe1ada6d5c552150f, 0xf292cf9f9243028f, 0x84857ad43ab40e02, 0x9009f165e55dccfe, 0x27feeb528ddc26f0, 0xf0f04c29fd3470f8, 0x4fb771b412b5fdce, 0x2de1b134d7439776, 0x0f90985aa7a6e3a2, 0x7e08c0030cc6ed9d, 0x8caa419b72d70344, 0x1e941950ee78396c, 0x5012381195c88ba9, 0xda07a6827460efe0, 0x19a6c4168ca9c9f3, 0x47913746092d94fa, 0xe2866e58b2b5ceb3, 0xd8d14e9088adfb11, 0x556db7f2f3c80a1f, 0xe24d5a7b13003271, 0x85940d0290156b82, 0x54ac384c6077a3dc, 0xd036ac3ff0336baa, 0xadb6509447c2d17b, 0x78e3969faa8e45bb, 0xa2736209de9b4463, 0x1f30ae95031047e9, 0xb88c54cf800824eb, 0xc1bc857bb646a0e9, 0x07e4f0d03cfbe2ec, 0xf9e96a7252327950, 0x907b6c1a2312a62e, 0xef009dac45abf0f5, 0xa48b2f2188ffd5d6, 0x7534afe9d945a1cc, 0x358722098f709af9, 0x3d608dda268136b6, 0xb597da6fc666f400, 0xb4146ee033cc228b, 0x24decb95390d2db8, 0x2ed9660fafdb2f69, 0xfbcb64fb3cd89b68, 0xeec35c7be6ded9c0, 0xf3f66ba4ea609a73, 0x62d552b9ff1887fc), (0xbe348b23d10d7b4f, 0x40c1f54be8f0dda0, 0xddfb9394b5354555, 0xe274b7e6d2ddae12, 0x8a12830eb74dab17, 0xa304f2b3f0c97c27, 0x2f9ba9dac55ba53c, 0xcef4e107bdc0bd9e, 0x57963f73c17f7615, 0x994550b75e2f4223, 0x270e7916443f64e4, 0xc0a236c03631c7c1, 0xa1aca1d2caab06a1, 0x5a048e077cf53ca8, 0x3c30852ca67d821b, 0x1ff921114b0c6c7f, 0x7d6a9a75c09de756, 0x6df03d13249a41a2, 0xf5748a9f92e0643f, 0x7858dde11ea61ae8, 0xa1d228742080dfe4, 0xa1951f54650f8569, 0x30501d3f01c47472, 0x44d8fe18b266b452, 0xc7a8fcbe5a2d67ea, 0xa8dd30e95e100d0e, 0xdbe15d22af3190d7, 0x5d694eda02e0e99b, 0x542cf17f8e16c598, 0x6b2d75b537ed55f6, 0xe72cd6969fc26b09, 0x754096f394a221ee, 0x4cefbab8f7c84614, 0x09d288766d7a515a, 0x095c37322eea6218, 0x7f962692d4a2a8fd, 0x0491de4031349fa7, 0x3f41005402841fbc, 0x539a2bb63c0e28cf, 0x5515f0c3f5fee1f6, 0xc0bf31ba72eda351, 0x9c25d0ab1b35ace3, 0x8345181a93be0d99, 0x996cf2533f0300a1, 0x3094d2b6eb06adba, 0x938a1ebfbbe6a20f, 0xd23e72bcc433afb6, 0xcc1ad49fbc3f3f5a, 0x6ff94ca1ca45ffc8, 0x528b377dd91ae7a6, 0x7e824d41e1cf28e1, 0x46f514cbcd03bce4, 0x106b1dceea28afd8, 0x8ab4d2c1c41096c3, 0x5fcad1d1cc5b8dea, 0xa745c59e2c053e8c, 0x28f7c7344e072952, 0x9cb99f431dfac6de, 0x7aa61bee783f694c, 0xcb5ecf0f07d1465f, 0x76e691b46f8d585c, 0xd781c989af3a4229, 0x38958cdea1325b58, 0x7e37d31061f16e9a, 0xd92e76ca380efc12, 0x7e0bae0b4240889f, 0xe20c0e7f01bc470c, 0x1383c94314e86bc3, 0xad6e62367f4b5a56, 0xcb20cba9fd783b98, 0xe0e869764c62a94d, 0xaf869e6dc9e7489b, 0xb3fa08a9fdf55cf4, 0xa3c9c0c483484703, 0x4d0c587bfc4d0f9d, 0x73b2ff142f64ca34, 0xbf495b1bcbdec6d1, 0x901baa3b2229b037, 0xc9b7e18522d3ee4b, 0x19db2e53d3da83c6, 0x8c5b5181f27d3ad4, 0xafcf3f86df2b7711, 0x75f89825ca0bfe03, 0xcbbea7075c2c47a0, 0xf0cc9de637a6d46e, 0x214443898f898e74, 0xc8da3731c2235a00, 0xbcdac16d2e768497, 0x4055e3476a2d6995, 0xffe1b23bbebd82f1, 0x50252b4b74768ddd, 0x0c83df35f75c190c, 0x19adf29ce33481a1, 0x019ba5dbbd97e4da, 0x0f23256aed1aa301, 0x0d15ba94b7f3fcb2, 0xb8d1f12f8aadab32, 0x0d2e91b01aff55d1, 0xf41f6d2c5c61e1d5, 0xbdb0af8a5e1e38d2, 0x6b999d358eb79681, 0xf21b1f2a7b39e73d, 0x6adf57dcc25caa21, 0x73ca7591f96f0cd9, 0x4bcba6680c7530d6, 0x35e21cda337d27fc, 0x555ec77fbf690840, 0x402f6d37ea15c8a8, 0x002c4e731a232f5d, 0xf7acd23d5f9a1ffd, 0x7918584bd270ec05, 0xe74055bc8223efeb, 0xc7c0b9943c61ca48, 0x0b9eb3783de2dd1d, 0x29a443615434ec1f, 0x31a9e6b0774884b5, 0xee0c4e12c2789c13, 0xcd8c94aec29354a0, 0x702bff6255fa69f0, 0xe4f8beb45907cf81, 0x10ebb97cc969f833, 0x472ddeb46297c3d8, 0x392c9cc92a2c09a8, 0xdfab83d9f5279d52, 0xeeb818297b82b5de, 0x328ea8951111a7ae, 0x24688e553f3474d4, 0x013dd22d0f08de3b, 0x26792f16903b850f, 0xf76e3096b9caa712, 0xbf0b5e1ee53110f0, 0xf8a044a2739f5156, 0x30308dc3716ef64f, 0xcdc8cec0d8174e52, 0xdcfbd1f236d26f16, 0xe405e0a29fa69d58, 0xc7a7ee8bad24a10c, 0xd1f5e4cadb6b37f5, 0x848b74adeef2c79b, 0xf844fcb68d9a37ec, 0x2091ddaf234ee660, 0xde80721815d2e82e, 0x6d8fd0650e5363e4, 0x008a279f43991046, 0x5fe70e76174a86d7, 0x7f0da06477173dd4, 0x4b680adcdf16c341, 0x29f3324add3ebb96, 0x048a68aa194d91ce, 0xe10d6708f269a621, 0x89ff975f00160ca8, 0x6284d38d385eb3b9, 0xf634a2f3522b240e, 0x31e830e06e80e5f2, 0x332b86ccd103e3b7, 0x7b296fc0b25bdc9d, 0x6f804ba2abeac454, 0xe2d873eebf569497, 0xd7636939fc779873, 0x06f26eac00da3fba, 0x2dbbccbffd826879, 0x5ea5050346e104a7, 0xa687b2edf3346013, 0x567c307d3bb0f21b, 0x00f237dfb81d2f65, 0xe1c22f647fbfa4fc, 0x55ae28cd2af0110d, 0x03a6b77e2cb45d0c, 0xcea4d06eb470d06c, 0x0154afb2d9e6c5d8, 0x9b1995f176c824d5, 0x38513dbe65b8d8e0, 0x146bce9a2eeb76f4, 0x9219d2dccfef3207, 0x2becfd10c29fac7d, 0xef40591659ea15b3, 0xb802dd6737a170b0, 0xf96623db86a8ea1d, 0x428430205744c5f0, 0x6c64081a476fbb13, 0xc3b653edb64c3323, 0xc657639465fecfb5, 0x49aae5194d9423a7, 0xe02d743e5d613a82, 0xf8c933182fe4045b, 0xd32f317e6d8f0dc3, 0xf231ee1b6c1411cf, 0xf218d5f525b2861d, 0x7d34789eda495257, 0xac5e2f30caf932e6, 0x39fe6401d46fabc5, 0x597e28e0e4957a7e, 0x1cef5c338f0d3005, 0x5e4d5f3308c48360, 0x502f42afb2ef14bd, 0x43d41bd084da9f29, 0xea02588bf05bdfd3, 0x3f8d2a1f8577a8c0, 0x78172abd431de6b6, 0xacbc0d7e42613357, 0x2ab3f38c055ce328, 0x8ab28068631c09eb, 0xa9981ee67f93750a, 0xc19fe8d739173703, 0xb3ada5e9ed09cefa, 0x57e5cdac9d5a40ad, 0x9296f3d81d59df71, 0xaa17dbca92640939, 0x79eeb04ef0f8e90c, 0xbc6d0ab366178d13, 0x263fef70706391f4, 0xa2e21380b0c81c88, 0x82ad167ee887ea83, 0x027d99d32a35cfa1, 0x459f02164ef9d886, 0x75796a77f2fe75ea, 0x1fa018d8871d0d33, 0xdcd2d8844f0e4775, 0xd5e630ce8c5be924, 0x1359754f0258c6ba, 0x78150161edbe74dc, 0x7e07095a379fa46d, 0x613dfd1107da59da, 0xd7d5967753e69708, 0xef6a6e405b2e1e90, 0x0919caf7bdc177fb, 0x469851f2e36683d4, 0xd8b7d85bde034612, 0x7fb03f04681e1a58, 0xeef1e28d28006902, 0xacb89171a566e2ba, 0x602b51033f45d47d, 0x59f04438780f1862, 0x960dff6aa407662c, 0x15cca679ab07839d, 0x571de60ee3b168e3, 0xf3c539c3c2529246, 0x029d6a832e1f6fcf, 0x3b07357a40eb0cb2, 0x7277538d7449f8c5, 0xc3ade43cc7dfe115, 0x8d282b2936d36b08, 0x5bfc7da1cd73b35d, 0x157698b2f873cddd, 0xcd80b79e3557039a, 0x7cd562e6196f8449, 0x3208c7b01905e581, 0xc533ad143a87178a, 0x07f729a3590cf562, 0xdddc34badd2bb9d4, 0xcc006019e3cad81b, 0x7de074e5c3792c99, 0xb7f0709ae5da71b0, 0x40d62ceae92a0642, 0xe3ca4fac06f01bbb, 0x4013cb0f40d3b35c), (0x2de8709d0c4b8c75, 0x67a2815b159c76d6, 0xa78d70baa98e94c3, 0x2ee1575e5765554c, 0x9f06e2cf1d42cad1, 0xabe9ed22448c8751, 0xf25fdaba8ff024ed, 0xa7b9c4cdb7e7e5a7, 0x837325bb846b1203, 0xc39d608dbbb30a63, 0xae22198010db94f1, 0xd55e6d7361d8ea44, 0x33ed9f53dd3cd03e, 0xfa0e1301f1f2c836, 0x641d400c898dfb9c, 0x370db380e29d73e4, 0x872291b925004ed3, 0x8693179ccb120134, 0x03fa20e0a578fe3e, 0x596128c7582e010a, 0xf839fcabf56d5062, 0xfa2eec45ff5bd762, 0xd15621babdfe40df, 0xf491f4b4838b07d9, 0x896297ba6df32846, 0xe5c47faff3ca165f, 0x1f6ed55abe22aec6, 0x140906c994e0921a, 0x7ba8d9b08907e64e, 0x4fd68f6bc3ab1393, 0x305e2d33ae5e4745, 0xa58786e09dcc5946, 0x946ac42eea6b0977, 0xbd3dce1d551b917f, 0x32222be5fb62b703, 0x498061848b420cae, 0x34e1f5af31ecc1a9, 0x2df0536939974555, 0x6434cd30357e1f11, 0xc70096d5bc67bca2, 0x394529ee24e5443d, 0x0ca96439869b64af, 0xce0cfb3b25600996, 0xda050e66a2e716e7, 0xf1a95471ea99a597, 0x79343b67fc83b547, 0xfb8e38dde82f5477, 0xa110fb1aa6036bf8, 0x742ae1e2640c43d6, 0x1e2d232dbfcc425a, 0xa68367c9c3ee3785, 0x576b5931f57ccd6d, 0xe7859af1c271a7a5, 0x6673afd326d83ecf, 0xb7c321a6566b8f53, 0x5660ddeb0ea7c2d6, 0x72f7a9b08f73ae4f, 0xb76cf83ba365f1d5, 0xf6ef55f01be84464, 0x6e183523bba44953, 0xb51ba6ecf2b1235c, 0x8ebe205354d3ff20, 0x2a50b0ff6f51389c, 0x9d6385b6273a702b, 0x4afd85b8293760bc, 0x2dc069afe8bb8acd, 0x93ccae50f52c3cfa, 0x0cbfaac2b9b26255, 0x15cedfefcc1e149f, 0x2203d12081461691, 0x224db607c0c5e8c0, 0xaff2ef85ddb7dd6e, 0x6f33027b9a2ef4e2, 0x1723066e7ed1f240, 0x64954168b5318148, 0x9708552697cccff6, 0x1f46f5a30d49e8a9, 0x64500bb6f8c52be7, 0x82f179823980e7b5, 0xb8741e2789277366, 0x931160a3a79c72f4, 0x2fbc2906d8f68b1f, 0xdb5a9c8168b0267f, 0x29f57974d3c91c18, 0x5962c1f7811b8d94, 0xbded3fb3d03d9675, 0x63d470f1f240248d, 0x8bc739ad0af66941, 0xd36d2fce10c3ac38, 0xa2c4d04690e7595a, 0x9dae51c7ee7a61af, 0xea05bfb85a3f9132, 0xae8feb4d07701045, 0xfb32d2ee0df5f6c8, 0xe7c97a5fcc52b5b5, 0x57c763ab1425ced9, 0xd6c462534d597273, 0xbc4ce6d0635c711b, 0x01d5c403f08a5566, 0xb6e98c0cc94f44c9, 0x23be49f1b92ad6ac, 0x1eaa926f94ca6b49, 0xdaa82559dd8b5e69, 0x3b3c2efd8f59a1f0, 0x9b0dc09b105df287, 0x8baeb2c8f24c8323, 0x7b82c906779dd003, 0x719c8ce941cfa5c5, 0x6e23e9c4285d4476, 0x1e2624c17d848e4d, 0xcca31de8875b4eb5, 0xc976b511ed9b520f, 0x3e23079a3a42eee9, 0x7622596526554deb, 0x376a7a6d3b3c8c52, 0x23af66a632a571d4, 0x98040d25e03264b6, 0x170c40866208b799, 0xffd33f1e83844fc2, 0xef2bc945b937f0b8, 0xc135a4e74956a0d6, 0xa3b068a2347c00d0, 0xc21f0fa7533b090c, 0x1d995d96e425aff3, 0xd79245778c00badb, 0x998cce96ebbd8b53, 0xce4096e29d920183, 0x3b606501109b0e0b, 0x9671aaa841cf66fd, 0x33203faabd0ba9bb, 0x7c79e769fd595b7d, 0x212b018943fbeafe, 0x858910bfa574331b, 0xee46ead9e6b3d527, 0xe01ec4d45b43e455, 0x42ff698f0e2514da, 0x54b8164b5b2806b3, 0xdc1067b4bd182b94, 0xa5e99202f45cb150, 0xb056bd82dc96c359, 0xa24a4542ca2d700f, 0xcde15ee05722c5c0, 0xd3759955b32fbdc5, 0x14d90ca7ecc7f2fd, 0x387de458d302b9f1, 0x195d1766f4323db5, 0x1498603613f3ae67, 0x14f9db6b884fb7c0, 0x91014ea8ce68b503, 0x8ee634945fb5e6c2, 0xfabc822d45022c71, 0x8726993a7da0e5e7, 0xcfb9ad11bcb6f32b, 0x843b0d0139e7ed13, 0x28f71e0f558c115f, 0xfd5df9e3297b7d5f, 0xe7e824d5c3a45143, 0xfd41017a98d916f9, 0xd695c9f736d2c2c7, 0xb4f67b5e4cb6f459, 0xfcbb73cf585b11cd, 0xf1ef34d7cfc3e906, 0x0b126da5fd01c3fc, 0x81ff5a25c8064638, 0x8cc47945b5c5eef1, 0x39f25741fa9a1c34, 0x67592bb4df6286f0, 0xf54357a44269cbe8, 0xb6d1006818895ce0, 0x06e3b27594551898, 0xdbe5c5367079aaa1, 0xe15f1b4b6b4bd407, 0x5117fdba0aa9c8fa, 0xa199bafd125e5053, 0x9a3db014318e6349, 0xb42d281f8956f143, 0xceb542ff797e36b0, 0x2cb2b1cbb1e42d77, 0x8ce901c26a95be9c, 0x06dbb13875f165f8, 0x8dc2bf2bc6ce04f0, 0x91c6bb67f5f4b99c, 0xd24c2a9392551b92, 0x61fd0cedcb54dd7b, 0x47764e4893c5cb3f, 0x82b0553e9fcafce1, 0x353ef87727ffcb08, 0xd4c421a8468ebafa, 0xc1486d7d9b670982, 0x0b0aae221e16a5e3, 0x07204d042b1b0d87, 0xf29994032248fc2e, 0xdc726bd3b86a60b5, 0x12b8ccf6a9df4f41, 0x886174a9c744fdd0, 0x50d5270a75a24b6d, 0x3a06d49a4c65c7d1, 0x4aa1240b6854b5f9, 0x4d6c4a96fd3e3214, 0xb98e740e68d55087, 0xac3f53f8430c60c6, 0x232cafe2f229a8ba, 0x3047a49337aefdee, 0x1a11826394386fad, 0x0d562995949a85ac, 0x632b8a918391e625, 0x5cd3a4737930dd35, 0xadf5da6c9c33d2e1, 0x918925ddb9ff05fc, 0x8c1ef5fe9dd540e9, 0x5834cf83b98ac211, 0x953c033674c73e41, 0xc18dc05a7db66a41, 0x1f02081896de1aa5, 0x943e99b71fe333ff, 0x05929070e436e5ec, 0x1f3d438a762d34c0, 0xa3056ca7a58c7195, 0xc582ae9e3146ff9c, 0x5d4a2125c27fe328, 0x19c55be502092507, 0x6a9dcf3a101cd2d0, 0x4633db2b52d0f3b5, 0xdecf40281385e0d4, 0xaf2308941e6ee0e5, 0x40ef3ee6f20ad9e8, 0xe8ca8099bfe762b9, 0x8c47989572c63d37, 0xf6c5e1e58c3943e6, 0xc40ad08c502664b9, 0x36a0c64933256c0f, 0x7f4f552215b5bf67, 0xcc196cb50410b62f, 0xf7397a765ececa6d, 0xcc137c3a36775e83, 0x8883057cbf0ee828, 0xff32849fc05d9c81, 0x4b8972c96fb4f26a, 0x03064c475bbc0d41, 0xea554d16b5882125, 0x48bd990f056002a7, 0x9788e121acf125bf, 0x094735033afb526f, 0xd49ca1d73044e933, 0xa460fc79bab7c96e, 0x1dd312da1e42329b, 0x4dc7b26303f719ec, 0x97f080c7875be0b5, 0x25edf65940efbc27, 0x8314adcd5a09b26b, 0x43d3e1839dcf22af, 0x2a86d88b75792469, 0x3cf5bb72707a76e3, 0xe6a0b1c4e6c50329, 0x5ffcbb61b323a730, 0x8245ca1455abe453), (0xa39aebb26364c177, 0xd21431e30117a48b, 0x200ae8e11a9fb12e, 0x84f01ac001ec2149, 0x327ee55621a0f4df, 0x57a704366e896f04, 0x913c040757a6e7a0, 0xbf0b9fe91f502985, 0x3cda730a20e71b97, 0x3c53647f263d4ffd, 0xe85e190e35896a0c, 0x1076ed3a88803fab, 0xeb56440a872cdce8, 0x97a471c7e744a2e7, 0xd1d1a7a51782d3b4, 0x8b7ffd145e31db03, 0xf204a9a834037db8, 0x46d2fa20f48bdae4, 0x08477cf66ebbd11c, 0xc8722f42b965dc35, 0x8de98b87354010ec, 0x007f84dad4206e41, 0xbf67aefa1d3cd0b4, 0xed651282c910b8f2, 0xb7cfe39a95e26dbb, 0xcd30bfa92b0d565b, 0x0192ed23b4769318, 0x234288821f1db144, 0xe2d48acb71317b54, 0xdad4a3a0674fec5a, 0x749032b682036959, 0x0abc12d3c080e393, 0x1e6b2172fa0ae191, 0xe9698c776bf6b219, 0x9a3a80bd00c31390, 0x29d3da5a5b531bd4, 0x9977177cf51d75d2, 0xb5cbcde79404cb56, 0x3f325f5a641e30b8, 0xe4eeb998b16f4a13, 0xffdc987747c15957, 0x047ef2e13358aa8a, 0x466463c90bb110dd, 0x713340d1d4824f5c, 0xc24a510905419239, 0x490321ad6862974f, 0xc7dc41b130466d7b, 0x6402b8ef43397b11, 0x225abc58a9e8ed60, 0x67340115db807af6, 0x43bf53f229945092, 0xb8e8bf1f2df170f0, 0x3bcd4f810978f0b0, 0xd84af74b9b01f9ed, 0xecd18323c47a4fd8, 0x93a30da4cd5beaf4, 0x07702b422dc5319b, 0x3998a933865aa09a, 0x08f71203845c92dc, 0xed3cb8cd2c08773d, 0xeea811cc6b9b4381, 0xa0b6b7d26dd82ab0, 0x03231fbbb7b57527, 0xfa48f7382a9c24fe, 0x13dc222ab03ab03d, 0xe55b5087aa23ddfd, 0xaa780415e0713910, 0x7bf004cf60dcaf8e, 0xfce7338b4fa11637, 0x059a4dd601a3146a, 0x81985c0adc2cf795, 0xb38e21ddc76007f7, 0xaf6e71a317393c73, 0x2c08417298afe2d0, 0x1399f92e1ffa3ba1, 0x3303fd6caad78942, 0x8669826536ca7e5e, 0xb9d464985ce4f171, 0x946ffa93fcaa54c6, 0x16387bdcce074149, 0x9c1de4c2bc278568, 0x360c30158fce90b9, 0x476972d6cba0a167, 0x9b089a0013d72462, 0x28df7ddf79caf10f, 0x1627685f6aae3340, 0x34efcdd66d148b9d, 0xd62ad5527548b30f, 0xf6e946be282a0d7b, 0x21661e1c039e7aa7, 0x5e6ed4638c9e311c, 0x93c65243924b6be8, 0x96ad58ca88c4a214, 0x802f728438363a9e, 0x3e57d4ef5d3b0e69, 0x410209e211903f3e, 0x8d8933ded72fe266, 0xa99548d311a78dc6, 0xb6b1a745bd696ba6, 0x6b1bcaed2387143a, 0x6fb9500a7118d183, 0x07380be8931b7bd3, 0x95298d9d3e64b1db, 0x0732918135cc6329, 0x345e097d088f25f4, 0x173afc52e212d294, 0xc46146f92cd9c41d, 0x88787984e33c3bb9, 0x760e61d0fca8c69e, 0xae4c02ca04604795, 0x2407dd5725403d3b, 0xbd656c1a2fc5f0ee, 0x8ab889113387e6f2, 0x7e25683493ce6ea4, 0x8147b6821387e427, 0xe6d9c5e310423fbb, 0xa97be07e787e4682, 0x96834ce5c841dd03, 0x0dcbcea94589bbd0, 0xab4bf901ef526c8d, 0x1ec6a1d473d4c76d, 0x74ea01c97649aa44, 0x90457f7beac3c86c, 0xa5f0d999e4a41cae, 0x41272a55cb08bb0b, 0x0c6e93c7fc785783, 0x477b804e392703dc, 0x88245b0345830c38, 0xda7e9980b2070805, 0x12d8b82ffc3f0ca2, 0x10a9124b2c4a7b57, 0x454626fa89395f11, 0xf57d30ac46d8325a, 0x6ffcaac42e29897a, 0x0f66ef9826a1155d, 0x1f23ebe7791874dc, 0xb03de53538f4456f, 0x662f5b71aa885d34, 0x2e4f6b95f42c2ba5, 0xb1d6d7dac9eed523, 0xca1f808374c1b0f2, 0x141100e9f48b4442, 0x1f9755ae68b2e203, 0x426302fd4170beab, 0xd4c99a92e8fc9ff4, 0x8cceef176eaff95a, 0xdfa4dbeb60a196f7, 0xc492bf921c4d0b56, 0x3d2a0ef9319d889b, 0x6cbcc10e809d64e2, 0x4075a1737a31f11f, 0x14f0bb5e26117338, 0x4a8ccd8e06f358ac, 0x2ae0da46b71219a4, 0xb932b5ad6cd6be5b, 0x7d4f58e5964358bc, 0xf472fb8bac9eafc7, 0x071ce6f706a4b2a1, 0xcf62be3a925e40b4, 0xe92fc78007c2dd87, 0xef788f99d4778c75, 0x14634c7ab372821c, 0xa8c0e16d3e86e871, 0xbd25beb0ee6fce26, 0xb40d7c8d41e3bd16, 0xfb5f3bd4c90c2a6b, 0x6119ada677d21ef8, 0x364e5995f95ff324, 0x38a2042705a5eac5, 0x1b2d59636382c950, 0x4a41e6c3e0d5f2f7, 0x82dc61c3d870e1f1, 0x1478bb62ea666922, 0x1cda3ca10f383751, 0x9ffb08c6fe63b9e1, 0x334b79b5cd42155a, 0xa4d1dee5e93626a0, 0xe01a64fb94281d0b, 0x36637777b84ca58a, 0x8e495381d641c5e8, 0x6f418314a0208848, 0x05d1f46052f86c92, 0x134f2aad2a8ad534, 0x9a5689b80e95ee3f, 0x0c1e855c26438d29, 0x9133a62d03bbda55, 0x69f8e3b9de94e397, 0x17652a75cba6601a, 0x95d85410337ae05c, 0xc53c186711af8978, 0xd7f7136d82d9bdd4, 0x9bd2d1d7f41c54f9, 0x74053b82e2bb0d39, 0x5d944dd003d50ae2, 0x7d893442cb6260fc, 0xfcd359ec15b4f2cc, 0x9f8c353469cfe051, 0xa0a91ca4559167a0, 0x8930acc90cc579eb, 0xa09008ca09eb9f34, 0xfa2061c9d34bac9d, 0xa4ace78d98b48d59, 0xfb4f2e12e0642d0f, 0x349dab42c255fc78, 0xa2350d1e7b1efbfe, 0x091987183a0f74ad, 0x13d0b75b5a868748, 0x7727d38da66dcc65, 0xc9f325c6c91b94cc, 0xe705236b98f825b3, 0xefb41d0276a0e451, 0xd64528df37b25e89, 0x2bf0833742081229, 0xd90d0f249765bf55, 0xde868ff2dee90192, 0xd46f31953624b60a, 0xe3d8705c86040eae, 0xa2f76fc5f3463469, 0xb3ed829bb7c6e8c6, 0x1468e17c5c273f81, 0x474fbc6e70b1dd24, 0xa0c069341b3982a4, 0x4cee8fda61742293, 0x0bbe9549357fd223, 0xe2403c8c806fc37c, 0x726954250d9b5ff6, 0xe2ceca8aa85a00e0, 0x45a6396d363a41f5, 0x26c53565a7ed97df, 0xbf143b13953fdfc5, 0x49d820a6c053aa36, 0xc00fb43d2fa790bc, 0x5c807ada71a8469b, 0xcb37e60f3820c2db, 0x09e3053a2e74c5d6, 0xcf7bc11db6157909, 0xc83d07021368f97d, 0x81cb75a62bf1a82d, 0xb458c2a811dd09d5, 0xba63a37c2acf7ecd, 0x8ec3fbc2a542b5df, 0x0b364530a6415a90, 0x5c9ac35f3de76cdc, 0x483490ca341c158e, 0xfa25b55d70ec5989, 0x7913285e265d759f, 0x4981e139498a459c, 0xa6f44be3326b011f, 0xc110247c5924a7bd, 0x57d146b0c58adf5b, 0x44fc171395618896, 0xaf3807509696818d, 0xbe6e00d29fccf989, 0xb4ed0b4f1ee28ab5, 0x76c5dd71ea224564, 0x0a779db19af5bdf4), (0x8270c58a7e3ccc55, 0xc233ac342fb4683f, 0xe224bda9638323b4, 0xe6d6678a5de3e25f, 0x37bb200b60e23781, 0x849fca9f7d38543a, 0xaecb4e719304d7a7, 0xe490f2ad25483714, 0xb2499c3485fc371b, 0x14c1985993203504, 0x6c51cb9659a5600a, 0xbb469328f84f9952, 0x7b1a013c71ec803c, 0x60c11789875c9c69, 0xaff39650732f8c3b, 0x90faf13bbd38b9a5, 0x0b46128962ba15e4, 0xf68a4a0b000d6cff, 0x4fb2ab2268f65187, 0x32412727f8b48061, 0xa05dc13c222c0f9d, 0x4adcfd5a2b8ce496, 0xb6708b3c0b9d8e50, 0x0dae5dfc7f27dcb9, 0xc2fefe7e8fcd4d37, 0x1575361be7b86c0c, 0x8457de191a7cc0d7, 0xfe86b35ff0b4b3b8, 0x920f38ae0544cd78, 0x124c97d18c96e73e, 0xc97a4e9d87f9c161, 0x9dba60028cf73da6, 0xe0b474b32cda577a, 0x2a95fd06fddfd532, 0x7d1465d4712cf98f, 0x301c0e8633f8ccdb, 0x05dba07c0fdf77cf, 0x7cc0bf8954b98a65, 0x4680b299c79fb341, 0x0cf5e7b4371dff6d, 0x5f1994caafba1e98, 0xe6645b0f1f440fbd, 0x2c8c94ee6470278f, 0x8f02fd7fbb2e40da, 0xe1b4d870b7bdf989, 0x717e1abb3b396de0, 0x389a1bdab7fdbb46, 0x76d0ffccf4e8f87e, 0xbe865038f9273587, 0xd7b5d492710db876, 0x3d9e45e8cddf25d1, 0xfb53731d6da261dc, 0xb1868b7a7de2372f, 0x4d597e60e87d884b, 0x46765a237229e195, 0x52291ce3db0a6b10, 0xbe9776b61721621b, 0x2766d780d6e62d98, 0x06e32e301143595f, 0x6544f519e588fa41, 0xca0daa6ab502b0ed, 0x7b3cf05dc78325ac, 0xcc3fd362e27263a9, 0x4b081bd3fb72735a, 0xc1110390963899bb, 0xf9b22c0aa55615d1, 0xd0def6aedb0545f1, 0x178be8edf572392b, 0x21d51fab649ff5bb, 0x0b5f7bb7d6f88aee, 0x693016779980c5ee, 0x6588cff8c739c8fd, 0x809339bba03af0c8, 0x9ff93a811b9f7782, 0xda2175316e787284, 0x8fe3a08408ce2329, 0x315241be048e6109, 0x5ee4b727ace3dad6, 0x9d0ebd6ce0263eda, 0x5aea1f8f61deee48, 0xf84c8757b784d393, 0xd53d112cd51feee3, 0xa992215443da96eb, 0xcceea913e4624064, 0xdca87aa21cf73c65, 0xc9d9033925116801, 0x7ee41bd62b871d38, 0xd90195c9fb7f603f, 0x297bf0ab2e2d641c, 0x44ea3afe613273e6, 0xd1e6789a6871b142, 0x18b3fa9d58346524, 0x7c793f07ee132a6b, 0x5bf6a805f23390b6, 0x8741a6b72f843ac4, 0x14e3e36aa8c8af74, 0xca46eead220e18b5, 0xf258fcff744b8239, 0xf9b9b6fcdd4aa1db, 0xffa98614b89f9d81, 0x803bd8f64d6576e3, 0x0fc5744d6039e100, 0x0b65224fce77ef5f, 0x851cd3141dbd8bd3, 0xc1bf2a205454f641, 0xca96208d50268b84, 0x39fe3e903b0dfca0, 0x7508555fbf6fe257, 0xa1e4472a60b5308c, 0x1f9916514a4611c7, 0x3ebe28bc7e7d6599, 0x1eab12c00e8a7d02, 0x8e3c97a493b2ef08, 0xcf441f3af50e8795, 0x78211ed51d3aa9a4, 0xf151b03024a78086, 0x719048537ddf8857, 0x08806ac4fe924b67, 0xf385b13e925b6401, 0x39d8631eeb975904, 0x9f6c76c2bae962d6, 0xa237a746297a56fc, 0xa94b7b9448318d1d, 0x979e9c1a90ea1e42, 0x4873af3c9027f3a8, 0xa083d55867da74a1, 0x81eb8297ebb3dbd1, 0x2ba416c7d37b42dd, 0xf5acc02c973baa7c, 0xa048c19d62c58d08, 0xd42cad12a3dc7030, 0x6e5667f26d6f05a5, 0x8b634592552ba1c1, 0xd4b3240e6e18672d, 0x35ad3954189666f5, 0x8e7738d435890db0, 0xf4e616bcf8e4e01b, 0xffd079a62bc3193e, 0x2b8955d4cdd477a6, 0xc7991845b81470ea, 0x4db8a889fef2fd23, 0x9aae7477ab6e9c92, 0x68ffdd11ed2f3988, 0x122454d2994c1f20, 0x9fd71180a2b7db24, 0xc3e5b812bb1e1c69, 0xd98d1dd83a49f119, 0x24e1a5c376f4442b, 0x9c6e7e1c316ed43a, 0xa50f3dff982047e5, 0x1b008dd50f89be39, 0xf7f490e059a1b6ef, 0x3fc2d7724c276c96, 0x19b92ad2b351118c, 0x03941a4d980fa379, 0x0dcc1db6393e5ae3, 0xef17fd708a0c428e, 0xe7b3603646b6889e, 0x3776ec163795447c, 0xdbe8dfec2fa2b2c3, 0xf6a1d2e719d2bac4, 0x05cce8208c359d8c, 0x664f9ab99a6a8281, 0x0e318802fc9d8ca2, 0x31b00c7761fd6ff5, 0xac2736c0bbd63ada, 0x3feb3e779c9b394b, 0x33c331f81c194e0a, 0xc0e1fad94915b0e8, 0xf1e778b4a953281c, 0x85c15d6b8a33e720, 0x8d1e7d0872e75ada, 0x5bdad425f0a52ef1, 0x6ee76541694a695c, 0xdf7328da8f3d5979, 0x60068f9a89907a16, 0x70280f5bbdc25287, 0xf3ad4f4e96e5f957, 0x76939a656087de97, 0xc840dd22eb2bc374, 0x53a484fcb09c498f, 0x604a4d01983cad39, 0x9a45d55ca6c551b9, 0xb72eefc7c9778b82, 0x4b957813ae06cccc, 0xf8e2a4f7e9400487, 0x277cf9aeaa2a51f3, 0x54dc111e6993141d, 0x003583b892c6a1dc, 0x3bdeaa04d0108724, 0x010c64c7c6530e36, 0xed7ed3b736208e3a, 0x846709ea1d334394, 0x794b3c5d9ca1a28b, 0xb55bf8831b4c1f8d, 0x73a632dbf724450c, 0xa783618e5edd45a2, 0x245209389ccdda11, 0x26ff76e28bcd5d77, 0x59c887ec8c6a5e9c, 0xb067867685f842be, 0xe0195a498885bbaa, 0xf00dbf801a1269d2, 0xec7f6ce6327cd328, 0x2971c4c0d4c61b48, 0x4a784da45ba69e1d, 0x8c4a5a3d2b8769ab, 0x53b9f81e78430f85, 0xcd8ef9c95d91a296, 0x81ad8a98131c14cb, 0x51b8aafc0c630a32, 0xf920b5cf3d7b6038, 0xd366018f6f9c074b, 0xd7b38fedb9576f7e, 0xaaa59aa8b435a722, 0x80cafdf61b69ddff, 0xbe142df6c9259793, 0x0918a3d95392401c, 0x297c19a3daffb8be, 0xd025aa11b15233ad, 0xf31db0f5e00a86fb, 0x5d66cb2aebd0952b, 0xf91980c469cf031c, 0x8e70d03da0149c0e, 0xb6297a9d0c95e7d8, 0x808242de858c00ab, 0x1b41e22654cf5bc7, 0xc83263b4994f2fd2, 0xf8d33416c21bd1fe, 0xba379dcd4d10a6e3, 0x404047126f7cbaa3, 0xd0e59e70f4b84cc5, 0x4f3f2291036c054d, 0x1be665cd0e1abdc3, 0xb2f216aaa3614021, 0x01daad23cf8fa18d, 0x99b0ae860e2143ab, 0x4b539c106ad94853, 0x251e2bf75a09d46f, 0x8a91b11368a439d9, 0xb906fb7d818f49e7, 0xe40b0441591e5d64, 0x3b46601a788e9739, 0x2b8a0d54887dd7df, 0x3f1a40fa2b72c27f, 0xff06eaed97ba852b, 0xc363936e8bc3c0c1, 0x20a642e5031f63ab, 0xe3d7257463fec615, 0xe0d30793ada218ef, 0x2bd10a033c03ea2c, 0x203d7bda407c6331, 0xc55d3294076aa056, 0xbd4783ee0e0550e7, 0xeba30c0be3421cf1, 0xf0e36e94dfed4aae), (0xca709918d4674db7, 0x9980136f76ad2016, 0xf3a8c0fe541f8b7e, 0xeeb110d70a4fac66, 0xf0b7a37877b3fa79, 0xf68c56de204a3920, 0x371206442c474bd1, 0xcb4c1c76b6355455, 0x59f758f5888db255, 0x0450d8d7f031e83a, 0x636f2b97b26a4f28, 0xee409bdc8e1b216d, 0x664740fabbe6cc4e, 0x76112ab97739204b, 0xdd38f6d09441d1dc, 0xd8bbc56b5ac87b72, 0x95cc221e671426e2, 0x974d2dedb037d072, 0x0810359b99aa43fa, 0xfb720030465a43d0, 0x4779ae4c942bb72e, 0xae22a12d2fad47c9, 0x04bd4be70a14ba4b, 0x1c67336c6e747bf8, 0xef0b5129fbf0d91d, 0xcb272425f4532b4a, 0x6a9e7887aa2b28d5, 0x1cbde852fae539cb, 0xb6ba3014a48647a5, 0x2687534ce4ee995f, 0x8810993fe0da779e, 0xebc20b97e29da1c1, 0x434d16e251a6b511, 0xc897a94a36ade6da, 0x09446b260794d068, 0xa63479adc3e71665, 0x36d3a7d7a92fbf1b, 0x48f1fafe31207be2, 0xe9732085c870e7b2, 0xd370294e1c71bae9, 0xf68160665e136f11, 0x67e82747df4d3084, 0x5902f43d20e9d1b0, 0x44c625483922fd5a, 0x5deada8439a78477, 0x17fcdb43729b4540, 0x4ffca6de1ca9ccf3, 0x944068a19d9aae66, 0x61eefdd640d12e06, 0xa8e7a96f92e6d53c, 0x90df02a0a3d70093, 0xfe5fee57dab1fdca, 0x698fa9f771dd0cbe, 0xd7d0403468ed71cf, 0x25b3da712d162b09, 0xdae0b97c7c28b614, 0xab8a0fc6b0325ddf, 0x0882e099428eaeab, 0x0aa1274dc32fc3a3, 0x2572e9c16486e5cc, 0x86c4ff6847db4659, 0x090fad6e12c39cf5, 0xa95de69ca84295cd, 0x5322b70f505d1678, 0x81ed84a8c416ad36, 0xcb87099698e8e46c, 0x822921e3333ec626, 0x51d4aa2e9b15ccd6, 0x8eea33fd0ffc7029, 0x69b7daaaf931bd18, 0xe400a4f9a8eb6041, 0xad11605ead5ab4ae, 0xd1d758873e7922c2, 0x18f4d2f70ed5640d, 0x3f06ca69722caf2c, 0x0aa52dbf9f4b326f, 0xda194bd52d15b2ed, 0x7674f798116a740d, 0x114482a29bf79877, 0xb8cc27bd1a2c5443, 0xda881e3161f17766, 0x0476035d52ad265c, 0x9e2309517ef3960d, 0xbec051a696e80916, 0x2094c117a2cb479d, 0xcb32cc3a27de99b2, 0x7536f87bea199279, 0xe1eebba1e9ed7e5f, 0x0cd60c97df496a00, 0x5cefeaac20377484, 0x20c448885596617f, 0xaa2d66e96249b7fb, 0x9fe4156fb3eeb3b9, 0x9eb8f2626d98faf2, 0xb6203d5b279d7a5c, 0x571b2acb59983fa6, 0x447881c2b4821687, 0x4c8f8fd7a37a163d, 0x2c488770b76a3036, 0x11a557733d626434, 0xfd32b63b973808fd, 0xcb795e1a42edbe35, 0xda4956d690564ddc, 0xa92a770fd031f2f6, 0x42ac68c41c86e6da, 0xc77eb52d0f755349, 0x7b92a7c2e754c636, 0xe22a33c034f1134e, 0x745cc33d9ab88f48, 0xdb9ca59ca276ed50, 0xd54d36604533ed2b, 0x6268fba366575684, 0x8c4a29ed9cae0972, 0xbc9574d774ec06cc, 0x52d2c4467ef96472, 0xce1547ef547453bb, 0x824c29126ffbfc29, 0x9871802783a606da, 0x3d3fc1e9dab2d847, 0xb5e32fbe1dddfb99, 0x430bbf105deba6cd, 0x1d47118c78b5a2f8, 0x3d21b37cb3de784e, 0xba6d7b79311d35cf, 0xff7cf77d29d2eae4, 0x3c1022bc81ca4e18, 0x7682704b51b97a04, 0x08346c16b460c9b3, 0x4fbf6e046715a9fc, 0x1630ab25ca3a14ce, 0xf16ec670d37298f8, 0x978a30e8fa599bd3, 0x9f1dda6a049340fd, 0x8a97c7fb09f3df94, 0x1eeb23cef56f51e3, 0x0cfd66798867001c, 0xba95dc91d8ec7ce9, 0x4a209b71ec0175a6, 0xdd1835f85d96ac71, 0x4ffe327fd6c47d36, 0xa7d28df2f4da0443, 0x02d92a90899ea6e5, 0x3c14a80d8958bd30, 0x8d35946880cf6958, 0x9440dc0d6b24f39e, 0xade0f776ab78c03e, 0x5e81d3fd307139ce, 0x36412afff7bc4a96, 0xb7a69cc6fb82ca0d, 0x2e95055f61b6dca8, 0x8e06f4373c4ea917, 0x1f25aa32fe4acb35, 0xe1cb358cbfdb8981, 0xc6d073de4ec88b6e, 0xb4c023525078286a, 0x4a9e91e56b096795, 0x45fcc4aa02107997, 0xaf07e20f66a17c3e, 0x66318ed34c34de17, 0xf9ef098225a6d28e, 0x819649e651114d9b, 0x5337ee1e912524e5, 0x0697beb41f1c23c0, 0x41c57aa9e77b6be2, 0x5825b9601bee8458, 0x6e7a4efae63a6d2d, 0x9e56110b55f3854c, 0x44c8e4b83508dd44, 0xef3544b71e68ac53, 0xc1fb80eff7c243ce, 0x7eb8208e0d5121a7, 0xee4249354859975a, 0x39178e5512230967, 0xc4838a093cad664d, 0xf86d1c00090c9351, 0x87b5debd410430bb, 0x7b65756fabe5b840, 0x4f20b39554bef6d4, 0x75d3811e0d0cc031, 0x4d068d625ce7fbb7, 0xb16b5316544547c3, 0x38716bd7b3289406, 0x654d6350745b58e2, 0xf6e28b7a70eb055f, 0xd1690380fb174b82, 0xfe9ddc9417eb2325, 0x988b4c4848605f30, 0xa5bb93f7e6fe9f01, 0x384df62d394ad805, 0x879b038b5b1b64e5, 0xef6faa6b5a1abe20, 0xb9a43eae5da71a25, 0xc363b4e94dc22e2d, 0x924f761510841cfa, 0xe4efbbbb942e34a2, 0x49f242cbb35c9cb1, 0x1978444a67080892, 0x7dae453cfd96587e, 0x7579c6ffafc8962a, 0xa6f9bf1beae0e677, 0x6d48cfad33401238, 0x8a8c3e6dc3f8c98f, 0x5ba1dc6dd7024014, 0x7a5d3ecc08819c56, 0x211afc4f71c9acbd, 0x119a330e8277350e, 0x81f2371e54173463, 0x02d4cc9b04c1ee60, 0xa4d3ad7adae50672, 0x2f8bc9dec9175da6, 0xdb34e18fafdd2b69, 0x086542d87c42dfef, 0xd9d6c5fef0cd9ea2, 0x5cd8c313d763e88a, 0xd719d48ac8a6cf91, 0xe07b3b116137ccc5, 0x8f1418b3f4c772a9, 0xded46f5faf6d817c, 0xd19882dedce50a76, 0x354312c092efd6c9, 0xc25233908865480c, 0xab9121670176e53a, 0x3b77b1d301a0748f, 0xc3dfbf827f3f0be8, 0x1d3729daaa2fa930, 0x97d250db498cf6e0, 0xea07abedf61c84af, 0x67b956aff567c13c, 0x35f362e22495673a, 0x61f90020eddbe016, 0xbc18aacfed843c1c, 0x34aaf990489c15eb, 0xc3f01f91389fc1c5, 0x86136ea3ffbd3fe5, 0x7ccd2751d0998e59, 0x052c2a6ad31edf95, 0x226a9c8d19cb55ff, 0xf70af1b692cc3335, 0x9737f6a8fc6a8715, 0xd32a1f7140233fb3, 0x874614619540983a, 0xf7dbcfb8ba5216d7, 0x965d4f63a70b2435, 0xfee9bf29fcb54dec, 0x4d30e81cf590128f, 0xffe049b9465da940, 0xe35d7f2c40c6b2f4, 0xda583eefb727d5e9, 0x7e12e0ee7e7ac524, 0x5a640a83a0fbc881, 0xe88b1e190a281119, 0x8bf16b7a31711a04, 0x3936bfd17c458f2e, 0x60dae6795ffe4002, 0xb3aa4c8dedff59c5, 0x422fa605da7cc141), (0xc804edee57e3de5f, 0x4ff50140ec29112c, 0xe63dda807dfa6b91, 0xaf720da490dc3139, 0xb842a003d00c454b, 0x55339a0568c0f9c4, 0x140e1607ec6074fc, 0xcb926943a01f2b4d, 0xda4a1128ca62e533, 0xcf464699adba7092, 0x6132af6bbef58921, 0xfd0b9f20f502151a, 0x921b9fe41b412cc3, 0xdb1a2c2b18508a4f, 0x12fc6b1236404302, 0x1ab16a5fe8ce0984, 0x1ab4b92f414e0a20, 0xdc6f0217d9a8ba62, 0xa3c7f39d4ce3e9d5, 0x3201a021500d3ddd, 0x0d9e08a057012c1d, 0x896dd8376459edb7, 0xe65dee0701f0616e, 0xed844879e823d1b2, 0x9904183f320cba38, 0x2e3784984fd94848, 0x6054e806984b2147, 0xe25866894e587f3a, 0x6cbc38c6c4c996cb, 0xcdc7d97817b7efbe, 0x91b62b80e4e1a20c, 0xf0e30aa575d8d502, 0xbb5206febb15d2f8, 0x90e5f7f88f8fd09c, 0xb43d69c9f8286d1f, 0xeb1888abe773878b, 0xc54dabada7f18a98, 0x95cff74bcb63f360, 0xbd0a7df239e60df4, 0x03ce04abc7c60e85, 0xf3542fea4a20937a, 0xb633bdd33714f428, 0x2d557009aa025a9a, 0x4cb2310795a4874b, 0x2a279056883a7dcd, 0x0b7b7c24797330eb, 0x14ee9b6c3ecb24a8, 0x7a0eb7a4630173fd, 0x35f82776fd33a403, 0x43dd344b21a8090d, 0x601708befa9bf889, 0x01a9c7307c41355d, 0xf0db0602d45783c4, 0x33c9b46ca768f0d7, 0x5c0a089550da90d2, 0xe571d6ffd0619f33, 0x8fee54b472c2bb31, 0xe2025828c56c5807, 0xba0adbc1810c033c, 0x6f4fb2682c3bd0da, 0xfa2023a98ad18f14, 0xe093cbd0a6777072, 0x2cb96c03f45d6633, 0x79fd8178e3132b6c, 0x8bcc3658b4fd3c4b, 0xf6a6551813b55d1a, 0xfbae8f669f4a17ff, 0x4052992e22dd64f4, 0xf5f83bc727df97fb, 0xbacf24db23f04bcf, 0x9c543017354f4e82, 0x7828655719074c90, 0x078daa9b312a9149, 0xe04eb974c360acaf, 0x8e9c9a5e699e18a9, 0x5a3ffacaabe2742c, 0xb1a3e7414a3e97f7, 0x339aecdfeeacc924, 0xe7c8fa89a6d09c97, 0x64bfa5ed3f11a374, 0x302f07f185d843a7, 0x34cea7fe17078249, 0xdec5333351d7f552, 0xd809bedbfe2f8baf, 0x42b7b8aa7284b21e, 0x6f0287a4f75f6916, 0xea6a4a3bf700d39e, 0x1a3543c4bafa07ab, 0xfacf571bb9843617, 0xe35f4337b70142db, 0xb0c1a0c3b85490d9, 0xa6684240522f30a5, 0xb1e3c0817eef50f6, 0x87092c229d553a71, 0xf938eb0a52136c6e, 0x7e586080388dc383, 0x209d388c4769f544, 0x70e8ad81a712d6c0, 0x18e618db5b241cad, 0xd47f6f3785a190db, 0xee33d326db697592, 0xc0133b2c8ab7019d, 0x6212ce9b37597b36, 0x59c94fd8c8ac07b3, 0xb75580e20b400235, 0x846f814c92f4db76, 0x52b706f3323f668d, 0x284ccd1e71a1070f, 0x42de1e1e7e62e08d, 0xadce75d37937c9f5, 0xf27015671ece558a, 0x22412a56a3559ab9, 0xff13d8bbbb1c91cd, 0x83cffaded5dfd327, 0x6656726282256c66, 0xe7f5e6e657da4e9a, 0x6dc35a67254252b1, 0x7a1cc8aed3e8b5f5, 0xfb7fe9759517ea1c, 0x35db3f21e8ac587a, 0x0a3b32c2b62222c5, 0x86ebc76d20013362, 0xfd22033c16698156, 0x0b7d6878e5d5261f, 0x6cb86aab8cade5cc, 0x0186e38a4b93e756, 0x7234ca42a899e20f, 0x2576c2e71a4161e3, 0xf8cfdcbdb0c32022, 0xe50a6c345c90f816, 0x4cf69b6e26560f5a, 0xf035d71fb1fe25df, 0x39ff0a390ce3f7d4, 0x21928751f4b2140b, 0x63eb9be9cb376caa, 0x61134c6a42f7ff9c, 0x5fac915dec874d4a, 0x6cd454e00e9b1b8e, 0xbbad6f4969b3cde4, 0xe781e328c7752927, 0x6d543d918ed8ca4d, 0x44b07dff09639568, 0xd3292eb7aae8da20, 0xb548fc70f27ee3ee, 0x4ea744f31c981ffc, 0xb979b36fd349e688, 0xc2be28758b0cff1e, 0x4234e72b69160928, 0x7525bcc40e3da71c, 0xfae8b088157cd9f0, 0xcd520ef009b862ed, 0x9c684b3206b6e201, 0x03a18c124be050e3, 0x8d8d97703dfcd3bb, 0xe1e5128526a918e3, 0x56b1f91bb411b2a3, 0xcf129b1be917e949, 0x0aa8968474daefd5, 0xe4b745ada29e731c, 0xd7913c263b25f9a0, 0x2d3aaea5dfd84c2f, 0x6cecfd0fce3f273d, 0xd0e5d746b7ab1e82, 0x8d6922bad25001d2, 0x97806cc1f157a8f5, 0x26fec2eba7e1b1c9, 0x8c85ee20906a06da, 0xdde12accc358d2f3, 0x1e20ed16b8bc5068, 0x01ababa4030758d8, 0xa06f4d1dd75ca9dd, 0xee949588a565b1e9, 0x4762bea3bd18edd3, 0x2dba9476212321c3, 0x5a316dfc9118f020, 0x1bec8ace987e46a4, 0xab885c69038e875b, 0xa95354d1623ae9b5, 0x95e1cc46c42a9340, 0xa2f839edf76fa7ee, 0x2c28d9c8a1db5e7c, 0x948928363f5f90f0, 0x0757fc1e26b6e2b2, 0xf245230b34db888f, 0x683f1c2c27e5beb7, 0x3b85f46940197af6, 0x3e46b40a112b6b83, 0x864e7625a88e3ca0, 0x7e5984364c466cb2, 0xa9ad10857bd17810, 0xbfadb9f7e045b769, 0xecb502cb00da43d7, 0x11e6f043047f54b6, 0x3862f07c4db0abfd, 0xdec212b4ddb23f80, 0x3605c85c47436ecd, 0x8b37c80fa80924a4, 0x39c67e11dabeded3, 0x853da3f47532d9d7, 0x2ddac5f96a7000b7, 0xc7fe0adfee2a4902, 0x8742e55ab323278a, 0xe2d46fc06d2b72c1, 0x507109efce3767c9, 0xa97fa06d5f13c0b5, 0x54d679e5c4b1f6ad, 0xfc26d9ae2c2dc8d6, 0x1c48720ed2d273da, 0xa6d6cc35fd24bb9f, 0x237ab4e9ce169be2, 0x53c673e876aa9ceb, 0xa1f5c6a5ee086b6b, 0xc3f023805e00b1f4, 0xd68c5aed5c25f8b4, 0xaf88e48d10ec6b61, 0x9ba8b3acf4363c77, 0xe1c3bee2560b539b, 0x6381dfb23b78c85d, 0xbe60935c0c68ab71, 0x7247bb4def641456, 0xdfd58d5150a6311b, 0xabee54ceccaef468, 0x0b6f4093cef10425, 0xb7b0e8756bd4ef63, 0xcfa6156796ce41fe, 0x1640f376d4388478, 0x5b1e8fc28d2a3140, 0x1b2f4ebabb48fe0d, 0x50ecad92253d6004, 0xb056dd5ae2bb9a7c, 0x5b7b150bbcc8ced2, 0x2892bbc96a6f54bb, 0xfceae6a3aabbd5ab, 0xa4edb52f67c7f721, 0xefc68181d34ce47a, 0x652a946e177a00fc, 0x135d154cd28cd2ec, 0xd81321b19eb95fc1, 0xb1123d17eff6c815, 0x10ec163ce6162837, 0xd2a8c3a8ddf49b84, 0xb44db8f3396898b8, 0x5cb537c9435dc2cb, 0x6adf1bb4555aa69b, 0x2b2b34937603f245, 0x366dc6a366619401, 0x12cb56e84ac63b8f, 0x2cb7bebd22da51a4, 0x76c56f58f680110d, 0xf966c92840b3c553, 0x2150fb6ef409243b, 0x1694094dda05a74b, 0xec5140ad14ef9474, 0x8ccabec8d608df75, 0x098eb7ef7572439e, 0x4f016099a5413f65), (0xac60f33fef14e9a3, 0x2f7af0c548d5d036, 0xf75513473f00e14d, 0x393d9c31c0ecc664, 0x541e6099be2c4250, 0xdd9ea65ecd24e89d, 0xbc4b9108dd636534, 0x0574bef5752184ae, 0x72047a70e457b144, 0x456971aa2e9b4c68, 0x76749a98130b3fa0, 0x59292b65b3ad70a0, 0x96115ef618a0ea66, 0xbb70457711491b83, 0xc88ab40225a68029, 0xaa2816536bff98e6, 0x133fd3e4b42d3348, 0xeef1e04bb4a0c33a, 0x0c78ccb7a380e639, 0x57a6f47e925f7344, 0xfe2522a34cc06337, 0xc05ebb3ae14d1226, 0x58e6ec70e09e8418, 0x3c6dc7f848ebf1fa, 0x434b1fab03d094e6, 0xfc16d9b69f2ff1f1, 0xe8f644b2cb3c0be4, 0xed6e470a5bc386e0, 0xf2d524607afee41d, 0xd9e3a5ce625d3d84, 0x445af03de29f10a9, 0x515653bb5b17f900, 0x71a7ad14d7aa923e, 0xcfbcf6978d5d2aa0, 0x60f45e2204bfcdf8, 0x80a33f06a503996d, 0x2bfa8a71a020e61c, 0xfde9f1eb96e0e04a, 0x853369f528e68e6c, 0x2ce1daff6bda0093, 0x59c7554e279660ec, 0x46d0639b0960b914, 0xfc43f98b728b5e59, 0xfad268f8ac898e31, 0x9c225381d33d7da9, 0x8b4e9f2dd3b81b58, 0x0559eb72e0f87165, 0xa3cd793c7555cdc9, 0x0094ef30619429e0, 0x93db15cec13296eb, 0x73ef37fe1a9afa57, 0x90de384d4bbb7a9f, 0x0d1dfc879a679c27, 0x2e2e9e076f982be0, 0x4095196bab4436eb, 0x938e974569c10ff4, 0x16d1af8aab7cff8d, 0xa8a6249c60d53936, 0xa239c82cd46a5ca1, 0x94f362854b20f768, 0xbd67e627ca78cbd3, 0xb5884328f108eb21, 0xf2262141e3c75b3b, 0x0b276c57ab60a874, 0x6fa8f31190b90807, 0xb0af77f3fd7d1cf8, 0xd20e5dc7232c7a9c, 0xcb8e09ab7a3c99cb, 0x8e9073f0ecef2971, 0xa57e01437d524f22, 0xabe3fe24ac1777df, 0x382f0bf3502dd26d, 0xef79716628447c1f, 0x919e905491330a7e, 0x082dccdb66ceab89, 0x4b4cb672ff05e12b, 0x404f81490729453f, 0xf57b600697c3d5b2, 0x6204a3640312aaae, 0x62f840a82428773a, 0x011b7a80e8041ee7, 0xa93dfab562fa5cf8, 0x7a5155414b0b9fa7, 0x34f98c9d960011e3, 0xe56330a31e5c8751, 0x539be72a779615ce, 0xa7ebfb8a3814f521, 0xfd125a3da530257a, 0xa3552f576f152939, 0x7f3c3f88c38968a1, 0xc4b0bc896f2450e7, 0xd9f4121e3cc3c820, 0xd30770eabe841d4b, 0xd732bc49cb84d3e5, 0xb00c5eb51fd0b688, 0x697c104ffe1ae9c9, 0x830cf860debf4c2e, 0x6b0f34f759d2a7b8, 0xcd63072f288d0c80, 0xbaef7ba24f1a4258, 0xd4707936b3cda587, 0x17ae67fa0ea5045a, 0xae700c86da523649, 0x28140d753d9ef5a5, 0xd0aff83f51241c8b, 0x8827bdf2082e5fa0, 0x0f6775e1672feaf1, 0x388b9eb32fa7c616, 0x5ebb73c5bd5eee0d, 0xe8d0133c1ec57b90, 0x114504185b735f18, 0xc13dec6c220efeae, 0x38e5a8f0ab29cc67, 0x344abc46c78c45b0, 0x1a9739a8aa9bf511, 0xbe9167efe362b077, 0x36ff7638878dd932, 0x469b250eadaf7a0c, 0x7ad1f8bcf690ae78, 0x958f348e015e2092, 0x9bac0bb6d07100a9, 0x802743a4a901fcdb, 0x60e8d0ea0ea1c6d4, 0xdf54ab4edc3c5fcc, 0x381754f9e67e5af2, 0x3c35af9b3c65766a, 0x24b68fe61e4ad469, 0x878ac507e9cb9621, 0x370c0e927005fdf4, 0xd5a6d9b8cdb92f15, 0x1836a00e3b57dbd9, 0x43e8ec62f3ec5963, 0x88759e780356affc, 0x0397b28c5fe00043, 0x00797c9a4e079e44, 0x3a56946a1276ea3d, 0x0c9552cd40bd0824, 0x67477fa426ce0e14, 0x51f70f2ae24905c3, 0xf578516bb7cea7a9, 0x1fe4a2b427b8a0d4, 0x0cd38587f8354328, 0x03fdb43ce60edb4f, 0xe57908e768d9885d, 0xb8e81c3b0a5d05f7, 0x0b8d1cee48754dfe, 0x7e8e61efa3e49a74, 0xd6a4cfe6d0402285, 0x2ef75a157acab606, 0x51bb51361faddb1b, 0x9f8ff918bbfb7f94, 0x381dc83117d9b6d0, 0x9c64c64e984a111c, 0x2e3e23e2a2e99b7c, 0x7cb9c08ac71725ba, 0x6af3bd9e4b7c386d, 0x733f9184a461f2df, 0x2d179b072da80a3d, 0x327a4b44123691ce, 0x14e307ed70e470ff, 0x8408def64a5f0ea8, 0xde8e56787ca750d8, 0x8bdd2556a643bff1, 0xbecf74cbe3c0a6a9, 0xc3081555efbab872, 0xeb35c7de8774f7a9, 0xd3860d5e83cfeda7, 0x71ee4189b39f9707, 0xb611bf0e352ed4a8, 0xca3eb620b5e48269, 0xf5093fa10fea1d77, 0x31edbb5cad31a827, 0x4dd198ebe5f45a4c, 0x66dd6b4d2cba8e16, 0x4909d02e020d6458, 0xf49929d3471655e7, 0x5c40710b351974ea, 0xe08910e407caf00a, 0xda5cc7054654d6ad, 0x2dbbf6e58e626b6e, 0xe388ce61d37084e5, 0xa2610d1eabddcdc4, 0x7d874631dc3ac7de, 0x9e52b3430539ee00, 0x82e7f6c2a8aee4bf, 0xe6c425c7dc3058a3, 0x41521966e6a249c4, 0x27faba6a12753542, 0x8e8bc91904f95c27, 0x6b9018032120cac5, 0x5fb646a0ec00c629, 0xe6c01bf7cb5f864e, 0xaf49ec6930400e81, 0x7fce854c97c61473, 0x7b5e758fe38eb625, 0x0b5d9c0618b5c989, 0xfcc99c5c22bc9482, 0x6b84fca413ca8b68, 0xb554a5194e5bae1a, 0x7d298232c2f6283d, 0x05ddb8ba6252b9d4, 0x443d3fc6b30fbe50, 0xdeef5dc29f8e1cf0, 0xa944111642e76525, 0xb342b5b24f544a8a, 0x4138adb392e91fe1, 0xb08f54e3a6de964e, 0x6896b210ab7a1772, 0x5da9a88179ea1774, 0x020a8f7fa60cb9ed, 0xc25ae22dddd99063, 0xaf5840ca8b5c2cec, 0xfc7b449e244339dd, 0x73fbaafe93519e29, 0x6f69d46157cbbb7a, 0xc1b426ed233d55dd, 0xfe5f9c65afb8af29, 0x066a31461b2a68a3, 0xd8c5e28da71f37bc, 0xdcbb2d5d24b9d31d, 0x9628cd8a30d97bb0, 0xd7419e3d60d9be5d, 0xf010333ef4c82358, 0xf4b6fbdba89dae3c, 0x4a47ff5f74bf2d9a, 0xd2b55c8efd0e151b, 0x1ccec79733d50144, 0x680fd540021eb92f, 0xa92702ff6872f116, 0x21cd25592baba658, 0xd32fef70dac46306, 0x77922186f30810e7, 0x362ed1ead287aa20, 0xb1d14d1f7bf74a6e, 0x37f08d4a70496f59, 0x7187cef7d26b5888, 0x406410ebe2546681, 0x9bfceca6f2b4a259, 0x3d7b2e52eff20614, 0x330053eaaab001c2, 0xd7c1a87550fae02d, 0xf2f58cc3aa431f66, 0xa917ae293a689836, 0x40defa3b3a47005c, 0xa2d46ab5f811fd09, 0x9abb9ee97d7c4d02, 0x444a1d0e3cb8b66d, 0xac8b2a76aafccd70, 0x0111c0a9a0715e00, 0xc3f433bbfb5e46d2, 0xafe013a30de78e26, 0xdd0e24b79b03a31b, 0x69e6883af93ca504, 0x2af968c9df400dd9, 0x43f3f9c04b2a9237, 0x4579bd2653b7f1ee), ) mds_16x8s_rhs_0 = ( (0x7b5405da4f5ed4e4, 0xf8fd84b900031246, 0xfb022fed7aa7ceaa, 0xdb162985e2acfc02, 0x74f753304c9b4fbe, 0xab2fa79df052dad2, 0xcfa9d1a1b5259ad5, 0x806286258a8a459d, 0x2d48b6ded05ea7d2, 0x7ae661bfb397cc74, 0xe3bc905c3881b15b, 0x33b9057dce8b5a03, 0xb6e6202f32a357f5, 0x64ca3da1f5dd49ca, 0x53cc6be8ebefe332, 0xcca80a945b88ce44, 0x0f0075ea3885ae68, 0x97316bd6afbfb1b9, 0x7f329e27f674f1f0, 0x483a70d6f4c75981, 0x49686ed711a73e86, 0xb6a79ae82db73cd2, 0xf7867740485d4f34, 0x77cbb448b18a9c24, 0x7e0ebe7c7ad1d4e3, 0x9148d7464983a606, 0xe1f44933efee41a2, 0x57f1409e1b8f8825, 0x1f811b8bdc1be71d, 0xf5ad0e0277f9d5d4, 0x2f850bc498a62e9b, 0x27ba39a1e752248c, 0x1dbfa7f8545d5ebe, 0x838bbf46bf2409ca, 0x0ac3b854cfeb0502, 0x715225dc46964f04, 0x029c0e4058387870, 0x6b78087af5eebc42, 0x4fe958357c2cd8d3, 0x634be90a48431c68, 0x9b385d7d7875bc0d, 0x09c26b2d3d1330c9, 0x86aa0dbd0f1e9bfd, 0x1d7c6c0220466ef8, 0x8870d7f9359e0084, 0x3a812263d72aa96b, 0xc2e2e528a97aab17, 0x7965ca0146dbd2c7, 0x804403a5d2380f05, 0xa904bbf3e05a5c50, 0x9e6a207c9f6f37b7, 0x9dc44269898f6e01, 0x5f82ca1ad2d2c5a4, 0x72bbcdfada4c570f, 0xcade330ba925cd49, 0x873485ec57ac6569, 0x0e0f6753b501cfb2, 0x42fa7a089e97824b, 0x9a589f7dabc0bbbd, 0x17d3c497d5007f7a, 0xa150604b8cdd5633, 0xe33dff10e6c5e281, 0x6eff024ad8e026c1, 0xf90e719751845217, 0xbb5102da2352de4f, 0x21a981a5bfe52f80, 0xe9bf702f64d37c29, 0x2055dc40943c719f, 0xc7eb4e1ecc5c16d2, 0x195a957ed9609823, 0xfcd5a64928cd462b, 0xd4e8246cfbfd14af, 0x1bc274a42b3a7097, 0x1fb1bd5f55e7d517, 0xd51bfd86da1e618a, 0xd4d4c7f3a179147f, 0x55943280700c1295, 0x4b9e96573ea6e312, 0x90791350e7bd6341, 0x83d07cf43cd922ab, 0x46f06a9aff574af4, 0xef548a7dd178d39f, 0x77f4ea8f8e0bcbfd, 0x900afbb292c1b1ef, 0x4ecf00f896161bfc, 0x8d619d0036c9d73f, 0xe4f8d71b83eff881, 0xec97917a8d2623cf, 0x025d58eeb3292d68, 0x53a5e90aaa48f0b0, 0x72dac7f32bfb6daa, 0x529eb18f5077a1b1, 0x50a1dcdfe620b3c9, 0xa56248c279d4416d, 0xe0b0dbf11608f74b, 0x4b2fd92def200429, 0x45addd0e6b1f10eb, 0xdcb9b263c254146a, 0xa3ae62f397a83e8c, 0x43cb570af792e8fd, 0x838f40dc0aa9360c, 0xcb6a7c6147119c98, 0x4b3a2d52d7e8e8ea, 0xd226339807dde2d3, 0x7ff8a2d4f113fb82, 0x3e85ee1d8c83f977, 0xf5b69d102605993c, 0x6e7c6fd5ddc468a7, 0x791b948a89f90930, 0x7d295f6345ec09bc, 0xb8896ada129d54bb, 0xeab51c407522dc9a, 0x44b1be3b403f318a, 0xce4f6fdb264131e4, 0x9a222b26bbd5acf3, 0xdcbacce86b0b919b, 0x00e78ac875367f36, 0xfebf5fe592ede037, 0x6d348734cbebf898, 0x7ccada20861c0e1d, 0x45990fb85f886b9c, 0x604e7b9712881fa6, 0x448c5d70a4e87769, 0x19e12df50e5c957b, 0xb824e6fdf3464e04, 0xa9bfd6c6fc1565e6, 0xb97513862e1d8e53, 0xf0b10b6401874145, 0x98407247b8c08806, 0x448246483c13a393, 0xead1f448292bda60, 0xd90ee1311b47e413, 0x69f2c70641dd92f8, 0xb353ce89dd475070, 0xc6930485f04e000a, 0x3376426a9d8c7d3e, 0x89a6e5e853719af6, 0xb8c90c9fe3f83fb1, 0x7582c8746d66125b, 0xd47d34339c8727fb, 0xda38a8f30aa7982f, 0xc1ad5c530be777a8, 0x1627ddcded1d9f28, 0x6b054644a3833c04, 0xca7c89447cdf6aac, 0xdba1d71f2dc29020, 0x0aeab647ab987a32, 0x568a21e5c399430e, 0xfa07bfd2d0929145, 0xe13c528cfbbb4672, 0x152a0423e72d3c30, 0xa6f16733fa3eda50, 0x783a3336af0e4ed3, 0x6f24a5a36f8ec102, 0x1a9c2c7a8a61b3aa, 0x64ea6282e3c44fed, 0x125f7e9ce6c36680, 0xbd92e7c6caecbc61, 0x6384910802ce8184, 0xa4be37009b5068fc, 0x4079e8895618fa0b, 0xff4a1c99cc0efbf3, 0x3a5c71681ab257e5, 0x6731622f8ecec4b2, 0x8637eb9fce55e605, 0x286148d20db27106, 0x623f4697e3377fb2, 0x3ead1095df86a4ca, 0x38d8bcea4f58d873, 0x7b095776db75c5a2, 0xaf688d2eb6ddc956, 0x527ccd607071ef97, 0xee0d909e4886e333, 0x62f532208b74625a, 0xb8e86d32c6fae00e, 0x534e104743ffa7fb, 0x99951175f6182d1a, 0x4af08112f4db7c5e, 0x736e5c82f8200194, 0x75a01d4b27f49dad, 0x9ee9bc2f2c509ec3, 0xefa3c464a2072f27, 0x1aa97940b98a1959, 0x679ceb874f1dc9be, 0xb6e4f60ebef750b3, 0x7c3e7a9c4a142d87, 0x8e92a5cfe1e1259a, 0xf89a74b14c1468d5, 0x84401d16aea7f4ea, 0x62599cb07c8a9a4b, 0x38db6ecd2975322b, 0x7ce1acf51d719e44, 0x1a8a99ae0ca37660, 0x1076138e64bfb63c, 0x9bbc76b47eaade6b, 0x6a1ba01bd9c6ba21, 0x52e8905fa31fb7c0, 0x789d2c12bd5fa4ea, 0x15c52922d025c876, 0x79b7955df6555c72, 0x5ff888139cfdfd00, 0xcd5708248327e6a4, 0x87af7cf2008ffc15, 0x322221cd5bf0648d, 0x3d46288b5ad7f419, 0xa2a9cf6036baf8e5, 0x0fbffffddbfce771, 0x0a22b23227fed985, 0x05272d336b17e055, 0xc6d3a2a6864a3129, 0x052153252a2a20e1, 0xdc541a9e0ffe3070, 0x3979e4bdf2802ae1, 0x90ac763213322376, 0x7243850c65c9d334, 0x45a61655819f267f, 0xc92fed5f61d886fb, 0x7bfe3460ded5ac13, 0xdd6bf7ef8f5e7d6d, 0x08be0cfd70fd1481, 0x4bb614fa45ef1ed3, 0x06d01cc60a3ba058, 0x20280f1be2ce9639, 0x491f5e3de4884ef3, 0x4ab7c708db3b764c, 0x2fc9ed9d47f82738, 0x7092ec34232476c3, 0x12b46e47e9f302b9, 0xcb07a63268042437, 0x215b61e2986886bc, 0x904937f0803ee368, 0xe2476b0bac87dd72, 0x6f56e320d447d14e, 0x581c46c7ef86ebb8, 0x7310c73b142c425b, 0xa266517a7c6acc7d, 0xfb1ceaa18a6cf0ea, 0x4a7347a7a6e89155, 0x066e8418e37ff2ac, 0x1f998578b3c0a266, 0xf13ef4d3f65f724d, 0x66a91808510cca1b, 0xede5e0a875eb2127, 0x90b0a6d4ca49f102, 0xa7724c8db3049ea4, 0x933bc3660bf1547e, 0x676d57f6a686fd97, 0x0e21ca4f3c9d322e, 0x6672940363d546d5, 0x513500f399bdd1c1, 0x47d0a89ff271f698, 0x9b2b11012c9ade70, 0xe851d3963ee9bcad, 0x9e23e30ed7221589, 0x463086e03c14eef2, 0xeac9d721211834c6), (0xd43863a29fce4caf, 0xd1f77c2e61c0ecd8, 0x01f7788ad2eb015a, 0x966af10c5cf1beff, 0xe8e7137eb4cf0eb7, 0xb2a439fa2017014c, 0x9b0b78a36de1796f, 0x91f12223e6273a35, 0x5947e435e134d548, 0x4a07df1aeabc026b, 0x486eea0975b6aade, 0xdb902085adfacda6, 0x71867eccfe2f6d28, 0xb340cdcfb22d7b38, 0x8519fc79a34be08e, 0x59660544fdbc86f8, 0x6757fef209eaddea, 0x9388f19f55edae77, 0xcf5405771f31f222, 0xb791072ef03eeb91, 0x796d6bb31046d979, 0x70a69af15e8ade39, 0xaac233b370388516, 0x57ac23719198ffff, 0xdde1335e79c3cb13, 0xc7ea82ef550e6ad8, 0xb0f05c77ac59464e, 0x53c4801c3a95d7ba, 0x06a38b7a8347bba9, 0xaed3b5a5de2d7b8a, 0xcc183e7cfb5fbb29, 0x55de503e98fb8116, 0xc8938ac3baea81d4, 0x554f08e865be7945, 0x76280e298219eb63, 0x6c1619f21f533a35, 0x488dc03ec1651d7b, 0x68fa3ffa7e4c006c, 0xebe2574d06228d85, 0xbb60ab8cfffbd547, 0xf3ee3eb124537ab2, 0xe5c7d4ca22fe50ce, 0x6bf06a74c5222a90, 0x6fe52288be953a3b, 0x470c8c5dd3587dff, 0x2d61882ce837899f, 0xc1132ce841b6ece0, 0x3b95af4ec78fb231, 0xbc64ba5e09c79bdb, 0xe58a3e48840a572b, 0xb06ddd7df1603abf, 0x24db4c93860fd48b, 0x9695113ca974924f, 0x2b532322275453ee, 0x0a002e8eb550989f, 0xed136b8214a76a57, 0xf73121a75e93bfb6, 0xcd89db30753678ec, 0xa1ac8eb0e05dc867, 0x50722aba8b18be0f, 0xb221e00ee27d41e2, 0x5c6cb6d5929424e7, 0xd293306e7f335786, 0x68237d69a65a7771, 0x04b28710cba89102, 0x592dd5c0b511ee48, 0xd19182480755626d, 0x38fe0c1ae869eeb9, 0x16290f5d08fbf89a, 0xaba2ed0f6d3ba43e, 0x514beec64682c5c0, 0xdc883413b0b7ff2d, 0x1e23d38c8911d1bb, 0x171a6ce65ba94b26, 0xc306e7f4825954b4, 0xb32ff62030b60d54, 0x54f8e49f214d2827, 0x110b0edcc507b21d, 0xa0118ad74c2c74a7, 0x67947e4f7a7450af, 0x047618f9da2b39ab, 0xffab1ddb88ccd140, 0x300b87f8cf45c038, 0xbd90010489cbd1b2, 0x1013fce8b1bc5a25, 0x79da66595e992633, 0x4f03998c0dcd44ee, 0x1f6c3f6c5119f877, 0xe0a6e1e128374520, 0x719b9d6cbdce375a, 0xbe8331b9ab75d84f, 0xd6badd6ae474e662, 0x5d2240e6658b6613, 0x97fa9cfc9a6ba857, 0xea256a8689c7f0b9, 0xcb83a4bc1f9499f6, 0xb11eb43db2e57da9, 0x68e499f61d66cc94, 0x0e6d18c599dd8818, 0x8935ee2e08b44ce3, 0x911c628f2fa3360b, 0x638ba97dc5f4a206, 0xc68fc56b2819e1f2, 0xae63c95f76b5874c, 0x000d8e628767ca7a, 0xcd5b606b23c4a326, 0xeb3cb092decbc573, 0xc65414f9bfe88187, 0xe037249b6c632e57, 0xa1a68dccec6d9cfd, 0xfb517a5568110de9, 0x504991977c9bff46, 0xd00436058087e88f, 0xbfe29edfacfd91e9, 0x365c532a2a92aad9, 0x5f94f35832674fd6, 0x662989b76237b529, 0xd08b6a1089644f37, 0xc0ad1c1243002312, 0x56ed52bd1f043bc6, 0x439b19e58dab04de, 0xc221d4139d7e1e5b, 0xe6f144eebd104125, 0xa931e8a55725ca10, 0x3eeb498e6d2dc5fd, 0xcb12122827b85f6e, 0xce76524f0e9bc1a1, 0x35f1c031e7ef0b81, 0x37abf9d4986184b7, 0xccd035b261a940e9, 0x8ef8691824b8801b, 0xac7e10852887a5a3, 0x5836372db1ab952f, 0xb475ff08be417661, 0x83f3dd426e5a3175, 0xa1e697956c4f2855, 0x912c215e372288d4, 0xf9f1ab1e0b8722d9, 0x933694cdf3be5548, 0xa6ceadb7dfa0c1a3, 0xb25c74952603d21f, 0x23c2e214d14dadcd, 0x9cc4f666029fb345, 0x590383b005d8821e, 0x4ed00f0c3b9e55ce, 0x871740afddb0c984, 0x308d3c0524105fa8, 0x8f3d94989664b39f, 0x8fbc2c4c512fc699, 0xc9d0d6474d3192e8, 0x63442f5a7048c7fd, 0x49b37fc9b5445cb4, 0x6ebfd4ee67f8d15f, 0x8150ee5543607d61, 0xa60912285d1799ab, 0x46c3551c2751b798, 0xc54ae65e22f84091, 0x5b0113f2cf31cf16, 0x35a476e47e01d921, 0x0c6354f0cbedcf8b, 0x1a732d689b5ec1d4, 0x7432facd0b5557c0, 0x751f3b9d974a4f10, 0xbfc8dc0e7ced256a, 0x0b53a07e6a09fe44, 0x33624b164c66fb6f, 0x3c93d41ec476fa84, 0xc12b3407294f2b4f, 0xb31e9f43d49b153a, 0xd52e81ef052573fc, 0xe2305a62880fbae8, 0xe0a9523c17590024, 0x2051a121a2c4bbc4, 0x814615ad23bdc77a, 0xe701fd9e2b8d53e6, 0xa0f1b1135c2671b5, 0xc20b9f2a6942a174, 0x917c6bee95eded6e, 0x2db5fabbcd0913da, 0xde3e529da4705c4c, 0x8eea9b10353b1bc6, 0xeb80b2db7473e4ee, 0x8f00c3a62bb4bf84, 0xd282fb27ffa88a04, 0x05935de2aafdfbdc, 0xe441f07d0f3f08a7, 0x3a356eb326948d44, 0x73a5f2eee0699fab, 0xf25ef55e5b93a90f, 0x763b4f632293c364, 0x8d8da4d4ed99fa62, 0x9eb41689337b5ac2, 0x2acb8d76674d3892, 0xa27f063821025f7d, 0x949873d001ef3210, 0x0c8326aa5fe6166a, 0x15425bd94d5df8f2, 0xc9bd96289c1b5656, 0x2fc1bb264eeed1cd, 0x50ed7b3c60445587, 0x6749642b54b05c57, 0x0f2058c76c4d4330, 0xb6a97915c10d330d, 0x89a397169815dc95, 0x95257d30085d1f77, 0x996ca839cdb80677, 0xe65260b79e0f7396, 0x49216926970451e5, 0x54ce6865efd99307, 0x22ece3fe40ddd1f5, 0x3604944a66a5fae6, 0x9a107654fa893440, 0x6b60e2f570d459f6, 0xe935e41cd7ba42ef, 0x6f1714641be49d7e, 0xa56dc49ac49e6e4c, 0x9537555179eeb30a, 0xe4e80fd3044cd407, 0x2021a190c0791006, 0xb07d410d97d0c2d9, 0xba5c9d52e66c1983, 0x9e00876df2c4faf0, 0xb204711935f47c59, 0x9e26c4510b6571ad, 0x02360526c218c4be, 0x6cad5fb217f9e8d3, 0xf031356c0301310b, 0xb8a8c5ee78bb81f1, 0x31279f7356064f8a, 0xbcb162fc9ef3d102, 0x873a9a1b1b005b20, 0x3a890ccece158054, 0x8ccea8ed039eb080, 0x3bbee8c4a60c41bb, 0x6eeaf7174c09c9e7, 0x5ba2e2ce58159174, 0x1ea1ae9c1180a540, 0x24524d4cf173f740, 0xd7573147b66a5554, 0x7ccec589c714b586, 0x24c4e9319b2ea96a, 0x92640db5c2b47d01, 0xfadcebcd8d3e0a5e, 0x82a3f5fda9bad37f, 0xabbcbaca04512f77, 0xcb7d30d04aa7c1b3, 0x0c74f1a0f2b69836, 0xa8f9b0431089e767, 0x6301a2346da8c9f7, 0x56353bc8bbcbc8ff, 0x46f217a40146f427, 0x46cd44b4cbdaea73, 0xc6783026936ff9ec, 0xe376abb1576c5cd2, 0x6949d69f5ea8c483, 0x394c0b39f10f02c3), (0xf987ce1b7de79c34, 0x89ef6028495653fd, 0xd5c7fd7f395e930d, 0xdb0c980cd98883ea, 0x11775433c7a3a517, 0x0b345b26b7d529a3, 0xf297cc2e9688378f, 0x6ffaf6e5165fba89, 0x2fade6df306d5e2d, 0x34c6d9e698e1c2fd, 0x46f57ec72b828b4b, 0xc26181f80ac0ef6a, 0xdfcd3eb1cdca3306, 0x0ce4b08441ef5246, 0x99a6fd50ed34b608, 0xa004bf4a061b4c7d, 0xe1288c03b8402f3d, 0xd424feda3ff7a1f5, 0xb83f9dc40c67ed9e, 0xf62f690aba884e63, 0xd69aa1e11aeb238b, 0x76afcd45d6b6743b, 0x0f156a99d72073bf, 0x56f35eb5c92a7e8f, 0xd1b77c80ef26c743, 0x4789e8870c67086e, 0x9b65a6c56bf72cc5, 0x842eaed11e8f7532, 0x85a121639d0aabcc, 0x5741890139807e12, 0xd3c1dabe78701253, 0x47d3fd902f81c50d, 0xe73793709c0e97c3, 0x5cd80fe24e284acd, 0x9cdaf886bc34adaf, 0xd1c88626230d3964, 0xa649ef0135eb7743, 0xfa4e5d75fdae1de7, 0xc63c28fca43f5833, 0x626c6f32c7d06ba7, 0x7890938153a65ec4, 0xdefef3f714d5d1b6, 0xaa3807c9991714e2, 0xe1d3a0a56b06da34, 0x41be8990459a7ecc, 0x9f34d86fb115d61a, 0x100836311a78faa5, 0x5bf5e0fe1a4addcd, 0xd5d8163e3ea8cdaf, 0x590d54737fcba052, 0xd905536efc13387d, 0x2507bf019a120ff0, 0x6988cd9f1941918c, 0x7cb721166b4bf763, 0x111a086e26e1350a, 0x2fe4fce74c800be2, 0x1cdc3e575b115932, 0x81764e96341b6fb4, 0xd8475b6fd8c1c93a, 0xdbc048e39d954afb, 0xb39df7d6da996002, 0xf5322d04807585c7, 0x35f55702dbd43521, 0xa1c2cca670496b4a, 0xd4a03ba1b99c34be, 0x35f22f6e627797ab, 0x393093b3f44dece3, 0x1f92f76306e4407e, 0x69c203b3cd91cd5e, 0x80b888dcf5411643, 0xe9823f72de5e24d8, 0x4e7c6022d2fe926e, 0xbb69aed07d4e781b, 0x18009b0716413eb8, 0x130fad5fbc14a109, 0x9fe7c6debbd3292d, 0x5057cbca8c8df6e6, 0x09e60c0452703f3f, 0x452f8904c765091c, 0x42cf79a1d3b50780, 0x2e822737f0b2e65f, 0x2517fbacda7829ca, 0x6aff5a14c89f95e9, 0xa0cf91e5afacd706, 0xd7113a77348d3ae5, 0x403e7038fc5659f6, 0x857b8bc141b72a32, 0xa8e3d50d2b2dac5c, 0x87757318d75ade7e, 0xc1f73a4b473a6047, 0x6d637def9cf80b0f, 0xaf1c2b9e14d92e01, 0xd0d5315289a0061f, 0xbdcca07023cf38f7, 0xb4475d7cb25c8e87, 0x374a5dbef0036bac, 0x3bcae5d0a06ccb7d, 0x93fdf5955c93888a, 0xb57a781e3e603608, 0x32ec12d38c68ecfc, 0xeeaae41494c48da6, 0xf07f2a677ba5491d, 0x73b91f54e256bd56, 0x442e20749ff8ec5d, 0xcaf159fc7f904888, 0x06d73433459002b0, 0x7eaafb855a6484b3, 0xbd2481c09eb5fa3a, 0x496260a0e81b2c46, 0x66d68337a4c8651c, 0xa6b4af65828ab3d8, 0x999a064eea8b3e6c, 0x38910586863acfed, 0x6f63d40949877e31, 0x327cc52278e80276, 0x6dbb8a53732d9d2b, 0x5b23563622eb32e5, 0x0a64a26d96a30127, 0x3d20c85fe4dca4d0, 0x1d25df176e6956c7, 0xa1e0480ad97b95ea, 0x7567ab11c9bf4222, 0x06596b0c1a3c8cc2, 0x5b5a8be8db9943e7, 0x0e2c4d8a47ca1f44, 0x4d349a8fe9f302c3, 0xd03dd7b3ccf9f24d, 0x1e265ed3b44cd57c, 0x796eee12c3d8dafd, 0x7f534dbf7eaf77b7, 0xeaed78867981a6dc, 0x66cd4fdb8f041d17, 0x6a52123f6e65cc12, 0x0dd3003d6bfcf432, 0x2fab1492451569bc, 0xc1c1f7becbed77ed, 0xda5ac0c88c84b0e4, 0xac66ca142c7a64e6, 0x39a90859d0367114, 0xea08cdbd2af6416a, 0x7096f65dfb91be5f, 0x328b3003cf39b55c, 0xac2a5d20dfeb945d, 0xb629781894d51e99, 0x6dd436191156896d, 0x5b77f8904017037c, 0xf597b0db3c2b77be, 0x852cb51886b4c94b, 0xefccc501d48fc994, 0x1d7104975de00816, 0x50d3d03f9ee3cc84, 0x70cbeeb9f99dacf3, 0x75adb7ef2758bec0, 0x2a14cb5b6e608f6e, 0x03516d82039635fa, 0x9f7885dcd122635c, 0x079779e43e364076, 0x5025ed998c5ac0c9, 0x72ca6eb6d550b6f9, 0xcd53d8db38c64d5a, 0x466f6acc6661b4b7, 0x54d7e8eea54256ba, 0x6862219ff84c9978, 0xf7a654e0338a2c37, 0x8f9645b4c299b845, 0xba7018b7befc74e4, 0x031f90fdffa1e8fb, 0x2ca8ace55f3d49d6, 0x4f249eb71889c020, 0xaef307183bfa2ee6, 0xcdfaa776d6bc2d31, 0x8a31e61da5c86831, 0x5921bfc7c3799c3d, 0x2726cb7cabc8f6d9, 0x4bb8d9ebf89a8adc, 0x120cc437369eb077, 0xed2a23e863f665ac, 0x21a75461636833d2, 0x17147ce594e8706a, 0x3acb409ad45d1612, 0xf29b1d44d17d5f42, 0xc45798c29f3f5f0c, 0x61a699424ca2c3c1, 0xc97d8e5f2db3214a, 0x4675e89387ee7265, 0x17b326971f8f28fb, 0xbbd41befbfe1f48c, 0xc36636d10d861850, 0x71f609b0cf6d2515, 0x9c1707c85f10bf95, 0xd883ddd38480e862, 0x34d67d131a1cb204, 0x95e5fd6bdb3e5bb3, 0x05b3f289abed58b6, 0x301ccc825b9bfc62, 0xe3cc0836a38517dc, 0x002c38b90c385fd8, 0x9f90c36f990a07c2, 0xb1566b1646232f91, 0xa717b465de39132d, 0x5dbeaf0d78afd510, 0x934c0a2a36339574, 0xca825df90a486b3c, 0x85ee009effeabb5e, 0x7479516fb8b57885, 0xed69080938dd5ac3, 0xa999752f3c7a1cf6, 0xf0d98bd4798789a1, 0xb520f40f2c640546, 0x714c42179a7f66d1, 0x8e574253ecfe14f8, 0x4d226ea04ec9d9b4, 0xcb6a76870cb1bb33, 0xd83b952a79288369, 0x36b56685b69ea825, 0xde2e13dad1109304, 0xca036cdc2ecd2525, 0x5af5b81cc8bd782b, 0x684d505065707b99, 0xf4827631a966de08, 0x138ac651f449474c, 0x13a66326495429c2, 0x974ea67295d0a630, 0x2b18e9f5f5c8d3ab, 0xd682630a0f31e9b6, 0x097a83277bd33daf, 0xda3460a7e8817d1a, 0x64bb4bb0002ea83b, 0xf4b1f183da9a134c, 0x1338ded8b67342ed, 0x1c6a0c2aa8a6e09c, 0xeb01c62fc7ecf5a9, 0x1efb0a59e39fdfe3, 0xaec4128045a2b2df, 0xcc1949c958862357, 0xe6c8f0593ac344b4, 0xdf4cf97d2110a0d5, 0xf5ddaa2d4cf81a86, 0x0b41a9b6a8118e54, 0x868a4cc9e9d5ea63, 0x5e5254120879697c, 0x2589490ebd6fdeb2, 0x8f2cdba1ed7da07c, 0x399f6cca1a8b8a63, 0x80801ffa53037696, 0x8f9db099b5a55518, 0x30482c70f1aff592, 0x73cfb47d90386028, 0x53dfd2899f3d6937, 0x33a917a7ef991675, 0x006053437a2e78f3, 0xa797ee98f01ee2e9, 0xa0158b16aeee887a, 0xb176a1aa4a52f3b1, 0x44e58752993b76d8, 0xb5b1e72c2293f0c0), (0x4dca1a825300db02, 0xb9343bd268f6bd2d, 0x93daae2b2af1e2c6, 0xf4af6316c3bf64af, 0xf2492e73459faac3, 0x3d06bcb9e85eb893, 0x2b927d35681d4af7, 0x1aa685115c4d1d03, 0xb76eec2a12c4cb7a, 0x27a6e93ae2fac5b5, 0xe0d100fb52b0b28d, 0x896c6c1882c25322, 0xce90a23ac650b354, 0x732b511e15215002, 0xedc310e4dfe7cfb7, 0x1ff7a2801ffd513e, 0xcb5369d898a38a43, 0x35ed7a9b1bc63c00, 0x46a86bcbc47a245f, 0xff32b95bd0d994d7, 0xb34ad7b95eaa8979, 0x4e9dac9fa9ac657a, 0xb10bc1331d8a97d8, 0xb07aaf3e0b5f98c4, 0xfe565051945e1361, 0xe38e09297ebea2da, 0x48efc0ba0eae451a, 0xaf5aa5e74e76bea7, 0x9867545d957f0fa1, 0x5aec19fbb0f7f31f, 0xba0dd90eceb1d07b, 0x7748d5df3a89d021, 0xd06b63d69b9569f5, 0x80459b4cb6096109, 0xc49fac84267db83c, 0x2bdf4b8adb1dc88f, 0x8d298a1785a47700, 0xa1204c985ce35562, 0xcb977ffcb4b6d9f2, 0x5f653878188f404c, 0x0cde2bc627bc615a, 0x26a2e6ccf5490dae, 0xae8b5ab13e431213, 0x1a499faef00a014b, 0x8d7b6bbf787ec10b, 0x5bf078b658febf76, 0xdc06b46cc52482fd, 0x4a24118085d036bd, 0x194ca853179146d9, 0x6d59cbbc46110b15, 0x07ea2bf3d93c00fe, 0x08c4a4b423edefb5, 0x8cd1633d0301c30e, 0x8ae5efedcb872a67, 0xfa731883f816d9c7, 0xe773e1fe804ac05c, 0x3aeac5e3a28ebb5d, 0x9fb62a187829c56e, 0xbb4681390e72748e, 0x06d68673ac353e66, 0xecfa28737fd6d6fd, 0x9db4e81a3f246066, 0x5f1260a51ca423d0, 0xebf80331bc7028c3, 0xb8a4842101222847, 0x3eeb7cac75459c74, 0xab15cdb3783bfc70, 0x01777afe877d3a43, 0xe537abe872b55bbc, 0x1c0dd002e7005fff, 0x7b91d3143f42cb8f, 0x4bcf48161492e3b5, 0x1ae21286c60f3a28, 0xcb0e69314caab217, 0x47363d76585d58a7, 0xcd2312767345f98a, 0xd6434ff6466c1615, 0x582ee858bde5c9da, 0xce745f31a836e71f, 0xe45343ef7d3731e2, 0x99772c3dda2746b2, 0x8ca2c3467d7564e0, 0x7db814e3e5723b16, 0x91acfae6d12d1c45, 0xe6774ca8d4f59a75, 0x219829a3e63ef13d, 0x47ea3c953ed22008, 0x9e4ee4a569346c65, 0x6220aeb6a04b3f17, 0xecd6f0d75a4db7a0, 0xd8f2d43a81ae0084, 0xa58cc0c1ede529aa, 0x78d531ee93e2e698, 0xd397ce175f0a27dc, 0x14817817feb77286, 0x724f9c7861567855, 0xd658024518be7ce3, 0x9e0be6a6c4be7ab9, 0x94cfd2e7e2f50a79, 0x9f73132d46444702, 0x5f832eb06080d210, 0xf7ae4d13fd0f013d, 0x8f4ce851d4b94890, 0x4762b29566ca688e, 0xf2ed7514fd654743, 0x4ce004d1a5bc5b52, 0x5f64cfa948688fc1, 0xc466d37d6cfdf310, 0x6b85f57ab01d9bec, 0xb0caffb1f0cbfd10, 0xcbf58491ed121b71, 0xc0f25264891f921e, 0xe63e63d709ca96b2, 0xdc399d6fd29544cf, 0x765fe2917c154fd0, 0xa328f5d5a3528c89, 0x694ea8e3b91d2acf, 0xdc9513aadb87c05a, 0x9c1d2ea58e20a5ef, 0x78fc1752de162b1b, 0x8b1a9812f62b876a, 0x089f045991fb60c0, 0x14346dc4b32b9a9c, 0x239956efeaa52413, 0xcf5593d9701cbb40, 0x1c48feb4a8b50ac6, 0xacf074285fc4eee3, 0x7280cd9421a95450, 0xdf1b74580d6f4a1d, 0xd62949bdf87b839d, 0xaae54d7deee3b2c3, 0x7669224de9304206, 0x11638085cf914c78, 0x91fdc62bd0b8b3d4, 0x18f9c980a9d7367c, 0x0f0fb48d869ec40d, 0xaad4152fa53fbf6d, 0x5c45ec44b363ca61, 0x39a28322a46effb4, 0xf58ed9ddbc4f831e, 0x801ded20f9f1a994, 0x21fe23bf1490c4d4, 0x0295dd5e49bcbeb8, 0x8993e1b5e30f4021, 0x41270ac9ae891f0b, 0x6b0334a55893b556, 0x370c856168ea69cd, 0x78024bc77d152b20, 0x38431962bdce628e, 0xa0f0526f53344319, 0x4664b1bae48205ea, 0xd09d22145b93b783, 0x19ef61c56d544a6a, 0x1814f785d9ba7411, 0x6ed26bb559773b9f, 0x52130a50b0a9b9b7, 0x9e7f44e60d22092b, 0x741b09a280cdea0a, 0x77d378be36890a65, 0x3fc39d9665e4db6d, 0xad7278832795abda, 0x2aaf6f567d8a8c7f, 0xb468c894eb9cde9e, 0x2994d7f89d88be5f, 0xe59c5f327f7945ae, 0xbeb4a543a4156313, 0xb3c75d9d166d4e6a, 0xba4ed81c9ad100eb, 0xaaab815961165c2a, 0xe0c18303f53113a4, 0x36e0f0558472405c, 0x427da74c44846014, 0xc145bceb93c74c9b, 0x7ec8c49276f06a1e, 0xf52796970543a936, 0x6838040ad34bf480, 0xeb3dd4509894c76f, 0x88e3827fb1368fd0, 0x549adc52e546824c, 0xfcccb79e3c6bdf3b, 0x19bce5c64ed0c97d, 0x0b81c8d285fcfd8b, 0x0fedd0d4e54cc089, 0x5326545c483a6e9d, 0xddfe91ea51c0375d, 0x3f45304b3b91abe2, 0xd571dcba54c57910, 0xae04061cca379ddf, 0x3091a57358ae0acf, 0x281d46e786d96058, 0xca5ba1a471819be1, 0x4ad023ac32842f04, 0xf5524eaa7ef0fc57, 0xe47d219b13ead088, 0x11786f211efc8601, 0x7cab777e435be69b, 0x65d36c47ec18eeac, 0x44dcf19b1bd83139, 0xffc70a8189447fbc, 0x6c25fec42789e4f0, 0xf031361f7660b542, 0x36572c375f7854b7, 0x0476534008f18314, 0x15f56b377ef8db4e, 0x2c5d60c26e1d5b73, 0x9d6233cafd3e8f29, 0xf993c2f5d6d54e98, 0x8df422a4e1977b45, 0xf8239c98c8028caf, 0x897bf629544b2065, 0x478d36da56c6ff94, 0x1a6e7baa1431a5d3, 0x93cb87f8abc4d5ea, 0xf0b34023d7b3fd64, 0xb2a7da7c1ce31446, 0x9ba93b3fbce31046, 0xe6000e4a17a68521, 0x7c42245a9932c590, 0x9bb04b26a1232a50, 0xecfc64847caf1ed9, 0xb4b2aaca5efdb265, 0xd23ceddd16c2ec75, 0x0b642e421a6d53f3, 0x6c90c567af3d094b, 0xec70d305ebfbdd77, 0x3df1aa03dcead9d6, 0xb220618b3f55cefe, 0x78ca2cced5abb0c7, 0xa74cdd64078514f1, 0xdef8b1678069e1ef, 0x88e888ac5bcef390, 0xe1d7548bdc6522ca, 0xac9665dbd5a667bb, 0x85459801beb5c5d0, 0x04b4727d8b5d6e80, 0xb8ef83cf166a63d6, 0x308561bbf9e1c92e, 0xc2fd7a2a0498967b, 0x6794b01ef8e14af1, 0x4d203020a0fa8b82, 0x73e1b7f062570657, 0xa91eb44f0b799ec6, 0xb271aafcffdf56ec, 0x490a0d530d4eda82, 0x5ed0ac345e1aecbe, 0x3cdbb6c849599a73, 0xc4b42cbb2e82a921, 0x41c2b3235f74dd25, 0xd56728ca00b27e00, 0x3f7dc62b1edfd462, 0x5a493aed9b9d8a4e, 0xa9d8d8d66f8bb4bc, 0xe319d3395a6d9caf, 0x4f3deb2eaa6a3cd1, 0x93b57ff0fef5469a, 0x65beba75f7e357dc), (0xbf5bb7aff5363401, 0x9c3ddf3ecf6e48ca, 0xe2d27b1b78479892, 0x2a2d8d13c8a6b4c4, 0x0e3e4d92735feb63, 0x2734f1a3fb75a234, 0x4f565b236e43dfbe, 0x9417529599010d99, 0x09ead2b8decd5dd6, 0x208b87ed414200c1, 0x0a23ad24cb9dde5f, 0xeda93b2f1ef8ed2f, 0x554c86f48765260a, 0x581dfa747cd4856e, 0x42782bdc2f35d2e8, 0x0c72609ce619df16, 0xa9838ab3c8a69dbf, 0x04c4080316bf852d, 0x72df9b3a26a78dd7, 0x95e145f6c5f11a31, 0xce523ada8ae99282, 0xd53688bb8aa8c33d, 0x3072282aaac91a87, 0x211b3ab247f5a42c, 0x6ae30c7b64c04358, 0x345d6ca85c39cfda, 0x941cb04cf007bf5a, 0x5a4aee9c9ea5dace, 0x9220411196a65a3c, 0xd052b4539a6a1cbc, 0xadeb6c143d7a3e56, 0x772ff6d3200d2ea9, 0x3d2438abc2400945, 0xac2a15c31160a41a, 0x2299cc3118c51b94, 0x4d906bd8cadda908, 0xc2a49dfc20fc1658, 0x0af4096ee1057ac1, 0x7e260e75f5d3ef9b, 0x007c917f7d0ede63, 0x88b725197c7c3978, 0x6101744459a42280, 0xefff451f0aca4536, 0x07d0d15896d95d2f, 0x14e029e9d58d619b, 0xeba34e3474a0fba0, 0x559aaa25768ce06c, 0xbc6bf05387f0ead0, 0xa0dbc1b225a8148f, 0xecb1f81f3fd83805, 0x3bf409e325041c28, 0x9bca9a7739e0db6f, 0x3d611be40d88028d, 0x6f8e6cc701a742ba, 0x5b2db0fc280d804e, 0x86b671b61d5aeefd, 0x5b1d5b698ade55f1, 0xba16e89ec4adca67, 0x5ccb306411d8bbff, 0x8b93c052bb4732be, 0xddc5cf73ecfd2443, 0xaff1d7d9c565d760, 0x65c735d271cde51a, 0x29ef9025d1cfced2, 0xbc88b130ca8f0305, 0x5a60bf82796583a4, 0xa07cf63d3225b7aa, 0xe2ac3b558fba6bca, 0xf4ee1635c4015e70, 0x47f253b434dc7e26, 0x6e85e4ce870289e6, 0xf1622e5c375d4c7c, 0xae95cd4cef468fe1, 0x4490c855e17d3dde, 0x9bcc89a628a90c78, 0x9bc0d4964cad6664, 0x022c274f0dedca14, 0x99610e91387c11ba, 0xfc8338857634baee, 0x4f332a1212da8f74, 0xad5fdffa1bddfcfa, 0xc5a610ed9387c782, 0x999ee466699bc267, 0x490985f68a57c81f, 0xb0be3870679832f5, 0x63aaf30aa71bc94c, 0x3b39ff979fef80d2, 0x1b8ab0c01bf1fe7b, 0x2ab1412d30e5d3d0, 0x17174d4ca002d38c, 0x3de2fca76e01fb8c, 0x05d19690cdb87b08, 0x30463932e56d0c3a, 0x5bd32f42f105166e, 0xa957c1ad4f1e0ce7, 0x9ea15a67e9667f8c, 0x8d1cc03669bb73a6, 0x56e97953824428fa, 0x944cad0e7d3a61ba, 0xa28d2a1e44e17383, 0x37b15edc4f0a6a88, 0xf70c0e999cbb49d5, 0x959b85df8e4d2bc4, 0xe41ea844c0acf427, 0x08ee07cbcf509a2c, 0x4b0ba281445b0f80, 0xce44c8b85333a86d, 0xaeba7c4c831dd0b1, 0x8d96f7ff0ccaa3ab, 0x79903323056e801b, 0x1b7b11da9b5faddf, 0x23b38c25a7eaa542, 0x19aba628d18ea022, 0x718a80709cd3647f, 0x2e03cb06f89e67de, 0x80565a8869ca5a07, 0x990fcbdba7ce7524, 0x1247239364097b87, 0x51f82ac2d79c0443, 0x96e8ce33acb84e9e, 0xd31b8ee8bb3cdf06, 0xa614d5f216d7b090, 0xe3aead9e991dc9de, 0x275a5476ca8dcc1e, 0xc90284932b75d730, 0xdb88bb9344965f39, 0xeb6210325a39377e, 0x151fef44a7d40511, 0xb2e4db0c29bdad41, 0xbbd3d93802d41d84, 0xecafd657edb5fac3, 0x9d9074765e0cdc5c, 0x2120a60350871bb0, 0xd8ef2c0a3fe26f48, 0x7abbadfa14658cc7, 0x85ad9f778a67a401, 0x1754b8b63d2e4475, 0x6e0f28541b535ed0, 0xb5984c9842f6ea75, 0x7304f55b91c215bb, 0x8c2f8b9e9fe34fc1, 0xef3a028b55997260, 0x8dfe9a2a71ea32b4, 0xae32e5977ab05e51, 0x817c0277b0957ec7, 0xb3dbf9fb42894c7e, 0xde7c15b3f555ecc6, 0x71bfe3cddb8b0877, 0x9b346be5776621d4, 0xcfbeb1c6f0c5888d, 0xecfa8d3c41572a9d, 0xc35cb3c9ed598754, 0x72ae23f86e22c4b0, 0x0e50d50ad64ebc4d, 0x0efa589804ecff40, 0xb6fa944e65b458d9, 0xd55c7c104e68e42b, 0xfe91e9f9223dbf07, 0x2abc07c3b51bc1d9, 0xd92315feaf04b311, 0x9ec6d966b29013ab, 0x9b3f0b580a98fa9a, 0xc955a984b6018e53, 0x8407d2a52ab0fe74, 0xb92f8d0d4a06ff77, 0x11f81d5764bbeb8f, 0x9fa0e31fdab84b15, 0x0d61061a8a81f56c, 0x535e3554d3e7a683, 0x67567defb540f61b, 0xe64746b4dc62d117, 0x36e9f07c00790379, 0x0324a3726dc81c8a, 0x01dd3dfc3e4b7f55, 0x42a4368b26ce291d, 0x472022dfe13db72a, 0x5e616553c000ebb4, 0x36731ca0a66396c0, 0xe07160c1b7c9fd73, 0xb44bab7f6205f6cd, 0x2a2c832f03040224, 0x4594a635cd4069e1, 0xfdd3f2acd8055b48, 0xedc14c2503a32381, 0xaba617bb88e7e1c8, 0xb6c6cee8227b7068, 0x40eb7cf967290837, 0x0947cfc56a4060a2, 0xfb6929956cfb289b, 0x214dd72cd4add5e6, 0x8678c57744fd7fa4, 0x7962be8dc9955e58, 0x8b1cb2f022c96793, 0x75ddf91d8fef5e2b, 0x34ef97de1a154ec7, 0x8ead5c3c862c6ba5, 0x0d07d64183db286f, 0xb56144f8c4a4d971, 0x9d8ab040a2d6a204, 0xde03278ae9af1008, 0x874634ce136bdce2, 0xef6931372c48387c, 0x63d71feeb9f98d4e, 0xb4bb6abba50e0e9c, 0x50a018992013ebca, 0x4cacf4c7fbfd635b, 0xc20e1c7931e9ebc9, 0xb5c8fd27dccc2923, 0x9eb774daa25ef041, 0x494aa25db2adeab8, 0x9e62cfe7a1caf51a, 0xf52e2f71c2b6fca7, 0x707d8bfbf23d0dd0, 0x3c60a2abac354fa8, 0x2d108c433eac331d, 0x97419e8f556ed698, 0x7008b6375eccb4ab, 0x9d9f78dced7dd9a2, 0xd46072f4542b30c9, 0xd24b7c4cdfa48400, 0x9a785d241f2f6287, 0x3d5ac4c8eb2ca4ea, 0x8f7031f6ce346b1e, 0xdd83b4433f34589a, 0xb52477e38861f49f, 0x4fb8521eb35e8d59, 0xfd1538346bbad5e3, 0x4619143da06f17ba, 0x512b704d547b4273, 0x298165a520f421f8, 0x127b7cf86d43a26b, 0x4f6cd778723ec7ea, 0x7cd61a6a896f0ad4, 0x3b943d89ec0a1933, 0x0c466429f3b39849, 0x32fff146f2559ad8, 0x54a0d507c56ddc25, 0x358d5381f5c44672, 0x4b9d2dd06bf17ea9, 0x1d02e7bd173f0e41, 0x43649b466291defa, 0x98e86710ad256f57, 0xec4fcb54cf9f3785, 0xf6d07e993d8996d6, 0x7b7ae354dab3b2c1, 0x6ed6d0844170e398, 0x7e8de8f550ebe178, 0x5c045ebc1222a78e, 0x596113eb59f3cf69, 0xee6e98bcb9c9e067, 0x6dfeb6e2d557b611, 0x1c6505311bc9d3b0, 0x34d3c8933ee5cea6, 0xcc73b4694344d28b, 0x806e0d3906d87b91, 0x51b0cdeb0f0a12d0), (0xf29a8a91a0750a4d, 0x937ced8661e9bec0, 0xa7dc88eba783114e, 0xb65ec4686b0fac9d, 0xc05dde06243020f6, 0x35dff3df2c586145, 0x098cf3c7b6889667, 0xd6a7af4a57c0c8d6, 0x9c57b19f0a8fdb09, 0x7b7f1d7ad720076f, 0x06546272f1898004, 0x5cb15e638c48520d, 0x4a1e19ed3afda4b2, 0xf53a860e6283f16b, 0x3605077f3a32eaa0, 0x4c60808c1ce8de0c, 0x7af701d6fe9640e4, 0xcf7edb4c89cfb979, 0xe443b7768a415d1e, 0xab238f840343e08f, 0x64560c5aa03de304, 0x94f5ba6df58e5b6f, 0x89ed49e454d12173, 0xbfedfa5ce82f2901, 0x0f3c2c1f9a998455, 0x27af65de99efa76c, 0x252fdb569f4e2b24, 0x7a784408ee74d712, 0x4ac2d177626092a5, 0xa2b98d053f617622, 0x6fed52d56a16a217, 0x844fd56166192ecc, 0xc62225604a1d8e55, 0x0b27f8494f80f755, 0xc226e6016cb913d7, 0x345145b5d82b6533, 0x3e97529f7a39d4a5, 0x5a4243149496bd6c, 0x3c5ed99212e54d5c, 0xd2147fb349c4e345, 0x2d1fcb65808d6bfc, 0x134415a4a911001c, 0x32adc1b9c3094f3d, 0xdbb19c2cd0865cd3, 0xdaa6a23a70b55c06, 0x797ac16fa44eb177, 0xa16622142a3ba561, 0xb9d5bf184f2bf360, 0x3dd7d1b9311df0d3, 0x510069c555dc1383, 0xb93d846fb4b4fbea, 0x75e687ac068f5c16, 0xc0adbf9b85e989dd, 0xf3562cab6efe1304, 0x7f9b422736a97fbf, 0xebe0b95fcc1e3653, 0xd14f50f76b1f4e1b, 0xbc949e0cb9134cee, 0x3f7124c4c4842eb8, 0xcc6fba244cd48e71, 0x1c2bdaf272679639, 0xd66bbe47b6adccd2, 0xd7985b5241711192, 0x63340c32ec3549ee, 0xcbee1e0e10b3892a, 0x7a84ae25ea3730ab, 0x4fb09bb763c64249, 0xf8d3e2659f4e763e, 0xb4f515eade807671, 0x1b1ee41130eba35a, 0x388ce50c04588384, 0x23fb5b32801b7161, 0x4abd5cc68244f18b, 0x7e2d3255d5235b4e, 0xe70a7014993c9b50, 0xca9264c3b51d19dd, 0x69ba418094629ae4, 0x69c7fd50333d3ba6, 0x5bd3d0fdf4776cea, 0x6fc20f551d218ec2, 0xe4e51f8de0695bde, 0x19be3fb8427a19a8, 0x6ae1bc72ba0786cf, 0x2b64b6c3f4fb96d7, 0x739819bbf57b3902, 0x6bb8fcba76922ee4, 0x1191e0253008c889, 0x4d9e6f7b5b961501, 0x319a4948f2b5f13b, 0x68cbf5ffc3d2b42d, 0x6c7ff1e5c8681145, 0xc70d64dc0fdca744, 0xe145f0da34006e3c, 0x8c028c83b0cb4d88, 0xa3d2e1c600007277, 0x31aa6f699b25e51e, 0x3637a6918666791b, 0x0d16744732ce37ae, 0xa333cb314750dddd, 0xed9b0831b11ce6b6, 0xae5d00d085f1647d, 0x4fa3de9c0799ecd3, 0xf479d734d66aa341, 0xcca0909a41c93889, 0xa90c2e71dad84d9b, 0x7e67bb22c2f4a38c, 0xc4eb203dff1168aa, 0x9cd7cf6be1ee0827, 0x35c5ee1be927593b, 0xce9b0a900b9a889e, 0x4b350c5b7dae1652, 0xc3289170cf1ea082, 0x6d7e2daef71d440d, 0x3c81174359316f24, 0x174511982caad30f, 0x89e83e25fce17a65, 0x70b97405e3d874e5, 0xb7e0542de9f8953c, 0xc3606b5321b89d5a, 0x87f129b0ab87832f, 0x747d0a1e22fd859a, 0x73bcc8aa81e4d8df, 0x6eb0087c2f5bf23e, 0xeca70f330a145757, 0x9e747c3779e02544, 0xaa00a9595b3c8965, 0x119644292048975b, 0xa6c41e60a824f81c, 0xf2aa43b2134dca8f, 0x321cd04b5f83ac42, 0x03f6a5e07b1cb442, 0x06be31c077ff6efa, 0xfad864bb4a6c6789, 0x1baaeac01e69e854, 0xdb3e4d269495bc33, 0x0e61d9340a538001, 0x147f33a3763d38be, 0xc58f029afd8a86a7, 0x1cfe6ad170c18af8, 0x2cd546a9adbddda6, 0x58063cb844de57e8, 0x4e6391c91124a92a, 0x1861f94c7bafdf3d, 0x2869da3f4299dbdc, 0xc1bb2fdad04f1880, 0xe8a0b4a517bac896, 0x8f4b2b5ddbd39025, 0xac95ca947d8a6201, 0x807cb198f79432eb, 0xa9480e5d6bef579e, 0x1c3fd944b443003d, 0x76f3cde863c6b417, 0xdce1d75aeb2c2d6a, 0xff7cfc966f1ebc17, 0x976499d02367cfc7, 0xddb91faae0de3cfb, 0xa002469382a28090, 0x7c952039737863c4, 0xaf51610c9aadba71, 0x70e7ce7d56854d64, 0x98c43003bab78c9c, 0xd9404ff815ea78a9, 0x8bfe1034816b3b79, 0xde24883516b1bdc8, 0xd81ae9f150a855b4, 0x40bac6321139c405, 0x15311c277cd4e14d, 0x8792d6f94a102855, 0x4eeb36194f2df70b, 0x3de70f9c83c86057, 0xc9362bd97a60f870, 0x6018ff3644504aba, 0xce327aa0068ded51, 0x69045ad6ad2cb735, 0x768f41bc5825b39f, 0xd862472fad944a6b, 0xff7f49a7458d1296, 0x449e3609f3eab0fd, 0x0ea15ec431212f06, 0xa47acab8d112810c, 0x2648739eaf266d40, 0x74f9eaa523233e14, 0xc4200e4b73845f4e, 0x7378fccac6ed6b5a, 0x58ff3886c522b38d, 0x70708a4909f57f9e, 0x901eee9056cdb042, 0xbd2b8fe1a9c1528f, 0x78c604c10c5484a4, 0xe714e2decf085c76, 0x57c647c4639a75d3, 0x1d93fb79e93b1469, 0xf70a2898d4964e81, 0x0877df4fb718ea42, 0xb43b9ee3dfd832fe, 0x56a87de1d5f825ca, 0xfb9fb3634bd71cfd, 0xed4e53c166cfa15a, 0x74f49673683d7eb1, 0x08e92206dda7e969, 0xed16f66458422208, 0xe8fbc577d06a1679, 0x8f1d9023bd17e564, 0xec640b8eacadfb25, 0x2dfe0d536fae8204, 0xb1c931d830c778c0, 0xa8e0a5c74ef5d8ba, 0xc7faa81ba481976a, 0xb2405b58793caeab, 0xd200eef64b5a851f, 0x25d844e3d67ae874, 0x691a89771f137ccd, 0xca8f6453f012d613, 0x158a56bffbb6542e, 0x9667b2df43adc18a, 0x82a482cdb82ccd33, 0x86f23a6a7d54ee73, 0x10b288302011630c, 0x72f0b5e8108f2e3e, 0xd08816485419d44b, 0x292749c4fbc842ad, 0x07fa57fd3f710576, 0xbdfe186fb6e9605e, 0xfdcbc952e08575b4, 0x5e1dbd6529d264e3, 0xc73b293a3b75aaea, 0x20e2746b424bff06, 0xbe8dcee4f3793f95, 0xfc37bafd71d28d02, 0x146bc82688a600f8, 0xa4e31a7921f4866a, 0x6be384144f50b823, 0xd05767e270a2e1a1, 0x41bfd973068a4c4f, 0x1e4873b521193e66, 0x130153168fb5aaa1, 0x93fbaec1542cbc12, 0x6e1f19d495c361c5, 0x4329283df219a2c5, 0x672bf020e0c1d4e4, 0x3f48a8b31537b316, 0x87668440288d52f9, 0xc8fd2dd7b4b72b4b, 0xb11a3203b4f8f7c9, 0xb72a14543a3b9ae9, 0xe51b7042e992ac55, 0xee499e479bf861bf, 0xb04bae711f12d1ea, 0x42bcf82439cf0c22, 0xca1614395a48815d, 0x54efb183aea373f1, 0xf67a16a89843e5d8, 0x6ed5f88106841757, 0x21d5f0e808994091, 0x451bbbfe4dfa9d92, 0x0b4aa49a1a7a4f1b), (0xdba1a6db9d03c7d9, 0xbeb755b1ae73cc3c, 0x1988659f145c1d8c, 0x274c07aa5123a5fc, 0x0ad221abb3826faf, 0xf4a9318402059f40, 0x7b390278b43e57c4, 0xb0ff9ea2672298ad, 0x783c161fdd897dab, 0x4260287a0bde44ee, 0x443b90f2dd98abee, 0x0bcca40dfeddad64, 0x8330c8c401ffe27b, 0x32c5866a4750c213, 0x5245655283521bdf, 0x41d6b7329ff37fd9, 0x0d5df9f5232c5c74, 0xef4e0d30a927627a, 0x0585d814bab77de4, 0x9312658f34327f6f, 0x33dfdb7b1e300c26, 0xa1107ce213b370fc, 0xeaf19df730f494cc, 0x4582dea0e7428018, 0xc5449280de42fe75, 0xfe6b95ad548ab948, 0x393fefe06f21ab10, 0xde90892ad990e0f3, 0xd2e30c290f336f84, 0x2ddde78b6865c65d, 0x04bbd0370c926e83, 0x6cddb9419983a582, 0x9231e322f8fcdf5d, 0x10377b0fd20bf266, 0x5154a13903eb47e9, 0x69cf3a796939eef4, 0x082cbd2ad26fec3c, 0xf725fbd2534f26b6, 0x5b601e461807a6cb, 0x20a468fc66e8e0af, 0xbfc9c06b56da2e16, 0xcb7bbd21f4f37a0b, 0xc1c2a595d07f6fc6, 0x8748ab5f2289128b, 0xdcaf70b2a21831b2, 0x35b91d50af5141b4, 0x208bb4889120ea69, 0x85ad8e1ad9ab868b, 0x0ba7a0a8d51957db, 0x3ac5ff57b4ae1e4e, 0xeceb63c54a40e4d9, 0x221bb31ae61cf708, 0xa7555562d793ffa3, 0xa07ab01631b5937f, 0x3bb3fe79183ce5a1, 0xddcb113e60c5fbb6, 0x29d683e168e53782, 0x9a02e59892b2c39a, 0x6329071f09d80fd9, 0x7bf818b62be8ebe7, 0x76913be4ce37854e, 0xc80786d5d30a3c0b, 0x822c349d71730f41, 0x1dd3c708bfc04117, 0xb6b814d594190e8f, 0x10347607e7b0ea1d, 0x5c9aae9aebc23d64, 0xf13d661425a55e2b, 0xe02308f5a5dc5f4d, 0x2460d10affb6ba4d, 0xcc12129caec36255, 0x0896a4e21c2b83ca, 0x09cf4f9fcaaaba24, 0x843ccd131956f015, 0x0a23760f57baf4a4, 0x27401e08fc43b587, 0x5600e3ac14476e12, 0x024abd19ab8fdf0a, 0xad6f0b4576e82ad5, 0xdcc4f056feebf4a6, 0x2d63e08f57c7db99, 0x26bdf096064dca2a, 0x32a1cb0ad5e3a427, 0x1d80c9a23268fdb6, 0x9ebc813ffea99923, 0x1961a9811949bc83, 0xd2c118948f835f66, 0xa7778ef1ae0cbfde, 0xbe681ab20b681aae, 0xf5edbfc7c8b3383d, 0xb111fb62de87bb1d, 0x689249a67ae8a7b3, 0x38e0b6c3909fa0a7, 0x0a2de5a12a26e529, 0x5ea086b878cc3486, 0xe09fbd15339524ca, 0x219564e3b22ef108, 0x911d75ad4d239572, 0x4cf9c35a3d6c28eb, 0x01e750d8ff5266ca, 0x0b50a9abe6a5d479, 0xdfb823ed3d783ac7, 0x876e4d170e691d21, 0x1667b2b8378b456a, 0xe53793eb4f5e1dee, 0x4aea9c48853d73ad, 0x2632a2b0dc982f20, 0x945221a01fd8c169, 0xb75e5c4da922b356, 0x5a1a7ede0f59d734, 0x7aecf3b3019490f1, 0xc187366ba239b7c1, 0x1ce3c5ca9e0d49b6, 0xb95f5954f25956ad, 0xd7158d7e25906462, 0x1e4d0b59b69a0c6f, 0x987e6168bf9fb0ae, 0x46add5103f7dbca7, 0x41ab9d2f951d9f0b, 0x3f6cebc12227d563, 0xb31cb2f0b4169683, 0x1cc57c0b0c409c60, 0x8d9f9335eae1b2f8, 0xc12fa4638c2a4210, 0xe93950402aabe307, 0xcd571cb16bf81f09, 0x020ba289957936fe, 0xbbc82ce80443c76e, 0x1962ce1ed7c04f69, 0x5132a571dd4350ce, 0x1c714f9a34d03f06, 0x7106138554debefe, 0xc66866133b80e4e7, 0x3846cabeb8b6f670, 0xe9258d9fcf1d96d4, 0x5afcaa0a17ae66fd, 0xe71334bf8f5f1646, 0x82508467aea9d877, 0x76cbc63a883d6a08, 0xfc8f27eea94e8c3a, 0xe96b1adfac6c277b, 0xbfc9389b8fb7350f, 0x04ec384f9f264c33, 0xe834f24eeab98f5e, 0x8d6f1a744009d5e0, 0xe23381fc46da95e2, 0x9b10773bf64bfc7f, 0x76f4478140c2ea4c, 0xfb687a04824d6208, 0xef91fa7c132618fb, 0xb11b008ca4ea937f, 0xfca2e2f6c66f8065, 0x481819ddb24f0c82, 0x64b0cf5a6394bf4b, 0x512d9a63f0ef981a, 0x54f2ad79838d0552, 0xe2ea9435db0e17e5, 0x5c4c2f06f2f3df06, 0xe9115b96ab390fe6, 0xf71ce5ea5d747f55, 0xc320df1bf143f16e, 0xbff59038dfa39bf9, 0x4006d3e07753c04c, 0x781343c38cdb4b6b, 0x13a73766a6453470, 0xf459e22033c7546c, 0x68aa9a20670775a7, 0x5e0678ed6d04b048, 0x2838bb1e96873975, 0x7107ed53726516e8, 0xe03fbb85382deb83, 0x77a765418ada547d, 0x9c58101f7c81b337, 0xf428789abda860b8, 0x50bd03bb87c98869, 0x3a40958fecf1b84b, 0xd64960737ff771d3, 0x5e5cd9f5037928ad, 0x050c0df313f3d698, 0x74d882312d04bee8, 0xe46d7dc8ce461092, 0x585fd8cfb0cd3340, 0x22480b6f7830bcb7, 0xfdf5c84ce6ed2689, 0x08a31565fc904f8f, 0x9c33e2cdb664bd1a, 0x61bf5a1fc92e4e4c, 0xc656cd95bf46e7d0, 0x3f965b5137bd3cd0, 0x693e57e031686f68, 0x5f16a18b864a08c7, 0xd90d9514bac21cdf, 0x0e9f69772d87961a, 0xa63c41ea522271f5, 0x33dd1742bf48cb69, 0x5f9e93ab95d4a003, 0x8811aa89c122f6d4, 0xc7cf93b14d1c3ff5, 0xfacfdee630b4aeb4, 0x35f9c10cedf41e90, 0x2db4cc50fe6c0ea9, 0x3e7a241fcb613c3d, 0x9b82bc3751a8ebb3, 0xc6e11185c8fe231d, 0x8e913ab258c9cf96, 0xb22289f6b3481888, 0x4312047c3dac6d40, 0xa24cd705f4fa4cc3, 0x5b2b37ac67a9e33c, 0xeba946d2ebb6a8ef, 0x7e84cb4bf5a72a46, 0xbdf64b9de64f4826, 0x3be96f604de555d9, 0x9a997f0d154cfb0d, 0x1a445450c798849c, 0x559a441390e0a572, 0x4b16da28419114f4, 0x1b222eb6c4f6bcdc, 0xa9b5767e0368dc24, 0x5829fb955d05e0bc, 0x5b35773563a2570e, 0xf42dae3b54fb849d, 0x10ff2ae1a4352a41, 0x68603aef8fbc1c2c, 0x30ffc33d8617b7af, 0xa6884c3017938234, 0x86ce8b5f89d70aec, 0x2fa6b07f2303359c, 0x05122025d4ce7012, 0xaee0366c024e91bd, 0xe86552b06751d6b9, 0x5aa33a2c23ffaa71, 0xa6d52a9b4e149662, 0xbe7386bf8ce08425, 0x7e359d55881a1265, 0xe6925965c0325717, 0x03e7a35cceaed7f9, 0x09710f9214147168, 0x311b86c8d4ee5f46, 0xd7149408a9b70a88, 0x8673af6da58ce7fc, 0x4b41bfd656279783, 0xad88f4d8baa2b5a1, 0xed090a2e7896bd39, 0x9e55b77f0ea9df30, 0x06a14ba450faf565, 0x538e277d514f5cd7, 0x553ef83ed3d930c4, 0x628be1f5401120e0, 0x748a10b2457c2b5c, 0xe74843561286c395, 0x376dfe7d45dee31c, 0xda0616758ae4226c, 0x520f40616e96b69b, 0x13af9a7055f0b44b, 0x1fd54758acf80009), (0x1c9ceb260efb4863, 0xb5e64895da4f5677, 0xebf43d472f8942f3, 0x31b59aa2862c2d1a, 0x7b602cff0a2c5723, 0x9408dc928c3f2db3, 0xd8f871861f114d25, 0x47d6f1f4a4b1ab85, 0x8eab4171e6529724, 0x46a99ee8ebc73c43, 0x1b9f4886c2d1caed, 0x1dce744ac3157036, 0x00c7b0f962c06a04, 0x5d310ae7406bcb76, 0xfcdbefb5050c11f6, 0x7593822a4c557e6d, 0x8a2d97bf7f265fab, 0x93501bf7692c7501, 0x1821f065cdb26114, 0x44ec3aadcb095a07, 0x229d0ac323d5ab09, 0x875910368d8ed0fa, 0x2d5d79a0b94f004b, 0xa15d15079248dc22, 0x8c810b38cafc3691, 0xcaf07e2ab87c0258, 0x67dd27b48487e8a1, 0x5471a842fa5bbf5f, 0x265890478b12e557, 0xf81f5bda06ae9417, 0x00f86596d7edcded, 0x0030f7852ff9d71c, 0x2539c27d89586275, 0xee4a74af3670f093, 0xdda7c30323de09e2, 0x073a367c5db02841, 0x1b50ace0779e84cf, 0xe2d428e81a75518c, 0xe4db050d15c36858, 0x169290f8e3fab8c5, 0xa1047ceb315bb5b4, 0xd2427b2c39e1f66d, 0xa389c20cdaddda66, 0x3fe0ef0e9f1690e4, 0x181426cf6bc6930a, 0x65e0afa1d6d3796e, 0x40d68002b8f495b5, 0x66991d3b2f2cd956, 0x0e8f03f33699fc27, 0x15c466e742967cce, 0x99fee8904409a756, 0x8ee27d6df2c298ed, 0xa1c4b56252b427e0, 0xe636ba49739e4c17, 0x71cc69cf5afd7dd6, 0x79f7cab33b359fb4, 0x51d31fd8ff01fb52, 0x9522d41b433c11b5, 0xcb8ab38e38c8dee9, 0x07486593f8ca03ca, 0xf4387de357cd35da, 0xd08c8891d1034c59, 0x9fa078346c5d9de5, 0xb563fb67842a8ac1, 0x79c236ac45c40e83, 0xde2b773ec7f775aa, 0x60782783a67275d7, 0x19e824da1d7912cb, 0xd6aa9882aade1567, 0xd8639f3d4b67de1d, 0x9917ae518899b980, 0xf287544cedb2ecee, 0x51ab50ce1c172c2a, 0x24957d15dacc94cb, 0x0ff852467d881fe4, 0x5175f7d8037fa1c8, 0x4c4635177fb8df41, 0xf98124e670c524e8, 0x2e61208ad685f8d7, 0x7a913ef6edf8cbe6, 0xfb17e2a30a94fba7, 0x882b441f6c2c2998, 0x426faf5a8ade4d26, 0x67f1823a69ad1445, 0x328f18d49b7db179, 0xcce7a451fd097485, 0xcfb9ed933d37d7ec, 0xaf691ade2646298d, 0xb9fae9dcf358f8e3, 0xabaa4dbec4d6819d, 0x4f975bee83279f1d, 0xad5245e1005db9c7, 0x79f056b64ef71cc9, 0x88b3732cbb8f1d71, 0xb681232346209515, 0x6dda5d47e8f8a443, 0x4b02ec20db42fdd7, 0x0507170dec533926, 0x23e2b004a89fc2cd, 0x5b8d60f96e527eb0, 0x5b31089aa4820bbc, 0x2a11677b0f6b3020, 0xba8c9040fed3a1bb, 0x1466a4446cb33c0c, 0xdb7b32b40c53a9f1, 0x50a567acd661a265, 0xb697d2ca8e9cadc1, 0xcfcca1ee97abcfc8, 0x299ce083fc4f1af8, 0x3e0db18b6eb8ef95, 0x98685cc8c1bf21cb, 0x670a3229fe3006d1, 0x5217d4075227c253, 0x1657536e2e662bf1, 0x6f747d623ed31eea, 0x58e022afe49b5964, 0x1dff848c3758ff16, 0x27906a3903b21cf7, 0x2e14f735660ac57f, 0xe6711122acc1cfde, 0x981361e61556bbc8, 0x6ddbe0e2c0463b2d, 0x5650bbecb0057bd2, 0x5581e0628dbf3f91, 0x140d54c08e98ee67, 0x6ae633c69d48b304, 0xcf2371f33622ff68, 0xa0251b2559e52f82, 0xcd0165b13afe86b8, 0xb9e142b723b2cc6c, 0x9a46a66a5ae6fe31, 0x42379d4aba1d104c, 0x47d2179bc036ee92, 0xc030e5091430af0b, 0xa6bc28272f1afa32, 0xd7ac66565c2b4e88, 0x3931d78eabb40696, 0x292b8155482f6612, 0x8d3b7c5705235a17, 0xe688091ef33efe07, 0x64c2177375c3c069, 0x738c4737ad92b9ee, 0x97b448154c2eb02b, 0x85d3bfb29eb5edd6, 0x7d12c9e923b5e330, 0x0dec3056060509b4, 0x5c329afb0708f16b, 0x3a087f4cf16881d3, 0x34b3b572c287950a, 0x4709b3ce10791a8f, 0x762de64a09165c99, 0xacd9678cb2604388, 0xaa2ac95f4acfc5d7, 0x10b33dd533859362, 0x366aa3dd012f184b, 0x76ded6ef532963d2, 0x02957eb65ec79291, 0x36ecb5f9cf7a2464, 0xae398b1adf5b0959, 0x5c8dba6b4797ac80, 0xb68d6784f56b595b, 0x50ccf406ef0180fa, 0x803c90965a7329b5, 0x08c9139fb14fe480, 0xff23e983abd0e7a8, 0xef64426bc7f34a3b, 0x4b92344b842c24d3, 0x27de77709d3b251d, 0x66ede61d436dbb37, 0x5cee38caeca68765, 0x61fabde49a8e4b2c, 0xcb5fbdbf6eb3942a, 0x97c61a7a3dac0f0f, 0x99568cfe37427c21, 0xd4d8b10f4ca79c4d, 0xc99b17b21f9bb0f9, 0x78fce2d5b949497d, 0x5cc2dedf952bd11b, 0x7c3b120802a2885b, 0xa4aaf4d0c75e3f2f, 0x3f3bf1ab4838089d, 0x7f157c23da273fb0, 0x747f4617e50a2b38, 0xc67de91e0729abbd, 0xec766c666e2f37e0, 0xd241d030f0c5c9a1, 0x26ba5e3163761972, 0xe4d166acf3bc3a73, 0x5af5fc5c953177ba, 0xf80bac4ec52ffdf5, 0x0535e40e0222ed2b, 0xe1d3956e11e28a08, 0x3db9a75d62f12f29, 0x81ec42da9af1da3a, 0xdbb44dc0bec9d9ad, 0xd20c5227583d0c9e, 0x801c730bfbc819e5, 0x4bdb274da311ffe4, 0x8456ca52190ee3cc, 0x2cfbf4d98148532e, 0x3d3964de84036924, 0x48a992691e6691c6, 0x6f90bc13ac87e4cc, 0xd0934edc521fbe3a, 0xf739aab91ff97fa9, 0x8210d82c1ea5d4d8, 0x36b65f9f60808070, 0x7c2783dc70301c75, 0xe2a897cf0781aac4, 0x21554fd0122a8018, 0x6f14b37d58c4e36a, 0xf16a7bdf721ac27d, 0x3d2ecb3fa9ee8f4c, 0x50e260fe66e59749, 0x710ab9b2cc61fa6f, 0xe20cfbaf3a670969, 0xcefdeebf79bd58b0, 0xa048e75462f66078, 0x3ee16582f74e36cc, 0x39fa658b4903e708, 0x76b8c756a09dc967, 0x1696f5db678bb730, 0xaaed7509014b8d5f, 0x7afa4992e49778d8, 0xafa09a650a395974, 0xf9cf8b48502810e3, 0xee04ee85a34b9b98, 0x974443475211c235, 0x5e0cbf485b68c912, 0x0ba950e1c4183c7e, 0xb12678f53d6a66e7, 0x28669b636c985d7f, 0x79c6bdaab0993317, 0xe442453da404fc48, 0x08cb45985e160edf, 0x2858f31c2796e052, 0xcf62b7f1c2c22b9f, 0xf0788972068a4f62, 0x34f9887f76eba575, 0x95c434b64ea81f6d, 0x258ca9b0f2f17f14, 0x5de8573df81886c2, 0xb8d99c6ff54b4618, 0x5dd87e7747a2b098, 0x7d5ffb65d569a1c1, 0x986e09f219740612, 0x655a6f1639597f41, 0x70423f3dab01a457, 0xba24e574338a9020, 0x361df588a5d4ee5c, 0x8d9ffca0bd1a97b2, 0x2855802582629929, 0x034d70bf9b925a2b, 0x4fcf470059f750de, 0x2cfe1d4d15048e72, 0xfcdb30c6e791eccf), (0x42af1c2461e205e7, 0x575163c5cc6ea89c, 0x0a5e2231dbd13a7e, 0x052f2656048b3d9a, 0xcfca8811d34bbc81, 0x7a6b05722299f06f, 0x09b1731972cb6200, 0x4c83fef43dc6ffdc, 0xcf493d7d10a65fab, 0x030924cf1b61d3e8, 0x5a5b3534f9a3b1ac, 0xc93ede4e88e2c3a8, 0x54a9479be3eb2262, 0xf295a3a61efa6184, 0xe89292aaa9aa5595, 0x6349754f7cc8a777, 0xd6b389a222048d46, 0x55d2389e9659f77c, 0x93ec80e9994c6e0d, 0x0e63628f031b254d, 0xede4a35132aed725, 0xd1cdaf18b9f0e8aa, 0x16cd3baa0d9a0a39, 0x83b529a04c7900e9, 0xc90084bcbec8f7d5, 0x849cd2e219f6f6cd, 0xe277cb4cc38b851a, 0x6feb04f4c1f813d9, 0x190bbcd015ff7318, 0xe45e2db48fb8d45a, 0xfa4cfb3ae5b4bbc4, 0x9af58102f299a42f, 0x95e9e75ae76d00fc, 0xa0297e960da629cb, 0xe676ae50bd91f44a, 0x9a1e2d852fe1d296, 0x6f0e433828439cd4, 0xa90fb781ffb87580, 0xe7cedbea3a6c1790, 0x87e559e79059af37, 0xf2136faa4e5c323b, 0x46959be7c458a6d1, 0x5e3520906f1ba20e, 0x5d87501855a949d4, 0xea52b82f62967ccd, 0x6a7032b0dfe7644a, 0x6b73ea2c7231dbb6, 0x583cdcf825d59277, 0x704781b5979c4223, 0x3e3fb166357c1d7e, 0x92ac7147c41cadc5, 0x24eb2e0d1baad9b8, 0x8c7a85466a8a8da1, 0x8e8d3fa81d46bda9, 0x2869b279c5e01a4a, 0x3a9f00c89a3cccbc, 0x1b8ae167bbd11c67, 0x0ffeb7d05f954f4a, 0x5be8eb33187df70c, 0x80fc601151069e1a, 0xbcc29bb2e7e6b1d4, 0x62ef511ee8e1dfb8, 0x35c74ab75717626a, 0xb49ccf45e424dbbc, 0x05ae4f9b1c30e72d, 0x78bdaa1a88a99472, 0x539e5acd58e30cff, 0xc2cb802ea3ecf9d7, 0x3dd8150b45e46891, 0xea9fc6d642be554b, 0xf952d6a5e49814bc, 0x21694e510861d401, 0xe7ae0f8fd6f0da78, 0x3796d3bf1898bb84, 0xa9075e588c1b8d9c, 0xece61995ba2c87f8, 0xa70aed7e57159651, 0xce8dfb1046ba1234, 0x6ebbe219690eef84, 0xedfb303192440949, 0xac9f4a3313d8ebff, 0xe598def435bc34ce, 0x28a2888414730528, 0x57dc9eeab1cc8ded, 0x12099528c032c7a9, 0x13cee73289831672, 0x8bb08e6c6e32d73e, 0x76f47a8774c49419, 0x7fc05bb3d783f5c0, 0x7b6d0086e94d5105, 0x7d9ae4d4359e5e0a, 0x0792ece3010fdf11, 0xa8ff03386e1f6857, 0xa1da1915fc9de751, 0xd06f8a3972f19626, 0x010b60dca0d384d7, 0xbfdd5c5f1aec26b1, 0x684c2375819c18cc, 0x3bb5b153bb269a4a, 0x217e7960a13ff22f, 0x2c93f2580ec88b5e, 0xbb735b32ca656dd2, 0xcc860be37f19c5c7, 0x8da68c9071221ac0, 0x14d2e8365d101c68, 0xad635519b02e6c43, 0x6a59e2f0adaca340, 0xdc20187b5df9e2d9, 0xb73fe387a26e44b6, 0xd32f3294c4e283ff, 0x76e1b5e8bf04cce7, 0x57d618d1c73c5cbf, 0x36ff07d6559b8789, 0xa942501bea6b5c99, 0x23f8b954897a8a34, 0xfdec0cf58bfd5803, 0x455ea0db4d6ae2f6, 0xc787cbe98da8c071, 0xdf713aca5b552a8a, 0xd34293079fd7143c, 0x404e6e6595de26df, 0xe545450314552116, 0xb599fb1c0a0cf203, 0x817ca7bd53127a57, 0xca0cacdbaa65a933, 0x65da7e15f4801c8c, 0xed826d65f985c801, 0xe124058598f74d7c, 0x1c4148d3aa15f0fb, 0xe40da0eed0ffc7cb, 0xd7fb021d5e68c5ed, 0x022e761e51a65629, 0x92416761f1d78545, 0xd5eded28964a5dc2, 0x1cbbdd86568b1301, 0x7d1d9c9d5191fa23, 0x9c84122a23e787a3, 0xf0aae22438d61d14, 0x767bdcb894e9b5f1, 0xc8e54f085d3e85ad, 0x107e80aa2de943ac, 0x9ee3c641f9303797, 0xdbbd06c34163adcb, 0x4ff559a34150f8e4, 0xeffed9fcea4d55a6, 0x100d3d521795c966, 0x84a9872617ba987a, 0x8245b0be30851879, 0x4a7a00c0cfc62d66, 0x1a9c2f7c0af63c89, 0xc627f1e50eae1edd, 0x27a7e28f2376f50c, 0x583b7830d443d79b, 0xe99ea7649a62932a, 0xed6a91b87a7f5398, 0x19207a5c0010f2c1, 0x4c9151ec2540092b, 0x9dafeb33ab61373f, 0x6e25827a66c7139b, 0x0695bc78b75b7417, 0x91eeb83fb91d0093, 0x487878828a059ccb, 0xa972d875f120beab, 0x9ddcde21e6708ae3, 0x1bcbeea1cad06878, 0xd3b35c29b512f927, 0xa21df9bcfaa42752, 0x5d4f6279bc577455, 0xc700104604bb7094, 0x13bb0d4d74468a55, 0x98c963425fdaa618, 0x1083bc2b3e7e4169, 0x3fc24373f7f14efb, 0x2c806da85c981170, 0x38471408d474e25a, 0x3bcc8b68de988a47, 0x7f466e2394095394, 0x5f79fa82cc84b4fe, 0xe4ef25f3a6005a8e, 0x2616c3f5d347b849, 0xa91309a99648cc51, 0x3ad18baafa3a3785, 0x646493d438846b93, 0x33cbdafb2f71a5e4, 0xf078d4b7316983d4, 0x56c08649ba5ee554, 0xdca51fada3fc2de6, 0xd1bffce0bfe748d6, 0xffcc723739d0887b, 0x1c872c55ad37b228, 0x90c1504a645e985e, 0xa74cebf5085c23ac, 0xf42c8e93ed09f34e, 0x71209b98f7bb6c11, 0x54b56973d9fe2c21, 0x3ea1209fde2330e4, 0x7766c42c4f04cf59, 0x03686a90950de638, 0x78dadf41a9b86257, 0x9a2718692e0c8272, 0xb13f41655a094cfb, 0x14c8a7e37255920c, 0xf235feb8f3f47556, 0x3885ebcef31c6408, 0xb98fb084dfdd8d03, 0x74f66ecff26f4061, 0x844c37174a0c4372, 0x76f99c708ba74d83, 0x4983890bf6c09aaf, 0x9b6fefb3dd0c3174, 0x62f617b31ea74edf, 0x25b21b3cd24cc684, 0x04d7ac4c7759fba4, 0x8482fe65c71d6336, 0xfc7861d998e02073, 0xd7f9722db6aacc8f, 0x6870ab492b30b3c4, 0x2ece0a7a8a5fed2f, 0xeef1d6c4bb09dcf6, 0x5435ad078617bc61, 0xe6379f0e8aeb78eb, 0x6c20e46fa4221ebb, 0x3e46478bd8152093, 0x7123630de738c9ad, 0x978ab949e097ced6, 0xd9e7e7e7fc05b3e7, 0x8b39851640afb2fd, 0xda2f9ab8a8daefa1, 0x702d92e74a49a01e, 0x8f27c82c59cb4dcc, 0xa02c191c19e50c37, 0xb27067bc3f3699a2, 0x3bf3bd9826ed637f, 0x5704a1588c2f9363, 0xb9f98c6b4f32dd38, 0xe5b042e74bb01dec, 0x5028dd896bd10768, 0x8c08ce41850b1547, 0x239315793f2ca3bd, 0x0828fdec05cb4523, 0xc4c81295367071fe, 0x15e0f0d81954258c, 0xa6088f12f97d8f02, 0xd8d0f216072e98ce, 0x726eb08402fe516d, 0xcec857de79f4a362, 0x6a7960a53f296fac, 0x81ca44e6fce0bf94, 0x3530e7498f1810fb, 0xd9c441e49a61a487, 0x6da37e884f2ee127, 0x4fe38bcc0e50de75, 0x8e6df28b5a2b61ba, 0x7c6c9ad29a009d5c, 0xefabc870b24ee954, 0x35002af7092296fc), (0xd643397f935094cf, 0x870bd92e1f824ec4, 0x34bdf0801de60cf7, 0x0d88ec4692f77eab, 0x096f64e22f2b82c0, 0xb53458674eb5723e, 0xf1fc71e1c11345d8, 0x0e964db1ad53757a, 0x988a3bdbc77f8978, 0x35e1545894f83b08, 0xe1275b6e6589915c, 0xf4354bf61444cf4a, 0xa58d717df825f33c, 0x4326b9b7fd30372f, 0x9814ed164f854d06, 0x4dc8b7c29b22a57c, 0x07ab775a341455ce, 0xd4a93150c9420e9f, 0xbeeb68e2fba01f79, 0x0a6a8784da51c85b, 0x6dc1cac53356069f, 0x3ec8402647a540b9, 0xabb002be62f631c2, 0xc8578e9eeb8dbcb4, 0x72f35b79c1a6d5f6, 0x56127777c0b225fe, 0x8b035e5efed57610, 0xa15af83a006047c5, 0xd42bd6d1a406a7c7, 0xf45ac35f8017e304, 0xd8f9d5eee6d21114, 0xae4dc44e7cbaf815, 0x76da39c947457f35, 0x6c59b49c36990bb3, 0x81e9cf29dad76c01, 0x018e04d96b5e00f7, 0x0d0e0000c582dd99, 0x3a48024755598a84, 0x4f1ea17482332fa4, 0x5b2d5257cf3cc329, 0x35b78e8183be6809, 0xb66b74cec64e10be, 0xc43704cdf86c82c7, 0xba20dd2fd22245f9, 0xd3f155e74d3c34ef, 0x773386a7f5b97891, 0x7f47bfb5080fabfe, 0x698f0ea424892589, 0xe02aeedc1d0465de, 0x397c03a1efe48995, 0xf7391bf919396f17, 0x065b71e10dfc5c78, 0x4976dc3d03263662, 0x8ecdc2f3253d965a, 0xabbdfe3331b3a292, 0x77838268923d9e45, 0x7aae87626b2f234b, 0xf46129cc031f40df, 0x1f212e0a147417c2, 0x55b6b1aa224dfcb0, 0x5133b4500bbe43cd, 0xfbc79204742ce63f, 0x03876309d0e8385f, 0x6febcd9a1f6079a8, 0x7ba954c6455054ea, 0xf3d956ebf6bf754c, 0x787136d760fa86ab, 0x03103c0af24ad845, 0x3f9bf3a7803d27ed, 0xc7758304e9a1d7dd, 0x68b776103718d679, 0x647918cb0c3b1002, 0xe83ffcfc7a293210, 0x1827d2fe3cdb6100, 0xa66a981415a7fe9f, 0x5e3bf0ff95ef70b4, 0x31adabfc1ef6d618, 0xba89ee519226ed52, 0xf72d137a34f811d3, 0x36fa9a52547c9d90, 0xfa3fdac9426fb713, 0xe075b10c22cb29c9, 0x10e6f71130f4f17c, 0xb6b08ee0ab994e27, 0x38eb08e2e96206c0, 0x28ab4ba59aa7f167, 0x6855d4430923669e, 0x15283aac25c5a57b, 0x18223fed9f4ab72c, 0x13de014e80359fd5, 0x97b7031627ce8ca6, 0x25534e7038e0be03, 0xca53a631b6036a63, 0x615f7f968bba6ec3, 0x3d139c34669ace55, 0x2560953fbc375004, 0xf098997f78499943, 0x67b7293962a8e3b3, 0xd4e976b0bb5f200a, 0x88045a1eec3b915f, 0x650579c0fd409f13, 0x0f080e3aa4423824, 0x770af774ba6af720, 0x5be15bed26f796e4, 0x236490967eb624ba, 0x2b8d21d13c3b7d5b, 0x2d9b375a42fe5ee4, 0xddfdf2d9de6a561a, 0x1c2486976109d824, 0xdec3081cdfe358d6, 0xfe951624333136f4, 0xbe686a96a0b52b1d, 0xe66e8bce1e364bea, 0x6df104e7bfdf7e91, 0xe7f33ef7311a6099, 0x6b32fffc9718ed4a, 0xaa0199b08b186928, 0xcc9f16f855ef9c1a, 0x017092dd07213183, 0x064abbfbc44dd974, 0x62bbb46b60f500f5, 0xa1bd0f9095445a46, 0x97ee11dffa92815d, 0x26d12a66ae07d435, 0xf7de1e92d1be930c, 0x6616e6c83a5b8f01, 0x2dc4784fa0abad63, 0x66739cb22920f6d0, 0x75b41d4c0b6ea297, 0xa4674e33c01b6cb4, 0x2491ee2be9d7e7e2, 0x2bd62ac084f447ad, 0xdb9dd852332b8ab3, 0xf256424278e96d76, 0xb95a91ee0dcc405f, 0x101284726a2333c3, 0x16267ae96c1574d9, 0x93662e5f54bd05ba, 0xc4a5dcfaffad5a43, 0xe645e365d2e1b59a, 0x1204d3ee428de7ac, 0xff8ea450245313a6, 0x2d6d1774b16daa30, 0x73ef7d8e469a4618, 0x4bf3aa705a8c2da3, 0xda274e53250d5b5c, 0x00794714fc3fc6c2, 0xf00146dd8c1ee01b, 0x47612f7f8fe3848a, 0x2fc781f293466b0e, 0x28e1cdd7147f927c, 0xf5b62e523877e1e6, 0xd948975345d7e13d, 0x58d54af4c24bd134, 0x21f3c1bf27507421, 0x2d2f90f8dc746967, 0x28dd99817b768a6c, 0x57915a1583ce87d3, 0xac0ab23a40616f11, 0xa65dbfd006915044, 0x557c0895a56b4245, 0xfe4f40cd22fc47d8, 0x42c9ce0545f64fdc, 0xb2546279b2330ef6, 0xd557684a79886980, 0x1f14fd7add35998c, 0x349abdfb60fd25b2, 0xedacca8c71183fe6, 0x5d66f18300f698fa, 0xf82a907433149c24, 0x46d5b460f6fa5d08, 0xf9a0397d4b052203, 0xf2f805f60108cd85, 0xac4f73cbb1d4d0db, 0xf4a1e9860cbd7b36, 0x5fabe1ff3943e95e, 0xb157d7e510f316d2, 0x29c71cb4dce9c30d, 0xbd1600c8742d913e, 0xf0fad8ac8544166c, 0x0e4a3176f004252b, 0xb16042e9722b2d1a, 0xe71c2541a526341d, 0x940969bf250d8683, 0x97069a9a389a42be, 0xa1cc8c18cc01b000, 0x0fd4dd012f2f40a5, 0x8a416107beadb308, 0x0533a4832ed2b8f0, 0xd15c4d5a6acc8833, 0x06f0bbf439a2df7a, 0x2c9417a7282ff845, 0x2c5b9c3a132f2fa2, 0x5bb512e952bcb7ec, 0x793d839a35909222, 0x07f5d8cdf3863822, 0x2da8ccf7e602cbb5, 0xeb400de34a6e633d, 0x765975de142722cf, 0x56106b69601e5a71, 0x039eba1989e2c864, 0xbecf64aef6421c51, 0x51df9f37865400e0, 0xd1f6cc3e1d125b17, 0xd424ece671812913, 0xc2684cdf7860fcaf, 0x2522584ef611555f, 0x63e0f604eff52201, 0xe6bce91f1e547ce2, 0x7856ec4341d227e7, 0x674c5be6ca5f2bd2, 0x3d7901ffe4bed041, 0x3fe32ab3540e6748, 0x5d200d621dfee900, 0xedd6ac7cfab1d5b5, 0x004d378c84357354, 0xa82b91a70d0716b7, 0xb25a34b14222bcb1, 0xca8f24e379ab04ef, 0xcea7a37de4e0044d, 0x5a08b0ad0aab8e1b, 0x6bf17b936dbbdb4c, 0xdca48d8a94a3cecd, 0x0c895559f1cfd968, 0x889c9d47c765f0e2, 0xac8a0aef36dead83, 0x18744af10d3c269a, 0xbff26b4469ec3d61, 0x487e36d51f0b9f86, 0x73f5efe068da0513, 0x5e1c11d9f32cbcf7, 0x851cdcd1e3bee01f, 0x56dca724e1e26db3, 0xe831d5508bfc95ee, 0x219a2c6d9d1edee6, 0x1699b9eaa186998c, 0x6934dfbc8fbd6fd4, 0x48cc722371320267, 0xb0aedfee9a9715ba, 0x594f6ee424021157, 0x142e4edd538c5c1f, 0xa615f2953f111f75, 0x89de86c2261972ec, 0x10cc626499f63bf0, 0x4942effa89b2e655, 0x99bcfb8618553cf1, 0x2f1af6b902b3795a, 0x3f56cdfd0e7c83b6, 0x9202b3fc89224414, 0x3ceda32cf3708424, 0x93317b7884fbac5f, 0xcc61992cd32685d5, 0xbedfc1ed564197e0, 0x33da22dce6730ae2, 0x48d3520aca2906c1, 0x91264a3cbcb0427c), (0x084ffffbb5c696c6, 0xcbe7059301030cca, 0x10dba0a89b7ae484, 0x6c4be36a95bb4173, 0xb0700c47b4283978, 0x96a1905e518eb231, 0x5541ae6048b87bd3, 0xebdffdf2ec4a8965, 0xe7dac93967257a3c, 0x2e53806d3dff5790, 0x7db8a1b7c2a1aef3, 0x014e4499be9dfc3a, 0xf532a41b1a1851cf, 0xd07218ea39b41d88, 0x7e4f56c1c847497d, 0x21cf671753ce6dd0, 0x1b94883a9bf080f2, 0x98ee47908c850f29, 0xea6cc9e9d9faf55b, 0xe4a551ab0b7e8776, 0x02a62b1b39d35f9a, 0x2007a88147f3e621, 0x3eee06e77958eb5b, 0x20417def7ce2e665, 0xb6c688852c50d648, 0x87de5c6b5dad8485, 0xf98939b74fcba7ed, 0x17f3d88e86b881b5, 0xd2580044a988599b, 0xfa3561cee6119564, 0xd6c26fcd95868567, 0x234fe27482a48621, 0xab8dd0919dd841e1, 0xe6c5b5b21cdd2ca6, 0xbc5f9aa66aabb40a, 0x259b421a4b3a97d8, 0x8a77055dac35ffb8, 0x4a81d2e3a067d324, 0xcaab1deef8052774, 0x228341d3379c7353, 0x5aa2bd3dc88ba9cb, 0xd3033422daaefa72, 0xcb6b79fdadb6e597, 0xfda6806f00b4d073, 0xd08715b956ed48ab, 0xc9feee61fba754df, 0x820c488b911de24a, 0x9ac172eae6b1aee8, 0x8f273efc20c0598a, 0x97c4f75a1965ecd0, 0xd38ed06f0267024f, 0x3ed630c7078ebdc6, 0x7722dedc161851c1, 0x528f82cb8517e4a5, 0x47b7ebabd8c1ada4, 0xe489c2a875f29fd3, 0x23ace5009500964b, 0x7d7334269ea7c026, 0x98d0c89edfb4b624, 0xc818556b98ed63ad, 0x484aba554482daef, 0x5e709598872e48d9, 0x80454a5cc07c5208, 0x3059c5b7c3108393, 0x8652d42f18215c51, 0x85f204507877bc59, 0x1e9db87d7857e5a8, 0x4c3f1cdb4a71aa80, 0x584dc898ad8e843a, 0x1a184498b6a7ea43, 0xd1c0daa5a7df4757, 0xa7c4bf4f6828f1d8, 0x8ffa874157444ae3, 0x56801a315748568b, 0x6bb9e29543dac710, 0x57971aacfcec1304, 0xa688901e2be3b820, 0xf7efe98c3ff5e067, 0x67b2538b98b97770, 0x46817d416eca68df, 0x9e6cf28e916362dd, 0x06e7bdb094d57ac6, 0x6c6bb1fe23ce6509, 0x22804213bfc128db, 0xa4589d1a48a638e0, 0x38c7eedf25cc18f3, 0x2a4678ceadaa91d6, 0x4604b7238747672b, 0x82523e882e0c598e, 0xe369a168c43a2d03, 0xb49e8ceececf0d1e, 0x17a66c0499a732bc, 0xc387ac5f99788e71, 0xfb46d56c604e813e, 0xa9e743ca0d78e71e, 0x34b11c1b86f01fc3, 0xe631825b6f03b639, 0xfcc5b829197e4f79, 0x8597bc4ee6fbd911, 0xd862c82d14bdc588, 0x91ded91bf232754f, 0x535a9ccd70dd2910, 0x8524f5b5233f357c, 0x1b742e1751d255b5, 0xd12726532a84f084, 0x6235d35f0ccaa60a, 0x39c325166b6061a3, 0xd207e962e32ac20f, 0x26e70d9ddf841f6b, 0x4824094da830b678, 0x1331c7082c4cdac8, 0x0417d8c7d030e8c8, 0x51ed41c4c8488a14, 0xa4e156901d8a4f67, 0x65135a1bba00bdaa, 0x4fed5f5a6a5fa345, 0x632a3b3fd4af6f69, 0x59a6dde422be83f4, 0xffe748b5947e037e, 0xe812f59b25594cbc, 0xc9dca9c00f9db3ab, 0x8f81991d125f011b, 0x09da8808c57748dd, 0x2171e539d6c2dfc6, 0x3b581ddc83d1d949, 0xe8e583b0cbb1fae8, 0x48544b8891d1af18, 0xda82d2b157a455fd, 0x780bb515954b0de2, 0xd176d9a017cb4243, 0x0ea83126593f50c3, 0x6cf30f0f0ad18c91, 0x9c642774b17c5995, 0x44c55a544e3fad22, 0x2c0058e2585d332e, 0xbce3315de008b756, 0xbbaed746efb1b82c, 0xc29eb7dfb59a6cd1, 0xe12751f6e72149d2, 0x90af883bbf9264d1, 0xf3ddfab262f75e13, 0xd722f377fc8ded28, 0xaffa5fa4a7ef885d, 0x6a36d972c412eeb2, 0xcaf7f49c141bc5fa, 0xb637c407342f6f21, 0xbe1dd4cf7cf16093, 0x2fe8215ed1f51b9c, 0xcc8ec810c12b2900, 0x21d3656c80aa2375, 0xce0e08f184c38a94, 0xbb9679c6903eb836, 0xbb680ceb9ce9c7c4, 0x97f7f3cb0ca7848d, 0x9e0bf18fcba0055d, 0xb6dd9d6bef26db9e, 0x7ca9cc9a2590017a, 0x7eeb34f72cc16be3, 0x52ede7cb7f1c8537, 0x9ea75aaa6749b4bd, 0x9792f16570408254, 0x4dd796fb9588162b, 0x7085612d543bcb8b, 0x6b26fc51f73759b5, 0xc1689fe5b7d6c1c4, 0x26dff1655657dffd, 0x873d75acdff1e0c7, 0xa2813248ebaf4a3d, 0x6e72e8249a82d279, 0x46ce7c09ec6963ca, 0x1ce96baabfac8ff4, 0x50ce053dd535e6aa, 0x81d02358596d03e4, 0x277d1ecd00d37cf7, 0x48572038c9c420f4, 0x53b31c9ef3e4fc9d, 0xc3f0a26eb2028341, 0x5b5fe659da6de89c, 0xe916b753475f5069, 0x3a9218b4b99a6779, 0xa350047ea0d3495b, 0x24dc1575f99c372e, 0xd834152f7df3b27a, 0x8054c58bd47d2989, 0x8d6bc5a2fdfdb4d8, 0x4847278629fc7e0c, 0x99c8a0548c5498cb, 0x5ae85634d41cebbb, 0x2504128c70a640f4, 0xfe97981373ecbd93, 0xc51fee579294f879, 0x9b722e425909fe6c, 0xcb85da2b0b24c681, 0x439453225bafd434, 0x38ecaab092f5c4f7, 0x7adac2ef7141d33b, 0x2928d2245ec7d6c9, 0x378bbea38f2e9c3c, 0xde54e18dfcd61349, 0xc3e2d5eeb99da1d4, 0x0db0119e1a49f712, 0xdf44a83c5aeac964, 0x9dca2fc7650fc89f, 0x474f8306e2d0a038, 0xe034ae0cafde884f, 0x2028a7060404229c, 0x0bf18db570529a7f, 0x8da4e1f979a89418, 0xa1155a0d3e9e3580, 0x72e301904d125ddf, 0x7addd9e135d4536c, 0x9ca0de59a162e0f4, 0x6a9cdf844b2a728e, 0x9fb99a8e024b16ad, 0xb1d048bdec41a300, 0x85141c031c2af09a, 0x2ce594f8e860b414, 0xfc000df5267e26b0, 0xf90606d58d7c9544, 0xfd4f99f8c5f092af, 0x411f84ba8a0cd678, 0x38f5d09ddc852396, 0x5ed7cbfec1e480fe, 0xabf50f7e8e98a922, 0x6efcb9ebb4217cb9, 0x2c9e09fd2363a2ec, 0x664669c9fe52c790, 0xf8f6bc9998df87e7, 0x875c4cd61ce89b3a, 0x172a71d4b3228e18, 0x6138a69668253823, 0xeea2323c7ee068e1, 0x078b037fc5eb3378, 0xc6d01ec2e6b70b7b, 0xf9996aa470e99fd3, 0xd4414993552c525d, 0x4eafd7f70466637b, 0x193289bc8dd0b3de, 0x6aa965929bed2206, 0x213c372d8a2ca9fe, 0x79ac861e1a994af0, 0xad06caa0fc5126d5, 0x48049be7af6bd0c5, 0xd667ed766ee33bd1, 0x097cb7e0344f9b37, 0x8f77098ccfd6fa91, 0x3ff2ebb44510a1ca, 0x79c55b63d42592a9, 0xd974cd1b268615c8, 0x7d52e5c34dfb7d49, 0x9776105d8b9204a0, 0x0840458b38b14d42, 0x1066c46c11e85f3f, 0x7601ead727ab97a1, 0x7b2fbf3ae778c770, 0x38a6d63621daec6a), (0xdf24348581199113, 0x9a7b6c1f3b151829, 0xe4f2fe468e70656f, 0x34b96cc6a061dd43, 0xaa7861d8aed5f733, 0x24863c82dbb04e09, 0x0eed2da59932c9cf, 0xff4a665498a8c039, 0x3f0b229cd7e87885, 0x1b9e6809c9d696f7, 0xb102222a12f298d8, 0x725a2f0751bf356a, 0x692835a3ba1118d6, 0x81e609f5d249df6d, 0xe8f3b161aa4ae7eb, 0x94b12d6492cf94be, 0x1e67eea5746ea61e, 0x8ce6c2710a246469, 0x8c99185499237c3f, 0xaefaf8eb2a0a87ba, 0xaec223a6ce0ebbd0, 0xf84595cbe9a82180, 0x5470caf43074a23c, 0xe398478e5128ff19, 0x25af45b92b3aac01, 0x55495ab6e2116bce, 0x9a5b792c96f9e5ff, 0xae64519bc81d6cc1, 0x5a00b476c48dc4ab, 0x92debd7fea7986df, 0xaceebd147e4651d7, 0x86752eb512f0a471, 0x4ef1e846d787dec9, 0xb06be0d0985e6b67, 0xb957e6c4ab228053, 0x4573afb5d2cf0755, 0xb2ee6f2b9d274518, 0xad2a55d9bc65f216, 0xbfd8a89f0c2f1aac, 0xb2c92d4fc68fbd00, 0x9620105253677c37, 0xac92120431c8bcb5, 0xf8c0ce4043433141, 0x89babcb97025f41a, 0x13139a8bde9f054e, 0x7355431e6394e54b, 0x94751623df1d61b2, 0xb74989bc03954dce, 0xafbc71237e850d1e, 0x42aa31cbb034b90e, 0x4a52cc67d4907ed9, 0x6694861b35a989ab, 0xaf43b5c9282de130, 0x9b2524c038094f35, 0xa891d9dc64431766, 0xd6ea4a3692312b3b, 0x7b877de1b97265bf, 0xa8ead120be843e63, 0x72d9befdf168d866, 0x6effe0e05c25fe6d, 0x50b8701f80433c56, 0x061cd6b0c0d4f0d0, 0x981e14a20819a413, 0xd56a4b8d531fe266, 0xe1ae82fbaf09f512, 0x027ebd50377876ea, 0xa13eb84494130fd7, 0x5c60d48c1e0ab4b5, 0xef495aa80b2d5672, 0x342f57349c57da99, 0xbef93e7b4a3178ae, 0x03ffdef7946c14b4, 0xc90e466017f6e9ae, 0xa01df96a428b0797, 0xe362af0ac7c877ff, 0x2f4ec2db7215aa4a, 0xdc817e6771e9bbc8, 0x097df4cc57fa19da, 0xa94939a5fa44bc8e, 0x964d78e85ce6d413, 0x05d0cc26358754f8, 0x4a1b690e1cba6999, 0xc796c3cb03285249, 0x623f8404a8d3ae07, 0x330abe6f0032bee3, 0x4d5e7454a690a264, 0x88d54f674fcd951a, 0x8c79665da824cd57, 0x24c0f3426c661409, 0x55c099f5c0352376, 0x4dfc4bc9c267426a, 0x9745eb304a429e7e, 0xdab498cc80d782da, 0x83775c4d5d61e966, 0x8bbfca82d03e9784, 0xf215fa8171365903, 0xd8ab9b7a2c48f5de, 0x0d3f4451bfbfa72a, 0x1b576c12562e413b, 0xd7c515ba764227fb, 0xdd9feb19e8e2ed91, 0x21b314081fcc5315, 0xa39bd235a1c57d5f, 0x6b2067a259f0d370, 0x8cc2cfcd4a10ff70, 0x7803125f3b96c1c6, 0xf12dc1d1abc15ebc, 0x475a2a10cef75bab, 0xce466cc75ccae9fc, 0x040995805a98d780, 0xd91feae8c2c1f331, 0x94e4a42ce98e9e8a, 0x0be6c20662130247, 0xe8dc46f283d4c7fd, 0x1a478d61e61fe5fe, 0x1fde86528989571e, 0xd68feb051e23e5af, 0x1e337a18cc53179b, 0x4ae1edd8ec4fcf32, 0x5c116bebe598fdb1, 0x7cd8afd395c12c1d, 0xe7eacb5af7cf0777, 0x45728549d07f5049, 0x2a89af71334aa453, 0x11ee66e8e2c74e9b, 0x1b7919c8c6a661ed, 0xb8947b7068fc14d0, 0xe4265054d72106e7, 0x9c6527d0723938f5, 0x6153c08ae5f9f638, 0xa8b37c49746eed31, 0x9372fb87535dd818, 0xd437a89d0f2112b3, 0x5f87b2c413a1c8e4, 0x2cf986d863a5b4b1, 0x64b17e4099f35a93, 0x97c10947a4f3d2d3, 0x14351b6f88fe5d53, 0x93411abafeda8cce, 0xa10ed5d2346ca3a6, 0xefe43ba32fc1ab2c, 0x36f3d130f30daff4, 0x54967369ac6dbaaa, 0xbe793498711eef82, 0x668fe9c6f5abe761, 0x6387b893754072f3, 0x103bca34315c21e2, 0xa247c616f02648e3, 0x67278d2b2ab3cf64, 0x0f3ae903a941d891, 0x55c9dc9cda81ac39, 0x8d2bf9fa036ba37c, 0x32a05248578a4057, 0x6abf5c16871596a7, 0x62563b7b963ad490, 0x506aa0ccb59b2ede, 0x0a1e902799e9374d, 0xf981495fbf3a4a16, 0x69454a124507bb42, 0x846e8ccbbb287ced, 0xe51f185cc9ccb046, 0x0e42a8baa552bbd1, 0xa9c16a27d154ac3f, 0x95093a77632a72ad, 0x4b4333abae4f6e62, 0x42421d81960120de, 0x189f7b9a79fb3bae, 0xf6b8849c817854b9, 0x49781ba526172a23, 0x752c9fd952ca3858, 0x74de21e5db23a6f9, 0x0ba5a8de0bde499a, 0x817b84f0ee955cb4, 0x028b725848f88ae2, 0x48cdb46d1892e832, 0x36b3888b8a7369bf, 0xfa375338469f631a, 0x96b3caba829cacb7, 0xacbb6117e1c9a888, 0xb6ffae54262615ac, 0x52a207a253c802d1, 0x420cf3acbba8cb3c, 0x1e4a893628bebcc7, 0x879fd2e603cf131e, 0x258ded7fa4a5ef27, 0x3a3826a0ba606899, 0x869ccc9f1fd0dbce, 0xf71c614b35fb9b89, 0xb0725500c8b36026, 0xa38715eb8d60f4d7, 0x2ddb385582cc588e, 0x96c5105990363a3b, 0x4a37eb6a5f39ba75, 0xa557ede9c2a66414, 0xc8c24fb4206506fc, 0x1339c3979636cf3c, 0x0faf35529fe6ef91, 0x315caf70a442ea17, 0x8b0e1ba6c4935d0d, 0xbc9d7229b067be13, 0x18e52b50a02a9e7c, 0x365e11d0e1753af1, 0x796ef5a69c43b5a6, 0x788485989ac5e029, 0xf87a54b3499ef731, 0x954e63f5000bf8b1, 0xde8274450d9faabd, 0xe92daa708134a5a4, 0x558155f751b7b7e2, 0x51c9c68cceaec498, 0xab6e00d49b9693a8, 0x8b925bd6af11fa25, 0x84e3adeba06dead2, 0x9f4ed5c163ca36c0, 0x68ea05242a18d454, 0xa80690d1298dddbb, 0x1dc36a80f1d9e0ca, 0x6206cb0a0e05060b, 0x1b5d2f3d29bdbdc0, 0x4b4686b0686ab19a, 0x67eee9f260bd774c, 0x08fbde7aa347ed01, 0xebe62c9a60137a4b, 0x7b7a4c34ef79a5cb, 0x81441ee74c209701, 0xaf13008ef29d4342, 0x325c19cd67e762c8, 0xbea2d038f3e3f7c1, 0xbe93e3d330f67211, 0xc9b7edf2a2f9d040, 0x5eac7e72fbd51366, 0x3a00e3789dc81285, 0x23f52f6679afaaf8, 0x3f2c9f5d1e56e9fe, 0x8e1cab9afd7056ed, 0xc90d41ee1b2f0a2e, 0x110895854db39478, 0x2971c3745f85c258, 0x2d7e095b004808e4, 0x5625bf01aa9812eb, 0x4a0c0fa579a9022c, 0xc0de1c2eb51c29eb, 0x8f361ecfcec81d38, 0x2312bb5328ad87aa, 0x7aabfbdcd230d48e, 0xcc8274f399678965, 0xfa31bdb231e5a075, 0x471969d284c63124, 0x2aa5a9393d40101e, 0xc2e69c892318111d, 0x89dc76e1d181ddc7, 0x191929d70209e10a, 0xc4886ac30d285b1f, 0x0d522df68c8777c2, 0xcd8047002c5cba8b, 0xab356109cc867f0a), (0x931cb035ac6bb768, 0xbfa80a8d9e4bd8a2, 0x5d137e149618427e, 0xdf95f99904effc3e, 0xf92d05d9a179c11d, 0xb9fb18042dcbc3d4, 0x85ff915fac6e73be, 0x2e88eb6a8a08b99d, 0x1e6d83fd54d8aa03, 0x9615ce3c686f5784, 0x72ed5048c8ef82a9, 0x82a59f81596c35b8, 0xf20d9714ce00f83d, 0x8883ff3ead83dcea, 0xe1ed81d1178f2cc0, 0x1b52c26af375fe82, 0x916289ecf7a61041, 0xa90218edb5aa6995, 0x71cea9b75a7d0632, 0x1d84d43e3458f8e8, 0x09707011e0cadbc2, 0x976249104898f0bf, 0xae6fb8ce00fc93db, 0x085d1f43ce0ee3e5, 0xda50d3e53eecf781, 0xb2ca14a2fda59754, 0x1764ebf11980456d, 0x06767769961474b0, 0xfd1532435d668d7f, 0x3db633fd8a9c835e, 0xd9d4a0b8054536d1, 0x9d7d13b8fb606221, 0x9dc26b8b7a91ecaa, 0x0385625c645069d6, 0x7634092d0498bf73, 0x2189f48d366f9bea, 0x36b750efbf5d6187, 0x162b1e87111c2100, 0xd73dd1dc29990717, 0x7c6c98b663d828e9, 0x96c4f32732ad9bff, 0xf7cc3eae1b44924e, 0x0712a9f1e996a8c6, 0x8b6647e631806b2d, 0x270f9ccc5b8b680a, 0x08e74b4ef3859fbd, 0x7fb38372ec360c33, 0xe57d880233eef8ae, 0x3985811b5fb36e98, 0x84d4ed2ef3e98ddc, 0x944ff191655b5b0a, 0xa12d8a4a68b9ec05, 0x12b844cc07dff539, 0x70e01ff9f6ce1f76, 0xad1f84a36a5ff1fe, 0xb47ca4b44bf4b92d, 0x2beea21d10be4352, 0x5bd31fb951ae70d6, 0x8e71d877506e628b, 0x2488e100c7f1d5a2, 0x209694ecf10291a5, 0x9b09d9057284edc6, 0xf9d26ecb402ed285, 0x44e7aeb448f0af75, 0xddb772678239bf63, 0xbc6d35a4af3832bc, 0xd5eb06a718243e68, 0x951a26fc3f178a5e, 0x3112d9f0c640c540, 0x492d63e72afda07c, 0xabb49249a02733c4, 0x460fe268a82a2741, 0xdd40f0eeb83a6c40, 0x55481f9d73c21db4, 0x856cdcc765d6ecc2, 0x71aea36aabf441b5, 0x0fb2828f02938b93, 0x7c88742dad3739c4, 0x0464c4276aa8db82, 0x10dfcb408149115b, 0xba138cd6dd725f3a, 0x71fc7feb1db9b1b2, 0x2acc6a2e6765f0f5, 0xfbd0e83246e549b4, 0x5527f84219106d3a, 0x40d6e8eb3b0e42e4, 0x573f9704661c0808, 0x5baa6acae3ffa0e7, 0xb57f26021ef13b93, 0x328ebf7cc0fc7535, 0x393b11720e5f773a, 0xd26868af6f7d6bdf, 0x0bfaeffef33a8e16, 0xca0d0a85554b81af, 0x2cc18103a223fd80, 0x2d2d4999e35fcf33, 0x8a0d6f7ca4387829, 0x4d1a4a328bee7622, 0xd888b14cd6d37363, 0x6f0971bb3cf040fb, 0x6d922fe33a3f6047, 0xfc2fd28c3ab9c135, 0x15215d4e9b754ee1, 0xf36eaa7c1e0ff208, 0xce8e6c1bf059809c, 0xc0f91dc18e3d8513, 0x8ff64a22efccfa29, 0x656a5d009d48dfe3, 0xb4b98234b65d9cc3, 0x8896632dc9e06907, 0xe6661a74281f9b59, 0x61cfb97446f9437d, 0xb0414f43e6f51fab, 0x0e606f509506c36b, 0x48bf15980d489f55, 0x00f79ac76b1e3567, 0x07153c17df813cbc, 0xac503c34b95d52be, 0xaa275bc7ef3c4ab1, 0x3b981db85e7faa56, 0xe262bb93fca05c3b, 0x105e0068706d9cf2, 0xeb60a2d3aa4e9917, 0xc60d1774724bc2e8, 0xd5a9d18b128e5b71, 0x5fd812bca76c35ac, 0xa79cd46e1691e159, 0xc914bdd00bc862a0, 0xdd1ee0a685e6a7a3, 0x6f60e3821a8e6fd7, 0x305691e86fc78f62, 0x5f04cac8da4a1a2a, 0x79f08683a3bf7f34, 0xfac08dd0755b005b, 0x9ff5cf7a3e97a0ad, 0x1408969d073d531b, 0x8290a0b0898dc84c, 0x3878685a8b4694f6, 0xd8d254eb026ab4ac, 0xbcc26c325570bd36, 0x56dbb1f6e2541a9c, 0xb1bdef1f739d2a2a, 0x96e98cdc40c1e395, 0xb007e430a841adca, 0x03585cbd563fa3e9, 0x300a86654757c390, 0x48a6e299ed7efeb5, 0x4fdbc7932aae628c, 0xfdfe5d4fd9668820, 0x57a40feb0740811d, 0xd2bdbd5f8290c6fa, 0xc91da2ed354a524a, 0x7965f42e61c00a80, 0xd9a30f8cf3704b6b, 0x60d50353f72d911c, 0x08b9aa008e78d1c9, 0x0a449e76cf39d7a9, 0x2b37438f5cda9b6b, 0x1c0977595d44d097, 0xe9407c7f6b4c76c2, 0xb316508087aa285e, 0x90abf9094ecee888, 0x6f76d048a47f7e14, 0x9592a82c1a366410, 0xa2e7771c9c3d1813, 0x3cb10c30fbda8d2b, 0xbd4517ef009137e4, 0xce9d59b6b05e7e8f, 0x57b735aa4c196549, 0x86427cf2e6a864d7, 0x19bdb39c337dadc2, 0xa3075b3dc2d172de, 0xf396c71ea2520809, 0xfc164018f194e85c, 0x7d544962af8becf5, 0xc52db79422c79dad, 0xb5b379d8fb23cee8, 0x3510ad4b4025aa99, 0x5c0ac4a2abfe0b26, 0x2cd007eb02a6e2ed, 0x983c5b1fa6075285, 0xc162260a8537b2f8, 0x22d65e57d029fcab, 0x794c2262eb4ee66f, 0x9f6c5e46c0ef22b0, 0xfc72f9d7732ce448, 0x3b245dbb638125a1, 0x81db6c6d46728a6e, 0xb9d7aeee2fc298d5, 0xf4000c6eea0b6d13, 0x42508c1ba6d81262, 0x3580b165a059b0ac, 0xa8f619af03486aac, 0xa127e1f044aa3ee4, 0xdfe55950b8eee3f8, 0x393d3190790822a5, 0x00e914b409eceb2b, 0x26b2f3952b8a4b0c, 0x39b5cd0fd63be3ab, 0xb240b51052941c78, 0xe9ae0fc9ad088fd0, 0x2cc7b5f2b8aaa05a, 0x0662dbceb630050e, 0xc92922f9077a8a6a, 0x80f5a00afdcd0079, 0xfc2c1951df5ef4a5, 0x4ac3c7554a958921, 0x00b9954a39d5be94, 0x6ba1927024bd5429, 0x46e6f6f1b0d47727, 0x44f62997bddb5665, 0x7e1aafb1cfc5f7b7, 0x6c56eb47958a5471, 0xa81b7f65e0936f1c, 0xdcf784250697d675, 0x5e2d1ef2acf4c2be, 0x4597fbcac3d403cd, 0x5dae96d2fb2a3eda, 0x1e9408d9d4d83062, 0x50fbabdcb47f3cee, 0x39416e9c61a96794, 0x9cc5eb45435d5d9f, 0xc017244b14bf2d02, 0x953ecb4b8bf35ce7, 0xa3036e0b52d9e35e, 0x4ddd24c92fb06947, 0xec9d2524db620446, 0x58ed6013a087439e, 0x042da7362aef6802, 0xaf9ce2d38e3833e7, 0xe3a469df5275000d, 0x42c5960a748c952a, 0xb17cf7245f06f4ec, 0x14154c6af69815ca, 0x65df4c947544ef97, 0x3ced4fc491284e80, 0x79cd74b64b4b2adb, 0x2658b0a46a12d2a6, 0x55dd8c0d8a2ef1e7, 0x34b5d9477de45fa2, 0x9d930f0fc7393a93, 0x74e423162de06741, 0xcde1fdc705a074c8, 0xe786c32e9fadc3e8, 0x9831100ee3fd0ad1, 0x4c94e887d0114d54, 0x335519442cdd8351, 0x957258d721f855eb, 0xb9ae7eec017741e8, 0x09c43e84456be8a6, 0x1b6d21ecaf1f43d1, 0xc0398a08479053ca, 0x10fa18eaa3056185, 0x5368b7a423934ce8, 0x3d20c4a6fc1f4551, 0xa320930c5539470b), (0x5a5f2b70494307dd, 0x72930b7be37ed0b0, 0xfaf828dda7c1acfe, 0xecc68cb29173ff5c, 0xc6375cec82d39856, 0xd8fb2ef8cc253487, 0x15cc8a41be4831a7, 0x97589dfa9d69fd67, 0xc8cbe785f4e1734c, 0x7cbdf12d9a9ad679, 0x88a442bb9b75d1a2, 0x5cb283d0b3188693, 0x6c64c756ddc2c1e4, 0x8abc13331b7fb87e, 0x37ca21ffe8ec1c67, 0x9c752e9b66569eb0, 0x9706bc35662425a8, 0x1b29d3c67aae88b2, 0x20a4e86b441041a5, 0x81cc9158637c5040, 0xcb11f4d7d5660299, 0xb3ae3f85ef87523d, 0xa5c6c18ae692c0a6, 0xb72fa98dba822edd, 0xf49f60bc02791ebe, 0xafd47f9c33ea0d5d, 0x4177990f9c01f0f1, 0x52c8ae39162dec76, 0x76dc6b75ca5f080e, 0xd8b6cf872916877a, 0xc685b8e5613c7ef8, 0x082c5cd7b7929cd0, 0x94a797eb33b15625, 0x55a8642c2ccb42fb, 0x1c8993d2f0d09a5d, 0xf5b39dbbf55e8451, 0x13c8a4f1df2dc36e, 0x94e69a990b2570b5, 0x20f8f26e5e45fcc0, 0x59e28e6f87a2bddc, 0xb4613195ba7666a0, 0xcefc14b159e4f9f4, 0x047df56b60d2db36, 0x54f8ace11a828be5, 0x91015b526c14e020, 0xed9773f0247fd60c, 0xebf1caec4ba9b72b, 0x5d117ef513f8f23f, 0xbd62b1f323dcefd7, 0xa005492b12a6e137, 0xa3c4f25022f91f83, 0x7359d88f6633fd91, 0x8b15de7e0f0d3690, 0xa7453f23bd99f2bb, 0x0308c3829e5942e2, 0x55a4343a422ff513, 0x099a77760c67a1c4, 0x6a1e7b6cadbf1026, 0xba0db9a40fc5687a, 0x93f50b1881335b39, 0xc88551e546a9dece, 0x811747afa04af6ea, 0x0e995cbf2dd4460d, 0x5156fd6f897398b0, 0x5cba9038e42d3719, 0x1b94a90ffb753c8c, 0xeffb4703e9535dde, 0x5a3a25040d2e679c, 0x9b488a3d89ea9d87, 0xad2a98c29418535d, 0xc8986d218033e165, 0x4fc690d6de5a58dc, 0x8dcf27010b5b03e3, 0x9615fc448f33ad3b, 0xef363eb8921e04f6, 0xa3735ad37fc14eb2, 0x4854d39c8a751127, 0xa190db29d7e70371, 0xd5576df11553a604, 0x3d206d8340e07d2b, 0x1b8a47c68ba7227d, 0x00db94d6c5b532f3, 0xe171aa2d18ba7fb2, 0x2eb6d6cffc6ad272, 0x389280e358a1abf2, 0x2da30d5ce430e7d3, 0x1494483c00f0cdcc, 0x2d459d82da577f2a, 0x29043b8b05116b0f, 0x1a7de7eefc8f9125, 0xdf58f0c37abf586d, 0x00de7b4d751b5945, 0x2d869810b2518aab, 0x718fd89782454402, 0xe17b7b6d3afcaa02, 0xe8ca8822e8f3149f, 0x54e367c8c02ed5cd, 0x4d6b91f02de9c163, 0xdefe44e4883741a9, 0xa549e6727125c604, 0x3092a7183577828b, 0x47fdb5df077f39ba, 0x1f321a400a30d97c, 0x78ade03df798ce7e, 0x49c942815849a8c9, 0x84c488f9d601f60e, 0x3f259d2397e49e92, 0xea627a3246bf99ee, 0x714a38da940b6d41, 0x0bd6c0313bc5d2ac, 0x1efffd15c9997fd0, 0x9605042e69a563d3, 0x6fcbe8ac92977801, 0x842f529ebef4e5d6, 0xed59ceb1d99f4329, 0xd23577f8bbe59b98, 0x5d657000a922390a, 0xc5634b5dbfd89d46, 0x0c8ff0158a3390f4, 0x9f7d403c614f950d, 0x376e7e445951f74d, 0xf114977a656083c3, 0xf544fe83ad9869dd, 0xe2a1f7f4067211a1, 0xe20a8f4c53c1d75d, 0x590fa8d9cb0cedfb, 0xf9bdf66b3a348edb, 0xd8b030ef6ac057b0, 0x8194f3257de0113b, 0x853fa01bca32dec0, 0xfc24284ac65ec104, 0x4d1a7e4b5bb94c9a, 0x4dd58183824bda15, 0x0d2be3143b01d753, 0x154e10ef48807062, 0x6d65b422dc651001, 0x80befad40aebd628, 0xc9eadb734dd3bba7, 0x875d8819322b464e, 0xba74670eb4edc9c8, 0x4b4171d3095c4633, 0x0151e40f6b1b63d6, 0xfc97c4feab55b135, 0xc6f4c962f1e7f43c, 0xd26a1c0df0310a4c, 0x949159fa52b7227f, 0x9ce8f9598f593b81, 0x7609fc84c749a14d, 0x261fd19e5fef6891, 0xc774d6a03cf79fdc, 0x00344e64251195c1, 0x90787e833f8647c3, 0x727bc4c495bed76f, 0x430912d91400065a, 0x48cac527bf4e941e, 0x6c90e1a4369ec849, 0xf120d5256725a207, 0xa38b366f5d1cd376, 0x2bcc30ee411d146f, 0x5248ad95b8e3eb34, 0x8bb22d59ea4a28af, 0x3b2b14be1be0256b, 0xd6c771fa9b06f27d, 0x2877faa019e5cead, 0x5ee89270979c629d, 0xd961db4ea1940353, 0xacf4b393766e1a58, 0x62d4d67f08ed982f, 0x45d11111025cbb48, 0x72de79b796b70e6a, 0x002c828238360087, 0xddd194d72cbefb7f, 0xbb2871e6c3dad0ec, 0xc6057ec73a437f34, 0x21da9d18f930d961, 0xe47ec49391742c20, 0xb82b8f7685031db5, 0x71655563b75f97b0, 0xe89ef5aacfbf0cf9, 0x14b8e01e96445db2, 0x614cac6bb0c63ef8, 0xf4f07e379bef3eab, 0xb83b96684e95a30f, 0x6959ae7b08f4d419, 0x30e39f3b7e45283d, 0x15ad0077e6747489, 0x1b06a5db4d644abb, 0x859bc190045e688d, 0x37e560aeff805804, 0x4305b906ba22d4fb, 0xce67a3b95f508e32, 0x8e19b1ebab90822e, 0x4d3b69bd5aac1fc3, 0x9d0ed93e21a01343, 0x6a3480f5a38b401c, 0xa484f7c000892eb9, 0x6245848a614bb023, 0x181a86fe0459f808, 0xe80b0f0cb28857a0, 0x529d39759c3028b9, 0xf1a057cf47cc3ba7, 0xecf88df79971a926, 0xf491bda75971d77c, 0xfced9b5292e3d14e, 0xd979d8f026416414, 0x05ffb33474e3af7c, 0x59a2fe4e71477dd4, 0x7f835fb951774d60, 0x1dabc30f4d614159, 0x6d9662824b02b036, 0x122548c2846cb619, 0x850c795fd0b4c43c, 0x83586779377c7ec9, 0xe3e0ee80239c7a5b, 0x40dbdf115bde20bd, 0x61ffab4c796356d4, 0xbb7f24feef4d23f9, 0xa83614b05a912745, 0xbaaf881989bb3c9e, 0xeaed7bf486e75172, 0x7bbce58b0da1a96a, 0xbd7574a4473e6a3e, 0x523d0b14b7aacb7d, 0xec041de2cc710b1f, 0xe6a85da8993b2c12, 0x10be8dd5b51e6f31, 0x046733c636a365be, 0x55de65a1838b4f91, 0x93806e289edc2c83, 0x7351c2bd7894e1a0, 0x99fb055f778f2e8b, 0x773b9a425de82c62, 0x3717d3bd0620aab5, 0x5f33819c85199fc5, 0x627c635334c7a40e, 0x6339138b8da6d110, 0xacd8d57af56c99e7, 0x3931a02164ab8988, 0x31974fc4e9071e72, 0x8dcae4790ad949c1, 0xb1e1fb7664d48947, 0xd8c9821f0bdc5f13, 0x45b03c7379209e55, 0xbe052d408273a1b0, 0x20719464e8080b0d, 0xfada5e9479d5602c, 0x7cf57599ea5c5392, 0x8fc0d54238cd2c4c, 0xd6cdce30cf9c801e, 0x9560761fa928e2ca, 0xbc2c4f8378cfe9a8, 0x323c988ef232455b, 0x8f1e430d0d34a6ef, 0x7891ad779e323c47, 0xb2123d58ae0d80c2, 0xd2857d26481fcb53), (0x0cab3d71bf1661d6, 0x3ddcb1aa52ee68c0, 0x546b89a41ec207cf, 0x531803f682e513bd, 0xdb2e3de789783ad7, 0x1c66055492f57832, 0x517ece97a71c5b71, 0x404fd190195d10d9, 0x231d52a4b165e2ba, 0x5a3891d35a1b64d2, 0xbf790cb7dc0fce5f, 0x14ee58825f7c68f4, 0x73f9c9105bfd84eb, 0x219382307fd9a283, 0x7cbc56e77eef89de, 0xc1f5ee71e19beee4, 0xd70dd014ac5e524d, 0x7829d95f2d6b92b1, 0x8518e0e48192589a, 0xc826e41089f56eec, 0xbabd6a185d578936, 0xc47ace297fa15a50, 0xac7c7f712f907f82, 0x62da660a92233789, 0x4b06d6266c796007, 0x088f424108e4c43f, 0x9a87a91151fdaf14, 0xeabcb303b17fe2ff, 0x461bbbcc429728b4, 0x12ea009858921e53, 0xeedf5def0559bcfc, 0x9360684d126b14e4, 0x6d4dda3cf67ecb6e, 0xeeedddf8d5f76cb0, 0x9e810b40de1da08a, 0x237eae68d2380185, 0x1cddafd1b873f849, 0xa2a56b7ee0983815, 0xa9b9bba49e1e9193, 0xaef50aa29833c03d, 0x32fb5b9151ba8890, 0x84dd609b177ff5c3, 0xbc00f90427fa2cd6, 0x27e1ed7218bc5417, 0x36cfe1ea05ac9222, 0xb545a009de902a1f, 0x553da262173647ea, 0xe79130ec5138727b, 0x896239516935f4be, 0x70be82381aa0cfb0, 0x8dc788dffc1f6f67, 0x63cb73dd06c0d0f4, 0x795ef45b3d542444, 0x07a9faf5e0c4bf4d, 0x49cbc38eefad68a1, 0x6ab53044df8401c7, 0xc20f85e22a87d07e, 0xb6277dbb4ffbb972, 0xcb0c7960c923bf81, 0x1b4e3e4e580e19d8, 0x14833df6862ff3db, 0x0dcad4d9ef2b307f, 0xb2f67855732ce6d3, 0xe73b4535674a1f37, 0x7148f5b5cf4720a0, 0x4d0a76eaf82a6e0d, 0x0ff70066d543a9f1, 0xffd83a4739aa69f6, 0xf2466c1b86cdf3f6, 0x8782e3c7573834cb, 0xf8102f4d17302cfa, 0x5743542f72a36aed, 0x3e389a160a9de6fd, 0x300d6df61aa84508, 0x0a2e58978d02b6d4, 0x2fd8cf521f14a1a5, 0xe83786ff9d9f68a6, 0xbadbb5d305bb3890, 0xaa38bf55c6a5ec68, 0x45e2a3fda3f7b3c5, 0x9f9e8036136f7512, 0xc9830b4b21a95988, 0x0b1dc1ed0ad50661, 0x68f3736316c9d180, 0x6d50832521e71e40, 0x9bd1f442d7115c9f, 0x7d18456be814690c, 0xc880ea1440ceacd0, 0x7355c2aca7a0f893, 0xfd9efb37e430f59f, 0x087f6f9afd357ebf, 0x61402feadf5d6a00, 0xa354f2ca60f5349e, 0x0c2b1dd0d6569ee3, 0x6f1e328f8c1106b3, 0x5c9eba478c47a6d9, 0x86e9673b9a4d5a53, 0xaeab1ce726a39885, 0x2e9c50641d0e52ae, 0xc6a20a3f2c8360b5, 0xe6790adb6ebcd579, 0x85ded4bd7a731aaa, 0x04767d7d1de8bbd8, 0x3e3335c22917d603, 0x7bb8bd1e84247f1f, 0x752c3e809fa18c74, 0x4243814ada5009e9, 0xe150d5f44f7a6363, 0x3f34e8b7ee4f3e76, 0xa2a9c149ea3aefab, 0x53f0a631b2639fab, 0x2b474dc79c6319d1, 0x0332d38671279325, 0xbb2728ccf07f4e92, 0x07a6b2f0a0e3ca7e, 0xdbce77ce02cd836b, 0x7d804bfd2600b5eb, 0x80caee2b12e711b5, 0x60e2f8a7008364f5, 0x04956388fad63494, 0xdf39229a3cb17be6, 0x59fac787534f9df4, 0x3674244c4a0c9a4e, 0x245234df35544f16, 0x6179f627058c9625, 0xd84381849ad4745b, 0xb20fd54c2b54320f, 0x0d27606f3a3690f2, 0x2a85f6d9a335bc17, 0x9cd581a1094bbcd3, 0x16804d4a6ded086d, 0xcd9b5de41c39d2a6, 0x0f95daade155526b, 0x7dcdfd0571bcae4b, 0xc18c84c8ee308849, 0xc52112d51899326a, 0x3079ef9952a57410, 0xd9f7a222e6abd6c0, 0xa37d042d11a03ac1, 0x6ba676d13fe067de, 0x0447e0d951288de1, 0x8e4e96d7900b2911, 0xad8a6d836960502e, 0xc44150a166fb3385, 0x020b5d1c535c54a2, 0x127befc0cada76bc, 0x6cd29f272b487385, 0xe08eb02145bac838, 0x853707ff2729f612, 0x197fe14dc2d61fe3, 0x5527e5b831c2762d, 0x7d989f1ff4f6c1be, 0xe1b8ed6f7e738565, 0x98a9c602ef2f9dce, 0x0f385402a9ff4454, 0x0ab7e0e167a88e19, 0xae181850444c3bfe, 0xd12722e7d4db9407, 0xd4f40c25c6ad6e8e, 0xca751d59cc94ac6c, 0xd8bf3cafc9ab70e5, 0x7584e890c3d4c392, 0x7ffaf4f138ff3096, 0x37921e6a8cf6f853, 0x75ef4a862e659393, 0x71d20c4416e4f428, 0x53016dd6d345018e, 0x6e46324f828683d2, 0x49e7fb094eddbfb3, 0x13ea9caf94d725cc, 0x377d89e9ed96c39d, 0x6c6952ab450f4bc4, 0x363ce43ab1a1ea8d, 0x0a61e48acf922826, 0xc4563c40e5ae4da3, 0xd17ade584ade38c0, 0x2fdc528bf3afc0a2, 0xf5d9b137f5896076, 0x4bdc4fd6aed31609, 0x16344ae7d927afd7, 0xb702d85c2efc81e6, 0xd40d676794801f35, 0xff7520be35863160, 0xec547121a523ba9f, 0xe969862a41b5b2f9, 0x2f524c322c46fb86, 0x5c6871ba2f89868a, 0x171a03f9da10159c, 0x36c179180afe9e2d, 0x764a05a232a8dfc3, 0x1fa125a98f563e07, 0x27582172d1f2340e, 0x21952dc880608156, 0x91a189d8e6aa1519, 0x2281cb304f17bd4e, 0xe3d5bf09552b7fca, 0x7b9eda98fb344c1b, 0x6c301ee00551ee36, 0xc49a4a4cec63f40b, 0xe40ccb4351cbf563, 0xf8cea582dbcd91c5, 0x92a26734b1ebcec7, 0xad136281d02f08bb, 0x7994c1f97d336835, 0x1272a65bb58b25f1, 0xf7c5c094042783ac, 0x1bbbd73e194ae179, 0x69e0e8590f883651, 0x82eece76f3bf11a8, 0x5064028e9b39bb23, 0xa13ef4ab9f3b178a, 0x8579c8278423023d, 0x8deec47144b97d21, 0xe97a84015b8010cc, 0xf157bbdeddf271fe, 0x9067eb9c8c8e3220, 0x7c01db8dbd636a71, 0x122eae56b4d6c95a, 0x61b1218d53076b3b, 0x307b8662286d62af, 0xefb13b46c5a441ff, 0x861d90605801df8e, 0x7c2534239f61d44f, 0xc828eebc58db32cf, 0x49d5591f720cbdd3, 0x4d14a9f768123894, 0xe10c57a74c258e22, 0x43b8142b21a056a2, 0xb4b05e871a9c8ee6, 0x9c0eded797b3c78b, 0x4af8e75802d517d3, 0x23bbf79b2705b9b2, 0x0b7e93cde839d781, 0x6603feeaced03c5a, 0xc851b09a9d820229, 0x068a21f58f5a6b2c, 0x354817e59dca1b00, 0x8a093e7c1c9360eb, 0x4ce12658cf1e3908, 0x2e3ac173bafcbb8f, 0x96b25a8f83a38970, 0x28520a713bc19d39, 0x38719cd7c4daf089, 0x64ce9f8be2f082ca, 0x8e81a207793f986a, 0x89aa89d72fc4610e, 0x0053aa24cbb14c48, 0x93ee13a94f1fde75, 0xd44c1865786b285f, 0xa90400f19779dd17, 0xc4d3bdcc436f42da, 0xa76afd902a011e0f, 0x7413cf10ef992d51, 0xfbba460310b78d60, 0x5d5611e3abb7eb32, 0xd8563881cce40b28), (0xc3b69c6e7ad1b521, 0xb83461f504aeb007, 0x24b84f66c30466b2, 0xfe32d834a0176293, 0x2c4b4ce462094314, 0xedaa901b0949d6cc, 0x1aab2615e6765bb3, 0x71651ee51f5231d4, 0x717c8f152877ddd0, 0xc183ed77f9808e73, 0x4c3f7c59868c0521, 0x11bb872a154aa98a, 0xf485ea5aeddfcef8, 0x16b2e2e955acb368, 0x16d5d232e76d5db6, 0xb09af72fb7c1944a, 0x9476700470d6c0ee, 0xec4702ea9c3612ef, 0xfa4bde51b07fdd0e, 0x9aa47edef681e779, 0xaf71bec8995460e4, 0xba19a70e71b2f73e, 0xd60e9893bfd34e16, 0x2619b3c12d9e4a50, 0x4935f0f0886c4936, 0x71beaacba788485a, 0x3236722d27e43147, 0x6c740f1ba4db1de9, 0x9fa6beedf9fea8e4, 0xbe9ccbaf01059da7, 0xc072eac3ae089304, 0x22812a4c38e0c179, 0x0d4e977f34d1ed93, 0xcc9da626b8a417b3, 0x4e4b62bd7d29331b, 0x7153cc08711c5f8b, 0x5154f9dfaf484f41, 0xb87aab01a4b0d356, 0x29b8cdb000eb65f1, 0x04e50b6e32b0a843, 0x829a80ffd1145aa8, 0x55811a0944edd201, 0xbc49e444b1c48a03, 0x942794a82735a88b, 0xe5108c4bff93a724, 0x945de567aa309cd4, 0x203c019361158735, 0xa708d8accac692a2, 0xf62c4a379d793f7f, 0xed3999334bdfb72f, 0x42275cb4acbcf1e4, 0x58a3d3fcda6ba7a7, 0x28c6131ad22a3f51, 0xd79c3b6a3926ac7e, 0x00fd64c6d03dc5a5, 0xfe39a19599f3269d, 0xde6872d14e1a7579, 0xc52b75a9962133b0, 0x8981ee0996f9ae2b, 0x97e9f3f5c19ef6e8, 0xbba6907caf47a70a, 0xfdd66db8893e8e40, 0x56f8adde89d1073f, 0xcf3b478daae1a38c, 0x48ce0a4f33fe3546, 0x7ba57efd825296ac, 0xec6eb6e6ceb805f4, 0x24dfc5238c4186be, 0xe53782e959495682, 0x143be1ec1defd678, 0x022db48ef1aec14b, 0x3113c66887a09d7b, 0xfd232bd4fe1e7d4c, 0xbeae37d742ee8dee, 0x39b020cd05a9f62a, 0xefbd67181d5d1bad, 0xaf6eae3864d68701, 0x1a557d677bc23488, 0x5d697a501c6ab311, 0xfa541a4f123f0f62, 0xd64a8ed6975819f5, 0x782beed14f7e3cda, 0x0bb642fed4c4b097, 0xa10625c24976982e, 0x4b2324366ad654f4, 0xf133aab98f16f8a4, 0x10d21ba98e046d23, 0xfffef3ebf25864e2, 0xce96ea05b6246b81, 0x2ff277540aa08e80, 0x1c859fd9a2476216, 0x6f213450584db7de, 0xfd475b29c980f789, 0x8dc53e8e2c6990fa, 0xe6ee0fa04050017a, 0xef99d43ba2c77fd6, 0x62f4078a96a4d194, 0xa42d2bf32f36a846, 0x5751eac1757dfeeb, 0x327a55aba397e016, 0x46510ed598b7b017, 0x14fbfe5f5981df43, 0x884e6da9e63c8d22, 0xdf6609144d900aae, 0x2876707ff2e20b4f, 0x16c43f443088f50f, 0xf6169a93127ce177, 0x4fafec7931d44b2b, 0x08dd95ee1bc1eea9, 0x646b858af6c5470f, 0x5675d6875f6d12f1, 0x31bf801c8390d095, 0x6593eeff9c81e008, 0x315c9d22e09fca07, 0x9b115bf49f3c3421, 0xde4b958decddf48d, 0x0304c955b92895ac, 0xf2ff3483f25e6f73, 0x53ddc4f61b87e246, 0x2bd13a56df043d70, 0xe0b4964bf75327c3, 0x65ad426a4813b00d, 0x090767d1b2b0706a, 0x4d5d59fbae67df6d, 0x6201101d3311d90a, 0xedf65b5596524e50, 0x04e04a78c8d18844, 0x9e009dfcf7391a6c, 0x4fd059a2faa35b66, 0xa31529b47d1a9326, 0x529377a47e6beb67, 0xedfbbfab1813d99d, 0x29f671ce55586ef6, 0x2f45fb86f9825461, 0x773ab7c4f2ec1f7d, 0x68c3873307d8f6c0, 0xdc8878b498b8f07d, 0x6fdb5d5e758edff0, 0x0a2adea78852fbb1, 0xccd21b402b443308, 0x8d9092f2f1ffa68f, 0xb6e52b065751669c, 0x8b2c2869a10ce122, 0xa5a9f55b0f094a8b, 0xaaf9f14c12c16e2d, 0x733b56395d2baa73, 0x611fec16de26072d, 0xb3b3e72596503247, 0x6592fdbc5dc6b421, 0x8418fe6e65d8b814, 0x497302787dcc3561, 0x592b37ae2eb056c4, 0x719d042f5ca3d03a, 0x77f4903a2c0e3f6b, 0xc05f96a96e730716, 0x4df942fd615e3c16, 0x6bbf741b2ce4df63, 0xd50443df26c087fe, 0x30019611842dc907, 0xd3d96884e7e8b89d, 0xcda8cbd3cbca4328, 0x09940773e1e256b5, 0x348184104465c535, 0xecb14daa27da1e69, 0xa80f0f64b7465c8c, 0x3070ff21d6a21b10, 0xf825e35805046748, 0x87e874c00729f8b9, 0x8d9f6d61bde4806d, 0x311ce5d9bed931fc, 0x54d80b2c7c3a3d49, 0x97dcc311ea2419d2, 0xec9e1b80a2151b1a, 0x249a65eadb6ee3b9, 0x581f59c5f9e57c22, 0xf299a67879b9de5e, 0xada698e5349ce1fa, 0x3fe96a4e6f58576b, 0x892755c1c17c8a37, 0x0978e35c015ea5df, 0xe58cea5361f450fe, 0x8582cc4e9b13fba9, 0x99dccf4ef39e2668, 0x9fef19d85d0b9f95, 0x0dad2cd0170776be, 0x29b8ae793c6360ec, 0x556ef3267dfd6efa, 0x21486b06e8e8e12c, 0x6ca9d5b7e6c4be80, 0x6a9b1d6cf9bb1cbb, 0x86f5722a5eaec1bf, 0x6c7724b208716b06, 0x013ef708482a976a, 0xf04a6ac95c99de3d, 0xe72754d81d84c5a8, 0xdbf01d3958a05a4a, 0x48dfcb31567566e7, 0x18721c84a95496a5, 0xcc1955062ff1283e, 0x6f6ae1af34b09d64, 0x9192b678f25ad7fc, 0xadf9655ba8019a55, 0x7546b5e819321454, 0x22a161318ededc7e, 0xa990d6c3895f474b, 0x886d5da19b88efdc, 0x9afcc8a2c6dcbd80, 0xfbaf28f2df752020, 0x92f3fa431f3d5c74, 0x4185507fa796bf0f, 0xcbbd79ab59a1a67d, 0xb222c7a1f41dd966, 0x202f4c88ba100c25, 0xb8297aad3eeb65d4, 0x2509be61b580a5b4, 0xb7caf283979f9847, 0xc60057b504c4a348, 0xa874cdf5f3f4cb8e, 0x5268f54d7f87f444, 0x1767902717d5f500, 0xf6a0054519ab2e79, 0xf3ed8b63a5bfd3eb, 0xcea63ecc54519530, 0x42ce3c2c76f7ec15, 0xe7b68d7f862c7fe9, 0x7498cf34b11d0f56, 0x8695310916f3f818, 0x2f4d89f6097b981c, 0x0a13905c92cb9c68, 0xcf7483d6df2b8ed5, 0x84be93ea1850a0c7, 0xeb52bb51bb4f4a87, 0xccc5bb2c8b37f99f, 0xb26db71e3c159126, 0xa60be3bac2e3b6a9, 0x11aad070e155e0ef, 0x756ed7407d54f5f9, 0xbceea2f8838602f9, 0x02895b32052852c1, 0x5aa3ff55e85e239c, 0x126ef5701882a941, 0xb29bd590fee8bba2, 0xca959a059366b4c0, 0x60f2e60c9eba7c20, 0x359a3f5a6fb1c52b, 0x7aad7a5a66ebf722, 0x08d6cf88cbf0a0a4, 0xc7475c9a11b06767, 0xa7c1214aba4c302f, 0x08f5237aa9e38103, 0x269123941e6292bd, 0x75751264077928df, 0x4fb574e025d51786, 0xba84b1d412d860a2, 0x6d759898e92a0c3c, 0x68cb09dbe879db67), ) def __init__(self, data=b""): if self.digest_size is None: raise ValueError("digest_size must be defined in subclasses") self.hashbitlen = self.digest_size * 8 self.centre_rounds = self.centre_rounds_for_digest_bits[self.hashbitlen] self.secret_key = [0, 0, 0, 0] self.serial_number = 0 self.block_count_high_word = 0 self.block_count_low_word = 0 self.buf = bytearray() self.finalized = False self.final_bytes = b"" self.state_array = self.bytes_to_words_le(bytes(self.sbox_0)) if data: self.update(data) return def copy(self): other = self.__class__() other.secret_key = list(self.secret_key) other.serial_number = self.serial_number other.block_count_high_word = self.block_count_high_word other.block_count_low_word = self.block_count_low_word other.buf = bytearray(self.buf) other.finalized = self.finalized other.final_bytes = bytes(self.final_bytes) other.state_array = list(self.state_array) return other def update(self, data): if self.finalized: raise ValueError("hash object already finalized") if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") if isinstance(data, memoryview): data = data.tobytes() else: data = bytes(data) if not data: return self self.buf.extend(data) while len(self.buf) >= self.block_size: block = bytes(self.buf[:self.block_size]) del self.buf[:self.block_size] self.process_full_block(block) return self def digest(self): if self.finalized: return self.final_bytes other = self.copy() other.finalize() return other.final_bytes def hexdigest(self): return self.digest().hex() def finalize(self): if self.finalized: return self.process_final_block(bytes(self.buf)) self.finalized = True return def rol64(self, x, n): n &= 0x3f return ((x << n) | (x >> (64 - n))) & self.word_modulus_64 def bytes_to_words_le(self, data): if len(data) % 8 != 0: raise ValueError("data length must be a multiple of 8") if not data: return [] return list(struct.unpack("<%dQ" % (len(data) // 8), data)) def words_to_bytes_le(self, words): if not words: return b"" return struct.pack("<%dQ" % len(words), *words) def single_mds_8x8s(self, input_vector): tables = self.mds_8x8s_0 result = 0 result ^= tables[0][input_vector[0]] result ^= tables[1][input_vector[1]] result ^= tables[2][input_vector[2]] result ^= tables[3][input_vector[3]] result ^= tables[4][input_vector[4]] result ^= tables[5][input_vector[5]] result ^= tables[6][input_vector[6]] result ^= tables[7][input_vector[7]] return result def single_mds_16x8s(self, input_vector): lhs_tables = self.mds_16x8s_lhs_0 rhs_tables = self.mds_16x8s_rhs_0 lhs = 0 rhs = 0 for i in range(16): b = input_vector[i] lhs ^= lhs_tables[i][b] rhs ^= rhs_tables[i][b] return lhs, rhs def xor_key_with_state(self, state_array, round_key): for i in range(32): state_array[i] ^= round_key[i] return def full_mds_state_update(self, state_array, key_array): self.xor_key_with_state(state_array, key_array) state_bytes = self.words_to_bytes_le(state_array) out_state_array = [0] * 32 for i in range(16): start = i * 16 lhs, rhs = self.single_mds_16x8s(state_bytes[start:start + 16]) out_state_array[i * 2] = lhs out_state_array[i * 2 + 1] = rhs return out_state_array def permutate_xlate_buffer(self, xlate_array, key_array, initial_j): sbox = self.sbox_0 counter_j = sbox[initial_j] for counter_i in range(256): counter_j = sbox[(counter_j + xlate_array[counter_j] + key_array[counter_i]) & 0xff] s_counter_i = sbox[counter_i] xlate_array[s_counter_i], xlate_array[counter_j] = xlate_array[counter_j], xlate_array[s_counter_i] return def xlate_state_mds_8x8s(self, in_state_bytes, xlate_array): tables = self.mds_8x8s_0 out_state_array = [0] * 32 for loop_counter in range(32): local_loop_counter = loop_counter << 3 mds_result = 0 mds_result ^= tables[0][in_state_bytes[xlate_array[local_loop_counter + 0]]] mds_result ^= tables[1][in_state_bytes[xlate_array[local_loop_counter + 1]]] mds_result ^= tables[2][in_state_bytes[xlate_array[local_loop_counter + 2]]] mds_result ^= tables[3][in_state_bytes[xlate_array[local_loop_counter + 3]]] mds_result ^= tables[4][in_state_bytes[xlate_array[local_loop_counter + 4]]] mds_result ^= tables[5][in_state_bytes[xlate_array[local_loop_counter + 5]]] mds_result ^= tables[6][in_state_bytes[xlate_array[local_loop_counter + 6]]] mds_result ^= tables[7][in_state_bytes[xlate_array[local_loop_counter + 7]]] out_state_array[loop_counter] = mds_result return out_state_array def process_preliminary_key(self, final_block_bit_count): preliminary_key = [0] * 8 current_word = self.serial_number preliminary_key[0] = self.single_mds_8x8s(current_word.to_bytes(8, "little")) current_word = (self.secret_key[0] + preliminary_key[0]) & self.word_modulus_64 preliminary_key[1] = self.single_mds_8x8s(current_word.to_bytes(8, "little")) current_word = (self.block_count_low_word + preliminary_key[1]) & self.word_modulus_64 preliminary_key[2] = self.single_mds_8x8s(current_word.to_bytes(8, "little")) current_word = (self.secret_key[1] + preliminary_key[2]) & self.word_modulus_64 preliminary_key[3] = self.single_mds_8x8s(current_word.to_bytes(8, "little")) current_word = (final_block_bit_count + preliminary_key[3]) & self.word_modulus_64 preliminary_key[4] = self.single_mds_8x8s(current_word.to_bytes(8, "little")) current_word = (self.secret_key[2] + preliminary_key[4]) & self.word_modulus_64 preliminary_key[5] = self.single_mds_8x8s(current_word.to_bytes(8, "little")) current_word = (self.block_count_high_word + preliminary_key[5]) & self.word_modulus_64 preliminary_key[6] = self.single_mds_8x8s(current_word.to_bytes(8, "little")) current_word = (self.secret_key[3] + preliminary_key[6]) & self.word_modulus_64 preliminary_key[7] = self.single_mds_8x8s(current_word.to_bytes(8, "little")) for i in range(8): previous = preliminary_key[i - 1] preliminary_key[i] = (preliminary_key[i] + previous) & self.word_modulus_64 return preliminary_key def pht_a_diffuse(self, state_array): state_array[0] = (state_array[0] + state_array[17]) & self.word_modulus_64 state_array[17] = (state_array[17] + state_array[0]) & self.word_modulus_64 state_array[2] = (state_array[2] + state_array[19]) & self.word_modulus_64 state_array[19] = (state_array[19] + state_array[2]) & self.word_modulus_64 state_array[4] = (state_array[4] + state_array[21]) & self.word_modulus_64 state_array[21] = (state_array[21] + state_array[4]) & self.word_modulus_64 state_array[6] = (state_array[6] + state_array[23]) & self.word_modulus_64 state_array[23] = (state_array[23] + state_array[6]) & self.word_modulus_64 state_array[8] = (state_array[8] + state_array[25]) & self.word_modulus_64 state_array[25] = (state_array[25] + state_array[8]) & self.word_modulus_64 state_array[10] = (state_array[10] + state_array[27]) & self.word_modulus_64 state_array[27] = (state_array[27] + state_array[10]) & self.word_modulus_64 state_array[12] = (state_array[12] + state_array[29]) & self.word_modulus_64 state_array[29] = (state_array[29] + state_array[12]) & self.word_modulus_64 state_array[14] = (state_array[14] + state_array[31]) & self.word_modulus_64 state_array[31] = (state_array[31] + state_array[14]) & self.word_modulus_64 state_array[9] = (state_array[9] + state_array[16]) & self.word_modulus_64 state_array[16] = (state_array[16] + state_array[9]) & self.word_modulus_64 state_array[11] = (state_array[11] + state_array[18]) & self.word_modulus_64 state_array[18] = (state_array[18] + state_array[11]) & self.word_modulus_64 state_array[13] = (state_array[13] + state_array[20]) & self.word_modulus_64 state_array[20] = (state_array[20] + state_array[13]) & self.word_modulus_64 state_array[15] = (state_array[15] + state_array[22]) & self.word_modulus_64 state_array[22] = (state_array[22] + state_array[15]) & self.word_modulus_64 state_array[1] = (state_array[1] + state_array[24]) & self.word_modulus_64 state_array[24] = (state_array[24] + state_array[1]) & self.word_modulus_64 state_array[3] = (state_array[3] + state_array[26]) & self.word_modulus_64 state_array[26] = (state_array[26] + state_array[3]) & self.word_modulus_64 state_array[5] = (state_array[5] + state_array[28]) & self.word_modulus_64 state_array[28] = (state_array[28] + state_array[5]) & self.word_modulus_64 state_array[7] = (state_array[7] + state_array[30]) & self.word_modulus_64 state_array[30] = (state_array[30] + state_array[7]) & self.word_modulus_64 return def pht_b_diffuse(self, state_array): state_array[0] = (state_array[0] + state_array[3]) & self.word_modulus_64 state_array[3] = (state_array[3] + state_array[0]) & self.word_modulus_64 state_array[2] = (state_array[2] + state_array[7]) & self.word_modulus_64 state_array[7] = (state_array[7] + state_array[2]) & self.word_modulus_64 state_array[4] = (state_array[4] + state_array[1]) & self.word_modulus_64 state_array[1] = (state_array[1] + state_array[4]) & self.word_modulus_64 state_array[6] = (state_array[6] + state_array[5]) & self.word_modulus_64 state_array[5] = (state_array[5] + state_array[6]) & self.word_modulus_64 state_array[8] = (state_array[8] + state_array[11]) & self.word_modulus_64 state_array[11] = (state_array[11] + state_array[8]) & self.word_modulus_64 state_array[10] = (state_array[10] + state_array[15]) & self.word_modulus_64 state_array[15] = (state_array[15] + state_array[10]) & self.word_modulus_64 state_array[12] = (state_array[12] + state_array[9]) & self.word_modulus_64 state_array[9] = (state_array[9] + state_array[12]) & self.word_modulus_64 state_array[14] = (state_array[14] + state_array[13]) & self.word_modulus_64 state_array[13] = (state_array[13] + state_array[14]) & self.word_modulus_64 state_array[16] = (state_array[16] + state_array[19]) & self.word_modulus_64 state_array[19] = (state_array[19] + state_array[16]) & self.word_modulus_64 state_array[18] = (state_array[18] + state_array[23]) & self.word_modulus_64 state_array[23] = (state_array[23] + state_array[18]) & self.word_modulus_64 state_array[20] = (state_array[20] + state_array[17]) & self.word_modulus_64 state_array[17] = (state_array[17] + state_array[20]) & self.word_modulus_64 state_array[22] = (state_array[22] + state_array[21]) & self.word_modulus_64 state_array[21] = (state_array[21] + state_array[22]) & self.word_modulus_64 state_array[24] = (state_array[24] + state_array[27]) & self.word_modulus_64 state_array[27] = (state_array[27] + state_array[24]) & self.word_modulus_64 state_array[26] = (state_array[26] + state_array[31]) & self.word_modulus_64 state_array[31] = (state_array[31] + state_array[26]) & self.word_modulus_64 state_array[28] = (state_array[28] + state_array[25]) & self.word_modulus_64 state_array[25] = (state_array[25] + state_array[28]) & self.word_modulus_64 state_array[30] = (state_array[30] + state_array[29]) & self.word_modulus_64 state_array[29] = (state_array[29] + state_array[30]) & self.word_modulus_64 return def quad_diffuse_q0(self, state_array): qd0r0 = 28 qd0r1 = 6 qd0r2 = 55 qdx0 = 9 # noqa: F841 qdx1 = 18 qdx2 = 27 qdx3 = 36 state_array[0] = (state_array[0] + self.rol64(state_array[0] ^ state_array[14], qd0r0)) & self.word_modulus_64 state_array[2] = (state_array[2] + self.rol64(state_array[2] ^ state_array[0], qd0r1)) & self.word_modulus_64 state_array[4] = (state_array[4] + self.rol64(state_array[4] ^ state_array[2], qd0r2)) & self.word_modulus_64 state_array[6] = (state_array[6] + self.rol64(state_array[6] ^ state_array[4], qd0r0)) & self.word_modulus_64 state_array[8] = (state_array[8] + self.rol64(state_array[8] ^ state_array[6], qd0r1)) & self.word_modulus_64 state_array[10] = (state_array[10] + self.rol64(state_array[10] ^ state_array[8], qd0r2)) & self.word_modulus_64 state_array[12] = (state_array[12] + self.rol64(state_array[12] ^ state_array[10], qd0r0)) & self.word_modulus_64 state_array[14] = (state_array[14] + self.rol64(state_array[14] ^ state_array[12], qd0r1)) & self.word_modulus_64 state_array[0] ^= self.rol64((state_array[0] + state_array[14]) & self.word_modulus_64, qd0r2) state_array[2] ^= self.rol64((state_array[2] + state_array[0]) & self.word_modulus_64, qd0r0) state_array[4] ^= self.rol64((state_array[4] + state_array[2]) & self.word_modulus_64, qd0r1) state_array[6] ^= self.rol64((state_array[6] + state_array[4]) & self.word_modulus_64, qd0r2) state_array[8] ^= self.rol64((state_array[8] + state_array[6]) & self.word_modulus_64, qd0r0) state_array[10] ^= self.rol64((state_array[10] + state_array[8]) & self.word_modulus_64, qd0r1) state_array[12] ^= self.rol64((state_array[12] + state_array[10]) & self.word_modulus_64, qd0r2) state_array[14] ^= self.rol64((state_array[14] + state_array[12]) & self.word_modulus_64, qd0r0) state_array[1] ^= self.rol64(state_array[0], qdx1) state_array[3] ^= self.rol64(state_array[2], qdx1) state_array[5] ^= self.rol64(state_array[4], qdx1) state_array[7] ^= self.rol64(state_array[6], qdx1) state_array[9] ^= self.rol64(state_array[8], qdx1) state_array[11] ^= self.rol64(state_array[10], qdx1) state_array[13] ^= self.rol64(state_array[12], qdx1) state_array[15] ^= self.rol64(state_array[14], qdx1) state_array[16] ^= self.rol64(state_array[0], qdx2) state_array[18] ^= self.rol64(state_array[2], qdx2) state_array[20] ^= self.rol64(state_array[4], qdx2) state_array[22] ^= self.rol64(state_array[6], qdx2) state_array[24] ^= self.rol64(state_array[8], qdx2) state_array[26] ^= self.rol64(state_array[10], qdx2) state_array[28] ^= self.rol64(state_array[12], qdx2) state_array[30] ^= self.rol64(state_array[14], qdx2) state_array[17] ^= self.rol64(state_array[0], qdx3) state_array[19] ^= self.rol64(state_array[2], qdx3) state_array[21] ^= self.rol64(state_array[4], qdx3) state_array[23] ^= self.rol64(state_array[6], qdx3) state_array[25] ^= self.rol64(state_array[8], qdx3) state_array[27] ^= self.rol64(state_array[10], qdx3) state_array[29] ^= self.rol64(state_array[12], qdx3) state_array[31] ^= self.rol64(state_array[14], qdx3) return def quad_diffuse_q1(self, state_array): qd1r0 = 36 qd1r1 = 58 qd1r2 = 9 qdx0 = 9 qdx2 = 27 qdx3 = 36 state_array[1] = (state_array[1] + self.rol64(state_array[1] ^ state_array[15], qd1r0)) & self.word_modulus_64 state_array[3] = (state_array[3] + self.rol64(state_array[3] ^ state_array[1], qd1r1)) & self.word_modulus_64 state_array[5] = (state_array[5] + self.rol64(state_array[5] ^ state_array[3], qd1r2)) & self.word_modulus_64 state_array[7] = (state_array[7] + self.rol64(state_array[7] ^ state_array[5], qd1r0)) & self.word_modulus_64 state_array[9] = (state_array[9] + self.rol64(state_array[9] ^ state_array[7], qd1r1)) & self.word_modulus_64 state_array[11] = (state_array[11] + self.rol64(state_array[11] ^ state_array[9], qd1r2)) & self.word_modulus_64 state_array[13] = (state_array[13] + self.rol64(state_array[13] ^ state_array[11], qd1r0)) & self.word_modulus_64 state_array[15] = (state_array[15] + self.rol64(state_array[15] ^ state_array[13], qd1r1)) & self.word_modulus_64 state_array[1] ^= self.rol64((state_array[1] + state_array[15]) & self.word_modulus_64, qd1r2) state_array[3] ^= self.rol64((state_array[3] + state_array[1]) & self.word_modulus_64, qd1r0) state_array[5] ^= self.rol64((state_array[5] + state_array[3]) & self.word_modulus_64, qd1r1) state_array[7] ^= self.rol64((state_array[7] + state_array[5]) & self.word_modulus_64, qd1r2) state_array[9] ^= self.rol64((state_array[9] + state_array[7]) & self.word_modulus_64, qd1r0) state_array[11] ^= self.rol64((state_array[11] + state_array[9]) & self.word_modulus_64, qd1r1) state_array[13] ^= self.rol64((state_array[13] + state_array[11]) & self.word_modulus_64, qd1r2) state_array[15] ^= self.rol64((state_array[15] + state_array[13]) & self.word_modulus_64, qd1r0) state_array[0] ^= self.rol64(state_array[1], qdx0) state_array[2] ^= self.rol64(state_array[3], qdx0) state_array[4] ^= self.rol64(state_array[5], qdx0) state_array[6] ^= self.rol64(state_array[7], qdx0) state_array[8] ^= self.rol64(state_array[9], qdx0) state_array[10] ^= self.rol64(state_array[11], qdx0) state_array[12] ^= self.rol64(state_array[13], qdx0) state_array[14] ^= self.rol64(state_array[15], qdx0) state_array[16] ^= self.rol64(state_array[1], qdx2) state_array[18] ^= self.rol64(state_array[3], qdx2) state_array[20] ^= self.rol64(state_array[5], qdx2) state_array[22] ^= self.rol64(state_array[7], qdx2) state_array[24] ^= self.rol64(state_array[9], qdx2) state_array[26] ^= self.rol64(state_array[11], qdx2) state_array[29] ^= self.rol64(state_array[13], qdx2) state_array[30] ^= self.rol64(state_array[15], qdx2) state_array[17] ^= self.rol64(state_array[1], qdx3) state_array[19] ^= self.rol64(state_array[3], qdx3) state_array[21] ^= self.rol64(state_array[5], qdx3) state_array[23] ^= self.rol64(state_array[7], qdx3) state_array[25] ^= self.rol64(state_array[9], qdx3) state_array[27] ^= self.rol64(state_array[11], qdx3) state_array[29] ^= self.rol64(state_array[13], qdx3) state_array[31] ^= self.rol64(state_array[15], qdx3) return def quad_diffuse_q2(self, state_array): qd2r0 = 8 qd2r1 = 24 qd2r2 = 43 qd3r0 = 9 qd3r1 = 47 qd3r2 = 39 qdx0 = 9 qdx1 = 18 qdx3 = 36 state_array[16] = (state_array[16] + self.rol64(state_array[16] ^ state_array[30], qd2r0)) & self.word_modulus_64 state_array[18] = (state_array[18] + self.rol64(state_array[18] ^ state_array[16], qd2r1)) & self.word_modulus_64 state_array[20] = (state_array[20] + self.rol64(state_array[20] ^ state_array[18], qd2r2)) & self.word_modulus_64 state_array[22] = (state_array[22] + self.rol64(state_array[22] ^ state_array[20], qd2r0)) & self.word_modulus_64 state_array[24] = (state_array[24] + self.rol64(state_array[24] ^ state_array[22], qd2r1)) & self.word_modulus_64 state_array[26] = (state_array[26] + self.rol64(state_array[26] ^ state_array[24], qd2r2)) & self.word_modulus_64 state_array[28] = (state_array[28] + self.rol64(state_array[28] ^ state_array[26], qd2r0)) & self.word_modulus_64 state_array[30] = (state_array[30] + self.rol64(state_array[30] ^ state_array[28], qd2r1)) & self.word_modulus_64 state_array[16] ^= self.rol64((state_array[16] + state_array[30]) & self.word_modulus_64, qd3r2) state_array[18] ^= self.rol64((state_array[18] + state_array[16]) & self.word_modulus_64, qd3r0) state_array[20] ^= self.rol64((state_array[20] + state_array[18]) & self.word_modulus_64, qd3r1) state_array[22] ^= self.rol64((state_array[22] + state_array[20]) & self.word_modulus_64, qd3r2) state_array[24] ^= self.rol64((state_array[24] + state_array[22]) & self.word_modulus_64, qd3r0) state_array[26] ^= self.rol64((state_array[26] + state_array[24]) & self.word_modulus_64, qd3r1) state_array[28] ^= self.rol64((state_array[28] + state_array[26]) & self.word_modulus_64, qd3r2) state_array[30] ^= self.rol64((state_array[30] + state_array[28]) & self.word_modulus_64, qd3r0) state_array[0] ^= self.rol64(state_array[16], qdx0) state_array[2] ^= self.rol64(state_array[18], qdx0) state_array[4] ^= self.rol64(state_array[20], qdx0) state_array[6] ^= self.rol64(state_array[22], qdx0) state_array[8] ^= self.rol64(state_array[24], qdx0) state_array[10] ^= self.rol64(state_array[26], qdx0) state_array[12] ^= self.rol64(state_array[28], qdx0) state_array[14] ^= self.rol64(state_array[30], qdx0) state_array[1] ^= self.rol64(state_array[16], qdx1) state_array[3] ^= self.rol64(state_array[18], qdx1) state_array[5] ^= self.rol64(state_array[20], qdx1) state_array[7] ^= self.rol64(state_array[22], qdx1) state_array[9] ^= self.rol64(state_array[24], qdx1) state_array[11] ^= self.rol64(state_array[26], qdx1) state_array[13] ^= self.rol64(state_array[28], qdx1) state_array[15] ^= self.rol64(state_array[30], qdx1) state_array[17] ^= self.rol64(state_array[1], qdx3) state_array[19] ^= self.rol64(state_array[3], qdx3) state_array[21] ^= self.rol64(state_array[5], qdx3) state_array[23] ^= self.rol64(state_array[7], qdx3) state_array[25] ^= self.rol64(state_array[9], qdx3) state_array[27] ^= self.rol64(state_array[11], qdx3) state_array[29] ^= self.rol64(state_array[13], qdx3) state_array[31] ^= self.rol64(state_array[15], qdx3) return def quad_diffuse_q3(self, state_array): qd3r0 = 9 qd3r1 = 47 qd3r2 = 39 qdx0 = 9 qdx1 = 18 qdx2 = 27 state_array[17] = (state_array[17] + self.rol64(state_array[17] ^ state_array[31], qd3r0)) & self.word_modulus_64 state_array[19] = (state_array[19] + self.rol64(state_array[19] ^ state_array[17], qd3r1)) & self.word_modulus_64 state_array[21] = (state_array[21] + self.rol64(state_array[21] ^ state_array[19], qd3r2)) & self.word_modulus_64 state_array[23] = (state_array[23] + self.rol64(state_array[23] ^ state_array[21], qd3r0)) & self.word_modulus_64 state_array[25] = (state_array[25] + self.rol64(state_array[25] ^ state_array[23], qd3r1)) & self.word_modulus_64 state_array[27] = (state_array[27] + self.rol64(state_array[27] ^ state_array[25], qd3r2)) & self.word_modulus_64 state_array[29] = (state_array[29] + self.rol64(state_array[29] ^ state_array[27], qd3r0)) & self.word_modulus_64 state_array[31] = (state_array[31] + self.rol64(state_array[31] ^ state_array[29], qd3r1)) & self.word_modulus_64 state_array[17] ^= self.rol64((state_array[17] + state_array[31]) & self.word_modulus_64, qd3r2) state_array[19] ^= self.rol64((state_array[19] + state_array[17]) & self.word_modulus_64, qd3r0) state_array[21] ^= self.rol64((state_array[21] + state_array[19]) & self.word_modulus_64, qd3r1) state_array[23] ^= self.rol64((state_array[23] + state_array[21]) & self.word_modulus_64, qd3r2) state_array[25] ^= self.rol64((state_array[25] + state_array[23]) & self.word_modulus_64, qd3r0) state_array[27] ^= self.rol64((state_array[27] + state_array[25]) & self.word_modulus_64, qd3r1) state_array[29] ^= self.rol64((state_array[29] + state_array[27]) & self.word_modulus_64, qd3r2) state_array[31] ^= self.rol64((state_array[31] + state_array[29]) & self.word_modulus_64, qd3r0) state_array[0] ^= self.rol64(state_array[17], qdx0) state_array[2] ^= self.rol64(state_array[19], qdx0) state_array[4] ^= self.rol64(state_array[21], qdx0) state_array[6] ^= self.rol64(state_array[23], qdx0) state_array[8] ^= self.rol64(state_array[25], qdx0) state_array[10] ^= self.rol64(state_array[27], qdx0) state_array[12] ^= self.rol64(state_array[29], qdx0) state_array[14] ^= self.rol64(state_array[31], qdx0) state_array[1] ^= self.rol64(state_array[17], qdx1) state_array[3] ^= self.rol64(state_array[19], qdx1) state_array[5] ^= self.rol64(state_array[21], qdx1) state_array[7] ^= self.rol64(state_array[23], qdx1) state_array[9] ^= self.rol64(state_array[25], qdx1) state_array[11] ^= self.rol64(state_array[27], qdx1) state_array[13] ^= self.rol64(state_array[29], qdx1) state_array[15] ^= self.rol64(state_array[31], qdx1) state_array[16] ^= self.rol64(state_array[17], qdx2) state_array[18] ^= self.rol64(state_array[19], qdx2) state_array[20] ^= self.rol64(state_array[21], qdx2) state_array[22] ^= self.rol64(state_array[23], qdx2) state_array[24] ^= self.rol64(state_array[25], qdx2) state_array[26] ^= self.rol64(state_array[27], qdx2) state_array[28] ^= self.rol64(state_array[29], qdx2) state_array[30] ^= self.rol64(state_array[31], qdx2) return def process_principle_key_single_1_rounds(self, message_block, preliminary_key): temp_array = self.xlate_state_mds_8x8s(self.words_to_bytes_le(message_block), self.sbox_0) temp_array[0] ^= preliminary_key[0] temp_array[2] ^= preliminary_key[1] temp_array[4] ^= preliminary_key[2] temp_array[6] ^= preliminary_key[3] temp_array[8] ^= preliminary_key[4] temp_array[10] ^= preliminary_key[5] temp_array[12] ^= preliminary_key[6] temp_array[14] ^= preliminary_key[7] self.pht_a_diffuse(temp_array) self.quad_diffuse_q0(temp_array) self.pht_b_diffuse(temp_array) principle_key = self.full_mds_state_update(temp_array, list(message_block)) return principle_key def process_principle_key_pair_1_rounds(self, message_block_left, message_block_right, preliminary_key): principle_key_left = self.process_principle_key_single_1_rounds(message_block_left, preliminary_key) principle_key_right = self.process_principle_key_single_1_rounds(message_block_right, preliminary_key) for i in range(32): principle_key_left[i] = (principle_key_left[i] + principle_key_right[i]) & self.word_modulus_64 principle_key = self.process_principle_key_single_1_rounds(principle_key_left, preliminary_key) return principle_key def key_extract_x4(self, principle_key_extract, preliminary_key, round_index): rc = self.rc_u64 local_round_index_0 = round_index & 0x3f local_round_index_1 = (round_index + 1) & 0x3f local_round_index_2 = (round_index + 2) & 0x3f local_round_index_3 = (round_index + 3) & 0x3f local_round_index_4 = (round_index + 4) & 0x3f local_round_index_5 = (round_index + 5) & 0x3f local_round_index_6 = (round_index + 6) & 0x3f local_round_index_7 = (round_index + 7) & 0x3f ke_rotate_1 = (3 + round_index) & 0x3f ke_rotate_2 = (17 + round_index) & 0x3f ke_rotate_3 = (29 + round_index) & 0x3f round_key = [0] * 32 round_key[0] = principle_key_extract[0] ^ preliminary_key[0] ^ rc[local_round_index_0] round_key[2] = principle_key_extract[1] ^ preliminary_key[1] ^ rc[local_round_index_1] round_key[4] = principle_key_extract[2] ^ preliminary_key[2] ^ rc[local_round_index_2] round_key[6] = principle_key_extract[3] ^ preliminary_key[3] ^ rc[local_round_index_3] round_key[8] = principle_key_extract[4] ^ preliminary_key[4] ^ rc[local_round_index_4] round_key[10] = principle_key_extract[5] ^ preliminary_key[5] ^ rc[local_round_index_5] round_key[12] = principle_key_extract[6] ^ preliminary_key[6] ^ rc[local_round_index_6] round_key[14] = principle_key_extract[7] ^ preliminary_key[7] ^ rc[local_round_index_7] round_key[1] = self.rol64(round_key[2], ke_rotate_1) round_key[3] = self.rol64(round_key[0], ke_rotate_1) round_key[5] = self.rol64(round_key[6], ke_rotate_1) round_key[7] = self.rol64(round_key[4], ke_rotate_1) round_key[9] = self.rol64(round_key[10], ke_rotate_1) round_key[11] = self.rol64(round_key[8], ke_rotate_1) round_key[13] = self.rol64(round_key[14], ke_rotate_1) round_key[15] = self.rol64(round_key[12], ke_rotate_1) round_key[16] = self.rol64(round_key[6], ke_rotate_2) round_key[18] = self.rol64(round_key[4], ke_rotate_2) round_key[20] = self.rol64(round_key[2], ke_rotate_2) round_key[22] = self.rol64(round_key[0], ke_rotate_2) round_key[24] = self.rol64(round_key[14], ke_rotate_2) round_key[26] = self.rol64(round_key[12], ke_rotate_2) round_key[28] = self.rol64(round_key[10], ke_rotate_2) round_key[30] = self.rol64(round_key[8], ke_rotate_2) round_key[17] = self.rol64(round_key[14], ke_rotate_3) round_key[19] = self.rol64(round_key[12], ke_rotate_3) round_key[21] = self.rol64(round_key[10], ke_rotate_3) round_key[23] = self.rol64(round_key[8], ke_rotate_3) round_key[25] = self.rol64(round_key[6], ke_rotate_3) round_key[27] = self.rol64(round_key[4], ke_rotate_3) round_key[29] = self.rol64(round_key[2], ke_rotate_3) round_key[31] = self.rol64(round_key[0], ke_rotate_3) return round_key def key_extract_x2(self, principle_key_extract, preliminary_key, round_index): rc = self.rc_u64 local_round_index_0 = round_index & 0x3f local_round_index_1 = (round_index + 1) & 0x3f local_round_index_2 = (round_index + 2) & 0x3f local_round_index_3 = (round_index + 3) & 0x3f local_round_index_4 = (round_index + 4) & 0x3f local_round_index_5 = (round_index + 5) & 0x3f local_round_index_6 = (round_index + 6) & 0x3f local_round_index_7 = (round_index + 7) & 0x3f ke_rotate_1 = (3 + round_index) & 0x3f ke_rotate_2 = (17 + round_index) & 0x3f ke_rotate_3 = (29 + round_index) & 0x3f round_key = [0] * 32 round_key[0] = principle_key_extract[0] ^ preliminary_key[0] ^ rc[local_round_index_0] round_key[2] = principle_key_extract[1] ^ preliminary_key[1] ^ rc[local_round_index_1] round_key[4] = principle_key_extract[2] ^ preliminary_key[2] ^ rc[local_round_index_2] round_key[6] = principle_key_extract[3] ^ preliminary_key[3] ^ rc[local_round_index_3] round_key[8] = principle_key_extract[4] ^ preliminary_key[4] ^ rc[local_round_index_4] round_key[10] = principle_key_extract[5] ^ preliminary_key[5] ^ rc[local_round_index_5] round_key[12] = principle_key_extract[6] ^ preliminary_key[6] ^ rc[local_round_index_6] round_key[14] = principle_key_extract[7] ^ preliminary_key[7] ^ rc[local_round_index_7] round_key[1] = self.rol64(principle_key_extract[8], ke_rotate_1) round_key[3] = self.rol64(principle_key_extract[9], ke_rotate_1) round_key[5] = self.rol64(principle_key_extract[10], ke_rotate_1) round_key[7] = self.rol64(principle_key_extract[11], ke_rotate_1) round_key[9] = self.rol64(principle_key_extract[12], ke_rotate_1) round_key[11] = self.rol64(principle_key_extract[13], ke_rotate_1) round_key[13] = self.rol64(principle_key_extract[14], ke_rotate_1) round_key[15] = self.rol64(principle_key_extract[15], ke_rotate_1) round_key[16] = self.rol64(round_key[2], ke_rotate_2) round_key[18] = self.rol64(round_key[0], ke_rotate_2) round_key[20] = self.rol64(round_key[6], ke_rotate_2) round_key[22] = self.rol64(round_key[4], ke_rotate_2) round_key[24] = self.rol64(round_key[10], ke_rotate_2) round_key[26] = self.rol64(round_key[8], ke_rotate_2) round_key[28] = self.rol64(round_key[14], ke_rotate_2) round_key[30] = self.rol64(round_key[12], ke_rotate_2) round_key[17] = self.rol64(round_key[7], ke_rotate_3) round_key[19] = self.rol64(round_key[5], ke_rotate_3) round_key[21] = self.rol64(round_key[3], ke_rotate_3) round_key[23] = self.rol64(round_key[1], ke_rotate_3) round_key[25] = self.rol64(round_key[15], ke_rotate_3) round_key[27] = self.rol64(round_key[13], ke_rotate_3) round_key[29] = self.rol64(round_key[11], ke_rotate_3) round_key[31] = self.rol64(round_key[9], ke_rotate_3) return round_key def key_extract_pre_whitening(self, principle_key_extract, preliminary_key): round_key = [0] * 32 round_key[0] = principle_key_extract[0] ^ preliminary_key[0] round_key[2] = principle_key_extract[2] ^ preliminary_key[1] round_key[4] = principle_key_extract[4] ^ preliminary_key[2] round_key[6] = principle_key_extract[6] ^ preliminary_key[3] round_key[8] = principle_key_extract[8] ^ preliminary_key[4] round_key[10] = principle_key_extract[10] ^ preliminary_key[5] round_key[12] = principle_key_extract[12] ^ preliminary_key[6] round_key[14] = principle_key_extract[14] ^ preliminary_key[7] round_key[1] = principle_key_extract[1] ^ preliminary_key[7] round_key[3] = principle_key_extract[3] ^ preliminary_key[6] round_key[5] = principle_key_extract[5] ^ preliminary_key[5] round_key[7] = principle_key_extract[7] ^ preliminary_key[4] round_key[9] = principle_key_extract[9] ^ preliminary_key[3] round_key[11] = principle_key_extract[11] ^ preliminary_key[2] round_key[13] = principle_key_extract[13] ^ preliminary_key[1] round_key[15] = principle_key_extract[15] ^ preliminary_key[0] round_key[16] = principle_key_extract[16] round_key[18] = principle_key_extract[18] round_key[20] = principle_key_extract[20] round_key[22] = principle_key_extract[22] round_key[24] = principle_key_extract[24] round_key[26] = principle_key_extract[26] round_key[28] = principle_key_extract[28] round_key[30] = principle_key_extract[30] round_key[17] = principle_key_extract[17] round_key[19] = principle_key_extract[19] round_key[21] = principle_key_extract[21] round_key[23] = principle_key_extract[23] round_key[25] = principle_key_extract[25] round_key[27] = principle_key_extract[27] round_key[29] = principle_key_extract[29] round_key[31] = principle_key_extract[31] self.pht_a_diffuse(round_key) return round_key def key_extract_post_whitening(self, principle_key_extract, preliminary_key): round_key = [0] * 32 round_key[0] = principle_key_extract[0] ^ preliminary_key[0] round_key[2] = principle_key_extract[2] ^ preliminary_key[1] round_key[4] = principle_key_extract[4] ^ preliminary_key[2] round_key[6] = principle_key_extract[6] ^ preliminary_key[3] round_key[8] = principle_key_extract[8] ^ preliminary_key[4] round_key[10] = principle_key_extract[10] ^ preliminary_key[5] round_key[12] = principle_key_extract[12] ^ preliminary_key[6] round_key[14] = principle_key_extract[14] ^ preliminary_key[7] round_key[1] = principle_key_extract[1] ^ preliminary_key[7] round_key[3] = principle_key_extract[3] ^ preliminary_key[6] round_key[5] = principle_key_extract[5] ^ preliminary_key[5] round_key[7] = principle_key_extract[7] ^ preliminary_key[4] round_key[9] = principle_key_extract[9] ^ preliminary_key[3] round_key[11] = principle_key_extract[11] ^ preliminary_key[2] round_key[13] = principle_key_extract[13] ^ preliminary_key[1] round_key[15] = principle_key_extract[15] ^ preliminary_key[0] round_key[16] = principle_key_extract[16] round_key[18] = principle_key_extract[18] round_key[20] = principle_key_extract[20] round_key[22] = principle_key_extract[22] round_key[24] = principle_key_extract[24] round_key[26] = principle_key_extract[26] round_key[28] = principle_key_extract[28] round_key[30] = principle_key_extract[30] round_key[17] = principle_key_extract[17] round_key[19] = principle_key_extract[19] round_key[21] = principle_key_extract[21] round_key[23] = principle_key_extract[23] round_key[25] = principle_key_extract[25] round_key[27] = principle_key_extract[27] round_key[29] = principle_key_extract[29] round_key[31] = principle_key_extract[31] self.pht_b_diffuse(round_key) return round_key def update_chaining_state_generic(self, preliminary_key, principle_key, block_count_low_word, schedule): xlate_array = bytearray(self.sbox_0) chaining_state_array = list(self.state_array) self.permutate_xlate_buffer(xlate_array, self.words_to_bytes_le(principle_key), block_count_low_word & 0xff) round_key = self.key_extract_pre_whitening(principle_key, preliminary_key) self.xor_key_with_state(self.state_array, round_key) centre_state_array = self.xlate_state_mds_8x8s(self.words_to_bytes_le(self.state_array), xlate_array) state = centre_state_array next_is_state_array = True for kind, offset, quadrant, round_index in schedule: self.pht_a_diffuse(state) if quadrant == 0: self.quad_diffuse_q0(state) elif quadrant == 1: self.quad_diffuse_q1(state) elif quadrant == 2: self.quad_diffuse_q2(state) else: self.quad_diffuse_q3(state) self.pht_b_diffuse(state) if kind == "x2": round_key = self.key_extract_x2(principle_key[offset:offset + 16], preliminary_key, round_index) else: round_key = self.key_extract_x4(principle_key[offset:offset + 8], preliminary_key, round_index) out_state = self.full_mds_state_update(state, round_key) if next_is_state_array: self.state_array = out_state state = self.state_array else: centre_state_array = out_state state = centre_state_array next_is_state_array = not next_is_state_array self.state_array = self.xlate_state_mds_8x8s(self.words_to_bytes_le(centre_state_array), xlate_array) round_key = self.key_extract_post_whitening(principle_key, preliminary_key) self.xor_key_with_state(self.state_array, round_key) self.xor_key_with_state(self.state_array, chaining_state_array) return def update_chaining_state(self, preliminary_key, principle_key): if self.centre_rounds == 4: schedule = [ ("x4", 0, 0, 0), ("x4", 8, 1, 1), ("x4", 16, 2, 2), ("x4", 24, 3, 3), ] elif self.centre_rounds == 6: schedule = [ ("x2", 0, 0, 0), ("x4", 0, 0, 1), ("x4", 8, 1, 2), ("x4", 16, 2, 3), ("x4", 24, 3, 4), ("x4", 16, 3, 5), ] elif self.centre_rounds == 8: schedule = [ ("x4", 0, 0, 0), ("x4", 8, 1, 1), ("x4", 16, 2, 2), ("x4", 24, 3, 3), ("x4", 0, 0, 4), ("x4", 8, 1, 5), ("x4", 16, 2, 6), ("x4", 24, 3, 7), ] else: raise ValueError("unsupported centre rounds") self.update_chaining_state_generic(preliminary_key, principle_key, self.block_count_low_word, schedule) return def process_full_block(self, block): if len(block) != self.block_size: raise ValueError("full block must be 512 bytes") block_words = self.bytes_to_words_le(block) preliminary_key = self.process_preliminary_key(0) principle_key = self.process_principle_key_pair_1_rounds(block_words[:32], block_words[32:], preliminary_key) self.update_chaining_state(preliminary_key, principle_key) if self.block_count_low_word & 1: self.block_count_high_word = (self.block_count_high_word + 1) & self.word_modulus_64 self.block_count_low_word = (self.block_count_low_word + 1) & self.word_modulus_64 return def finalise_chaining_state(self): state_array = list(self.state_array) if self.hashbitlen <= 512: state_array[0] ^= state_array[1] ^ state_array[16] ^ state_array[17] state_array[2] ^= state_array[3] ^ state_array[18] ^ state_array[19] state_array[4] ^= state_array[5] ^ state_array[20] ^ state_array[21] state_array[6] ^= state_array[7] ^ state_array[22] ^ state_array[23] state_array[8] ^= state_array[9] ^ state_array[24] ^ state_array[25] state_array[10] ^= state_array[11] ^ state_array[26] ^ state_array[27] state_array[12] ^= state_array[13] ^ state_array[28] ^ state_array[29] state_array[14] ^= state_array[15] ^ state_array[30] ^ state_array[31] result = self.words_to_bytes_le(state_array)[:self.digest_size] self.state_array = [0] * 32 return result def process_final_block(self, tail): if len(tail) > self.block_size: raise ValueError("tail too large") final_block_bit_count = len(tail) * 8 padded = tail + (b"\x00" * (self.block_size - len(tail))) padded_words = self.bytes_to_words_le(padded) preliminary_key = self.process_preliminary_key(final_block_bit_count) if len(tail) <= 256: principle_key = self.process_principle_key_single_1_rounds(padded_words[:32], preliminary_key) else: principle_key = self.process_principle_key_pair_1_rounds(padded_words[:32], padded_words[32:], preliminary_key) self.update_chaining_state(preliminary_key, principle_key) self.final_bytes = self.finalise_chaining_state() self.secret_key = [0, 0, 0, 0] self.block_count_high_word = 0 self.block_count_low_word = 0 self.buf = bytearray() self.serial_number = 0 return class Sgail224(SgailBase): digest_size = 28 class Sgail256(SgailBase): digest_size = 32 class Sgail384(SgailBase): digest_size = 48 class Sgail512(SgailBase): digest_size = 64 class Sgail768(SgailBase): digest_size = 96 class Sgail1024(SgailBase): digest_size = 128 class Sgail1536(SgailBase): digest_size = 192 class Sgail2048(SgailBase): digest_size = 256 class SHAMATABase: block_size = 16 digest_size = None hashbitlen = None sbox = ( 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ) mul2_table = None mul3_table = None @classmethod def ensure_tables(cls): if cls.mul2_table is None: mul2 = [] mul3 = [] for x in range(256): y = ((x << 1) & 0xff) ^ (0x1b if (x & 0x80) else 0x00) mul2.append(y) mul3.append(y ^ x) cls.mul2_table = tuple(mul2) cls.mul3_table = tuple(mul3) return def __init__(self, data=b""): self.__class__.ensure_tables() self.B = [[0, 0, 0, 0] for _ in range(4)] self.K = [[0, 0, 0, 0] for _ in range(12)] self.r = 1 if self.hashbitlen <= 256 else 2 self.buf = bytearray() self.msg_len = 0 self.block_count = 0 iv = b"\x00" * 14 + self.hashbitlen.to_bytes(2, "big") for i in range(8): self.process_block(iv, i + 1) if data: self.update(data) return def copy(self): other = self.__class__() other.B = [row[:] for row in self.B] other.K = [row[:] for row in self.K] other.r = self.r other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.block_count = self.block_count return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) if self.buf: need = self.block_size - len(self.buf) self.buf.extend(data[:need]) data = data[need:] if len(self.buf) == self.block_size: self.block_count += 1 self.process_block(bytes(self.buf), self.block_count) self.buf.clear() offset = 0 limit = len(data) - (len(data) % self.block_size) while offset < limit: self.block_count += 1 self.process_block(data[offset:offset + self.block_size], self.block_count) offset += self.block_size if offset < len(data): self.buf.extend(data[offset:]) return self def digest(self): other = self.copy() other.finalize() return other.produce_output() def hexdigest(self): return self.digest().hex() def finalize(self): bit_len = self.msg_len * 8 processed = self.block_count rem = bytes(self.buf) if len(rem) <= 7: block = rem + b"\x80" + (b"\x00" * (7 - len(rem))) + bit_len.to_bytes(8, "big") processed += 1 self.process_block(block, processed) else: block1 = rem + b"\x80" + (b"\x00" * (15 - len(rem))) processed += 1 self.process_block(block1, processed) block2 = (b"\x00" * 8) + bit_len.to_bytes(8, "big") processed += 1 self.process_block(block2, processed) count_block = (b"\x00" * 8) + processed.to_bytes(8, "big") for i in range(32): self.process_block(count_block, i + 1) self.buf.clear() return def produce_output(self): out = bytearray(self.digest_size) for i in range(self.digest_size): out[self.digest_size - 1 - i] = ( self.B[3 - (i // 16)][3 - ((i // 4) % 4)] >> ((8 * i) % 32) ) & 0xff return bytes(out) def process_block(self, data, blockno): self.load_data_block(data, blockno) self.clock_register(self.r) return def load_data_block(self, data, blockno): mul2 = self.__class__.mul2_table mul3 = self.__class__.mul3_table p = [0, 0, 0, 0] q = [0, 0, 0, 0] for i in range(4): a = data[i] b = data[i + 4] c = data[i + 8] d = data[i + 12] q[i] = ( ((mul2[a] ^ mul3[b] ^ c ^ d) << 24) | ((a ^ mul2[b] ^ mul3[c] ^ d) << 16) | ((a ^ b ^ mul2[c] ^ mul3[d]) << 8) | (mul3[a] ^ b ^ c ^ mul2[d]) ) for i in range(4): a = data[4 * i] b = data[4 * i + 1] c = data[4 * i + 2] d = data[4 * i + 3] p[i] = ( ((mul2[a] ^ mul3[b] ^ c ^ d) << 24) | ((a ^ mul2[b] ^ mul3[c] ^ d) << 16) | ((a ^ b ^ mul2[c] ^ mul3[d]) << 8) | (mul3[a] ^ b ^ c ^ mul2[d]) ) p2 = [p[2], p[3], q[0], q[1]] q2 = [q[2], q[3], p[0], p[1]] self.B[2][0] ^= p[0] self.B[2][1] ^= p[1] self.B[2][2] ^= p[2] ^ (blockno >> 32) self.B[2][3] ^= p[3] ^ (blockno & 0xffff_ffff) self.B[3][0] ^= q[0] self.B[3][1] ^= q[1] self.B[3][2] ^= q[2] ^ (blockno >> 32) self.B[3][3] ^= q[3] ^ (blockno & 0xffff_ffff) self.K[3][0] ^= p2[0] self.K[3][1] ^= p2[1] self.K[3][2] ^= p2[2] self.K[3][3] ^= p2[3] self.K[5][0] ^= q[0] self.K[5][1] ^= q[1] self.K[5][2] ^= q[2] self.K[5][3] ^= q[3] self.K[7][0] ^= p[0] self.K[7][1] ^= p[1] self.K[7][2] ^= p[2] self.K[7][3] ^= p[3] self.K[11][0] ^= q2[0] self.K[11][1] ^= q2[1] self.K[11][2] ^= q2[2] self.K[11][3] ^= q2[3] return def clock_register(self, r): for _ in range(2): tmp = self.B[2][:] for _ in range(r): tmp = self.arf(tmp) feed_k = [tmp[i] ^ self.B[0][i] for i in range(4)] feed_b = [(feed_k[i] ^ self.K[9][i]) ^ self.K[0][i] for i in range(4)] self.B[0] = self.B[1][:] self.B[1] = self.B[2][:] self.B[2] = self.B[3][:] self.B[3] = feed_b for i in range(11): self.K[i] = self.K[i + 1][:] self.K[11] = feed_k return def arf(self, words): sbox = self.__class__.sbox mul2 = self.__class__.mul2_table mul3 = self.__class__.mul3_table state = [[0, 0, 0, 0] for _ in range(4)] for i in range(4): word = words[i] state[0][i] = (word >> 24) & 0xff state[1][i] = (word >> 16) & 0xff state[2][i] = (word >> 8) & 0xff state[3][i] = word & 0xff for i in range(4): for j in range(4): state[i][j] = sbox[state[i][j]] state[1] = state[1][1:] + state[1][:1] state[2] = state[2][2:] + state[2][:2] state[3] = state[3][3:] + state[3][:3] out = [0, 0, 0, 0] for i in range(4): a = state[0][i] b = state[1][i] c = state[2][i] d = state[3][i] out[i] = ( ((mul2[a] ^ mul3[b] ^ c ^ d) << 24) | ((a ^ mul2[b] ^ mul3[c] ^ d) << 16) | ((a ^ b ^ mul2[c] ^ mul3[d]) << 8) | (mul3[a] ^ b ^ c ^ mul2[d]) ) return out class SHAMATA224(SHAMATABase): digest_size = 28 hashbitlen = 224 class SHAMATA256(SHAMATABase): digest_size = 32 hashbitlen = 256 class SHAMATA384(SHAMATABase): digest_size = 48 hashbitlen = 384 class SHAMATA512(SHAMATABase): digest_size = 64 hashbitlen = 512 class SpectralHashBase: block_size = 64 digest_size = None hashbitlen = None X_INVERSE_TABLE = [0, 1, 15, 10, 8, 6, 5, 9, 4, 7, 3, 14, 13, 12, 11, 2] AFFINE_TRANSFORM_TABLE = [7, 0, 8, 2, 12, 4, 13, 11, 10, 3, 14, 15, 6, 1, 5, 9] SG_TABLES = { 128: [0x01, 0x02, 0x04, 0x08], 160: [0x03, 0x02, 0x04, 0x08], 192: [0x03, 0x06, 0x04, 0x08], 224: [0x03, 0x06, 0x0c, 0x08], 256: [0x03, 0x07, 0x0c, 0x08], 288: [0x03, 0x07, 0x0e, 0x08], 320: [0x03, 0x07, 0x0e, 0x0c], 352: [0x07, 0x07, 0x0e, 0x0c], 384: [0x07, 0x0f, 0x0e, 0x0c], 416: [0x07, 0x0f, 0x0f, 0x0c], 448: [0x07, 0x0f, 0x0f, 0x0e], 480: [0x0f, 0x0f, 0x0f, 0x0e], 512: [0x0f, 0x0f, 0x0f, 0x0f], } IJ_COLUMNS = [] IK_ROWS = [] JK_ROWS = [] @classmethod def build_tables(cls): if cls.IJ_COLUMNS: return for i in range(4): for j in range(4): cls.IJ_COLUMNS.append([(i << 5) | (j << 3) | k for k in range(8)]) for i in range(4): for k in range(8): cls.IK_ROWS.append([(i << 5) | (j << 3) | k for j in range(4)]) for j in range(4): for k in range(8): cls.JK_ROWS.append([(i << 5) | (j << 3) | k for i in range(4)]) return def __init__(self, data=b""): self.__class__.build_tables() if self.hashbitlen is None: raise ValueError("hashbitlen must be set in subclass") self.digest_size = self.hashbitlen // 8 self.s_prism = [0] * 128 self.p_prism = list(range(128)) self.h_prism = [0] * 128 self.buf = bytearray() self.msg_len = 0 self.started = False if data: self.update(data) return def copy(self): other = self.__class__() other.s_prism = self.s_prism[:] other.p_prism = self.p_prism[:] other.h_prism = self.h_prism[:] other.buf = bytearray(self.buf) other.msg_len = self.msg_len other.started = self.started return other def update(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError("data must be bytes-like") data = bytes(data) self.msg_len += len(data) self.buf.extend(data) while len(self.buf) >= 64: block = bytes(self.buf[:64]) del self.buf[:64] self.process_block(block, apply_initial_swap=not self.started) self.started = True return self def digest(self): c = self.copy() c.finalize() return c.make_digest() def hexdigest(self): return self.digest().hex() def process_block(self, block, apply_initial_swap): self.fill_s_prism(block) if apply_initial_swap: self.initial_swap_control() self.compress() return def fill_s_prism(self, block): out = self.s_prism n = 0 for b in block: out[n] = b >> 4 out[n + 1] = b & 0x0f n += 2 return def initial_swap_control(self): p = self.p_prism s = self.s_prism for i in range(4): base_i = i << 5 for j in range(4): base = base_i | (j << 3) for k in range(8): idx = base | k st = s[idx] sh = st >> 2 sl = st & 0x03 idx2 = (sh << 5) | (sl << 3) | k p[idx], p[idx2] = p[idx2], p[idx] return def affine_transform(self): table = self.AFFINE_TRANSFORM_TABLE s = self.s_prism for idx in range(128): s[idx] = table[s[idx]] return def k1_swap(self): s = self.s_prism p = self.p_prism for i in range(4): base_i = i << 5 for j in range(4): base = base_i | (j << 3) current = s[base] ^ s[base | 4] for b in range(4): if ((current >> b) & 1) == 0: a = base | b z = base | (7 - b) p[a], p[z] = p[z], p[a] return def k2_swap(self): s = self.s_prism p = self.p_prism for i in range(4): base_i = i << 5 for j in range(4): base = base_i | (j << 3) current = s[base | 1] if (current & 1) == 0: p[base | 0], p[base | 1] = p[base | 1], p[base | 0] if ((current >> 1) & 1) == 0: p[base | 2], p[base | 3] = p[base | 3], p[base | 2] if ((current >> 2) & 1) == 0: p[base | 1], p[base | 2] = p[base | 2], p[base | 1] if ((current >> 3) & 1) == 0: p[base | 0], p[base | 3] = p[base | 3], p[base | 0] for i in range(4): base_i = i << 5 for j in range(4): base = base_i | (j << 3) current = s[base | 5] if (current & 1) == 0: p[base | 4], p[base | 5] = p[base | 5], p[base | 4] if ((current >> 1) & 1) == 0: p[base | 6], p[base | 7] = p[base | 7], p[base | 6] if ((current >> 2) & 1) == 0: p[base | 5], p[base | 6] = p[base | 6], p[base | 5] if ((current >> 3) & 1) == 0: p[base | 4], p[base | 7] = p[base | 7], p[base | 4] return def four_point_dft(self, a0, a1, a2, a3): a = a0 + a2 b = a0 - a2 c = a1 + a3 d = a1 - a3 return [ (a + c) % 17, (b + d * 4) % 17, (a - c) % 17, (b - d * 4) % 17, ] def eight_point_dft(self, c0, c1, c2, c3, c4, c5, c6, c7): t20 = c0 + c4 t21 = c0 - c4 t22 = c2 + c6 t23 = (c2 - c6) * 4 t24 = c1 + c5 t25 = c1 - c5 t26 = c3 + c7 t27 = (c3 - c7) * 4 t0 = t20 + t22 t1 = t21 + t23 t2 = t20 - t22 t3 = t21 - t23 t4 = t24 + t26 t5 = (t25 + t27) * 2 t6 = (t24 - t26) * 4 t7 = (t25 - t27) * 8 return [ (t0 + t4) % 17, (t1 + t5) % 17, (t2 + t6) % 17, (t3 + t7) % 17, (t0 - t4) % 17, (t1 - t5) % 17, (t2 - t6) % 17, (t3 - t7) % 17, ] def apply_k_dft(self): s = self.s_prism for idxs in self.IJ_COLUMNS: out = self.eight_point_dft( s[idxs[0]], s[idxs[1]], s[idxs[2]], s[idxs[3]], s[idxs[4]], s[idxs[5]], s[idxs[6]], s[idxs[7]], ) for idx, val in zip(idxs, out): s[idx] = val return def j_swap(self): s = self.s_prism p = self.p_prism for i in range(4): base_i = i << 5 for k in range(8): current = s[base_i | 16 | k] if (((current >> 0) & 1) ^ (k & 1)) == 0: a = base_i | 0 | k b = base_i | 8 | k p[a], p[b] = p[b], p[a] if (((current >> 1) & 1) ^ ((k >> 1) & 1)) == 0: a = base_i | 16 | k b = base_i | 24 | k p[a], p[b] = p[b], p[a] if (((current >> 2) & 1) ^ ((k >> 2) & 1)) == 0: a = base_i | 8 | k b = base_i | 16 | k p[a], p[b] = p[b], p[a] if (((current >> 3) & 1) ^ (i & 1)) == 0: a = base_i | 0 | k b = base_i | 24 | k p[a], p[b] = p[b], p[a] return def apply_j_dft(self): s = self.s_prism for idxs in self.IK_ROWS: out = self.four_point_dft( s[idxs[0]], s[idxs[1]], s[idxs[2]], s[idxs[3]], ) for idx, val in zip(idxs, out): s[idx] = val return def i_swap(self): s = self.s_prism p = self.p_prism for j in range(4): base_j = j << 3 for k in range(8): current = s[96 | base_j | k] if (((current >> 0) & 1) ^ (k & 1)) == 0: a = 0 | base_j | k b = 32 | base_j | k p[a], p[b] = p[b], p[a] if (((current >> 1) & 1) ^ ((k >> 1) & 1)) == 0: a = 64 | base_j | k b = 96 | base_j | k p[a], p[b] = p[b], p[a] if (((current >> 2) & 1) ^ ((k >> 2) & 1)) == 0: a = 32 | base_j | k b = 64 | base_j | k p[a], p[b] = p[b], p[a] if (((current >> 3) & 1) ^ (j & 1)) == 0: a = 0 | base_j | k b = 96 | base_j | k p[a], p[b] = p[b], p[a] return def apply_i_dft(self): s = self.s_prism for idxs in self.JK_ROWS: out = self.four_point_dft( s[idxs[0]], s[idxs[1]], s[idxs[2]], s[idxs[3]], ) for idx, val in zip(idxs, out): s[idx] = val return def nlst(self): s = self.s_prism p = self.p_prism h = self.h_prism xinv = self.X_INVERSE_TABLE new_s = [0] * 128 for idx in range(128): stemp = s[idx] ptemp = p[idx] sp = stemp & 0x0f pl = ptemp & 0x0f ph = ((stemp & 0x10) >> 1) | ((ptemp >> 4) & 0x07) spp = s[ptemp] & 0x0f new_s[idx] = xinv[sp ^ pl] ^ xinv[spp ^ ph] ^ h[idx] self.s_prism = new_s self.h_prism = new_s[:] return def rubic_rot(self): p = self.p_prism new_p = [0] * 128 for i in range(4): base_i = i << 5 for j in range(4): base = base_i | (j << 3) for k in range(8): idx = base | k if (k & 3) == 0: new_p[idx] = p[idx] elif (k & 3) == 1: new_p[idx] = p[((3 - j) << 5) | (i << 3) | k] elif (k & 3) == 2: new_p[idx] = p[(j << 5) | (i << 3) | k] else: new_p[idx] = p[(j << 5) | ((3 - i) << 3) | k] self.p_prism = new_p return def compress(self): self.affine_transform() self.k1_swap() self.k2_swap() self.apply_k_dft() self.j_swap() self.apply_j_dft() self.i_swap() self.apply_i_dft() self.nlst() self.rubic_rot() return def pad_block_from_buffer(self): buf = bytearray(self.buf) bit_len = self.msg_len * 8 buf.append(0x80) while (len(buf) % 64) != 56: buf.append(0x00) buf.extend(bit_len.to_bytes(8, "big")) return [bytes(buf[i:i + 64]) for i in range(0, len(buf), 64)] def finalize(self): blocks = self.pad_block_from_buffer() if not blocks: return if len(self.buf) != 0: for block in blocks: self.process_block(block, apply_initial_swap=True) else: if self.msg_len == 0: self.process_block(blocks[0], apply_initial_swap=True) return def mark_bits(self): sg = self.SG_TABLES[self.hashbitlen] marks = [0] * 128 p = self.p_prism for idx in range(128): marks[idx] = sg[p[idx] & 0x03] return marks def make_digest(self): marks = self.mark_bits() out = bytearray(self.digest_size) bit_num = 0 byte_num = 0 for idx in range(128): mark = marks[idx] cell = self.h_prism[idx] for l in range(4): # noqa: E741 if ((1 << l) & mark) != 0: out[byte_num] |= ((cell >> l) & 1) << (7 - bit_num) bit_num += 1 if bit_num >= 8: byte_num += 1 bit_num = 0 if byte_num >= self.digest_size: return bytes(out) return bytes(out) class SpectralHash128(SpectralHashBase): hashbitlen = 128 class SpectralHash160(SpectralHashBase): hashbitlen = 160 class SpectralHash192(SpectralHashBase): hashbitlen = 192 class SpectralHash224(SpectralHashBase): hashbitlen = 224 class SpectralHash256(SpectralHashBase): hashbitlen = 256 class SpectralHash288(SpectralHashBase): hashbitlen = 288 class SpectralHash320(SpectralHashBase): hashbitlen = 320 class SpectralHash352(SpectralHashBase): hashbitlen = 352 class SpectralHash384(SpectralHashBase): hashbitlen = 384 class SpectralHash416(SpectralHashBase): hashbitlen = 416 class SpectralHash448(SpectralHashBase): hashbitlen = 448 class SpectralHash480(SpectralHashBase): hashbitlen = 480 class SpectralHash512(SpectralHashBase): hashbitlen = 512 @register_command class HashCommand(GenericCommand): """The base command to calculate hash.""" _cmdline_ = "hash" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("file") subparsers.add_parser("value") subparsers.add_parser("list") subparsers.add_parser("test") subparsers.add_parser("known-collision") _syntax_ = parser.format_help() _note_ = [ "[128b/16B] means 128 bits (16 bytes).", 'The salt for BLAKE2s and BLAKE2b is blank.', 'The key for KMAC128 and KMAC256 is blank.', 'The key for SipHash is "\\0" * 16.', 'The key for HalfSipHash is "\\0" * 8.', "To calculate FSB hash, you need the `gmpy2` package (uv pip install gmpy2).", ] _note_ = "\n".join(_note_) def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return def get_valid_hash_funcs(self): yield "hashlib" hashlib_hashes = { "MD5": "md5", "SHA1": "sha1", "MD5-SHA1": "md5-sha1", "SHA-224": "sha224", "SHA-256": "sha256", "SHA-384": "sha384", "SHA-512": "sha512", "SHA-512/224": "sha512-224", # May not be usable depending on availability of OpenSSL "SHA-512/256": "sha512-256", # May not be usable depending on availability of OpenSSL "SHA3-224": "sha3-224", "SHA3-256": "sha3-256", "SHA3-384": "sha3-384", "SHA3-512": "sha3-512", "BLAKE2s": "blake2s", "BLAKE2b": "blake2b", "SM3": "sm3", "RIPEMD-160": "ripemd160", # May not be usable depending on availability of OpenSSL } for hname, hname_hashlib in hashlib_hashes.items(): try: hfunc = hashlib.new(hname_hashlib) except Exception: continue yield (hname, hfunc) class ShakeWrapper: def __init__(self, hname, bits): self.hfunc = hashlib.new(hname) self.bits = bits self.digest_size = bits // 8 return def hexdigest(self): return self.hfunc.hexdigest(self.bits // 8) def __getattr__(self, name): return getattr(self.hfunc, name) for hname_base in ["shake-128", "shake-256"]: for bits in [128, 256, 512]: hname = "{:s}-{:d}".format(hname_base.upper().replace("-", ""), bits) try: hfunc = ShakeWrapper(hname_base, bits) except Exception: continue yield (hname, hfunc) # SHA-3 Round3 candidates yield "SHA3 Round3 candidates" yield ("BLAKE-224", Hash.BLAKE224()) yield ("BLAKE-256", Hash.BLAKE256()) yield ("BLAKE-384", Hash.BLAKE384()) yield ("BLAKE-512", Hash.BLAKE512()) yield ("Groestl-224", Hash.Groestl224()) yield ("Groestl-256", Hash.Groestl256()) yield ("Groestl-384", Hash.Groestl384()) yield ("Groestl-512", Hash.Groestl512()) yield ("JH-224", Hash.JH224()) yield ("JH-256", Hash.JH256()) yield ("JH-384", Hash.JH384()) yield ("JH-512", Hash.JH512()) yield ("Keccak-224", Hash.Keccak224()) yield ("Keccak-256", Hash.Keccak256()) yield ("Keccak-384", Hash.Keccak384()) yield ("Keccak-512", Hash.Keccak512()) yield ("Skein256-256", Hash.Skein256(digest_bits=256)) yield ("Skein256-512", Hash.Skein256(digest_bits=512)) yield ("Skein256-1024", Hash.Skein256(digest_bits=1024)) yield ("Skein512-256", Hash.Skein512(digest_bits=256)) yield ("Skein512-512", Hash.Skein512(digest_bits=512)) yield ("Skein512-1024", Hash.Skein512(digest_bits=1024)) yield ("Skein1024-256", Hash.Skein1024(digest_bits=256)) yield ("Skein1024-512", Hash.Skein1024(digest_bits=512)) yield ("Skein1024-1024", Hash.Skein1024(digest_bits=1024)) # SHA-3 Round2 candidates yield "SHA3 Round2 candidates" yield ("BMW-224", Hash.BMW224()) yield ("BMW-256", Hash.BMW256()) yield ("BMW-384", Hash.BMW384()) yield ("BMW-512", Hash.BMW512()) yield ("CubeHash10+1/1+10-256", Hash.CubeHash(params="CubeHash10+1/1+10-256")) yield ("CubeHash80+8/1+80-256", Hash.CubeHash(params="CubeHash80+8/1+80-256")) yield ("CubeHash160+16/32+160-256", Hash.CubeHash(params="CubeHash160+16/32+160-256")) yield ("CubeHash10+1/1+10-512", Hash.CubeHash(params="CubeHash10+1/1+10-512")) yield ("CubeHash80+8/1+80-512", Hash.CubeHash(params="CubeHash80+8/1+80-512")) yield ("CubeHash160+16/32+160-512", Hash.CubeHash(params="CubeHash160+16/32+160-512")) yield ("ECHO-224", Hash.ECHO224()) yield ("ECHO-256", Hash.ECHO256()) yield ("ECHO-384", Hash.ECHO384()) yield ("ECHO-512", Hash.ECHO512()) yield ("Fugue-224", Hash.Fugue224()) yield ("Fugue-256", Hash.Fugue256()) yield ("Fugue-384", Hash.Fugue384()) yield ("Fugue-512", Hash.Fugue512()) yield ("Hamsi-224", Hash.Hamsi224()) yield ("Hamsi-256", Hash.Hamsi256()) yield ("Hamsi-384", Hash.Hamsi384()) yield ("Hamsi-512", Hash.Hamsi512()) yield ("Luffa-224", Hash.Luffa224()) yield ("Luffa-256", Hash.Luffa256()) yield ("Luffa-384", Hash.Luffa384()) yield ("Luffa-512", Hash.Luffa512()) yield ("Shabal-192", Hash.Shabal192()) yield ("Shabal-224", Hash.Shabal224()) yield ("Shabal-256", Hash.Shabal256()) yield ("Shabal-384", Hash.Shabal384()) yield ("Shabal-512", Hash.Shabal512()) yield ("SHAvite3-224", Hash.SHAvite3_224()) yield ("SHAvite3-256", Hash.SHAvite3_256()) yield ("SHAvite3-384", Hash.SHAvite3_384()) yield ("SHAvite3-512", Hash.SHAvite3_512()) yield ("SIMD-224", Hash.SIMD224()) yield ("SIMD-256", Hash.SIMD256()) yield ("SIMD-384", Hash.SIMD384()) yield ("SIMD-512", Hash.SIMD512()) # SHA-3 Round1 candidates yield "SHA3 Round1 candidates" yield ("Abacus-224", Hash.Abacus224()) yield ("Abacus-256", Hash.Abacus256()) yield ("Abacus-384", Hash.Abacus384()) yield ("Abacus-512", Hash.Abacus512()) yield ("ARIRANG-224", Hash.ARIRANG224()) yield ("ARIRANG-256", Hash.ARIRANG256()) yield ("ARIRANG-384", Hash.ARIRANG384()) yield ("ARIRANG-512", Hash.ARIRANG512()) yield ("AURORA-224", Hash.AURORA224()) yield ("AURORA-224M", Hash.AURORA224M()) yield ("AURORA-256", Hash.AURORA256()) yield ("AURORA-256M", Hash.AURORA256M()) yield ("AURORA-384", Hash.AURORA384()) yield ("AURORA-512", Hash.AURORA512()) yield ("Blender-224", Hash.Blender224()) yield ("Blender-256", Hash.Blender256()) yield ("Blender-384", Hash.Blender384()) yield ("Blender-384Spec", Hash.Blender384Spec()) yield ("Blender-512", Hash.Blender512()) yield ("BOOLE-224", Hash.BOOLE224()) yield ("BOOLE-256", Hash.BOOLE256()) yield ("BOOLE-384", Hash.BOOLE384()) yield ("BOOLE-512", Hash.BOOLE512()) yield ("Cheetah-224", Hash.Cheetah224()) yield ("Cheetah-256", Hash.Cheetah256()) yield ("Cheetah-384", Hash.Cheetah384()) yield ("Cheetah-512", Hash.Cheetah512()) yield ("CHI-224", Hash.CHI224()) yield ("CHI-256", Hash.CHI256()) yield ("CHI-384", Hash.CHI384()) yield ("CHI-512", Hash.CHI512()) yield ("DCH-224", Hash.DCH224()) yield ("DCH-256", Hash.DCH256()) yield ("DCH-384", Hash.DCH384()) yield ("DCH-512", Hash.DCH512()) yield ("DynamicSHA-224", Hash.DynamicSHA224()) yield ("DynamicSHA-256", Hash.DynamicSHA256()) yield ("DynamicSHA-384", Hash.DynamicSHA384()) yield ("DynamicSHA-512", Hash.DynamicSHA512()) yield ("DynamicSHA2-224", Hash.DynamicSHA2_224()) yield ("DynamicSHA2-256", Hash.DynamicSHA2_256()) yield ("DynamicSHA2-384", Hash.DynamicSHA2_384()) yield ("DynamicSHA2-512", Hash.DynamicSHA2_512()) yield ("ECOH-224", Hash.ECOH224()) yield ("ECOH-256", Hash.ECOH256()) yield ("ECOH-384", Hash.ECOH384()) yield ("ECOH-512", Hash.ECOH512()) yield ("EDONR-224", Hash.EDONR224()) yield ("EDONR-256", Hash.EDONR256()) yield ("EDONR-384", Hash.EDONR384()) yield ("EDONR-512", Hash.EDONR512()) yield ("EnRUPT-224", Hash.EnRUPT224()) yield ("EnRUPT-256", Hash.EnRUPT256()) yield ("EnRUPT-384", Hash.EnRUPT384()) yield ("EnRUPT-512", Hash.EnRUPT512()) yield ("ESSENCE-224", Hash.ESSENCE224()) yield ("ESSENCE-256", Hash.ESSENCE256()) yield ("ESSENCE-384", Hash.ESSENCE384()) yield ("ESSENCE-512", Hash.ESSENCE512()) try: # FSB requires a hexadecimal representation of pi. # If gmpy2 is available, it can be calculated quickly, # but if it is not, it will take a long time, so GEF will skip it. __import__("gmpy2") yield ("FSB-160", Hash.FSB160()) yield ("FSB-224", Hash.FSB224()) yield ("FSB-256", Hash.FSB256()) yield ("FSB-384", Hash.FSB384()) yield ("FSB-512", Hash.FSB512()) except ImportError: pass yield ("Khichidi1-224", Hash.Khichidi1_224()) yield ("Khichidi1-256", Hash.Khichidi1_256()) yield ("Khichidi1-384", Hash.Khichidi1_384()) yield ("Khichidi1-512", Hash.Khichidi1_512()) yield ("Lane-224", Hash.Lane224()) yield ("Lane-256", Hash.Lane256()) yield ("Lane-384", Hash.Lane384()) yield ("Lane-512", Hash.Lane512()) yield ("Lesamnta-224", Hash.Lesamnta224()) yield ("Lesamnta-256", Hash.Lesamnta256()) yield ("Lesamnta-384", Hash.Lesamnta384()) yield ("Lesamnta-512", Hash.Lesamnta512()) yield ("LUX-224", Hash.LUX224()) yield ("LUX-256", Hash.LUX256()) yield ("LUX-384", Hash.LUX384()) yield ("LUX-512", Hash.LUX512()) yield ("MCSSHA3-224", Hash.MCSSHA3_224()) yield ("MCSSHA3-256", Hash.MCSSHA3_256()) yield ("MCSSHA3-384", Hash.MCSSHA3_384()) yield ("MCSSHA3-512", Hash.MCSSHA3_512()) yield ("MD6-128", Hash.MD6(d=128)) yield ("MD6-256", Hash.MD6(d=256)) yield ("MD6-512", Hash.MD6(d=512)) yield ("MeshHash-224", Hash.MeshHash224()) yield ("MeshHash-256", Hash.MeshHash256()) yield ("MeshHash-384", Hash.MeshHash384()) yield ("MeshHash-512", Hash.MeshHash512()) yield ("NaSHA-224", Hash.NaSHA224()) yield ("NaSHA-256", Hash.NaSHA256()) yield ("NaSHA-384", Hash.NaSHA384()) yield ("NaSHA-512", Hash.NaSHA512()) yield ("SANDstorm-224", Hash.SANDstorm224()) yield ("SANDstorm-256", Hash.SANDstorm256()) yield ("SANDstorm-384", Hash.SANDstorm384()) yield ("SANDstorm-512", Hash.SANDstorm512()) yield ("Sarmal-224", Hash.Sarmal224()) yield ("Sarmal-256", Hash.Sarmal256()) yield ("Sarmal-384", Hash.Sarmal384()) yield ("Sarmal-512", Hash.Sarmal512()) yield ("Sgail-224", Hash.Sgail224()) yield ("Sgail-256", Hash.Sgail256()) yield ("Sgail-384", Hash.Sgail384()) yield ("Sgail-512", Hash.Sgail512()) yield ("Sgail-768", Hash.Sgail768()) yield ("Sgail-1024", Hash.Sgail1024()) yield ("Sgail-1536", Hash.Sgail1536()) yield ("Sgail-2048", Hash.Sgail2048()) yield ("SHAMATA-224", Hash.SHAMATA224()) yield ("SHAMATA-256", Hash.SHAMATA256()) yield ("SHAMATA-384", Hash.SHAMATA384()) yield ("SHAMATA-512", Hash.SHAMATA512()) yield ("SpectralHash-128", Hash.SpectralHash128()) yield ("SpectralHash-160", Hash.SpectralHash160()) yield ("SpectralHash-192", Hash.SpectralHash192()) yield ("SpectralHash-224", Hash.SpectralHash224()) yield ("SpectralHash-256", Hash.SpectralHash256()) yield ("SpectralHash-288", Hash.SpectralHash288()) yield ("SpectralHash-320", Hash.SpectralHash320()) yield ("SpectralHash-352", Hash.SpectralHash352()) yield ("SpectralHash-384", Hash.SpectralHash384()) yield ("SpectralHash-416", Hash.SpectralHash416()) yield ("SpectralHash-448", Hash.SpectralHash448()) yield ("SpectralHash-480", Hash.SpectralHash480()) yield ("SpectralHash-512", Hash.SpectralHash512()) # Other (relatively long) yield "Relatively long" yield ("Ascon-Hash", Hash.Ascon()) yield ("Ascon-HashA", Hash.AsconA()) yield ("Ascon-Xof", Hash.AsconX()) yield ("Ascon-XofA", Hash.AsconXA()) yield ("BelT Hash", Hash.BELT()) yield ("BLAKE2sp", Hash.BLAKE2sp()) yield ("BLAKE2bp", Hash.BLAKE2bp()) yield ("BLAKE3-128", Hash.BLAKE3(digest_bits=128)) yield ("BLAKE3-256", Hash.BLAKE3(digest_bits=256)) yield ("BLAKE3-512", Hash.BLAKE3(digest_bits=512)) yield ("ED2K-Blue", Hash.ED2KBlue()) yield ("ED2K-Red", Hash.ED2KRed()) yield ("ED2K-RedBlue", Hash.ED2KRedBlue()) yield ("ESCH-256", Hash.ESCH256()) yield ("ESCH-384", Hash.ESCH384()) yield ("FNV1-32", Hash.FNV_32()) yield ("FNV1-64", Hash.FNV_64()) yield ("FNV1-128", Hash.FNV_128()) yield ("FNV1-256", Hash.FNV_256()) yield ("FNV1-512", Hash.FNV_512()) yield ("FNV1-1024", Hash.FNV_1024()) yield ("FNV1a-32", Hash.FNV_32(variant="fnv1a")) yield ("FNV1a-64", Hash.FNV_64(variant="fnv1a")) yield ("FNV1a-128", Hash.FNV_128(variant="fnv1a")) yield ("FNV1a-256", Hash.FNV_256(variant="fnv1a")) yield ("FNV1a-512", Hash.FNV_512(variant="fnv1a")) yield ("FNV1a-1024", Hash.FNV_1024(variant="fnv1a")) yield ("FNV0-32", Hash.FNV_32(variant="fnv0")) yield ("FNV0-64", Hash.FNV_64(variant="fnv0")) yield ("FNV0-128", Hash.FNV_128(variant="fnv0")) yield ("FNV0-256", Hash.FNV_256(variant="fnv0")) yield ("FNV0-512", Hash.FNV_512(variant="fnv0")) yield ("FNV0-1024", Hash.FNV_1024(variant="fnv0")) yield ("FNV0a-32", Hash.FNV_32(variant="fnv0a")) yield ("FNV0a-64", Hash.FNV_64(variant="fnv0a")) yield ("FNV0a-128", Hash.FNV_128(variant="fnv0a")) yield ("FNV0a-256", Hash.FNV_256(variant="fnv0a")) yield ("FNV0a-512", Hash.FNV_512(variant="fnv0a")) yield ("FNV0a-1024", Hash.FNV_1024(variant="fnv0a")) yield ("FORK-256", Hash.FORK256()) yield ("GOST", Hash.GOST()) # codespell:ignore yield ("GOST94cp", Hash.GOST94cp()) # codespell:ignore yield ("HAS-160", Hash.HAS160()) yield ("HAVAL-128,3", Hash.HAVAL(digest_bits=128, passes=3)) yield ("HAVAL-128,4", Hash.HAVAL(digest_bits=128, passes=4)) yield ("HAVAL-128,5", Hash.HAVAL(digest_bits=128, passes=5)) yield ("HAVAL-160,3", Hash.HAVAL(digest_bits=160, passes=3)) yield ("HAVAL-160,4", Hash.HAVAL(digest_bits=160, passes=4)) yield ("HAVAL-160,5", Hash.HAVAL(digest_bits=160, passes=5)) yield ("HAVAL-192,3", Hash.HAVAL(digest_bits=192, passes=3)) yield ("HAVAL-192,4", Hash.HAVAL(digest_bits=192, passes=4)) yield ("HAVAL-192,5", Hash.HAVAL(digest_bits=192, passes=5)) yield ("HAVAL-224,3", Hash.HAVAL(digest_bits=224, passes=3)) yield ("HAVAL-224,4", Hash.HAVAL(digest_bits=224, passes=4)) yield ("HAVAL-224,5", Hash.HAVAL(digest_bits=224, passes=5)) yield ("HAVAL-256,3", Hash.HAVAL(digest_bits=256, passes=3)) yield ("HAVAL-256,4", Hash.HAVAL(digest_bits=256, passes=4)) yield ("HAVAL-256,5", Hash.HAVAL(digest_bits=256, passes=5)) yield ("KangarooTwelve128-128", Hash.KangarooTwelve128(digest_bits=128)) yield ("KangarooTwelve128-256", Hash.KangarooTwelve128(digest_bits=256)) yield ("KangarooTwelve128-512", Hash.KangarooTwelve128(digest_bits=512)) yield ("KangarooTwelve256-128", Hash.KangarooTwelve256(digest_bits=128)) yield ("KangarooTwelve256-256", Hash.KangarooTwelve256(digest_bits=256)) yield ("KangarooTwelve256-512", Hash.KangarooTwelve256(digest_bits=512)) yield ("KMAC128-128", Hash.KMAC128(key=b"", digest_bits=128)) yield ("KMAC128-256", Hash.KMAC128(key=b"", digest_bits=256)) yield ("KMAC128-512", Hash.KMAC128(key=b"", digest_bits=512)) yield ("KMAC256-128", Hash.KMAC256(key=b"", digest_bits=128)) yield ("KMAC256-256", Hash.KMAC256(key=b"", digest_bits=256)) yield ("KMAC256-512", Hash.KMAC256(key=b"", digest_bits=512)) yield ("Kupyna-256", Hash.Kupyna256()) yield ("Kupyna-384", Hash.Kupyna384()) yield ("Kupyna-512", Hash.Kupyna512()) yield ("LSH256-224", Hash.LSH256_224()) yield ("LSH256-256", Hash.LSH256_256()) yield ("LSH512-224", Hash.LSH512_224()) yield ("LSH512-256", Hash.LSH512_256()) yield ("LSH512-384", Hash.LSH512_384()) yield ("LSH512-512", Hash.LSH512_512()) yield ("MarsupilamiFourteen", Hash.MarsupilamiFourteen()) yield ("MD2", Hash.MD2()) yield ("MD4", Hash.MD4()) yield ("MDC-2", Hash.MDC2()) yield ("NTLM hash", Hash.NTLM()) yield ("Panama", Hash.Panama()) yield ("ParallelHash-128", Hash.ParallelHash128()) yield ("ParallelHash-256", Hash.ParallelHash256()) yield ("ParallelHashXOF-128", Hash.ParallelHashXOF128()) yield ("ParallelHashXOF-256", Hash.ParallelHashXOF256()) yield ("PhotonBeetle", Hash.PhotonBeetle()) yield ("RadioGatun-32", Hash.RadioGatun32()) yield ("RadioGatun-64", Hash.RadioGatun64()) yield ("RIPEMD-128", Hash.RIPEMD128()) yield ("RIPEMD-160", Hash.RIPEMD160()) yield ("RIPEMD-256", Hash.RIPEMD256()) yield ("RIPEMD-320", Hash.RIPEMD320()) yield ("SHA-0", Hash.SHA0()) yield ("SHA-512/224", Hash.SHA512_224()) yield ("SHA-512/256", Hash.SHA512_256()) yield ("Snefru-128", Hash.Snefru128()) yield ("Snefru-256", Hash.Snefru256()) yield ("Streebog-256", Hash.Streebog256()) yield ("Streebog-512", Hash.Streebog512()) yield ("TIGER-128,3", Hash.TIGER(digest_bits=128, passes=3)) yield ("TIGER-160,3", Hash.TIGER(digest_bits=160, passes=3)) yield ("TIGER-192,3", Hash.TIGER(digest_bits=192, passes=3)) yield ("TIGER-128,4", Hash.TIGER(digest_bits=128, passes=4)) yield ("TIGER-160,4", Hash.TIGER(digest_bits=160, passes=4)) yield ("TIGER-192,4", Hash.TIGER(digest_bits=192, passes=4)) yield ("TIGER2-128,3", Hash.TIGER(digest_bits=128, passes=3, version=2)) yield ("TIGER2-160,3", Hash.TIGER(digest_bits=160, passes=3, version=2)) yield ("TIGER2-192,3", Hash.TIGER(digest_bits=192, passes=3, version=2)) yield ("TupleHash128-128", Hash.TupleHash128(digest_bits=128)) yield ("TupleHash128-256", Hash.TupleHash128(digest_bits=256)) yield ("TupleHash128-512", Hash.TupleHash128(digest_bits=512)) yield ("TupleHash256-128", Hash.TupleHash256(digest_bits=128)) yield ("TupleHash256-256", Hash.TupleHash256(digest_bits=256)) yield ("TupleHash256-512", Hash.TupleHash256(digest_bits=512)) yield ("TurboSHAKE128-128", Hash.TurboShake128(digest_bits=128)) yield ("TurboSHAKE128-256", Hash.TurboShake128(digest_bits=256)) yield ("TurboSHAKE128-512", Hash.TurboShake128(digest_bits=512)) yield ("TurboSHAKE256-128", Hash.TurboShake256(digest_bits=128)) yield ("TurboSHAKE256-256", Hash.TurboShake256(digest_bits=256)) yield ("TurboSHAKE256-512", Hash.TurboShake256(digest_bits=512)) yield ("VSH-1024", Hash.VSH1024()) yield ("Whirlpool-0", Hash.Whirlpool0()) yield ("Whirlpool-T", Hash.WhirlpoolT()) yield ("Whirlpool", Hash.Whirlpool()) yield ("Xoodyak", Hash.Xoodyak()) # Other (relatively short) yield "Relatively short" yield ("CityHash-32", Hash.CityHash32()) yield ("CityHash-64", Hash.CityHash64()) yield ("CityHash-128", Hash.CityHash128()) yield ("FarmHash-32 (fp)", Hash.FarmHash32()) yield ("FarmHash-64 (fp)", Hash.FarmHash64()) yield ("FarmHash-128 (fp)", Hash.FarmHash128()) yield ("FastHash-32", Hash.FastHash32()) yield ("FastHash-64", Hash.FastHash64()) yield ("GxHash-32", Hash.GxHash32()) yield ("GxHash-64", Hash.GxHash64()) yield ("GxHash-128", Hash.GxHash128()) yield ("HalfSipHash-32_2_4", Hash.HalfSipHash32_2_4()) yield ("HalfSipHash-64_2_4", Hash.HalfSipHash64_2_4()) yield ("HalfTimeHash-64", Hash.HalfTimeHash64()) yield ("HalfTimeHash-128", Hash.HalfTimeHash128()) yield ("HalfTimeHash-256", Hash.HalfTimeHash256()) yield ("HalfTimeHash-512", Hash.HalfTimeHash512()) yield ("HighwayHash-64", Hash.HighwayHash64()) yield ("HighwayHash-128", Hash.HighwayHash128()) yield ("HighwayHash-256", Hash.HighwayHash256()) yield ("KomiHash", Hash.KomiHash()) yield ("MetroHash-64", Hash.MetroHash64()) yield ("MetroHash-128", Hash.MetroHash128()) yield ("Murmur1", Hash.MurmurHash1()) yield ("Murmur2", Hash.MurmurHash2()) yield ("Murmur2a", Hash.MurmurHash2A()) yield ("Murmur64a", Hash.MurmurHash64A()) yield ("Murmur64b", Hash.MurmurHash64B()) yield ("Murmur3a", Hash.MurmurHash3_x86_32()) yield ("Murmur3c", Hash.MurmurHash3_x86_128()) yield ("Murmur3f", Hash.MurmurHash3_x64_128()) yield ("SipHash-64_2_4", Hash.SipHash64_2_4()) yield ("SipHash-64_1_3", Hash.SipHash64_1_3()) yield ("SipHash-64_4_8", Hash.SipHash64_4_8()) yield ("SipHash-128_2_4", Hash.SipHash128_2_4()) yield ("SipHash-128_4_8", Hash.SipHash128_4_8()) yield ("SpookyHash-32", Hash.SpookyHash32()) yield ("SpookyHash-64", Hash.SpookyHash64()) yield ("SpookyHash-128", Hash.SpookyHash128()) yield ("T1HA0-32", Hash.T1HA0_32()) yield ("T1HA1-64", Hash.T1HA1_64()) yield ("T1HA2-64", Hash.T1HA2_64()) yield ("T1HA2-128", Hash.T1HA2_128()) yield ("WYHash-32", Hash.WYHash32()) yield ("WYHash-64", Hash.WYHash64()) yield ("xxHash-32", Hash.XXH32()) yield ("xxHash-64", Hash.XXH64()) yield ("xxHash3-64", Hash.XXH3_64()) yield ("xxHash3-128", Hash.XXH3_128()) # Checksum yield "Checksum" yield ("RS Hash", Hash.RSHash()) yield ("JS Hash", Hash.JSHash()) yield ("PJW Hash", Hash.PJWHash()) yield ("ELF Hash", Hash.ELFHash()) yield ("BKDR Hash", Hash.BKDRHash()) yield ("SDBM Hash", Hash.SDBMHash()) yield ("DJB2", Hash.DJB2Hash()) yield ("DEK Hash", Hash.DEKHash()) yield ("AP Hash", Hash.APHash()) yield ("JOAAT", Hash.JOAAT()) yield ("Adler32", Hash.Adler()) yield ("SuperFastHash", Hash.SuperFastHash()) yield ("BuzHash", Hash.BuzHash()) yield ("NHash", Hash.NHash()) # MD5/SHA1/SHA256 n-times yield "MD5/SHA1/SHA256 n-times" yield ("MD5 x2 (raw)", Hash.HASHxN("md5", N=2)) yield ("MD5 x3 (raw)", Hash.HASHxN("md5", N=3)) yield ("MD5 x4 (raw)", Hash.HASHxN("md5", N=4)) yield ("MD5 x5 (raw)", Hash.HASHxN("md5", N=5)) yield ("MD5 x2 (hex)", Hash.HASHxN("md5", N=2, use_hex=True)) yield ("MD5 x3 (hex)", Hash.HASHxN("md5", N=3, use_hex=True)) yield ("MD5 x4 (hex)", Hash.HASHxN("md5", N=4, use_hex=True)) yield ("MD5 x5 (hex)", Hash.HASHxN("md5", N=5, use_hex=True)) yield ("SHA1 x2 (raw)", Hash.HASHxN("sha1", N=2)) yield ("SHA1 x3 (raw)", Hash.HASHxN("sha1", N=3)) yield ("SHA1 x4 (raw)", Hash.HASHxN("sha1", N=4)) yield ("SHA1 x5 (raw)", Hash.HASHxN("sha1", N=5)) yield ("SHA1 x2 (hex)", Hash.HASHxN("sha1", N=2, use_hex=True)) yield ("SHA1 x3 (hex)", Hash.HASHxN("sha1", N=3, use_hex=True)) yield ("SHA1 x4 (hex)", Hash.HASHxN("sha1", N=4, use_hex=True)) yield ("SHA1 x5 (hex)", Hash.HASHxN("sha1", N=5, use_hex=True)) yield ("SHA-256 x2 (raw)", Hash.HASHxN("sha256", N=2)) yield ("SHA-256 x3 (raw)", Hash.HASHxN("sha256", N=3)) yield ("SHA-256 x4 (raw)", Hash.HASHxN("sha256", N=4)) yield ("SHA-256 x5 (raw)", Hash.HASHxN("sha256", N=5)) yield ("SHA-256 x2 (hex)", Hash.HASHxN("sha256", N=2, use_hex=True)) yield ("SHA-256 x3 (hex)", Hash.HASHxN("sha256", N=3, use_hex=True)) yield ("SHA-256 x4 (hex)", Hash.HASHxN("sha256", N=4, use_hex=True)) yield ("SHA-256 x5 (hex)", Hash.HASHxN("sha256", N=5, use_hex=True)) return None def make_line(self, hname, hfunc, h): if h is None: byte = hfunc.digest_size bit = byte * 8 line = "{:26s}:[{:4d}b/{:3d}B]".format(hname, bit, byte) return line bit = len(h) * 4 byte = bit // 8 if hasattr(hfunc, "digest_normalize"): hn = hfunc.digest_normalize(h) line = "{:26s}:[{:4d}b/{:3d}B] {:s} ({:s})".format(hname, bit, byte, h, hn) else: line = "{:26s}:[{:4d}b/{:3d}B] {:s}".format(hname, bit, byte, h) return line def should_be_displayed(self, hname, hfunc): if self.args.smart >= 2: if hname not in ["MD5", "SHA1", "SHA256"]: return False if self.args.length_filter is not None: if self.args.length_filter != hfunc.digest_size: return False if not self.args.filter: return True for filt in self.args.filter: if filt.search(hname): return True def norm(x): x = x.lower() x = x.replace("/", "") x = x.replace("-", "") return x for filt in self.args.filter: if filt.search(norm(hname)): return True return False @parse_args def do_invoke(self, args): self.usage() return @register_command class HashMemoryCommand(HashCommand, BufferingOutput): """Calculate hash from memory values.""" _cmdline_ = "hash memory" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address for hash calculation.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for hash calculation.") parser.add_argument("-f", "--filter", metavar="REGEX", type=re.compile, default=[], action="append", help="filter by REGEX pattern.") parser.add_argument("-l", "--length-filter", type=AddressUtil.parse_address, help="filter by hash byte length.") parser.add_argument("-s", "--smart", action="count", default=0, help="increase output smart level. (-s, -ss)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def calc_hash(self, hfunc, start_address, end_address): # When calculating the hash of a very large range, # it is not practical to store the entire data in memory. # It is preferable to calculate it in blocks. step = 0x400 * get_pagesize() if is_qemu_system(): step = get_pagesize() for chunk_addr in range(start_address, end_address, step): chunk_size = min(end_address - chunk_addr, step) try: mem = read_memory(chunk_addr, chunk_size) except (gdb.MemoryError, MemoryError): err("Memory read error") return False try: hfunc.update(mem) except ValueError: return None del mem return hfunc.hexdigest() def process(self): tqdm = GefUtil.get_tqdm(not self.args.quiet) hash_funcs = list(self.get_valid_hash_funcs()) pbar = tqdm(hash_funcs, leave=False, total=len(hash_funcs)) for elem in pbar: if isinstance(elem, str): if self.args.smart: if elem != "hashlib": break if not self.args.quiet: self.out.append(titlify(elem)) continue hname, hfunc = elem if not self.should_be_displayed(hname, hfunc): continue if not self.args.quiet: try: pbar.set_description(hname) except Exception: pass h = self.calc_hash(hfunc, self.args.location, self.args.location + self.args.size) if h is False: return if h is None: continue line = self.make_line(hname, hfunc, h) self.out.append(line) return @parse_args @only_if_gdb_running def do_invoke(self, args): self.out = [] self.out.append("Address: {:#x}".format(args.location)) self.out.append("Size: {:#x}".format(args.size)) self.process() self.print_output(check_terminal_size=True) return @register_command class HashFileCommand(HashCommand, BufferingOutput): """Calculate hash from file.""" _cmdline_ = "hash file" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("filename", metavar="FILE", help="the filepath for hash calculation.") parser.add_argument("start", metavar="START_POS", nargs="?", default=0, type=AddressUtil.parse_address, help="the start position for hash calculation.") parser.add_argument("size", metavar="SIZE", nargs="?", type=AddressUtil.parse_address, help="the size for hash calculation.") parser.add_argument("-f", "--filter", metavar="REGEX", type=re.compile, default=[], action="append", help="filter by REGEX pattern.") parser.add_argument("-l", "--length-filter", type=AddressUtil.parse_address, help="filter by hash byte length.") parser.add_argument("-s", "--smart", action="count", default=0, help="increase output smart level. (-s, -ss)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_FILENAME) return def calc_hash(self, hfunc, filename, start_pos, end_pos): # When calculating the hash of a very large range, # it is not practical to store the entire data in memory. # It is preferable to calculate it in blocks. step = 0x400 * get_pagesize() with open(self.args.filename, "rb") as f: f.seek(start_pos) for chunk_pos in range(start_pos, end_pos, step): chunk_size = min(end_pos - chunk_pos, step) data = f.read(chunk_size) try: hfunc.update(data) except ValueError: return None del data return hfunc.hexdigest() def process(self, filename, start_pos, end_pos): tqdm = GefUtil.get_tqdm(not self.args.quiet) hash_funcs = list(self.get_valid_hash_funcs()) pbar = tqdm(hash_funcs, leave=False, total=len(hash_funcs)) for elem in pbar: if isinstance(elem, str): if self.args.smart: if elem != "hashlib": break if not self.args.quiet: self.out.append(titlify(elem)) continue hname, hfunc = elem if not self.should_be_displayed(hname, hfunc): continue if not self.args.quiet: try: pbar.set_description(hname) except Exception: pass h = self.calc_hash(hfunc, filename, start_pos, end_pos) if h is False: return if h is None: continue line = self.make_line(hname, hfunc, h) self.out.append(line) return @parse_args def do_invoke(self, args): self.out = [] if not os.path.exists(args.filename): err("File not found") return self.out.append("Path: {:s}".format(args.filename)) self.out.append("FileSize: {:#x}".format(os.path.getsize(args.filename))) if args.size is None: end_pos = args.start + os.path.getsize(args.filename) else: end_pos = args.start + args.size self.process(args.filename, args.start, end_pos) self.print_output(check_terminal_size=True) return @register_command class HashValueCommand(HashCommand, BufferingOutput): """Calculate hash from specified values.""" _cmdline_ = "hash value" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the string for hash calculation.") parser.add_argument("--hex", action="store_true", help="interpret VALUE as hex. invalid character is ignored.") parser.add_argument("-f", "--filter", metavar="REGEX", type=re.compile, default=[], action="append", help="filter by REGEX pattern.") parser.add_argument("-l", "--length-filter", type=AddressUtil.parse_address, help="filter by hash byte length.") parser.add_argument("-s", "--smart", action="count", default=0, help="increase output smart level. (-s, -ss)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "\\\\x41\\\\x42\\\\x43\\\\x44"', '{0:s} --hex "41 42 43 44"', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_NONE) return def process(self, value): tqdm = GefUtil.get_tqdm(not self.args.quiet) hash_funcs = list(self.get_valid_hash_funcs()) pbar = tqdm(hash_funcs, leave=False, total=len(hash_funcs)) for elem in pbar: if isinstance(elem, str): if self.args.smart: if elem != "hashlib": break if not self.args.quiet: self.out.append(titlify(elem)) continue hname, hfunc = elem if not self.should_be_displayed(hname, hfunc): continue if not self.args.quiet: try: pbar.set_description(hname) except Exception: pass hfunc.update(value) h = hfunc.hexdigest() line = self.make_line(hname, hfunc, h) self.out.append(line) return @parse_args def do_invoke(self, args): if args.hex: # "41414141" -> b"\x41\x41\x41\x41" value = GefUtil.fromhex_ignore_invalid(args.value) if not value: return else: try: value = codecs.escape_decode(args.value)[0] except binascii.Error: err('Could not decode "\\xXX" encoded string') return self.out = [] self.process(value) self.print_output(check_terminal_size=True) return @register_command class HashListCommand(HashCommand, BufferingOutput): """List hash supported by GEF.""" _cmdline_ = "hash list" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--filter", metavar="REGEX", type=re.compile, default=[], action="append", help="filter by REGEX pattern.") parser.add_argument("-l", "--length-filter", type=AddressUtil.parse_address, help="filter by hash byte length.") parser.add_argument("-s", "--smart", action="count", default=0, help="increase output smart level. (-s, -ss)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = None def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_NONE) return def process(self): i = 0 for elem in self.get_valid_hash_funcs(): if isinstance(elem, str): if self.args.smart: if elem != "hashlib": break self.out.append(titlify(elem)) continue hname, hfunc = elem if not self.should_be_displayed(hname, hfunc): i += 1 continue line = self.make_line(hname, hfunc, None) self.out.append("[{:3d}] {:s}".format(i, line)) i += 1 return @parse_args def do_invoke(self, args): self.out = [] self.process() self.print_output(check_terminal_size=True) return @register_command class HashTestCommand(HashCommand, BufferingOutput): """Calculate and check hash from constant inputs.""" _cmdline_ = "hash test" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--filter", metavar="REGEX", type=re.compile, default=[], action="append", help="filter by REGEX pattern.") parser.add_argument("-l", "--length-filter", type=AddressUtil.parse_address, help="filter by hash byte length.") parser.add_argument("-s", "--smart", action="store_true", help="show only failed.") group = parser.add_mutually_exclusive_group(required=False) group.add_argument("-t", "--time", action="store_true", help="measure the time taken to compute the hash using large bytes of data.") group.add_argument("-T", "--time-with-sort", action="store_true", help="measure and sort the time taken to compute the hash using large bytes of data.") parser.add_argument("--size", type=AddressUtil.parse_address, default=0x1000, help="the data size of 'AAAA...' to measure the time taken to compute the hash.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = None def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_NONE) return # "The quick brown fox jumps over the lazy dog" test_vectors = { # -------------------- hashlib -------------------- # hashlib "MD5": "9e107d9d372bb6826bd81d3542a419d6", "SHA1": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", "MD5-SHA1": "9e107d9d372bb6826bd81d3542a419d62fd4e1c67a2d28fced849ee1bb76e7391b93eb12", \ "SHA-224": "730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525", "SHA-256": "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", "SHA-384": "ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1", "SHA-512": "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", "SHA-512/224": "944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37", "SHA-512/256": "dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d", "SHA3-224": "d15dadceaa4d5d7bb3b48f446421d542e08ad8887305e28d58335795", "SHA3-256": "69070dda01975c8c120c3aada1b282394e7f032fa9cf32f4cb2259a0897dfc04", "SHA3-384": "7063465e08a93bce31cd89d2e3ca8f602498696e253592ed26f07bf7e703cf328581e1471a7ba7ab119b1a9ebdf8be41", "SHA3-512": "01dedd5de4ef14642445ba5f5b97c15e47b9ad931326e4b0727cd94cefc44fff23f07bf543139939b49128caf436dc1bdee54fcb24023a08d9403f9b4bf0d450", "BLAKE2s": "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812", "BLAKE2b": "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", "SM3": "5fdfe814b8573ca021983970fc79b2218c9570369b4859684e2e4c3fc76cb8ea", "SHAKE128-128": "f4202e3c5852f9182a0430fd8144f0a7", "SHAKE128-256": "f4202e3c5852f9182a0430fd8144f0a74b95e7417ecae17db0f8cfeed0e3e66e", "SHAKE128-512": "f4202e3c5852f9182a0430fd8144f0a74b95e7417ecae17db0f8cfeed0e3e66eb5585ec6f86021cacf272c798bcf97d368b886b18fec3a571f096086a523717a", "SHAKE256-128": "2f671343d9b2e1604dc9dcf0753e5fe1", "SHAKE256-256": "2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca", "SHAKE256-512": "2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca1d01d1369a23539cd80f7c054b6e5daf9c962cad5b8ed5bd11998b40d5734442", # -------------------- SHA3 Round3 candidates -------------------- # https://crashdemons.github.io/BLAKE-wasm/ "BLAKE-224": "c8e92d7088ef87c1530aee2ad44dc720cc10589cc2ec58f95a15e51b", "BLAKE-256": "7576698ee9cad30173080678e5965916adbb11cb5245d386bf1ffda1cb26c9d7", "BLAKE-384": "67c9e8ef665d11b5b57a1d99c96adffb3034d8768c0827d1c6e60b54871e8673651767a2c6c43d0ba2a9bb2500227406", "BLAKE-512": "1f7e26f63b6ad25a0896fd978fd050a1766391d2fd0471a77afb975e5034b7ad2d9ccf8dfb47abbbe656e1b82fbc634ba42ce186e8dc5e1ce09a885d41f43451", # https://hashing.tools/groestl "Groestl-224": "8ce3ce0f7092cada755be8f614fd6d5e5738ff1f6cd5dabe42404c46", "Groestl-256": "8c7ad62eb26a21297bc39c2d7293b4bd4d3399fa8afab29e970471739e28b301", "Groestl-384": "9330aeb62a1fc0a464dd70ac27b57075e00ae5d627f9bd6ff72952b3857aba2cfbcc4345af9a04fcc13eb346829e4088", "Groestl-512": "badc1f70ccd69e0cf3760c3f93884289da84ec13c70b3d12a53a7a8a4a513f99715d46288f55e1dbf926e6d084a0538e4eebfc91cf2b21452921ccde9131718d", # https://hashing.tools/jh "JH-224": "bb21255e4a6bcbd3ddbf8694df2e7f41b74a69c1a7e1c2d36a3fd405", "JH-256": "6a049fed5fc6874acfdc4a08b568a4f8cbac27de933496f031015b38961608a0", "JH-384": "de44fe5f835f5518c603aec9d67363466d9f3a5b54d4cfbd4083b055f95a21a2562abaa59b830b3bc4e023d0b52a1268", "JH-512": "043f14e7c0775e7b1ef5ad657b1e858250b21e2e61fd699783f8634cb86f3ff938451cabd0c8cdae91d4f659d3f9f6f654f1bfedca117ffba735c15fedda47a3", # https://hashing.tools/keccak "Keccak-224": "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe", "Keccak-256": "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15", "Keccak-384": "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3", "Keccak-512": "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609", # https://hashing.tools/skein "Skein256-256": "c0fbd7d779b20f0a4614a66697f9e41859eaf382f14bf857e8cdb210adb9b3fe", "Skein256-512": "f8138e72cdd9e11cf09e4be198c234acb0d21a9f75f936e989cf532f1fa9f4fb21d255811f0f1592fb3617d04704add875ae7bd16ddbbeaed4eca6eb9675d2c6", "Skein256-1024": "20151f29d6e0c8bae62710bf8bd0ae97c9d8b20c0df932874bba412bb89b4420d910680ef9021e3fe8f7473ddce3cc01ec55b0d9c419c807339786e328b081c6" \ "0bbdeb57dc4f568e457a997c6cb91667d21c7183ddb9b6d6e7e733fa759cc9d86e4ebd273d0f105d3961f928c00b65ab43e62b33af851c42e3894917302801f1", "Skein512-256": "b3250457e05d3060b1a4bbc1428bc75a3f525ca389aeab96cfa34638d96e492a", "Skein512-512": "94c2ae036dba8783d0b3f7d6cc111ff810702f5c77707999be7e1c9486ff238a7044de734293147359b4ac7e1d09cd247c351d69826b78dcddd951f0ef912713", "Skein512-1024": "9a8faf76b15cb3335bbc1c2ef953d48916511eb3e07294d1d43087c47e78d753885623794963e66233cf3938912f6bad1d5b3c34dd1a2123be2afbd6bd45aed8" \ "12b44b4ea78cb7f7fd76e0b3daea51009c8ce44dea427bbae1c2e7200eab150bd88dd85e72980c98108c13671cbc1a66e866a0cdde8dc1ddfe58ba6140e8924f", "Skein1024-256": "054922d4393e36af62143986221555bee407671f6e57631bd7273e215a714833", "Skein1024-512": "a40ba71fa36a8c1d152bfc68b79782ef206d2e74b9a072b11aa874e6ec2148d937e9acd4ca1026ad636fed1a88b740112d782e2ca0e6c3bbe0dd2704a60a10a5", "Skein1024-1024": "4cf6152f1a7e598098d28f04e13d7742ba39b7fadbbcf2167bda4e1615d551f3f6b4edbbb391ffa09e6cc0a4af1eb366b30b5f107b437e2ea5cb586afb0341bd" \ "97dabe7cc46e7be3a054aa605395e43b243654c01ffc14c8b5443488f35d80b504a612f3d29d767106d0d9249aaa4fd99b67a94fb8661a3520004501192d84fa", # -------------------- SHA3 Round2 candidates -------------------- # https://github.com/aidansteele/sphlib "BMW-224": "278f7e6db8fd7c9353fc181d840bf20351e3a45229ff42983ac26697", "BMW-256": "ca0981a78ac2c97ecb358267f6d8d88216366024ef0d7137938b5a3165898dff", "BMW-384": "ca60ffd15eab7f4809f1b8f8daa5687f2192f872cc554303181403626cf5311be3c8f86e49aab330278f8e1b411d3c60", "BMW-512": "2998d4cb31323e1169b458ab03a54d0b68e411a3c7cc7612adbf05bf901b8197dfd852c1c0099c09717d2fad3537207e737c6159c31d377d1ab8f5ed1ceeea06", # self "CubeHash10+1/1+10-256": "217a4876f2b24cec489c9171f85d53395cc979156ea0254938c4c2c59dfdf8a4", "CubeHash80+8/1+80-256": "94e0c958d85cdfaf554919980f0f50b945b88ad08413e0762d6ff0219aff3e55", "CubeHash160+16/32+160-256": "5151e251e348cbbfee46538651c06b138b10eeb71cf6ea6054d7ca5fec82eb79", "CubeHash10+1/1+10-512": "eb7f5f80706e8668c61186c3c710ce57f9094fbfa1dbdc7554842cdbb4d10ce42fce72736d10b152f6216f23fc648bce810a7af4d58e571ec1b852fa514a0a8e", "CubeHash80+8/1+80-512": "ca942b088ed9103726af1fa87b4deb59e50cf3b5c6dcfbcebf5bba22fb39a6be9936c87bfdd7c52fc5e71700993958fa4e7b5e6e2a3672122475c40f9ec816ba", "CubeHash160+16/32+160-512": "bdba44a28cd16b774bdf3c9511def1a2baf39d4ef98b92c27cf5e37beb8990b7cdb6575dae1a548330780810618b8a5c351c1368904db7ebdf8857d596083a86", # https://github.com/aidansteele/sphlib "ECHO-224": "ea7548f1186079bea3b7002f7651b60cb1fd559191f3dde26700f069", "ECHO-256": "3c3c10b84e818cbddfd71e1aefc6cb9cd7fd1b84acb5765813e716734a97d422", "ECHO-384": "d045abb41ef43012e0436855f10f1a115eeec1f346ff119e86bf96cf427f453b625f0df8ee2b123e335a9a38446702c6", "ECHO-512": "fe61eba97bdfcaa027ded44a5f883fcb900b97449596d7b4a7187c76e71ad750e6117b529bd69992bec015bef862d16d62c384b600cb300d486e565f94202abf", # https://github.com/jonelo/jacksum "Fugue-224": "6e8e3280e6e3a4d8fcf27a82ea81d66f66f94b73dc3a85a361740b78", "Fugue-256": "4b2c2011fc9e5f5d6aed35dd20ce151af631db61aad0b2a5e12e17e01538d5ca", "Fugue-384": "3a327a7b4a05f05d0e5a06e2f1eb49a0e837a9e07bf60b5eeefe5fc8ca98cf85578ce856b60d6f3828b81c5eb051ace8", "Fugue-512": "ee1e53e892bedd72d753bd4c9f704201708fb9b79177816051ebca1dc1af7ee928b8996df0862bbea24503be2781b1a036079a88627d4d248f2d0ec77b579b7f", # https://github.com/aidansteele/sphlib "Hamsi-224": "0e0bd268a3c7d9ca55b12a03ae18d4322394178d042d0e28c8b7da9b", "Hamsi-256": "415e7fa87a20d942012c9b458507c247498043e09381a165a893e4d22c52246c", "Hamsi-384": "810b536315560820b2a4ebaf4680ba4fd77b577ade9b326029d1d4954b4d203fd9ab519ca8ebf1132369d3926e4e4572", "Hamsi-512": "d7453c84a10eab2d4eef9d8862ced59e0640fe0f3fb088812a8b71ac5ac68953b213492ce3d83415f22c7033573b66e28417da0cb728a18e8914e08140d0948c", # https://github.com/jonelo/jacksum "Luffa-224": "49ac0a3651e0dbf30224e2b0a8b7f24450c8b49f21e6eef9fc7968c3", "Luffa-256": "49ac0a3651e0dbf30224e2b0a8b7f24450c8b49f21e6eef9fc7968c33e25bef7", "Luffa-384": "e67f459e496dfe04a0091a2e2c253e5f48883472dc21dce1d6a0bb0359867fc11815d8e0f868bbfb102f412e24075107", "Luffa-512": "459e2280a7cdb0c721d8d9dbeb9ed339659dc9e7b158e9dd2d328d946cb21474dc9177edfc93602f1aadb31944c795c9b5df859a3dc6132d4f0a4c476aaf797f", # https://hashing.tools/shabal "Shabal-192": "c0629db89b2911e0febaabd7618a5da22ad6ea2638e13d40", "Shabal-224": "0afa60c76a61bcc73773ec8e2694862506f7782a088ce8a30ba5e789", "Shabal-256": "cdee2d6e35a1aa235c09e3d1a94e59207459c8da37cfaed0c2d51fab9a59f932", "Shabal-384": "c08623c184f728d3e35c15bd74a27f0480de3a837f3a14bef7df70edc0e4a9500e100092d3e3f3b464ed18cbc1121bc5", "Shabal-512": "f12f6893f4535d360b07ec15be706e5921b0358d736e61cb2e7ffd2157cd119dc1aeecbf2f1ac73552dc052ad4edcf8cbe87073a4db4d1b4f6a31e39edf5a96d", # https://github.com/aidansteele/sphlib "SHAvite3-224": "12a8401b9f8465ef01201698b66a21d3fb030c995f237da20377bafe", "SHAvite3-256": "eb43e5be6d6cab5d81910dec375120106936879e55e27188735e240144a36a66", "SHAvite3-384": "67e488432df469c810797aaa65c7e6622096c094439fedebba892ccab1547332f9fa506f9ea1ecf6d150a896141eeba6", "SHAvite3-512": "4dbd97835c4e5cfa14799884a7adc96688dd808ff53d5c4cfe7db89a55ee98d0260791ec0c9b5466482ab3f6f236da7e65e1cb6d1ee624f61a5b2b79f63c4120", # https://github.com/aidansteele/sphlib "SIMD-224": "964ff29db5c3d88794c6c488b274d77c52d5ff07509c5aa8d67a14b8", "SIMD-256": "c9deb40282ee7b66a6fc1c8e240ce73aac4252c30b48d247e8d8693ad8ae2e34", "SIMD-384": "f4f9ba9a4e4e870079c69cf61b5e52c39bab522782fda17d093d8757231539f2bfb72c8dbbe36bea8321520f59a2d378", "SIMD-512": "ca493ce78cc2a63b5a48393e61d113d59a930b3e76d062ab58177345c48b59890a08661d04dd6160a1b42d215f1e303d97ab0abb54e65f758f79aee2b182b34b", # -------------------- SHA3 Round1 candidates -------------------- # If the link is dead, use these. # https://web.archive.org/web/20170404095802/http://csrc.nist.gov/groups/ST/hash/sha-3/Round1/submissions_rnd1.html # https://web.archive.org/web/20170211075400/http://csrc.nist.gov/groups/ST/hash/sha-3/Round2/submissions_rnd2.html # https://web.archive.org/web/20170211075442/http://csrc.nist.gov/groups/ST/hash/sha-3/Round3/submissions_rnd3.html # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/Abacus.zip "Abacus-224": "2d4c4d46c6198fc5646345f78011e9c07ebf81a354acf40090dab750", "Abacus-256": "666cb308c69caa6d5043292be664218c9b928957703a04a3be89ffabfbbdb00f", "Abacus-384": "e7ee677abdc304988e76d96f1a44fbf9f71b57f1411456f3bd9459061e124919a5f5cc0d986c73694b639b00c8578a26", "Abacus-512": "0e115823ebeb52ed3d8d0280e1ae1c1204f5b74bc4644fc74a21c53fb4358c233a230f9bc012d8471d4acfde2b3ddb529dc5e39fb1315c45adcd90e9edaa41a3", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/ARIRANGUpdate.zip "ARIRANG-224": "84c24ed54d07dd09f6168c0f9ebfa79a334e7c26de49d5db26b2623f", "ARIRANG-256": "16ac451d0a5af18cad3218e8e6638b46db5637b6221efcc014b3062ade373ed8", "ARIRANG-384": "374f750f62f5d75c1a93cda8fff30cead2a5e1985dc4b7a45c9f6f758b48f3269db90a346412914f0c282608c568b870", "ARIRANG-512": "e9b2975ddea2a06e1a9d18ae9b8c64c78e3351140b2b1ea8c13f6cc59f42771b7d64ac2f53d3308d8cfa952be0f8e99687e2a8e6fb3878c0d88c32cd956d79c8", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/AURORA.zip "AURORA-224": "906d1d3a48807021fe633a69729f8635a91ae00c0f761cc088bfc5c7", "AURORA-224M": "6675d1dc85079a3615322dfa4b7c30a1ac1c954c622cf6fda307f5e3", "AURORA-256": "b8ed2c7f4de46220e9f0b8128a80dec4c610db13c8be6f0037ab67a878e44444", "AURORA-256M": "84e70ab7424da2030b95229895318e371153b20b831860e6453379a079a00f90", "AURORA-384": "9bfb11ee28694460da9cf58edb869c89b6f3bb1e403bcfd820839e5e033de214a06fc7f385f39a985d74e0e25c0ceb8f", "AURORA-512": "ca358e3ebf5b86d933eb29789180d0554b5187acc3aea4d2a9953a56d15b5486c74c210b66af8d71a8d9698d115b71c9915df9e82708fd6667ee3199005f3b7b", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/Blender.zip "Blender-224": "f22751ae660fdf910c58e9e0f5411a1f4c75047be98ee0ec227cf9c9", "Blender-256": "1e57c8b0a331abc5a89a4ac540f69473be495aa4cbfd1970783d4919d12be26c", "Blender-384": "bafe74892a4842b96807fbef086d923ebbe54e3b1d32c366abac2cc2f9a54e97ebf7e6b0826cd402e5854b1ab40baa9b", "Blender-384Spec": "d36e625afb6a617a54e195c138504ac85f6cdf17fb1e32e0d48182a1497bd3e5a7fd8f2fed89288f8c87fc9c23a5d27f", "Blender-512": "a70415a188effe1092f2c3eb164d2f19f4e09ad739eba9aee7b0c5df751f01bf0ecac5a73c278df009ab7f946461a654d4f893a364894d40d9bb728cea737296", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/BOOLE.zip "BOOLE-224": "4e8a0e1652f4ad9e6f9a69d22e68501e4d38152a9beb059a1367781f", "BOOLE-256": "bb6b6b1bc99d2a79ff1c78db3c2933395dde77053ec1a5b48b97ab034534a346", "BOOLE-384": "9acbd9b59a683e378227ebb02f2b9d1dc8141b7d8f583aa1d12fed2f6a577a36e8116c7010a1a4138baec994165bf5c2", "BOOLE-512": "ccbae29b9f0315207475991aaefbd873ebc07721a489dfe1aeaf0447a3808fe01966d3cf9cdb6f24377e8925aac446ddc07d5166b61228ce733371228b72ea4a", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/Cheetah.zip "Cheetah-224": "351e1feb561a84ce5b207a944131e240abda27a23227240790d131b5", "Cheetah-256": "2076a857145dffe17b2b7003f045daea62ab5efd7f9629897e0d912d55ca1b64", "Cheetah-384": "501341c228f121fbb1abd3b682c5b23ba7f06987fecedd3d09ca3c9855f611e64e8c6daf13ee0c5e5ef8f4d04e6751fa", "Cheetah-512": "b6888c9afc8f60cd62252571da8009ae10fa8458778f0fb596deb542fec9e1771171879a4c0e38410e257d119305d201b39c8d4c106c03756ee05e62635c5e7e", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/CHI.zip "CHI-224": "b3a3ba88e77c107ed348e077cdfe67fdd915f7bd5396f4463ac916d3", "CHI-256": "9473b99489da9a8e6b23a1477c287940125caeab3b3eeeab4d2ac2520a9c1049", "CHI-384": "f03857ce38f1b57eb8c23259741913d0728a2be831250df71824c5154cb68970b5c320c3f15904dfb3ff11d2b37dfc5d", "CHI-512": "0da90851211d39c54cc6c1ea0ba550877a28d436a8f8afa6108ee63337aa6af85926d5f9a4e11053493f5c260dda1022fb6fc69a407f2bbff6e004325d459d95", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/DCH.zip "DCH-224": "c41e268388c7a893269d4a0e8c92a1861cacd0abc5a96dacd29ddd01", "DCH-256": "c41e268388c7a893269d4a0e8c92a1861cacd0abc5a96dacd29ddd016f2d28a3", "DCH-384": "c41e268388c7a893269d4a0e8c92a1861cacd0abc5a96dacd29ddd016f2d28a388fd581dccfd6cdcf7bf7b1c252c1d01", "DCH-512": "c41e268388c7a893269d4a0e8c92a1861cacd0abc5a96dacd29ddd016f2d28a388fd581dccfd6cdcf7bf7b1c252c1d01d9a70791e900449340ed3e964aa7f23a", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/DyamicSHA.zip "DynamicSHA-224": "51c03bad3caca3a8c81b1d617c69c2b4ee0e3bab0933e1a63e2f1629", "DynamicSHA-256": "2d068470cc3877ed6b629a75b9b9927cae15068e8a369322c5a2511e7a9193c8", "DynamicSHA-384": "81e6601ae16b2e908c0efbe4313626d56648763319d38413b4f50fbbb712984ae36e8d1ee970c9da04bb8e2d9bba86ad", "DynamicSHA-512": "8f8063e1c54b851a38d9b2733ef2e02d5e4abe4c71d7040a3af7fc605a5bcc6c87450d326d93c3b90a98298aacac3c4d959766fa825d220e06aca799fa607e59", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/DynamicSHA_2.zip "DynamicSHA2-224": "09e20b9f90ac1c0b6253d54bf74c4736cbf81b0b8ce24a014adbb3b4", "DynamicSHA2-256": "fa27eba9050c38f21d9041c04c05b5ca0c26af46d4055dc5fa93aefd30ea0d39", "DynamicSHA2-384": "b02fe0c132d100f470101a3f38c15628ef19324962d536272bf84f93f9a8a9da5c4f397dc4401e661bb0adf35c223327", "DynamicSHA2-512": "48d905ed7644bb9dfe721ce5691543a844eeb53560f5cbb3a877fefab28ff639762bece763b8b3cf4cae870f1b25758877277a3b73657497030e77d1c2b46838", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/ECOH.zip "ECOH-224": "98d9adc87f734194b6f6e0e16df18428964acf76b34a816c1bcf3f06", "ECOH-256": "fc81d47498d9adc87f734194b6f6e0e16df18428964acf76b34a816c1bcf3f06", "ECOH-384": "17f8383d44dd29ae3290fda4c7a6ac2550ce82c3f53f736212d07d99e365d12627a79e3c92fc2eb177065ad884bde1da", "ECOH-512": "a7e9114eac54cc2d2014c5c976849739d66ad7f049fcd4047992f0bed1e7022e864cf12a2581254a44163b64156930632e28fca145c612d551507503734cd89c", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/EDON-R.zip "EDONR-224": "8f2cf96634e6be40f2534a9ab9e5ff18070f2ecd0e2040b2d1ebda4a", "EDONR-256": "42201941c0020a52cca6772d31fefddfc4db45deb64c7b7dc9e8e8f82cd7ad1e", "EDONR-384": "8c2bfcee0c7224d576c8153046d684ce1152c2db137228d3dc0dbe9f14f8ce8c32d7f0e3bb19c6de864865f0a8f2f7a6", "EDONR-512": "7d7bc0c23e810d16916f392738c9f58139bea582096517672963aae6608f3945f8ab1a7c6b32823a07b71dca57694230c88a708476696e220da6ca2a6a20ad56", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/EnRUPT.zip "EnRUPT-224": "18766355483f44a5789b2ed7d1c2cff79ec56913a7a0d83c80be55f4", "EnRUPT-256": "677177abac538e141964d7243ef6ad12ef1963f5471cc768da953340bec91502", "EnRUPT-384": "6b2ce288c98e161e8e41c3f5cb50a60eed3e71510c5f2fdc69e92f32ee6a01b57339c2652c875ff70de62d204e5a0906", "EnRUPT-512": "394803b9596ef8de269b0186f3926f7dbccc393a99bbebe535a0fb7d7ab8469be0efcdf369f8a45dc1edde5dc5e7474d431daed6e0c1f260286eeba6b1b6eb0f", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/ESSENCE.zip "ESSENCE-224": "a7a2791842a881db4abc4f8c0abe8b4c4df56cb7db7130d9b0f39e7c", "ESSENCE-256": "5ac3b1d03e501a31f34c94ff418a1b6612b375635de49a96132ae01885d0f33e", "ESSENCE-384": "1649cca251bacb951c0f69c8e3822eeeb9871537ef3d4d0517fd392e1b966cb7ff48aa14858cff9619ed1d32b2d750e7", "ESSENCE-512": "76b17eb4550e8a544e6a20a4f491d9489f5d875cc87c98ec3bdb9d7cd668628cd71d730ed697db4e9a8dfbf0855a725d14b442fb56b268694d59b5c329cb875f", # https://hashing.tools/fsb "FSB-160": "a25f6e24c6fb67533f0a25233ac5cc09d5793e8a", "FSB-224": "1dd28d92cad63335fcca4c64a5e1133ccaa8c3e6083ad15591280701", "FSB-256": "a0751229aac5aeba6aeb1c0533988302e5084bb11029e7bb0ada7a653491df24", "FSB-384": "4983ecfa3930e3cf61ac4c82695c01a394016b39cf22b5d6dcba447ef8cbcda46ac341ccf5835f331fed0abe73e9bf1c", "FSB-512": "6f87b9dc051330bfb0dd7ad35c05d6a2040e9a6110b06886368934d6ae25694fd9790b1bf1086af9da4b15619609b688fa576376f136adbd3b5a51ae1a1f2158", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/Khichidi-1.zip "Khichidi1-224": "0d0ae72a091bb130665bb1bfcd159c97e2b62eb0ebb56dca336faa9b", "Khichidi1-256": "5bc619118d9a4c4ca0b14504b22ffd68c5f35f760b327fb8d3e10029511617df", "Khichidi1-384": "3bbbddb487b42f0f65092f58717da7cf4c4112eff32e3bbf78530f212f40b799220f4408ac00789ae67432848b9157d1", "Khichidi1-512": "cb516489f37b2d9c26adfd9e99b06e855ac72903fd416dfd0216c5732228e503d961c853b2e80b70bdb6c5178aaf377bb8b755ac8f06bcbc3b31e04849deb59b", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/LANE.zip "Lane-224": "35ff2f82ede8c86f9ba7823a7d2363805ed72d748a5308a1c5242607", "Lane-256": "710ba632ed2581206fab281eb3ac8a4acd0b3cdfb1af8dc1b7a6b1679e9161e0", "Lane-384": "1d326d45b84a8a0db4733b91f68fa46cd16dc5004e28843b72333ffbcf5a528564558d3e4fe3d7723f46f9c242e4c489", "Lane-512": "bb8fcd93dddc1fe2c283dde8dff248ab69d67368146155cbcfa118e4e81a89d2e3455a5252f0cef43b177ec9d9de656b8c1b5fd7d8088241a7a73688ade0fb56", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/Lesamnta.zip "Lesamnta-224": "a99d16d2c059138b050658739b80e6528bb13714f7c6ff54042fa700", "Lesamnta-256": "55082b0519ca42ccf44ff280a8dbb35dfc38d232be64ab0709abb38b98ed963d", "Lesamnta-384": "5b9c850f8a4b648c3b2c3bd3e81bb03364679a52eaecbea95d47264f7dfb5da88d58754b6613e955ca79b9233c01a494", "Lesamnta-512": "4cc281facf12b10b086aa54d5e6293e8491e5cf4434ff1339f778869da59408ac78b59c6773826d66ee6e69bc1fce6036af60ada4b7735eb522cb8553db59e9e", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/LUX.zip "LUX-224": "1675a8d124c3d6b8fd51f586e3b2442ea41030f1b5352aa542fd9cad", "LUX-256": "1675a8d124c3d6b8fd51f586e3b2442ea41030f1b5352aa542fd9cade855521c", "LUX-384": "ef0398a401a0d275e6a6b46a71b376f31eeb66ad2856aa2c5c1537cc8103c06bb83c4b2853a29ffc887b4dbe777987de", "LUX-512": "ef0398a401a0d275e6a6b46a71b376f31eeb66ad2856aa2c5c1537cc8103c06bb83c4b2853a29ffc887b4dbe777987deefc5d80e2634a84987b69cfc3f1aa0d1", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/MCSSHA3.zip "MCSSHA3-224": "35d5ca41238a6dce20356e5f52567ad03bbe21e52e7015f0d1a79f2d", "MCSSHA3-256": "202285d5c2e3a90b4b8a982917a0e50b800757755a3f6c1888d75bf7de9f4872", "MCSSHA3-384": "a4a166d865205ed2fda693451c703d26e1d68edcd66a9cd123d9b1361b95b1504b92c90873cd0d44e657c946501062f2", "MCSSHA3-512": "db83972358b44966e8ab3ee23185d8de95c92aa410516d70646b8b144d9e30cb7990be8a752875263dd1e6d7bdac56616b7206a516f208805b1c6459a94a5f22", # https://www.browserling.com/tools/md6-hash "MD6-128": "7b428f5ec47e0174faf31dc7c89590c6", "MD6-256": "977592608c45c9923340338450fdcccc21a68888e1e6350e133c5186cd9736ee", "MD6-512": "dcba0c6593fbd83a0f5f148588baa79530579c1f5e7f19d500fe282d137bff465106f25c9f0619b4082a730683d5f58311c0c1913068e91b0ebdf9ace3ff5b9e", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/MeshHash.zip "MeshHash-224": "d282dbd98285891d65912cde56bccb031a03187f4b348ec649da9893", "MeshHash-256": "d7478933b34ee26b1c71d049f8eb3b42e72b142b2c63c6ebb14d87eb9c00506f", "MeshHash-384": "e79aadab0a169cfd4386188285fb9f62abeb78f1aaa9409f4074228ff48862b7a709bfb2c09a2045b249e54f9f2374f5", "MeshHash-512": "d479629fe2423dd5ca291731c76f805344d45a3ca5daf7f734d08af8157fcc315968aa9fe10560c57a0a5cef5f3848b4e9ea678f8fd2ce5706226259d76f48b5", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/NaSHA.zip "NaSHA-224": "32040ebcae67fb698ab2037dcb2d42b4b05405173b44b821ff611fcd", "NaSHA-256": "7c97fbe05876c7da8bb5eb8fa9ab45d6457e5796fda873b38b3e5e20bdef07aa", "NaSHA-384": "53ce208a677e8fcfe6c8a42f72e67cf0f9324f7c98651fea2c56a9e16f46fa4a546ec55d3bdd3d65a421e53d0b58b3a2", "NaSHA-512": "90c2bbf5379133e3468497a711d769afb18549d564063baf047802f5830fecec5be33b0d9d688345344c071341a923c721c9cf8e4a7d53c1dc3048f8f9e12aa9", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/SANDstorm.zip "SANDstorm-224": "e2ae7dbf4649c65e0ae19de85b3b4d2565862645146282ec57a64ef4", "SANDstorm-256": "607ac39b7cd6c1912e7934cdbbfceeea1fe5e8155a9726b36c96895c93e6056d", "SANDstorm-384": "e0fb1925617466e876152d6c632b4751829ee61036d1bccbe0d481ea5b0eab7f0a020c7cb9423cd7382a18b738609e5d", "SANDstorm-512": "c50794660a3672279018ec7ab26559bdea6565e191ba892ef4cc579c96fb33804255ad18a4b2f6604b34d5e2a4ca5d4dd1fc6fee882727ac54f6273706ea9ff6", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/Sarmal.zip "Sarmal-224": "5f208b2ba6bc33ebe5328ee21b604d31f18f06c511072decd5b55de7", "Sarmal-256": "0905b8e53840dcae98f19f7686cda237fc9e9eafa2ba3972fc057ada111daa59", "Sarmal-384": "f35e366df171e039a21b5554d8b8362272db97a927f87a881cf909106144215e337c714849a4d04a6f44d9bbd0aeea29", "Sarmal-512": "184b3f2413d62a802a653ac225595251b6db64802bb5728563f363595a0b4caaae56c1cc56c083dd4ef9cae016e9ffa44aac3006b67466e3018cc135ec97b61e", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/sgail.zip "Sgail-224": "b4d3cf522c909a4934bea1e08867a78121faa14fadb6b6652066e232", "Sgail-256": "b4d3cf522c909a4934bea1e08867a78121faa14fadb6b6652066e23274b5ffb2", "Sgail-384": "b4d3cf522c909a4934bea1e08867a78121faa14fadb6b6652066e23274b5ffb2b379c5cdba636a3e9c604d395b2b4c97", "Sgail-512": "b4d3cf522c909a4934bea1e08867a78121faa14fadb6b6652066e23274b5ffb2b379c5cdba636a3e9c604d395b2b4c97c68524b005dd53ebae00556630f35a6f", "Sgail-768": "9433dfc30b18390c21823cb0a47b615a57a5b8e283d6aec98b516656b2c986ae59d2bf74fd0b4d35b4f031d47512ce397e9739da43c127a793824b17700388fc" \ "b8346293f8052146f5ba35de4c22898ad945790c1a4f9f3ab4cb5f0e22b4acf6", "Sgail-1024": "9433dfc30b18390c21823cb0a47b615a57a5b8e283d6aec98b516656b2c986ae59d2bf74fd0b4d35b4f031d47512ce397e9739da43c127a793824b17700388fc" \ "b8346293f8052146f5ba35de4c22898ad945790c1a4f9f3ab4cb5f0e22b4acf6b9b6310fba3c3e7d1cd92e28829027d719c8de200d259e72de636f412e0804b2", "Sgail-1536": "103c7e0f4e3b8e1adfdfe2be45d19abe02cd115028d403427efd3af2d13f58e832af09f5bf60ba48716620cc62a4642a78965840b40df659e5cdfc6779458cfe" \ "8ebc3d37baa144d6d5548d76e952a3c8422566d1b2391bb886e4fb3c3321b141ec7adcc874b0945896dc838b7598eab99484c35fc3be4a690a030db275d32688" \ "b64741949fd0bbb48db227422751a8497862bf09335d8a80935d5a0660f292eda0f449c5cf6e130183e0c7e98e935c4b614e6460505b3bacdd3772ae360d2a24", "Sgail-2048": "103c7e0f4e3b8e1adfdfe2be45d19abe02cd115028d403427efd3af2d13f58e832af09f5bf60ba48716620cc62a4642a78965840b40df659e5cdfc6779458cfe" \ "8ebc3d37baa144d6d5548d76e952a3c8422566d1b2391bb886e4fb3c3321b141ec7adcc874b0945896dc838b7598eab99484c35fc3be4a690a030db275d32688" \ "b64741949fd0bbb48db227422751a8497862bf09335d8a80935d5a0660f292eda0f449c5cf6e130183e0c7e98e935c4b614e6460505b3bacdd3772ae360d2a24" \ "c391b1835e8d9d460b582597ba141143fd1c8a0705cf8cc91ccbcaa325013d8adb9e576efc15a37e9005c6c523f9aedaedb6dc33d01dd71274945ef514b212bf", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/SHAMATA.zip "SHAMATA-224": "c48da545eedb1b8d4ac6c92386ad8c1a5e14bca851634c9e7d59813b", "SHAMATA-256": "8eec361426176970f3ca307e298c985487466868cedbb981571158d6d1509a6b", "SHAMATA-384": "d866c0438ebd8dfe792008c5826a9534c43368809b3da8a7fceb3c77ac37c60ee3f5278b2b043f31c0caa6d5a9c17906", "SHAMATA-512": "fd3c751fd59dcd6fe2b381cf325bb11ec6a9172c4e1592df06bdd8ede7dbc182281b87e1e5fbe25753c098ca49c9997e50f88012c3cb0106f5566138a0b2e8bd", # https://csrc.nist.rip/groups/ST/hash/sha-3/Round1/documents/Spectral_Hash.zip "SpectralHash-128": "457b59f8e79683508275d50c4a8de4d4", "SpectralHash-160": "4051d7767e1abcb592d20c09db968639519e32ac", "SpectralHash-192": "4068ebb99fc0dad936e44a90b009a6e674ae9cd219e395da", "SpectralHash-224": "20347d739d75c076bb09b7112b22b4422a6f33b15eae6482cf8e2bba", "SpectralHash-256": "201a3eb9c76eb403bad609bdc421591ad8922c9bd55d94be5719902cf8714b72", "SpectralHash-288": "900d0dd69c79bdb400eebae04dee1085725acc24c2c9beaaf629764b8e660266e1d296b2", "SpectralHash-320": "900c86e5d38f9beda801ddba70137b84217732d3309312c8c7d55ee29bb25e1cc68098ae8f492d66", "SpectralHash-352": "9092ca5baad3e3e6fb6a00b9d5d38094f706185dce2d31844c4b238fb55d714dd92f4398d0132ae47d2a5b66", "SpectralHash-384": "9092e52dd569f1f9bf6d5005cf57470154fbc18b04ee7168c6488c4b10e3eda56b8b3bd917a1e69a0132ae47e95276ca", "SpectralHash-416": "4849729bbaae9f1fe6f6ed500373d4e8f40aa7de862c13bce368c332238ab10e2f6d2b6e1677990bd0f9a3405195b91f92a4ecca", "SpectralHash-448": "4849714dddabd3e3ee6db732a006e7ace97a02a9f7a18b047bcf1b530cc88e1ab12795eda56d6166bcc85e51f3432814669b88fa4a49d992", "SpectralHash-480": "48222716777557d378fb9b6dcca801dcfa674bd01527ef6185811ef3e1b5306646386ac49f2bd5a566b0b35e642faa3e6865028ca9b947e92c93b192", "SpectralHash-512": "4822278b3bba55fa6f1f39b36eb95003b9f46745e88549fbd86160436f3f0da4c19918e86ac44f457ab4acdb0b32f3217d2c7cd06240a3251b923fa4b9276322", # -------------------- relatively long -------------------- # https://hashing.tools/ascon "Ascon-Hash": "3375fb43372c49cbd48ac5bb6774e7cf5702f537b2cf854628edae1bd280059e", "Ascon-HashA": "f40d49f17c5b7efc22ef8a623c4117f532a94c60faff439d5b5f02a31e8933c6", "Ascon-Xof": "c100696bd70a3e731873bdc8a76ffb53b6cca80b694473b320d436883bbbc300dd5abfebcfdfdee1a6671a51f181543c3933b533e7e132e186bb557515b898cb" \ "d92d86ce999d979f25face87e00e9eea869a328f12537b8be359ec3a5064b4f03b95a1e4ecb85763fad1b29fed3415831602313f0687eef75d26aa56c3a03aca", "Ascon-XofA": "5c32bbe73bd8ea9191435d72cc973a2bb2d8f40410de6188e06c65b78401759c30a3f1b24ea251b12d468d729aad6570883b82438e798020d0ebb3e920490629" \ "051c64fe2ddb2ff14b89e9d2352cfda9bbffe4601d0e5bffb6c4b8165c44172cc0dc76430d7a38fbb5b57134936c53648989c49e79052fb1040a8b0a7f43e0ad", # https://hashing.tools/belt "BelT Hash": "b0333d1bb3c391893a5a1df907eaf2b5cc60e993bd4fa0cdd865d09a243183cd", # https://github.com/jonelo/jacksum "BLAKE2sp": "cf192976714bb648e72b29fa90e6bf0fbc5bf2efe7d5c26ed8ff34e855368691", "BLAKE2bp": "f10e0523631699102c63412c0701fa19f6550fbac0e9c035803c6033b50465222bb92ee0af0dad53edca32f0e08a72c077a6cafc6f4d24a7fb649079d47ce089", # https://emn178.github.io/online-tools/blake3/ "BLAKE3-128": "2f1514181aadccd913abd94cfa592701", "BLAKE3-256": "2f1514181aadccd913abd94cfa592701a5686ab23f8df1dff1b74710febc6d4a", "BLAKE3-512": "2f1514181aadccd913abd94cfa592701a5686ab23f8df1dff1b74710febc6d4ac0615cd845be939b4ef6aec25e799aaa450c63f8d9e333cdb0dd79b70ee69879", # https://github.com/Kimundi/ed2k "ED2K-Blue": "1bee69a46ba811185c194762abaeae90", "ED2K-Red": "1bee69a46ba811185c194762abaeae90", "ED2K-RedBlue": "1bee69a46ba811185c194762abaeae901bee69a46ba811185c194762abaeae90", # https://github.com/jonelo/jacksum "ESCH-256": "d43f87a0fe60fc5925064880c6116c136b6d94fa24a93dffcb35d178c3af932c", "ESCH-384": "0a8167a616fc8dbca06b877427c15fd27b7db3430f86794742394a5db6838d916843d739ac821c6b0b7df538aa4554ea", # https://fnvhash.github.io/fnv-calculator-online/ "FNV1-32": "e9c86c6e", "FNV1-64": "a8b2f3117de37ace", "FNV1-128": "185adb693e7c97844ecfa9497cb529b6", "FNV1-256": "5484a6fda3cdc44b211d89bcc7aba23d3db53b8d4a1c4f181452bea38d5e407e", "FNV1-512": "116c3e885f0cddf9a9c30a3f45515050f0bcc29201becf78ddd2cebfc099cbc8db9fbebdc5b4f08169eb705d8fbf92b2b7b9121d2991b91374b428901c2965ca", "FNV1-1024": "3a4d50796fdfef4453da11fd40f3e795c7335d9c1a76f44ec522a26cf89030ab56d73d675114a4d0448067000000002c78c4ffbd2756340f2832a6ab43a50f84" \ "596efc9c7ce1ea25ed87cfae6f9618346680553973accf6176f84a30253b45f8bfbd52c3e21ab215feaebe3dccd33670146f723b0bc667774d094a5e8ed2a36c", "FNV1a-32": "048fff90", "FNV1a-64": "f3f9b7f5e7e47110", "FNV1a-128": "68cce4cd885ea04239f02af30e297870", "FNV1a-256": "de8de01f19056b20fd89451c1046c67801f99f71264e4fff078e67f490022ab0", "FNV1a-512": "cd74f564c0520cbd816ee4a6a9efebce4865dd1c4152d79ecbb3acbb2e55d860bc17f0b55ae4686537b02bced854f29c3cc8785e72d030249267163b61128df8", "FNV1a-1024": "0480a708ee10a2217801aad08aa2c906aca079f6094db1580606e97a3f6e984598ccdba1e41d93a0ed3a40000000002c78c4ffbd2756340f2832a6ab43a50f84" \ "596efc9c7ce1ea25ed87cfae6f9618346863feabd80d9e025589dc2c79905a9f4a72e8adca100c3d2adf5090e1da90932a039a0112125dc44de5ba7fe1234040", # https://github.com/jonelo/jacksum "FNV0-32": "89074759", "FNV0-64": "1f7224b4c8a7bbb9", "FNV0-128": "517098213d50cd2fedca83d32b0c6e31", "FNV0-256": "1a723f286142d2b2969e1fef7bb017bede69fd112d7d28d967152bbe7feb60a9", "FNV0-512": "a6ace7075d8d3c8af92b4dcf579326ed4746b569286e16a8b960f80ec899f597dca8d79d4a538e6a92973bf13004bcae60b9b34435b916d6fc33a01fd246516d", "FNV0-1024": "c871a1311e02a022b212e0617ffa4e2095d93af593a4ddf83a96b4e0bec00845f41e576c181ff9f385eba7000000000000000000000000000000000000000000" \ "000000000000000000000000000000000001f7ee241391f3aa67eb21b33f526d0461ca0d711dea56c28ef23c9d2ae40acc520b2e03defb70f19bcbc6d8518dab", # self "FNV0a-32": "0f75511b", "FNV0a-64": "16b01830f503fb5b", "FNV0a-128": "08b63d54a36c79f9983034d5f84b964b", "FNV0a-256": "29966e65f4d5ec26c8a6f2188530edae6cf9eed2148fa579f25ba92b63670a5b", "FNV0a-512": "42d98f9b069cd7e71d3af9e52d255b8a9f8f563698805c1060ec5bcec6480e78a638e3be95f3d0ca68a150275658cda398cf3063fafd9a0be92b8aa2bc37190b", "FNV0a-1024": "ebc8ecd5f1fd778166986437d8f535f97fcac598859e7690069a8b5421d8027f720903930562657effffa6000000000000000000000000000000000000000000" \ "00000000000000000000000000000000030d7c4df25956df4327a342f932d30fcba656d86b64688bb7ada7ffbb83a4bedb3b565c00cbee26ae9b035d7678b22f", # https://github.com/jonelo/jacksum "FORK-256": "290f4a3bc99dd6edc87400af4d4daa10362b0fea41d7cd41710f4e9fe0964428", # https://github.com/jonelo/jacksum "GOST": # codespell:ignore "77b7fa410c9ac58a25f49bca7d0468c9296529315eaca76bd1a10f376d1f4294", "GOST94cp": "9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76", # https://gchq.github.io/CyberChef/ "HAS-160": "abe2b8c711f9e8579aa8eb40757a27b4ef14a7ea", # https://www.webutils.pl/index.php?idx=haval "HAVAL-128,3": "713502673d67e5fa557629a71d331945", "HAVAL-128,4": "6eece560a2e8d6b919e81fe91b0e7156", "HAVAL-128,5": "696f02111f2e1da5c21d50eb782b7e8f", "HAVAL-160,3": "b338ac397e8bccadcccd96549cadd4882d834107", "HAVAL-160,4": "6e739d01f5739ceed94da1a115b52d5951280560", "HAVAL-160,5": "ecce9fa8a428866304ff082af2f9062637d36b23", "HAVAL-192,3": "58e6ced002e311172483d434ba738ad033e7fa950e431503", "HAVAL-192,4": "228ee09bc7e36151c6f285f558e6aede66ad38c8341592b9", "HAVAL-192,5": "023d045f75d4bf051fd6e50f7b7417bf9949c4b5d2b4b7ef", "HAVAL-224,3": "e1d5792306f56b22419662b06d1885a66dca3eba01f53274c89aeaeb", "HAVAL-224,4": "dddd6689885f6db4ad91e35a35e1f4498446510df798d4fd54b8654f", "HAVAL-224,5": "03d953298c8e56b46385c6761cd4b2e377889a75c97eaea475421c73", "HAVAL-256,3": "9446028f42b3768a41bd873ca69b0c006341d986613567f39eb61f96ca683300", "HAVAL-256,4": "c0d4c6ea514105fd1a9c38a238553fb7fa21d4127eb1a3035a75ce9d06a83d96", "HAVAL-256,5": "b89c551cdfe2e06dbd4cea2be1bc7d557416c58ebb4d07cbc94e49f710c55be4", # https://marekknapek.github.io/hash/ "KangarooTwelve128-128": "b4f249b4f77c58df170aa4d1723db112", "KangarooTwelve128-256": "b4f249b4f77c58df170aa4d1723db1127d82f1d98d25ddda561ada459cd11a48", "KangarooTwelve128-512": "b4f249b4f77c58df170aa4d1723db1127d82f1d98d25ddda561ada459cd11a489242e112dbfb1f99a1de1d7e830d457778a66d1dc2aa44d61a1da91655122fb7", "KangarooTwelve256-128": "1848a4799bb4f5ca08ad8b1992fc9077", "KangarooTwelve256-256": "1848a4799bb4f5ca08ad8b1992fc9077998b4ad3f1f986d5c10da59de9f23e75", "KangarooTwelve256-512": "1848a4799bb4f5ca08ad8b1992fc9077998b4ad3f1f986d5c10da59de9f23e755c21f5e72959d76ff3b65e2347769d67393914bca74f42069062774e3776715c", # https://emn178.github.io/online-tools/kmac128/ "KMAC128-128": "b12d497a01f3b5c9faf89eab268ee7b0", "KMAC128-256": "d26ce5ceb3a1bccd03e454835b4b611aa0d5eba4c9940d05fae3deee7a7206b7", "KMAC128-512": "ba68f09ebe1332dfb1eedde4d47ddf9835bbb9723f429ec815b8e7d63c48828272cc9a16749b14f20f3e8ab865d8a5d82932947d53c01ccdd8d8c032b9fd1873", # https://emn178.github.io/online-tools/kmac256/ "KMAC256-128": "ab61345a6ca76d9f54f46b75acb150c4", "KMAC256-256": "19a6c774bfee80202736556b7aa0474e79bb1df95c62674c6e1246dc036c821e", "KMAC256-512": "c212695c45a612147c87e14d70890dd154af5704bbbe6e6f78aa6c08a1560eeccb1c9450a7ab0398625c7bd699557bc5c487a66d92a422626041aaa4a77a34e0", # https://github.com/jonelo/jacksum "Kupyna-256": "996899f2d7422ceaf552475036b2dc120607eff538abf2b8dff471a98a4740c6", "Kupyna-384": "0956d8afa9653b5231614decb1cceb8162ae5b8ff2dc3b02417f86dc4df621d0ca5b1ff399d494766c93a6d2513cae3a", "Kupyna-512": "d1b469f43e0963735b6cd08a6e75fc370956d8afa9653b5231614decb1cceb8162ae5b8ff2dc3b02417f86dc4df621d0ca5b1ff399d494766c93a6d2513cae3a", # https://github.com/jonelo/jacksum "LSH256-224": "6375aab1e1a1a446aa8d55a3c3f03e85edb96ab886649a34d1f1e876", "LSH256-256": "f8025b61eb10d80a7f03ccfb906222a0645bb175fdeee9595f223936edbf7070", "LSH512-224": "3b7d9fc1a1356755abefb5c4c24543068f0cd8d71b57129e8fb53dda", "LSH512-256": "5e4ebe2017e84f35420bda7486ebbd791e0ece579cc18e49341b9a526466e633", "LSH512-384": "f7c6f97cd902658ab17ba5696aa7bf79d49d36b46aeb9a8a563917a37459c8e28fe299cc8821d76fe6b94dfd5cfc8bc2", "LSH512-512": "bc0a2b9a0c99bdf2c8a83418c4bef13791c97cef25bd2be8fadbbb0f0807c44163085bde435cf7d41db0104dc87eb5cd47cf21698683375647bff65e2ef51e51", # https://github.com/jonelo/jacksum "MarsupilamiFourteen": "3611bcaa666347770dbffd4562f137c5adfe2e09f3c4268ef7c7d7c0e6c5d59c21fa67c4cfdba29e449c944b1a16c4583f2be8a75fb4f7649df6b98698708ecf", # https://marekknapek.github.io/hash/ "MD2": "03d85a0d629d2c442e987525319fc471", # https://marekknapek.github.io/hash/ "MD4": "1bee69a46ba811185c194762abaeae90", # https://github.com/jonelo/jacksum "MDC-2": "000ed54e093d61679aefbeae05bfe33a", # https://www.browserling.com/tools/ntlm-hash "NTLM hash": "4e6a076ae1b04a815fa6332f69e2e231", # https://github.com/jonelo/jacksum "Panama": "5f5ca355b90ac622b0aa7e654ef5f27e9e75111415b48b8afe3add1c6b89cba1", # https://github.com/damaki/ksum "ParallelHash-128": # ./bin/ksum --parallelhash128 --output-size=32 --block-size=8 - "a6eb1bcdfd9531a193e65ea9c58a1902fbb51ea575e482ef9096049055db520f", "ParallelHashXOF-128": # ./bin/ksum --parallelhash128 --output-size=32 --block-size=8 --xof - "762ca8cf752e88ebaf69c5b9c24728b0f63c95a2238cb9196998d282140d2989", "ParallelHash-256": # ./bin/ksum --parallelhash256 --output-size=64 --block-size=8 - "3f975d80fce91ea04f39e66052c6d35fc5bc8222c124063cbdb1328ea584c863bfbd4a41f667dfaf491564d244f1b1fce9c50767555655b3120ca253df50cda0", "ParallelHashXOF-256": # ./bin/ksum --parallelhash256 --output-size=64 --block-size=8 --xof - "b9fa18ce16f8b6a4da80deee19e3fb24b51cf4e5c3087a79faf7764a5428faa785b9602e569f011dc1f557dc969529d513c166a36bcccc082b0692cd14f515af", # https://github.com/jonelo/jacksum "PhotonBeetle": "5ced20c8d747c62114bf691739821516135aa8413997cf34b4b8e40a25489762", # https://github.com/jonelo/jacksum "RadioGatun-32": "191589005fec1f2a248f96a16e9553bf38d0aee1648ffa036655ce29c2e229ae", "RadioGatun-64": "6219fb8dad92ebe5b2f7d18318f8da13cecbf13289d79f5abf4d253c6904c807", # https://www.webutils.pl/index.php?idx=ripemd "RIPEMD-128": "3fa9b57f053c053fbe2735b2380db596", "RIPEMD-160": "37f332f68db77bd9d7edd4969571ad671cf9dd3b", "RIPEMD-256": "c3b0c2f764ac6d576a6c430fb61a6f2255b4fa833e094b1ba8c1e29b6353036f", "RIPEMD-320": "e7660e67549435c62141e51c9ab1dcc3b1ee9f65c0b3e561ae8f58c5dba3d21997781cd1cc6fbc34", # https://marekknapek.github.io/hash/ "SHA-0": "b03b401ba92d77666221e843feebf8c561cea5f7", # https://md5hashing.net/hash/snefru "Snefru-128": "59d9539d0dd96d635b5bdbd1395bb86c", # https://md5hashing.net/hash/snefru256 "Snefru-256": "674caa75f9d8fd2089856b95e93a4fb42fa6c8702f8980e11d97a142d76cb358", # https://asecuritysite.com/hash/gost # codespell:ignore "Streebog-256": "3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4", "Streebog-512": "d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe", # https://www.webutils.pl/index.php?idx=tiger "TIGER-128,3": "6d12a41e72e644f017b6f0e2f7b44c62", "TIGER-160,3": "6d12a41e72e644f017b6f0e2f7b44c6285f06dd5", "TIGER-192,3": "6d12a41e72e644f017b6f0e2f7b44c6285f06dd5d2c5b075", "TIGER-128,4": "c1f3a704e9f6267e9f75fa47191f83c3", "TIGER-160,4": "c1f3a704e9f6267e9f75fa47191f83c354100a04", "TIGER-192,4": "c1f3a704e9f6267e9f75fa47191f83c354100a04c4f1dc6f", # https://marekknapek.github.io/hash/ "TIGER2-128,3": "976abff8062a2e9dcea3a1ace966ed9c", "TIGER2-160,3": "976abff8062a2e9dcea3a1ace966ed9c19cb8555", "TIGER2-192,3": "976abff8062a2e9dcea3a1ace966ed9c19cb85558b4976d8", # pycryptodome "TupleHash128-128": "b0b09f20d5e98972f1f8272e700cf912", "TupleHash128-256": "1f223cc030a7f9e63d5207e191660e8d71d0773a50178bd63646a8767555bfdd", "TupleHash128-512": "fa5a91dd0ef12c48f23e035a3ed51502a4ade0524a30d834d28201535a111952c4b56dfd30937577dd955fbdd4128d277c0e3f364bd81d5a727abc5c3cc78fbf", "TupleHash256-128": "e20429d277603b218025dcba37c230e4", "TupleHash256-256": "ee14da8f09fd9a3dfd91ee0b84f75f96d57b979b934981b2cd631522e7279a3a", "TupleHash256-512": "9cd329b4ac886b3aab5c09e19dbd5368c33dbc9a48e57e918873e8d95096d43b390bd805d5b6be5d75e0549908861f5bcaf1d0857818eb9b3b2bc6249a333b5f", # https://marekknapek.github.io/hash/ "TurboSHAKE128-128": "76a1720a4848ab64e67e563f16b8c5aa", "TurboSHAKE128-256": "76a1720a4848ab64e67e563f16b8c5aa492b698a4d93429735fd02354657fbf7", "TurboSHAKE128-512": "76a1720a4848ab64e67e563f16b8c5aa492b698a4d93429735fd02354657fbf7a0689ec77b4c795fda9daab410c63092f54200846c34120ff2b253e9fd8d9fc4", "TurboSHAKE256-128": "b6e91a412c262c7936b069f67bd21c2f", "TurboSHAKE256-256": "b6e91a412c262c7936b069f67bd21c2f8ecc48bda8dc6eebfbaf6fcaa82191c3", "TurboSHAKE256-512": "b6e91a412c262c7936b069f67bd21c2f8ecc48bda8dc6eebfbaf6fcaa82191c3974462707ab2a5c5d704b0e874860a2a3fddb588f507c9b4f0417e2b66316090", # https://github.com/jonelo/jacksum "VSH-1024": "45f3882692a07aa2fd6c76815ac5f784453e09297a4c9374fb3b6a647b6569f8951c519676f89a7d7bb7f44faa025bea88900d3efcc3a4f739a748ac93f66c1f" \ "6391daf3daa5e73ae1aaef031b87f11ecd5b778f884cbe397a57ad61fc039981b7cea94843be90fab35c4b92e274343dc9b1b4d24bec6154b416b9597ad52bbe", # https://asecuritysite.com/javascript/js04 "Whirlpool-0": "4f8f5cb531e3d49a61cf417cd133792ccfa501fd8da53ee368fed20e5fe0248c3a0b64f98a6533cee1da614c3a8ddec791ff05fee6d971d57c1348320f4eb42d", "Whirlpool-T": "3ccf8252d8bbb258460d9aa999c06ee38e67cb546cffcf48e91f700f6fc7c183ac8cc3d3096dd30a35b01f4620a1e3a20d79cd5168544d9e1b7cdf49970e87f1", "Whirlpool": "b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35", # https://github.com/jonelo/jacksum "Xoodyak": "087376b970c53ed0339a4fe54f4462f0f34e4e50ed09b4314ed24b32ba9822cb", # -------------------- relatively short -------------------- # https://asecuritysite.com/hash/smh "CityHash-32": "a339c810", "CityHash-64": "c268724928feca7d", "CityHash-128": "a7f9a86a2d60c968bf1498f876dbe279", # https://asecuritysite.com/hash/smh # fp: fingerprint "FarmHash-32 (fp)": "ec998320", "FarmHash-64 (fp)": "abbe83f33b1b5134", "FarmHash-128 (fp)": "bf1498f876dbe279a7f9a86a2d60c968", # https://github.com/ztanml/fast-hash "FastHash-32": "136bd7e4", "FastHash-64": "4611ffb633a627d2", # https://github.com/ogxd/gxhash "GxHash-32": "0bc03dd6", "GxHash-64": "0bc03dd6b35d0186", "GxHash-128": "0bc03dd6b35d01867892d3b59509300d", # https://pypi.org/project/siphash-cffi/ "HalfSipHash-32_2_4": "ed285d61", "HalfSipHash-64_2_4": "31f20e6c986fe414", # https://asecuritysite.com/hash/smh_Halftime "HalfTimeHash-64": "45bf6c18f4c47ad1", "HalfTimeHash-128": "08f19709c03904d6", "HalfTimeHash-256": "4761c86ed217c2e0", "HalfTimeHash-512": "8dc86dd14f852fe8", # https://asecuritysite.com/hash/smh "HighwayHash-64": "552d6e674e35333e", "HighwayHash-128": "d59d55e677071404dcded33a97cfee4b", "HighwayHash-256": "40e0a9717f9dee85a7c86aadee4e2bd884656a3eec42a8172d340faa3cb127de", # https://github.com/avaneev/komihash "KomiHash": "4d86bedb30f8641c", # https://asecuritysite.com/hash/smh "MetroHash-64": "37b871151974389c", # need byte swap "MetroHash-128": "97d78a67ac6c62e9870198793485d405", # need byte swap # https://asecuritysite.com/hash/smh "Murmur1": "851e251a", # need int->hex (LE) # https://www.ciphertools.org/tools/murmur2/text "Murmur2": "d0292721", # need int->hex (LE) "Murmur2a": "e5b5e153", # need int->hex (LE) "Murmur64a": "1b862a0433ca8955", # need int->hex(LE) "Murmur64b": "51b7c28fccd78d75", # need int->hex(LE) # https://murmurhash.shorelabs.com/ "Murmur3a": "23f74f2e", # need int->hex (LE) "Murmur3c": "c383152f" "672ceeec" "6cf67b5d" "2c1de9e5", # need 4-byte swap "Murmur3f": "6c1b07bc7bbc4be3" "47939ac4a93c437a", # need 8-byte swap # https://pypi.org/project/siphash-cffi/ "SipHash-64_2_4": "0de4702506520059", "SipHash-64_1_3": "1e450cd0d376f68d", "SipHash-64_4_8": "ed013f3fab3d1abd", "SipHash-128_2_4": "df8c5ce876c57f25c03f1bb5df591ab2", "SipHash-128_4_8": "23e0a6e8aae3f2e571ba3536bfbea2ff", # https://asecuritysite.com/hash/smh "SpookyHash-32": "c79306aa", # need byte swap "SpookyHash-64": "c79306aa46e8122b", # need byte swap "SpookyHash-128": "c79306aa46e8122b1b340724747e361d", # https://asecuritysite.com/hash/smh_t1ha "T1HA0-32": "d0e6c0a9", "T1HA1-64": "86235f2773f9ada1", "T1HA2-64": "1f1d052e973ff69d", "T1HA2-128": "5891d221cdf479758dd36078748f9731", # https://asecuritysite.com/hash/smh "WYHash-32": "88ca02ad", "WYHash-64": "d986947fb5be3867", # https://www.coderstool.com/xxh-hash-generator "xxHash-32": "e85ea4de", "xxHash-64": "0b242d361fda71bc", "xxHash3-64": "ce7d19a5418fb365", "xxHash3-128": "ddd650205ca3e7fa24a1cc2e3a8a7651", # -------------------- checksum -------------------- # https://www.partow.net/programming/hashfunctions/ (C implementation) "RS Hash": "29a4500b", # https://www.partow.net/programming/hashfunctions/ (C implementation) "JS Hash": "dfeffe38", # https://www.partow.net/programming/hashfunctions/ (C implementation) "PJW Hash": "04280c57", # https://www.partow.net/programming/hashfunctions/ (C implementation) "ELF Hash": "04280c57", # https://www.partow.net/programming/hashfunctions/ (C implementation) "BKDR Hash": "c5181667", # https://www.partow.net/programming/hashfunctions/ (C implementation) "SDBM Hash": "8ca77173", # https://www.partow.net/programming/hashfunctions/ (C implementation) "DJB2": "34cc38de", # https://www.partow.net/programming/hashfunctions/ (C implementation) "DEK Hash": "ea0e6658", # https://www.partow.net/programming/hashfunctions/ (C implementation) "AP Hash": "a18caec3", # https://md5calc.com/hash/joaat/ "JOAAT": "519e91f5", # https://md5calc.com/hash/adler32/ "Adler32": "5bdc0fda", # https://github.com/mjethani/superfasthash "SuperFastHash": "e37cbf05", # https://github.com/silvasur/buzhash "BuzHash": "69ef5bad", # https://metacpan.org/release/MOOLI/Algorithm-Nhash-0.002/source/lib/Algorithm/Nhash.pm "NHash": "03f831", # -------------------- MD5/SHA1/SHA256 n-times -------------------- # self "MD5 x2 (raw)": "a5f6bc8c547364db2a98ccb9386ea241", "MD5 x3 (raw)": "7b99681999eccbd5f4cdbd87d3335691", "MD5 x4 (raw)": "842b4b942deaef9cb727bd067b9a20d1", "MD5 x5 (raw)": "55522480f27438c9b1afe39ee24dda1c", "MD5 x2 (hex)": "883c631dbcaca4373e1428a73c6cb19d", "MD5 x3 (hex)": "4381d02b989be5b7812e4d15c7d02b27", "MD5 x4 (hex)": "0903fc4520b46c84e0eb563036941e15", "MD5 x5 (hex)": "c9681bee6c4ab3bd0ea4b03e27341be6", "SHA1 x2 (raw)": "a4e4d26fd0c6455e23e2187c3aabe844332aa1b3", "SHA1 x3 (raw)": "ae3924f937127c28eb67b3c287416da8f7222b09", "SHA1 x4 (raw)": "22ce3d415294049921a5973b14cf86561d6a1020", "SHA1 x5 (raw)": "5410a01a85a03f60921eb5ae6fcf8bf219916385", "SHA1 x2 (hex)": "efc4fe664cbe7cec1a06f73305a414b55d7034d3", "SHA1 x3 (hex)": "ff5b671f805069f47c0446296f9043d27123d52c", "SHA1 x4 (hex)": "959bb6a982ae22b8dc7476b6a5099df4d449255e", "SHA1 x5 (hex)": "58464ba20599b425cbf762947a512ffeef6433da", "SHA-256 x2 (raw)": "6d37795021e544d82b41850edf7aabab9a0ebe274e54a519840c4666f35b3937", "SHA-256 x3 (raw)": "c9280b1eecf03730cd24fbf25fbb482e0efd423c1d8824f54056cf8390fdf445", "SHA-256 x4 (raw)": "0059752a917970d20f26f075a203df21a43416a9336fc1a40a7d74cb995898a0", "SHA-256 x5 (raw)": "03d5962dde4a1fc73e8351e3a659deeebd4b580649d344b8290473ecd5f47bde", "SHA-256 x2 (hex)": "d5074362d20d3c33a1a3e7235632271c276818ff686e25196cb6d89d98363f43", "SHA-256 x3 (hex)": "cb990af233d0d01de6f42683086b99b13332f26dc5e8d98991a97659be090b58", "SHA-256 x4 (hex)": "506d96a7ee221c2881cd0e73d7f9c8923ce26cfa189ffaf447807c3b8519bcf9", "SHA-256 x5 (hex)": "fb26873649b20d04274ebc569a8b6a06a5d80fcae6025b9631ecb25bc5f22a7c", } def hash_check_one(self, hname, h): bit = len(h) * 4 byte = bit // 8 expected = self.test_vectors.get(hname, None) if expected is None: self.err_add_out("{:26s}:[{:4d}b/{:3d}B] {:s}".format(hname, bit, byte, "Not found")) return if expected != h: self.err_add_out("{:26s}:[{:4d}b/{:3d}B] {:s}".format(hname, bit, byte, "Failed")) return if not self.args.smart: self.info_add_out("{:26s}:[{:4d}b/{:3d}B] {:s}".format(hname, bit, byte, "Success")) return def hash_test(self): value = b"The quick brown fox jumps over the lazy dog" self.out.append(titlify("Hash({!r})".format(value))) for elem in self.get_valid_hash_funcs(): if isinstance(elem, str): self.out.append(titlify(elem)) continue hname, hfunc = elem if not self.should_be_displayed(hname, hfunc): continue hfunc.update(value) h = hfunc.hexdigest() self.hash_check_one(hname, h) return def hash_test_time(self): value = b"A" * self.args.size category = None result = [] tqdm = GefUtil.get_tqdm() hash_funcs = list(self.get_valid_hash_funcs()) pbar = tqdm(hash_funcs, leave=False, total=len(hash_funcs)) for i, elem in enumerate(pbar): if isinstance(elem, str): category = elem continue hname, hfunc = elem if not self.should_be_displayed(hname, hfunc): continue try: pbar.set_description(hname) except Exception: pass start_time_real = time.perf_counter() hfunc.update(value) h = hfunc.hexdigest() end_time_real = time.perf_counter() bit = len(h) * 4 byte = bit // 8 elapsed = end_time_real - start_time_real if hasattr(hfunc, "USE_CFFI"): if hfunc.USE_CFFI: cffi = " (CFFI=True)" else: cffi = " (CFFI=False)" else: cffi = "" result.append([elapsed, i, category, hname, bit, byte, cffi]) if self.args.time_with_sort: result = sorted(result, key=lambda x: (-x[0], x[1])) cumulative_time = 0.0 prev_category = None for elapsed, _, category, hname, bit, byte, cffi in result: cumulative_time += elapsed if self.args.time: if prev_category != category: self.out.append(titlify(category)) prev_category = category self.out.append("{:26s}:[{:4d}b/{:3d}B] {:.6f} sec (total: {:.6f} sec){:s}".format( hname, bit, byte, elapsed, cumulative_time, cffi )) return @parse_args def do_invoke(self, args): self.out = [] if args.time or args.time_with_sort: self.hash_test_time() else: self.hash_test() self.print_output(check_terminal_size=True) return @register_command class HashKnownCollisionCommand(HashCommand, BufferingOutput): """Show hash collision example.""" _cmdline_ = "hash known-collision" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = None def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_NONE) return def make_cmp(self, data1, data2, show_full=True): diff_found = False asterisk = False hex_pad_len = { 1: 37, 2: 35, 3: 32, 4: 30, 5: 27, 6: 25, 7: 22, 8: 20, 9: 17, 10: 15, 11: 12, 12: 9, 13: 7, 14: 5, 15: 2, 16: 0, } for pos in range(0, len(data1), 16): # determining continuity f1_bin = data1[pos : pos + 16] f2_bin = data2[pos : pos + 16] if not show_full: if f1_bin == f2_bin: if asterisk is False: self.out.append("*") asterisk = True continue diff_found = True asterisk = False # coloring f1_hex = [] f2_hex = [] f1_ascii = [] f2_ascii = [] for i in range(min(len(f1_bin), 16)): if f1_bin[i] == f2_bin[i]: color_func = lambda x: x else: color_func = Color.boldify f1_hex.append(color_func("{:02x}".format(f1_bin[i]))) f2_hex.append(color_func("{:02x}".format(f2_bin[i]))) f1_ascii.append(color_func(chr(f1_bin[i]) if 0x20 <= f1_bin[i] < 0x7f else ".")) f2_ascii.append(color_func(chr(f2_bin[i]) if 0x20 <= f2_bin[i] < 0x7f else ".")) # formatting # ["00", "00", "00" "00", ...] -> ["0000", "0000", ...] f1_hex2 = ["".join(x) for x in slicer(f1_hex, 2)] f2_hex2 = ["".join(x) for x in slicer(f2_hex, 2)] # padding # ["0000", "0000", ...] -> "0000 0000 ..." f1_hex_s = " ".join(f1_hex2) + " " * hex_pad_len[len(f1_hex)] f2_hex_s = " ".join(f2_hex2) + " " * hex_pad_len[len(f2_hex)] # [".", ".", ...] -> "................" f1_ascii_s = "".join(f1_ascii) + " " * (16 - len(f1_ascii)) f2_ascii_s = "".join(f2_ascii) + " " * (16 - len(f2_ascii)) # make line self.out.append("{:#06x}: {:s} |{:s}| {:s} |{:s}|".format( pos, f1_hex_s, f1_ascii_s, f2_hex_s, f2_ascii_s, )) if diff_found is False: self.info_add_out("No difference") return def get_hex_colored(self, data1, data2): data1_hex_colored = "" data2_hex_colored = "" for d1, d2 in zip(data1, data2): if d1 == d2: data1_hex_colored += "{:02x}".format(d1) data2_hex_colored += "{:02x}".format(d2) else: data1_hex_colored += Color.boldify("{:02x}".format(d1)) data2_hex_colored += Color.boldify("{:02x}".format(d2)) return data1_hex_colored, data2_hex_colored def show_hash_info(self, data1, data2, hash_name): if hash_name == "md5": hash_func = hashlib.md5 elif hash_name == "sha1": hash_func = hashlib.sha1 self.make_cmp(data1, data2) data1_hex_colored, data2_hex_colored = self.get_hex_colored(data1, data2) self.out.append("data1: {:s}".format(data1_hex_colored)) self.out.append("data2: {:s}".format(data2_hex_colored)) bold_yellow = lambda x: Color.colorify(x, "bold yellow") self.out.append("{:13s}: {:s}".format(hash_name + "(data1)", bold_yellow(hash_func(data1).hexdigest()))) self.out.append("{:13s}: {:s}".format(hash_name + "(data2)", bold_yellow(hash_func(data2).hexdigest()))) self.out.append("sha256(data1): {:s}".format(hashlib.sha256(data1).hexdigest())) self.out.append("sha256(data2): {:s}".format(hashlib.sha256(data2).hexdigest())) return def show_md5_hash_collision(self): self.out.append(titlify("MD5 (md5-1block-collision-attack)")) self.out.append( "https://marc-stevens.nl/research/md5-1block-collision/message1.bin " "(https://github.com/corkami/collisions/blob/master/examples/single-ipc1.bin)" ) self.out.append( "https://marc-stevens.nl/research/md5-1block-collision/message2.bin " "(https://github.com/corkami/collisions/blob/master/examples/single-ipc2.bin)" ) md5_1 = bytes.fromhex( "4d c9 68 ff 0e e3 5c 20 95 72 d4 77 7b 72 15 87" "d3 6f a7 b2 1b dc 56 b7 4a 3d c0 78 3e 7b 95 18" "af bf a2 00 a8 28 4b f3 6e 8e 4b 55 b3 5f 42 75" "93 d8 49 67 6d a0 d1 55 5d 83 60 fb 5f 07 fe a2" ) md5_2 = bytes.fromhex( "4d c9 68 ff 0e e3 5c 20 95 72 d4 77 7b 72 15 87" "d3 6f a7 b2 1b dc 56 b7 4a 3d c0 78 3e 7b 95 18" "af bf a2 02 a8 28 4b f3 6e 8e 4b 55 b3 5f 42 75" "93 d8 49 67 6d a0 d1 d5 5d 83 60 fb 5f 07 fe a2" ) self.show_hash_info(md5_1, md5_2, "md5") self.out.append(titlify("MD5 (HashClash)")) self.out.append( "https://marc-stevens.nl/research/hashclash/SingleBlock/downloads/sbcpc1.bin " "(https://github.com/corkami/collisions/blob/master/examples/single-cpc1.bin)" ) self.out.append( "https://marc-stevens.nl/research/hashclash/SingleBlock/downloads/sbcpc2.bin " "(https://github.com/corkami/collisions/blob/master/examples/single-cpc2.bin)" ) md5_1 = bytes.fromhex( "4f 64 65 64 20 47 6f 6c 64 72 65 69 63 68 0a 4f" "64 65 64 20 47 6f 6c 64 72 65 69 63 68 0a 4f 64" "65 64 20 47 6f 6c 64 72 65 69 63 68 0a 4f 64 65" "64 20 47 6f d8 05 0d 00 19 bb 93 18 92 4c aa 96" "dc e3 5c b8 35 b3 49 e1 44 e9 8c 50 c2 2c f4 61" "24 4a 40 64 bf 1a fa ec c5 82 0d 42 8a d3 8d 6b" "ec 89 a5 ad 51 e2 90 63 dd 79 b1 6c f6 7c 12 97" "86 47 f5 af 12 3d e3 ac f8 44 08 5c d0 25 b9 56" ) md5_2 = bytes.fromhex( "4e 65 61 6c 20 4b 6f 62 6c 69 74 7a 0a 4e 65 61" "6c 20 4b 6f 62 6c 69 74 7a 0a 4e 65 61 6c 20 4b" "6f 62 6c 69 74 7a 0a 4e 65 61 6c 20 4b 6f 62 6c" "69 74 7a 0a 75 b8 0e 00 35 f3 d2 c9 09 af 1b ad" "dc e3 5c b8 35 b3 49 e1 44 e8 8c 50 c2 2c f4 61" "24 4a 40 e4 bf 1a fa ec c5 82 0d 42 8a d3 8d 6b" "ec 89 a5 ad 51 e2 90 63 dd 79 b1 6c f6 fc 11 97" "86 47 f5 af 12 3d e3 ac f8 44 08 dc d0 25 b9 56" ) self.show_hash_info(md5_1, md5_2, "md5") self.out.append(titlify("MD5 (FastColl)")) self.out.append("https://github.com/corkami/collisions/README.md") md5_1 = bytes.fromhex( "37 75 c1 f1 c4 a7 5a e7 9c e0 de 7a 5b 10 80 26" "02 ab d9 39 c9 6c 5f 02 12 c2 7f da cd 0d a3 b0" "8c ed fa f3 e1 a3 fd b4 ef 09 e7 fb b1 c3 99 1d" "cd 91 c8 45 e6 6e fd 3d c7 bb 61 52 3e f4 e0 38" "49 11 85 69 eb cc 17 9c 93 4f 40 eb 33 02 ad 20" "a4 09 2d fb 15 fa 20 1d d1 db 17 cd dd 29 59 1e" "39 89 9e f6 79 46 9f e6 8b 85 c5 ef de 42 4f 46" "c2 78 75 9d 8b 65 f4 50 ea 21 c5 59 18 62 ff 7b" ) md5_2 = bytes.fromhex( "37 75 c1 f1 c4 a7 5a e7 9c e0 de 7a 5b 10 80 26" "02 ab d9 b9 c9 6c 5f 02 12 c2 7f da cd 0d a3 b0" "8c ed fa f3 e1 a3 fd b4 ef 09 e7 fb b1 43 9a 1d" "cd 91 c8 45 e6 6e fd 3d c7 bb 61 d2 3e f4 e0 38" "49 11 85 69 eb cc 17 9c 93 4f 40 eb 33 02 ad 20" "a4 09 2d 7b 15 fa 20 1d d1 db 17 cd dd 29 59 1e" "39 89 9e f6 79 46 9f e6 8b 85 c5 ef de c2 4e 46" "c2 78 75 9d 8b 65 f4 50 ea 21 c5 d9 18 62 ff 7b" ) self.show_hash_info(md5_1, md5_2, "md5") self.out.append(titlify("MD5 (FastColl)")) self.out.append("https://github.com/corkami/collisions/blob/master/examples/fastcoll1.bin") self.out.append("https://github.com/corkami/collisions/blob/master/examples/fastcoll2.bin") md5_1 = bytes.fromhex( "2f 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 5c" "7c 20 20 20 49 64 65 6e 74 69 63 61 6c 00 00 7c" "7c 20 20 20 20 50 72 65 66 69 78 20 00 00 20 7c" "5c 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 2f" "37 9a e6 c3 dc 19 ed f5 72 5b b4 e4 73 df 31 bc" "c6 31 6c 9e df af 6c 7c 51 ce 44 4a c6 b3 a7 d4" "6d a2 fb e6 ea 6e 46 a5 4b 2a 5a 3c 8a 6b 6c be" "21 7f 84 d2 ae 75 06 11 da dc 4c 56 87 f3 78 b6" "64 c4 15 0a c4 b2 d1 c2 aa c9 57 3d 6f 35 7e 48" "28 6e 79 3b 25 c6 3e 27 c9 1a 76 39 ec 46 02 66" "1e 64 a6 57 04 d0 fa 4e 88 83 44 b7 f1 dc c2 ec" "e6 95 a7 9e 6d 52 bf 6b ba 60 99 02 a8 9e c3 9e" ) md5_2 = bytes.fromhex( "2f 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 5c" "7c 20 20 20 49 64 65 6e 74 69 63 61 6c 00 00 7c" "7c 20 20 20 20 50 72 65 66 69 78 20 00 00 20 7c" "5c 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 3d 2d 2f" "37 9a e6 c3 dc 19 ed f5 72 5b b4 e4 73 df 31 bc" "c6 31 6c 1e df af 6c 7c 51 ce 44 4a c6 b3 a7 d4" "6d a2 fb e6 ea 6e 46 a5 4b 2a 5a 3c 8a eb 6c be" "21 7f 84 d2 ae 75 06 11 da dc 4c d6 87 f3 78 b6" "64 c4 15 0a c4 b2 d1 c2 aa c9 57 3d 6f 35 7e 48" "28 6e 79 bb 25 c6 3e 27 c9 1a 76 39 ec 46 02 66" "1e 64 a6 57 04 d0 fa 4e 88 83 44 b7 f1 5c c2 ec" "e6 95 a7 9e 6d 52 bf 6b ba 60 99 82 a8 9e c3 9e" ) self.show_hash_info(md5_1, md5_2, "md5") self.out.append(titlify("MD5 (UniColl)")) self.out.append("https://github.com/corkami/collisions/README.md") md5_1 = bytes.fromhex( "55 6e 69 43 6f 6c 6c 20 31 20 70 72 65 66 69 78" "20 32 30 62 f5 48 34 b9 3b 1c 01 9f c8 6b e6 44" "fe f6 31 3a 63 db 99 3e 77 4d c7 5a 6e b0 a6 88" "04 05 fb 39 33 21 64 bf 0d a4 fe e2 a6 9d 83 36" "4b 14 d7 f2 47 53 84 ba 12 2d 4f bb 83 78 6c 70" "c6 eb 21 f2 f6 59 9a 85 14 73 04 dd 57 5f 40 3c" "e1 3f b0 db e8 b4 aa b0 d5 56 22 af b9 04 26 fc" "9f d2 0c 00 86 c8 ed de 85 7f 03 7b 05 28 d7 0f" ) md5_2 = bytes.fromhex( "55 6e 69 43 6f 6c 6c 20 31 21 70 72 65 66 69 78" "20 32 30 62 f5 48 34 b9 3b 1c 01 9f c8 6b e6 44" "fe f6 31 3a 63 db 99 3e 77 4d c7 5a 6e b0 a6 88" "04 05 fb 39 33 21 64 bf 0d a4 fe e2 a6 9d 83 36" "4b 14 d7 f2 47 53 84 ba 12 2c 4f bb 83 78 6c 70" "c6 eb 21 f2 f6 59 9a 85 14 73 04 dd 57 5f 40 3c" "e1 3f b0 db e8 b4 aa b0 d5 56 22 af b9 04 26 fc" "9f d2 0c 00 86 c8 ed de 85 7f 03 7b 05 28 d7 0f" ) self.show_hash_info(md5_1, md5_2, "md5") self.out.append(titlify("MD5 (Hashclash)")) self.out.append("https://github.com/corkami/collisions/README.md") md5_1 = bytes.fromhex( "79 65 73 0a 3d 62 84 11 01 75 d3 4d eb 80 93 de" "31 c1 d9 30 45 fb be 1e 71 f0 0a 63 75 a8 30 aa" "98 17 ca e3 a2 6b 8e 3d 44 a9 8f f2 0e 67 96 48" "97 25 a6 fb 00 00 00 00 49 08 09 33 f0 62 c4 e8" "d5 f1 54 cd ca a1 42 90 7f 9d 3d 9a 67 c4 1b 0f" "04 9f 19 e8 92 c3 aa 19 43 31 1a db da 96 01 54" "85 b5 9a 88 d8 a5 0e fb cd 66 9a da 4f 20 8a aa" "ba e3 9c f0 78 31 8f d1 14 5f 3e b9 0f 9f 3e 19" "09 9c bb a9 45 89 ba a8 03 e6 c0 31 a0 54 d6 26" "3f 80 4c 06 0f c7 d9 19 09 d3 da 14 fd cb 39 84" "1f 0d 77 5f 55 aa 7a 07 4c 24 8b 13 0a 54 a2 bc" "c5 12 7d 4f e0 5e f2 23 c5 07 61 e4 80 91 b2 13" "e7 79 07 2a cf 1b 66 39 8c f0 8e 7e 75 25 22 1d" "a7 3b 49 4a 32 a4 3a 07 61 26 64 ea 6b 83 a2 8d" "be a3 ff be 4e 71 ae 18 e2 d0 86 4f 20 00 30 26" "0a 71 de 1f 40 b4 f4 8f 9c 50 5c 78 dd cd 72 89" "ba d1 bf f9 96 80 e3 06 96 f3 b9 7c 77 2d eb 25" "1e 56 70 d7 14 1f 55 4d ec 11 58 59 92 45 e1 33" "3e 0e a1 6e ff d9 90 ad f6 a0 ad 0e c6 d6 88 12" "b8 74 f2 9e dd 53 f7 88 19 73 85 39 aa 9b e0 8d" "82 bf 9c 5e 58 42 1e 3b 94 cf 5b 54 73 5f a8 4a" "fd 5b 64 cf 59 d1 96 74 14 b3 0c af 11 1c f9 47" "c5 7a 2c f7 d5 24 f5 eb be 54 3e 12 b0 24 67 3f" "01 dd 95 76 8d 0d 58 fb 50 23 70 3a bd ed be ac" "b8 32 db ae e8 dc 3a 83 7a c8 d5 0f 08 90 1d 99" "2d 7d 17 34 4e a8 21 98 61 1a 65 da fc 9b a4 ba" "e1 42 2b 86 0c 94 2a f6 d6 a4 81 b5 2b 0b e9 37" "44 d2 e4 23 14 7c 16 b8 84 90 8b e0 a1 a7 bd 27" "c7 7e e6 17 1a 93 c5 ee 59 70 91 26 4e 9d c7 7c" "1d 3d ab f1 b4 f4 f1 d9 86 48 75 77 6e fe 98 84" "ef 3c 1c c7 16 5a 1f 83 60 ec 5c fe ca 17 0c 74" "eb 8e 9d f6 90 a3 cd 08 65 d5 5a 4c 2e c6 be 54" ) md5_2 = bytes.fromhex( "6e 6f 0a e5 5f d0 83 01 9b 4d 55 06 61 ab 88 11" "8a fa 4d 34 b3 75 59 46 56 97 ef 6c 4a 07 90 cc" "fe 19 d7 cf 6f 92 03 9c 91 aa a5 da 56 92 c1 04" "e6 4c 08 a3 00 00 00 00 8d b6 4e 47 ff af 7a 3c" "d5 f1 54 cd ca a1 42 90 7f 9d 3d 9a 67 c4 1b 0f" "04 9f 19 e8 92 c3 aa 19 43 31 1a db da 96 01 54" "85 b5 9a 88 d8 a5 0e fb cd 66 9a da 4f 20 8a a9" "ba e3 9c f0 78 31 8f d1 14 5f 3e b9 0f 9f 3e 19" "09 9c bb a9 45 89 ba a8 03 e6 c0 31 a0 54 d6 26" "3f 80 4c 06 0f c7 d9 19 09 d3 da 14 fd cb 39 84" "1f 0d 77 5f 55 aa 7a 07 4c 24 8b 13 0a 54 b2 bc" "c5 12 7d 4f e0 5e f2 23 c5 07 61 e4 80 91 b2 13" "e7 79 07 2a cf 1b 66 39 8c f0 8e 7e 75 25 22 1d" "a7 3b 49 4a 32 a4 3a 07 61 26 64 ea 6b 83 a2 8d" "be a3 ff be 4e 71 ae 18 e2 d0 86 4f 20 00 30 22" "0a 71 de 1f 40 b4 f4 8f 9c 50 5c 78 dd cd 72 89" "ba d1 bf f9 96 80 e3 06 96 f3 b9 7c 77 2d eb 25" "1e 56 70 d7 14 1f 55 4d ec 11 58 59 92 45 e1 33" "3e 0e a1 6e ff d9 90 ad f6 a0 ad 0e ca d6 88 12" "b8 74 f2 9e dd 53 f7 88 19 73 85 39 aa 9b e0 8d" "82 bf 9c 5e 58 42 1e 3b 94 cf 5b 54 73 5f a8 4a" "fd 5b 64 cf 59 d1 96 74 14 b3 0c af 11 1c f9 47" "c5 7a 2c f7 d5 24 f5 eb be 54 3e 12 70 24 67 3f" "01 dd 95 76 8d 0d 58 fb 50 23 70 3a bd ed be ac" "b8 32 db ae e8 dc 3a 83 7a c8 d5 0f 08 90 1d 99" "2d 7d 17 34 4e a8 21 98 61 1a 65 da fc 9b a4 ba" "e1 42 2b 86 0c 94 2a f6 d6 a4 81 b5 2b 2b e9 37" "44 d2 e4 23 14 7c 16 b8 84 90 8b e0 a1 a7 bd 27" "c7 7e e6 17 1a 93 c5 ee 59 70 91 26 4e 9d c7 7c" "1d 3d ab f1 b4 f4 f1 d9 86 48 75 77 6e fe 98 84" "ef 3c 1c c7 16 5a 1f 83 60 ec 5c fe ca 17 0c 54" "eb 8e 9d f6 90 a3 cd 08 65 d5 5a 4c 2e c6 be 54" ) self.show_hash_info(md5_1, md5_2, "md5") self.out.append(titlify("MD5 (TextColl)")) self.out.append("https://github.com/cr-marcstevens/hashclash") md5_1 = b"TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak" md5_2 = b"TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak" self.show_hash_info(md5_1, md5_2, "md5") return def show_sha1_hash_collision(self): self.out.append(titlify("SHA-1 (SHAttered)")) self.out.append("https://shattered.io/static/shattered-1.pdf (The first 320 bytes of 422435 bytes)") self.out.append("https://shattered.io/static/shattered-2.pdf (The first 320 bytes of 422435 bytes)") sha1_1 = bytes.fromhex( "2550 4446 2d31 2e33 0a25 e2e3 cfd3 0a0a" "0a31 2030 206f 626a 0a3c 3c2f 5769 6474" "6820 3220 3020 522f 4865 6967 6874 2033" "2030 2052 2f54 7970 6520 3420 3020 522f" "5375 6274 7970 6520 3520 3020 522f 4669" "6c74 6572 2036 2030 2052 2f43 6f6c 6f72" "5370 6163 6520 3720 3020 522f 4c65 6e67" "7468 2038 2030 2052 2f42 6974 7350 6572" "436f 6d70 6f6e 656e 7420 383e 3e0a 7374" "7265 616d 0aff d8ff fe00 2453 4841 2d31" "2069 7320 6465 6164 2121 2121 2185 2fec" "0923 3975 9c39 b1a1 c63c 4c97 e1ff fe01" "7346 dc91 66b6 7e11 8f02 9ab6 21b2 560f" "f9ca 67cc a8c7 f85b a84c 7903 0c2b 3de2" "18f8 6db3 a909 01d5 df45 c14f 26fe dfb3" "dc38 e96a c22f e7bd 728f 0e45 bce0 46d2" "3c57 0feb 1413 98bb 552e f5a0 a82b e331" "fea4 8037 b8b5 d71f 0e33 2edf 93ac 3500" "eb4d dc0d ecc1 a864 790c 782c 7621 5660" "dd30 9791 d06b d0af 3f98 cda4 bc46 29b1" ) sha1_2 = bytes.fromhex( "2550 4446 2d31 2e33 0a25 e2e3 cfd3 0a0a" "0a31 2030 206f 626a 0a3c 3c2f 5769 6474" "6820 3220 3020 522f 4865 6967 6874 2033" "2030 2052 2f54 7970 6520 3420 3020 522f" "5375 6274 7970 6520 3520 3020 522f 4669" "6c74 6572 2036 2030 2052 2f43 6f6c 6f72" "5370 6163 6520 3720 3020 522f 4c65 6e67" "7468 2038 2030 2052 2f42 6974 7350 6572" "436f 6d70 6f6e 656e 7420 383e 3e0a 7374" "7265 616d 0aff d8ff fe00 2453 4841 2d31" "2069 7320 6465 6164 2121 2121 2185 2fec" "0923 3975 9c39 b1a1 c63c 4c97 e1ff fe01" "7f46 dc93 a6b6 7e01 3b02 9aaa 1db2 560b" "45ca 67d6 88c7 f84b 8c4c 791f e02b 3df6" "14f8 6db1 6909 01c5 6b45 c153 0afe dfb7" "6038 e972 722f e7ad 728f 0e49 04e0 46c2" "3057 0fe9 d413 98ab e12e f5bc 942b e335" "42a4 802d 98b5 d70f 2a33 2ec3 7fac 3514" "e74d dc0f 2cc1 a874 cd0c 7830 5a21 5664" "6130 9789 606b d0bf 3f98 cda8 0446 29a1" ) self.show_hash_info(sha1_1, sha1_2, "sha1") self.out.append(titlify("SHA-1 (Shambles)")) self.out.append("https://sha-mbles.github.io/messageA") self.out.append("https://sha-mbles.github.io/messageB") sha1_1 = bytes.fromhex( "99 04 0d 04 7f e8 17 80 01 20 00 ff 4b 65 79 20" "69 73 20 70 61 72 74 20 6f 66 20 61 20 63 6f 6c" "6c 69 73 69 6f 6e 21 20 49 74 27 73 20 61 20 74" "72 61 70 21 79 c6 1a f0 af cc 05 45 15 d9 27 4e" "73 07 62 4b 1d c7 fb 23 98 8b b8 de 8b 57 5d ba" "7b 9e ab 31 c1 67 4b 6d 97 43 78 a8 27 73 2f f5" "85 1c 76 a2 e6 07 72 b5 a4 7c e1 ea c4 0b b9 93" "c1 2d 8c 70 e2 4a 4f 8d 5f cd ed c1 b3 2c 9c f1" "9e 31 af 24 29 75 9d 42 e4 df db 31 71 9f 58 76" "23 ee 55 29 39 b6 dc dc 45 9f ca 53 55 3b 70 f8" "7e de 30 a2 47 ea 3a f6 c7 59 a2 f2 0b 32 0d 76" "0d b6 4f f4 79 08 4f d3 cc b3 cd d4 83 62 d9 6a" "9c 43 06 17 ca ff 6c 36 c6 37 e5 3f de 28 41 7f" "62 6f ec 54 ed 79 43 a4 6e 5f 57 30 f2 bb 38 fb" "1d f6 e0 09 00 10 d0 0e 24 ad 78 bf 92 64 19 93" "60 8e 8d 15 8a 78 9f 34 c4 6f e1 e6 02 7f 35 a4" "cb fb 82 70 76 c5 0e ca 0e 8b 7c ca 69 bb 2c 2b" "79 02 59 f9 bf 95 70 dd 8d 44 37 a3 11 5f af f7" "c3 ca c0 9a d2 52 66 05 5c 27 10 47 55 17 8e ae" "ff 82 5a 2c aa 2a cf b5 de 64 ce 76 41 dc 59 a5" "41 a9 fc 9c 75 67 56 e2 e2 3d c7 13 c8 c2 4c 97" "90 aa 6b 0e 38 a7 f5 5f 14 45 2a 1c a2 85 0d dd" "95 62 fd 9a 18 ad 42 49 6a a9 70 08 f7 46 72 f6" "8e f4 61 eb 88 b0 99 33 d6 26 b4 f9 18 74 9c c0" "27 fd dd 6c 42 5f c4 21 68 35 d0 13 4d 15 28 5b" "ab 2c b7 84 a4 f7 cb b4 fb 51 4d 4b f0 f6 23 7c" "f0 0a 9e 9f 13 2b 9a 06 6e 6f d1 7f 6c 42 98 74" "78 58 6f f6 51 af 96 74 7f b4 26 b9 87 2b 9a 88" "e4 06 3f 59 bb 33 4c c0 06 50 f8 3a 80 c4 27 51" "b7 19 74 d3 00 fc 28 19 a2 e8 f1 e3 2c 1b 51 cb" "18 e6 bf c4 db 9b ae f6 75 d4 aa f5 b1 57 4a 04" "7f 8f 6d d2 ec 15 3a 93 41 22 93 97 4d 92 8f 88" "ce d9 36 3c fe f9 7c e2 e7 42 bf 34 c9 6b 8e f3" "87 56 76 fe a5 cc a8 e5 f7 de a0 ba b2 41 3d 4d" "e0 0e e7 1e e0 1f 16 2b db 6d 1e af d9 25 e6 ae" "ba ae 6a 35 4e f1 7c f2 05 a4 04 fb db 12 fc 45" "4d 41 fd d9 5c f2 45 96 64 a2 ad 03 2d 1d a6 0a" "73 26 40 75 d7 f1 e0 d6 c1 40 3a e7 a0 d8 61 df" "3f e5 70 71 88 dd 5e 07 d1 58 9b 9f 8b 66 30 55" "3f 8f c3 52 b3 e0 c2 7d a8 0b dd ba 4c 64 02 0d" ) sha1_2 = bytes.fromhex( "99 03 0d 04 7f e8 17 80 01 18 00 ff 50 72 61 63" "74 69 63 61 6c 20 53 48 41 2d 31 20 63 68 6f 73" "65 6e 2d 70 72 65 66 69 78 20 63 6f 6c 6c 69 73" "69 6f 6e 21 1d 27 6c 6b a6 61 e1 04 0e 1f 7d 76" "7f 07 62 49 dd c7 fb 33 2c 8b b8 c2 b7 57 5d be" "c7 9e ab 2b e1 67 4b 7d b3 43 78 b4 cb 73 2f e1" "89 1c 76 a0 26 07 72 a5 10 7c e1 f6 e8 0b b9 97" "7d 2d 8c 68 52 4a 4f 9d 5f cd ed cd 0b 2c 9c e1" "92 31 af 26 e9 75 9d 52 50 df db 2d 4d 9f 58 72" "9f ee 55 33 19 b6 dc cc 61 9f ca 4f b9 3b 70 ec" "72 de 30 a0 87 ea 3a e6 73 59 a2 ee 27 32 0d 72" "b1 b6 4f ec c9 08 4f c3 cc b3 cd d8 3b 62 d9 7a" "90 43 06 15 0a ff 6c 26 72 37 e5 23 e2 28 41 7b" "de 6f ec 4e cd 79 43 b4 4a 5f 57 2c 1e bb 38 ef" "11 f6 e0 0b c0 10 d0 1e 90 ad 78 a3 be 64 19 97" "dc 8e 8d 0d 3a 78 9f 24 c4 6f e1 ea ba 7f 35 b4" "c7 fb 82 72 b6 c5 0e da ba 8b 7c d6 55 bb 2c 2f" "c5 02 59 e3 9f 95 70 cd a9 44 37 bf fd 5f af e3" "cf ca c0 98 12 52 66 15 e8 27 10 5b 79 17 8e aa" "43 82 5a 34 1a 2a cf a5 de 64 ce 7a f9 dc 59 b5" "4d a9 fc 9e b5 67 56 f2 56 3d c7 0f f4 c2 4c 93" "2c aa 6b 14 18 a7 f5 4f 30 45 2a 00 4e 85 0d c9" "99 62 fd 98 d8 ad 42 59 de a9 70 14 db 46 72 f2" "32 f4 61 f3 38 b0 99 23 d6 26 b4 f5 a0 74 9c d0" "2b fd dd 6e 82 5f c4 31 dc 35 d0 0f 71 15 28 5f" "17 2c b7 9e 84 f7 cb a4 df 51 4d 57 1c f6 23 68" "fc 0a 9e 9d d3 2b 9a 16 da 6f d1 63 40 42 98 70" "c4 58 6f ee e1 af 96 64 7f b4 26 b5 3f 2b 9a 98" "e8 06 3f 5b 7b 33 4c d0 b2 50 f8 26 bc c4 27 55" "0b 19 74 c9 20 fc 28 09 86 e8 f1 ff c0 1b 51 df" "14 e6 bf c6 1b 9b ae e6 c1 d4 aa e9 9d 57 4a 00" "c3 8f 6d ca 5c 15 3a 83 41 22 93 9b f5 92 8f 98" "c2 d9 36 3e 3e f9 7c f2 53 42 bf 28 f5 6b 8e f7" "3b 56 76 e4 85 cc a8 f5 d3 de a0 a6 5e 41 3d 59" "ec 0e e7 1c 20 1f 16 3b 6f 6d 1e b3 f5 25 e6 aa" "06 ae 6a 2d fe f1 7c e2 05 a4 04 f7 63 12 fc 55" "41 41 fd db 9c f2 45 86 d0 a2 ad 1f 11 1d a6 0e" "cf 26 40 6f f7 f1 e0 c6 e5 40 3a fb 4c d8 61 cb" "33 e5 70 73 48 dd 5e 17 65 58 9b 83 a7 66 30 51" "83 8f c3 4a 03 e0 c2 6d a8 0b dd b6 f4 64 02 1d" ) self.show_hash_info(sha1_1, sha1_2, "sha1") return @parse_args def do_invoke(self, args): self.out = [] self.show_md5_hash_collision() self.show_sha1_hash_collision() self.print_output(check_terminal_size=True) return @register_command class JsonCommand(GenericCommand, BufferingOutput): """The base command to pretty print for JSON.""" _cmdline_ = "json" _category_ = "03-b. Memory - View" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("value") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return @parse_args def do_invoke(self, args): self.usage() return @register_command class JsonMemoryCommand(JsonCommand): """Pretty print JSON from memory values.""" _cmdline_ = "json memory" _category_ = "03-b. Memory - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address for json.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rdi", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def read_json(self, loc): pos = 0 s = b"" while True: try: blob = read_memory(loc + pos, 1) except gdb.MemoryError: err("Memory read error") break if blob == b"\x00": break s += blob pos += 1 return s @parse_args @only_if_gdb_running def do_invoke(self, args): j = self.read_json(args.location) if not j: err("Could not find JSON") return try: jstr = json.dumps(json.loads(j), indent=2) except (json.JSONDecodeError, UnicodeDecodeError): err("Invalid JSON") return self.out = [] self.out.append(jstr) self.print_output(check_terminal_size=True) return @register_command class JsonValueCommand(JsonCommand): """Pretty print JSON from specified value.""" _cmdline_ = "json value" _category_ = "03-b. Memory - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the string of JSON.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ '{0:s} \'["foo", {{"bar": ["baz", null, 1.0, 2]}}]\'', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): try: jstr = json.dumps(json.loads(self.args.value), indent=2) except (json.JSONDecodeError, UnicodeDecodeError): err("Invalid JSON") return self.out = [] self.out.append(jstr) self.print_output(check_terminal_size=True) return @register_command class CrcCommand(GenericCommand, BufferingOutput): """The base command to calculate crc.""" _cmdline_ = "crc" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("file") subparsers.add_parser("value") _syntax_ = parser.format_help() _note_ = [ "[32b/04B] means 32 bits (4 bytes).", ] _note_ = "\n".join(_note_) def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return def get_valid_crc_funcs(self): import crccheck for cname in crccheck.crc.__dict__: if self.args.filter and not any(filt.search(cname) for filt in self.args.filter): continue if not cname.startswith("Crc"): continue if cname.startswith("Crccheck"): continue try: cfunc = getattr(crccheck.crc, cname)() except TypeError: continue yield (cname, cfunc) class Crc32kWrapper: def __init__(self): self.buf = b"" return def cfunc(self, data): # CRC-32K (Koopman) polynomial: # normal: 0x741B8CD7 # reflected 0xEB31D82E (LSB-first bit processing) poly_reflected = 0xeb31_d82e init = 0xffff_ffff xorout = 0xffff_ffff crc = init & 0xffff_ffff for b in data: crc ^= b for _ in range(8): if crc & 1: crc = (crc >> 1) ^ poly_reflected else: crc >>= 1 crc &= 0xffff_ffff crc ^= xorout return crc & 0xffff_ffff def process(self, value): self.buf += value return def calchex(self, value): crc = self.cfunc(value) return "{:08x}".format(crc) def finalhex(self): crc = self.cfunc(self.buf) return "{:08x}".format(crc) def width(self): return 32 def bytewidth(self): return 4 for cname in ["Crc32K", "Crc32Koopman"]: if self.args.filter and not any(filt.search(cname) for filt in self.args.filter): continue yield (cname, Crc32kWrapper()) return None def make_line(self, cname, cfunc, crc): bit = cfunc.width() byte = cfunc.bytewidth() line = "{:20s}:[{:2d}b/{:2d}B] {:s}".format(cname, bit, byte, crc) return line @parse_args def do_invoke(self, args): self.usage() return @register_command class CrcMemoryCommand(CrcCommand): """Calculate crc from memory values.""" _cmdline_ = "crc memory" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address for crc calculation.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for crc calculation.") parser.add_argument("-f", "--filter", metavar="REGEX", type=re.compile, default=[], action="append", help="filter by REGEX pattern.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return def calc_crc(self, cfunc, start_address, end_address): # When calculating the crc of a very large range, # it is not practical to store the entire data in memory. # It is preferable to calculate it in blocks. step = 0x400 * get_pagesize() if is_qemu_system(): step = get_pagesize() for chunk_addr in range(start_address, end_address, step): if chunk_addr + step > end_address: chunk_size = end_address - chunk_addr else: chunk_size = step try: mem = read_memory(chunk_addr, chunk_size) except (gdb.MemoryError, MemoryError): err("Memory read error") return False try: cfunc.process(mem) except ValueError: return None del mem return cfunc.finalhex() @parse_args @only_if_gdb_running @ModuleLoader.load_crccheck def do_invoke(self, args): self.out = [] self.out.append("Address: {:#x}".format(args.location)) self.out.append("Size: {:#x}".format(args.size)) for cname, cfunc in self.get_valid_crc_funcs(): crc = self.calc_crc(cfunc, args.location, args.location + args.size) if crc is False: return if crc is None: continue line = self.make_line(cname, cfunc, crc) self.out.append(line) self.print_output(check_terminal_size=True) return @register_command class CrcFileCommand(CrcCommand): """Calculate crc from file.""" _cmdline_ = "crc file" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("filename", metavar="FILE", help="the filepath for crc calculation.") parser.add_argument("start", metavar="START_POS", nargs="?", default=0, type=AddressUtil.parse_address, help="the start position for crc calculation.") parser.add_argument("size", metavar="SIZE", nargs="?", type=AddressUtil.parse_address, help="the size for crc calculation.") parser.add_argument("-f", "--filter", metavar="REGEX", type=re.compile, default=[], action="append", help="filter by REGEX pattern.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_FILENAME) return def calc_crc(self, cfunc, filename, start_pos, end_pos): # When calculating the crc of a very large range, # it is not practical to store the entire data in memory. # It is preferable to calculate it in blocks. step = 0x400 * get_pagesize() with open(self.args.filename, "rb") as f: f.seek(start_pos) for chunk_pos in range(start_pos, end_pos, step): chunk_size = min(end_pos - chunk_pos, step) data = f.read(chunk_size) try: cfunc.process(data) except ValueError: return None del data return cfunc.finalhex() @parse_args @only_if_gdb_running @ModuleLoader.load_crccheck def do_invoke(self, args): self.out = [] if not os.path.exists(args.filename): err("File not found") return self.out.append("Path: {:s}".format(args.filename)) self.out.append("FileSize: {:#x}".format(os.path.getsize(args.filename))) if args.size is None: end_pos = args.start + os.path.getsize(args.filename) else: end_pos = args.start + args.size for cname, cfunc in self.get_valid_crc_funcs(): crc = self.calc_crc(cfunc, args.filename, args.start, end_pos) if crc is False: return if crc is None: continue line = self.make_line(cname, cfunc, crc) self.out.append(line) self.print_output(check_terminal_size=True) return @register_command class CrcValueCommand(CrcCommand): """Calculate hash from specified values.""" _cmdline_ = "crc value" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the string for crc calculation.") parser.add_argument("--hex", action="store_true", help="interpret VALUE as hex. invalid character is ignored.") parser.add_argument("-f", "--filter", metavar="REGEX", type=re.compile, default=[], action="append", help="filter by REGEX pattern.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "\\\\x41\\\\x42\\\\x43\\\\x44"', '{0:s} --hex "41 42 43 44"', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args @ModuleLoader.load_crccheck def do_invoke(self, args): if args.hex: # "41414141" -> b"\x41\x41\x41\x41" value = GefUtil.fromhex_ignore_invalid(args.value) if not value: return else: try: value = codecs.escape_decode(args.value)[0] except binascii.Error: err('Could not decode "\\xXX" encoded string') return self.out = [] for cname, cfunc in self.get_valid_crc_funcs(): try: crc = cfunc.calchex(value) except ValueError: continue line = self.make_line(cname, cfunc, crc) self.out.append(line) self.print_output(check_terminal_size=True) return @register_command class Crc32revCommand(GenericCommand, BufferingOutput): """Perform CRC32 reverse calculation limited to ASCII character range.""" _cmdline_ = "crc32rev" _category_ = "07-e. Misc - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-p", "--poly", type=lambda x: int(x, 16), help="generator polynomial in MSB form.") parser.add_argument("--poly-reflected", action="store_true", help="treat --poly as already reflected (LSB form, e.g., 0xedb88320).") parser.add_argument("-i", "--init-value", type=lambda x: int(x, 16), help="initial CRC register value.") parser.add_argument("-o", "--xorout", type=lambda x: int(x, 16), help="final XOR value applied after output reflection.") parser.add_argument("--refin", action="store_true", help="enable input reflection (LSB-first).") parser.add_argument("--no-refin", action="store_true", help="disable input reflection (MSB-first).") parser.add_argument("--refout", action="store_true", help="enable output reflection.") parser.add_argument("--no-refout", action="store_true", help="disable output reflection.") parser.add_argument("--preset", choices=[ "", "base", "ieee", "isohdlc", "adccp", "v42", "xz", "pkzip", "aixm", "q", "autosar", "base91d", "d", "bzip2", "aal5", "dectb", "b", "cdromedc", "cksum", "posix", "iscsi", "base91c", "castagnoli", "interlaken", "c", "nvme", "jamcrc", "mef", "mpeg2", "ether", "xfer", "koopman", "k", ], default="", help="quick parameter presets, explicit flags override preset values.") parser.add_argument("--list", action="store_true", help="print CRC presets.") parser.add_argument("wanted_crc", metavar="WANTED_CRC", nargs="?", type=lambda x: int(x, 16), help="target CRC value (hex).") parser.add_argument("--prefix", default="", help="prefix string (ASCII).") parser.add_argument("--suffix", default="", help="suffix string (ASCII).") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} --list", "{0:s} 0x41414141", "{0:s} 0x41414141 --prefix AAAA --suffix BBBB", "{0:s} 0x41414141 --preset mpeg2", ] _example_ = "\n".join(_example_).format(_cmdline_) preset_dic = { # https://reveng.sourceforge.io/crc-catalogue/all.htm # preset name: (poly, init_value, xorout, refin, refout, alias) "": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, True, True, False), "base": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, True, True, True), "ieee": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, True, True, True), "isohdlc": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, True, True, True), "adccp": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, True, True, True), "v42": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, True, True, True), "xz": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, True, True, True), "pkzip": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, True, True, True), "aixm": (0x8141_41ab, 0x0000_0000, 0x0000_0000, False, False, False), "q": (0x8141_41ab, 0x0000_0000, 0x0000_0000, False, False, True), "autosar": (0xf4ac_fb13, 0xffff_ffff, 0xffff_ffff, True, True, False), "base91d": (0xa833_982b, 0xffff_ffff, 0xffff_ffff, True, True, False), "d": (0xa833_982b, 0xffff_ffff, 0xffff_ffff, True, True, True), "bzip2": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, False, False, False), "aal5": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, False, False, True), "dectb": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, False, False, True), "b": (0x04c1_1db7, 0xffff_ffff, 0xffff_ffff, False, False, True), "cdromedc": (0x8001_801b, 0x0000_0000, 0x0000_0000, True, True, False), "cksum": (0x04c1_1db7, 0x0000_0000, 0xffff_ffff, False, False, False), "posix": (0x04c1_1db7, 0x0000_0000, 0xffff_ffff, False, False, True), "iscsi": (0x1edc_6f41, 0xffff_ffff, 0xffff_ffff, True, True, False), "base91c": (0x1edc_6f41, 0xffff_ffff, 0xffff_ffff, True, True, True), "castagnoli": (0x1edc_6f41, 0xffff_ffff, 0xffff_ffff, True, True, True), "interlaken": (0x1edc_6f41, 0xffff_ffff, 0xffff_ffff, True, True, True), "c": (0x1edc_6f41, 0xffff_ffff, 0xffff_ffff, True, True, True), "nvme": (0x1edc_6f41, 0xffff_ffff, 0xffff_ffff, True, True, True), "jamcrc": (0x04c1_1db7, 0xffff_ffff, 0x0000_0000, True, True, False), "mef": (0x741b_8cd7, 0xffff_ffff, 0x0000_0000, True, True, False), "mpeg2": (0x04c1_1db7, 0xffff_ffff, 0x0000_0000, False, False, False), "ether": (0x04c1_1db7, 0xffff_ffff, 0x0000_0000, False, False, True), "xfer": (0x0000_00af, 0x0000_0000, 0x0000_0000, False, False, False), "koopman": (0x741b_8cd7, 0xffff_ffff, 0xffff_ffff, True, True, False), "k": (0x741b_8cd7, 0xffff_ffff, 0xffff_ffff, True, True, True), } def print_preset_dict(self): fmt = "{:<10s} {:<10s} {:<10s} {:<10s} {:<10s} {:5s} {:6s} {:5s}" legend = ["name", "poly", "rpoly", "init_value", "xorout", "refin", "refout", "alias"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for k, v in self.preset_dic.items(): if v[5]: alias = "(alias)" else: alias = "" self.out.append("{:10s} {:#010x} {:#010x} {:#010x} {:#010x} {!s:5s} {!s:6s} {:s}".format( k or "''", v[0], self.reflect32(v[0]), v[1], v[2], v[3], v[4], alias), ) return def reflect32(self, x): """bit reverse.""" return int("{:032b}".format(x)[::-1], 2) & 0xffff_ffff def build_crc(self): """Apply preset parameters if requested. Explicit CLI flags override these.""" poly, init_value, xorout, refin, refout, _ = self.preset_dic[self.args.preset] # override from CLI if self.args.poly is not None: poly = self.args.poly & 0xffff_ffff if self.args.poly_reflected: poly = self.reflect32(poly) rpoly = self.reflect32(poly) if self.args.init_value is not None: init_value = self.args.init_value & 0xffff_ffff if self.args.xorout is not None: xorout = self.args.xorout & 0xffff_ffff if self.args.refin: refin = True if self.args.no_refin: refin = False if self.args.refout: refout = True if self.args.no_refout: refout = False assert refin == refout # build CRC = collections.namedtuple("CRC", ["poly", "rpoly", "init_value", "xorout", "refin", "refout"]) self.CRC = CRC(poly, rpoly, init_value, xorout, refin, refout) return def build_tables(self): """Build forward / reverse table and the inverse index used by backward steps.""" self.FT = [] # used always self.RT = [] # used when refin == refout == True self.inv_idx = [0] * 256 # used when refin == refout == False if not self.CRC.refin and not self.CRC.refout: for i in range(256): fwd = i << 24 for _ in range(8): if fwd & 0x8000_0000: fwd = ((fwd << 1) ^ self.CRC.poly) & 0xffff_ffff else: fwd = (fwd << 1) & 0xffff_ffff self.FT.append(fwd) for i in range(256): self.inv_idx[self.FT[i] & 0xff] = i else: for i in range(256): fwd = i rev = i << 24 for _ in range(8): if fwd & 1: fwd = ((fwd >> 1) ^ self.CRC.rpoly) & 0xffff_ffff else: fwd = (fwd >> 1) & 0xffff_ffff if (rev >> 31) & 1: rev = (((rev ^ self.CRC.rpoly) << 1) | 1) & 0xffff_ffff else: rev = (rev << 1) & 0xffff_ffff self.FT.append(fwd) self.RT.append(rev) return def calc_forward(self, accum, data_bytes): crc = accum if not self.CRC.refin and not self.CRC.refout: for c in data_bytes: idx = ((crc >> 24) ^ c) & 0xff crc = ((crc << 8) & 0xffff_ffff) ^ self.FT[idx] else: for c in data_bytes: idx = (crc ^ c) & 0xff crc = (crc >> 8) ^ self.FT[idx] return crc def calc_backward(self, wanted, data_bytes): crc = wanted if not self.CRC.refin and not self.CRC.refout: for c in data_bytes[::-1]: b = self.inv_idx[crc & 0xff] prev_top = b ^ c q = crc ^ self.FT[b] crc = ((q >> 8) & 0xffff_ffff) | ((prev_top & 0xff) << 24) else: for c in data_bytes[::-1]: idx = crc >> 24 crc = ((crc << 8) & 0xffff_ffff) ^ self.RT[idx] ^ c return crc def calc_crc32(self, msg_bytes): crc = self.CRC.init_value crc = self.calc_forward(crc, msg_bytes) return crc ^ self.CRC.xorout def find_bridge(self, init_value, wanted_crc, prefix, suffix): """Compute a 4-byte bridge so that CRC(prefix + bridge + suffix) == wanted_crc.""" # forward state after prefix (raw) fwd_crc = self.calc_forward(init_value, prefix) # map external wanted -> raw wanted (invert output formatting) wanted_raw = wanted_crc ^ self.CRC.xorout # rewind suffix to get the raw state right before suffix bkd_crc = self.calc_backward(wanted_raw, suffix) def state_bytes(x): if not self.CRC.refin and not self.CRC.refout: xs = [(x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, (x >> 0) & 0xff] return xs else: xs = [(x >> 0) & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff] return xs # 4-byte exact bridge between fwd_crc and bkd_crc bridge_word = self.calc_backward(bkd_crc, state_bytes(fwd_crc)) bridge_bytes = state_bytes(bridge_word) # sanity check test_seq = prefix + bridge_bytes + suffix assert self.calc_crc32(test_seq) == wanted_crc return bridge_bytes def find_reverse(self, prefix, suffix): """Search ASCII-only bridges of length 4..6(+7).""" self.build_crc() self.build_tables() init_value = self.CRC.init_value wanted_crc = self.args.wanted_crc & 0xffff_ffff ascii_range = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" solutions = [] # 4 bytes bridge = self.find_bridge(init_value, wanted_crc, prefix, suffix) if all(c in ascii_range for c in bridge): solutions.append(bridge) # 5 bytes for b in ascii_range: new_prefix = prefix + [b] bridge = self.find_bridge(init_value, wanted_crc, new_prefix, suffix) if all(c in ascii_range for c in bridge): solutions.append([b] + bridge) # 6 bytes for b1 in ascii_range: for b2 in ascii_range: new_prefix = prefix + [b1, b2] bridge = self.find_bridge(init_value, wanted_crc, new_prefix, suffix) if all(c in ascii_range for c in bridge): solutions.append([b1, b2] + bridge) if solutions: return solutions # 7 bytes for b1 in ascii_range: for b2 in ascii_range: for b3 in ascii_range: new_prefix = prefix + [b1, b2, b3] bridge = self.find_bridge(init_value, wanted_crc, new_prefix, suffix) if all(c in ascii_range for c in bridge): solutions.append([b1, b2, b3] + bridge) return solutions @parse_args def do_invoke(self, args): if self.args.list == (self.args.wanted_crc is not None): self.usage() return self.out = [] if self.args.list: self.print_preset_dict() self.print_output(check_terminal_size=True) return prefix = [ord(c) for c in self.args.prefix] suffix = [ord(c) for c in self.args.suffix] sols = self.find_reverse(prefix, suffix) if sols: for sol in sols: msg = prefix + sol + suffix crc = self.calc_crc32(msg) self.out.append("{}: CRC32({}) = {:#010x}".format(bytes(sol), bytes(msg), crc)) else: self.err_add_out("No ASCII-only bridge found under given constraints.") self.print_output(check_terminal_size=True) return @register_command class BaseNDecodeCommand(GenericCommand, BufferingOutput): """The base command to decode baseN.""" _cmdline_ = "base-n-decode" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("value") _syntax_ = parser.format_help() baseN = [ "base1", "base2", "base3", "base4", "base8", "base10", "base16", "base26", "base32", "base32-crockford", "base32-geohash", "base32-hex", "base32-z", "base36", "base45", "base58-bitcoin", "base58-flickr", "base58-ripple", "base62", "base63", "base64", "base64-url", "base67", "base85", "base85-adobe", "base85-ipv6", "base85-xml", "base85-xbtoa", "base85-zeromq", "base91", "base100", "base122", ] def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return def get_valid_base_decode_funcs(self): import codext for bname in self.baseN: bfunc = lambda x, bname=bname: codext.decode(x, bname) yield (bname, bfunc) return None @parse_args def do_invoke(self, args): self.usage() return @register_command class BaseNDecodeMemoryCommand(BaseNDecodeCommand): """Decode baseN from memory values.""" _cmdline_ = "base-n-decode memory" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address for baseN decoding.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for baseN decoding.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @ModuleLoader.load_codext def do_invoke(self, args): self.out = [] self.out.append("Address: {:#x}".format(args.location)) self.out.append("Size: {:#x}".format(args.size)) try: mem = read_memory(args.location, args.size) except (gdb.MemoryError, MemoryError): err("Memory read error") return False for bname, bfunc in self.get_valid_base_decode_funcs(): try: b = bfunc(mem) self.out.append("{:17s}: {!s}".format(bname, b)) except ValueError: self.out.append("{:17s}: ERROR".format(bname)) self.print_output(check_terminal_size=True) return @register_command class BaseNDecodeValueCommand(BaseNDecodeCommand): """Decode baseN from specified values.""" _cmdline_ = "base-n-decode value" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the string for baseN decoding.") parser.add_argument("--hex", action="store_true", help="interpret VALUE as hex. invalid character is ignored.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "\\\\x51\\\\x55\\\\x46\\\\x42"', '{0:s} --hex "51 55 46 42"', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args @ModuleLoader.load_crccheck def do_invoke(self, args): if args.hex: # "41414141" -> b"\x41\x41\x41\x41" value = GefUtil.fromhex_ignore_invalid(args.value) if not value: return else: try: value = codecs.escape_decode(args.value)[0] except binascii.Error: err('Could not decode "\\xXX" encoded string') return self.out = [] for bname, bfunc in self.get_valid_base_decode_funcs(): try: b = bfunc(value) self.out.append("{:17s}: {!s}".format(bname, b)) except ValueError: self.out.append("{:17s}: ERROR".format(bname)) self.print_output(check_terminal_size=True) return @register_command class BaseNEncodeCommand(GenericCommand, BufferingOutput): """The base command to encode baseN.""" _cmdline_ = "base-n-encode" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("value") _syntax_ = parser.format_help() baseN = [ "base1", "base2", "base3", "base4", "base8", "base10", "base16", "base26", "base32", "base32-crockford", "base32-geohash", "base32-hex", "base32-z", "base36", "base45", "base58-bitcoin", "base58-flickr", "base58-ripple", "base62", "base63", "base64", "base64-url", "base67", "base85", "base85-adobe", "base85-ipv6", "base85-xml", "base85-xbtoa", "base85-zeromq", "base91", "base100", "base122", ] def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return def get_valid_base_encode_funcs(self): import codext for bname in self.baseN: bfunc = lambda x, bname=bname: codext.encode(x, bname) yield (bname, bfunc) return None @parse_args def do_invoke(self, args): self.usage() return @register_command class BaseNEncodeMemoryCommand(BaseNEncodeCommand): """Encode baseN from memory values.""" _cmdline_ = "base-n-encode memory" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address for baseN encoding.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for baseN encoding.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @ModuleLoader.load_codext def do_invoke(self, args): self.out = [] self.out.append("Address: {:#x}".format(args.location)) self.out.append("Size: {:#x}".format(args.size)) try: mem = read_memory(args.location, args.size) except (gdb.MemoryError, MemoryError): err("Memory read error") return False for bname, bfunc in self.get_valid_base_encode_funcs(): if bname == "base1": self.out.append("{:17s}: Skipped because too long".format(bname)) continue try: b = bfunc(mem) self.out.append("{:17s}: {!s}".format(bname, b)) except ValueError: self.out.append("{:17s}: ERROR".format(bname)) self.print_output(check_terminal_size=True) return @register_command class BaseNEncodeValueCommand(BaseNEncodeCommand): """Encode baseN from specified values.""" _cmdline_ = "base-n-encode value" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the string for baseN encoding.") parser.add_argument("--hex", action="store_true", help="interpret VALUE as hex. invalid character is ignored.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "\\\\x41\\\\x42\\\\x43\\\\x44"', '{0:s} --hex "41 42 43 44"', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args @ModuleLoader.load_crccheck def do_invoke(self, args): if args.hex: # "41414141" -> b"\x41\x41\x41\x41" value = GefUtil.fromhex_ignore_invalid(args.value) if not value: return else: try: value = codecs.escape_decode(args.value)[0] except binascii.Error: err('Could not decode "\\xXX" encoded string') return self.out = [] for bname, bfunc in self.get_valid_base_encode_funcs(): if bname == "base1": self.out.append("{:17s}: Skipped because too long".format(bname)) continue try: b = bfunc(value) self.out.append("{:17s}: {!s}".format(bname, b)) except ValueError: self.out.append("{:17s}: ERROR".format(bname)) self.print_output(check_terminal_size=True) return @register_command class MorseDecodeCommand(GenericCommand): """The base command to decode morse code.""" _cmdline_ = "morse-decode" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("value") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return @parse_args def do_invoke(self, args): self.usage() return @register_command class MorseDecodeMemoryCommand(MorseDecodeCommand): """Decode morse code from memory values.""" _cmdline_ = "morse-decode memory" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address for morse code decoding.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for morse code decoding.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running def do_invoke(self, args): gef_print("Address: {:#x}".format(args.location)) gef_print("Size: {:#x}".format(args.size)) try: mem = read_memory(args.location, args.size) except (gdb.MemoryError, MemoryError): err("Memory read error") return False decoded = String.morse_decode(mem) gef_print("{!s}".format(decoded)) return @register_command class MorseDecodeValueCommand(MorseDecodeCommand): """Decode morse code from specified values.""" _cmdline_ = "morse-decode value" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the string for morse code decoding.") _syntax_ = parser.format_help() _example_ = [ '{0:s} -- ".- -... -.-. -.."', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): decoded = String.morse_decode(args.value) gef_print("{!s}".format(decoded)) return @register_command class MorseEncodeCommand(GenericCommand): """The base command to encode morse code.""" _cmdline_ = "morse-encode" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("value") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return @parse_args def do_invoke(self, args): self.usage() return @register_command class MorseEncodeMemoryCommand(MorseEncodeCommand): """Encode morse code from memory values.""" _cmdline_ = "morse-encode memory" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address for morse code encoding.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for morse code encoding.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running def do_invoke(self, args): gef_print("Address: {:#x}".format(args.location)) gef_print("Size: {:#x}".format(args.size)) try: mem = read_memory(args.location, args.size) except (gdb.MemoryError, MemoryError): err("Memory read error") return False encoded = String.morse_encode(mem) gef_print("{!s}".format(encoded)) return @register_command class MorseEncodeValueCommand(MorseEncodeCommand): """Encode morse code from specified values.""" _cmdline_ = "morse-encode value" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("value", metavar="VALUE", help="the string for morse code encoding.") _syntax_ = parser.format_help() _example_ = [ '{0:s} AAAA', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): encoded = String.morse_encode(args.value) gef_print("{!s}".format(encoded)) return @register_command class IsMemoryZeroCommand(GenericCommand): """Check if all the memory in the specified range is 0x00, 0xff.""" _cmdline_ = "is-mem-zero" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat ADDRESS as a physical address.") parser.add_argument("addr", metavar="ADDRESS", type=AddressUtil.parse_address, help="target address for checking.") parser.add_argument("size", metavar="SIZE", type=AddressUtil.parse_address, help="the size for checking.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def memcheck(self, phys_mode, addr, size): page_size = get_pagesize() start = addr end = addr + size is_zero = True is_ff = True current = addr while current < end: read_size = min(end - current, page_size) try: if phys_mode: data = read_physmem(current, read_size) else: data = read_memory(current, read_size) except (gdb.MemoryError, ValueError, OverflowError): err("Read error {:#x}".format(current)) return if data == b"\0" * len(data): is_ff = False elif data == b"\xff" * len(data): is_zero = False else: is_zero = False is_ff = False if is_zero is False and is_ff is False: end = current + read_size break current += read_size if is_zero: info("{:#x} - {:#x} is {:s}".format(start, end, Color.colorify("All 0x00", "bold yellow"))) return if is_ff: info("{:#x} - {:#x} is {:s}".format(start, end, Color.colorify("All 0xFF", "bold yellow"))) return # find non-zero address for i, d in enumerate(data): if d != 0: found_addr = ProcessMap.lookup_address(current + i) break else: warn("Scan failed to find non-zero byte unexpectedly") return info("{:#x} - {:#x} is {:s}".format(start, end, Color.colorify("NON-ZERO", "bold red"))) info("Around {!s} is NON-ZERO".format(found_addr)) info("Length of 0x00: {:d}".format(found_addr.value - start)) return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys: if not is_qemu_system(): err("Unsupported `--phys` option in this gdb mode") return if args.size == 0: info("The size is zero, maybe wrong") self.memcheck(args.phys, args.addr, args.size) return @register_command class StringLengthCommand(GenericCommand): """Detect the length of the string.""" _cmdline_ = "strlen" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat ADDRESS as a physical address.") parser.add_argument("addr", metavar="ADDRESS", type=AddressUtil.parse_address, help="target address for checking.") _syntax_ = parser.format_help() def check(self, phys_mode, addr): count = 0 current = addr while True: # calc read_size if current & get_pagesize_mask_low(): read_size = align_to_pagesize(current) - current else: read_size = get_pagesize() # read try: if phys_mode: data = read_physmem(current, read_size) else: data = read_memory(current, read_size) except (gdb.MemoryError, ValueError, OverflowError): err("Read error {:#x}".format(addr)) return None # count idx = data.find(b"\0") if idx != -1: return count + idx # goto next count += len(data) current += len(data) return None @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys: if not is_qemu_system(): err("Unsupported `--phys` option in this gdb mode") return length = self.check(args.phys, args.addr) if length is None: return gef_print("{:s} bytes".format(Color.colorify_hex(length, "bold"))) return @register_command class SequenceLengthCommand(GenericCommand): """Detect consecutive length of the same sequence.""" _cmdline_ = "seq-length" _category_ = "03-e. Memory - Calculation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--phys", action="store_true", help="treat ADDRESS as a physical address.") parser.add_argument("addr", metavar="ADDRESS", type=AddressUtil.parse_address, help="target address for checking.") parser.add_argument("unit", metavar="UNIT", nargs="?", type=AddressUtil.parse_address, default=1, help="the size for a target value (default: %(default)s).") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def check(self, phys_mode, addr, unit): target = None data = b"" count = 0 current = addr while True: # calc read_size if current & get_pagesize_mask_low(): read_size = align_to_pagesize(current) - current else: read_size = get_pagesize() while read_size < unit: read_size += get_pagesize() # read try: if phys_mode: data += read_physmem(current, read_size) else: data += read_memory(current, read_size) except (gdb.MemoryError, ValueError, OverflowError): err("Read error {:#x}".format(addr)) return None # init target if target is None: target = data[:unit] # count for elem in slicer(data, unit): if elem == target: # Equal in length and content count += 1 elif len(elem) == len(target): # The length is sufficient, but the content is different. return count, target # Consider the case where the length of the final element is insufficient if len(elem) != len(target): data = elem else: data = b"" # goto next current += read_size return None @parse_args @only_if_gdb_running def do_invoke(self, args): if args.phys: if not is_qemu_system(): err("Unsupported `--phys` option in this gdb mode") return if args.unit >= 0x100_000: err("Too large unit size") return addr = ProcessMap.lookup_address(args.addr) info("Check from {!s} in units of {:s} bytes".format( addr, Color.colorify_hex(args.unit, "bold"), )) ret = self.check(args.phys, args.addr, args.unit) if ret is None: return count, target = ret size = args.unit * count end = ProcessMap.lookup_address(args.addr + size) if len(target) > 0x100: target = target[:0x100] + b"..." gef_print("{!s} - {!s} is same value".format(addr, end)) gef_print("{!s} is found {:s} times, {:s} bytes".format( target, Color.colorify_hex(count, "bold"), Color.colorify_hex(size, "bold"), )) return @register_command class MultiLineCommand(GenericCommand): """Execute multiple GDB commands in sequence.""" _cmdline_ = "multi-line" _category_ = "01-c. Debugging Support - Basic Command Extension" _aliases_ = ["ml"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("cmd", metavar="GDB_CMD;", nargs="+", help="semicolon-separated gdb command.") _syntax_ = parser.format_help() _example_ = [ "{0:s} x/4xg $rax; x/4xg $rbx", "{0:s} x/4xg $rax; -; x/4xg $rbx # `-`: newline separator", "{0:s} x/4xg $rax; --; x/4xg $rbx # `--`: bold white line (`-`) separator", "{0:s} x/4xg $rax; ---; x/4xg $rbx # `---`: bold white line (`=`) separator", "{0:s} x/4xg $rax; -t TAG; x/4xg $rbx # `-t TAG`: newline separator with TAG", "{0:s} x/4xg $rax; --t TAG; x/4xg $rbx # `--t TAG`: bold white line (`-`) separator with TAG", "{0:s} x/4xg $rax; ---t TAG; x/4xg $rbx # `---t TAG`: bold white line (`=`) separator with TAG", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_COMMAND) return def do_command(self, commands): if commands == []: return True # make comnand string cmd = "" for c in commands: if "\\" in c or " " in c: cmd += " " + repr(c) else: cmd += " " + c cmd = cmd.strip() # blank command, so skip if cmd.replace(" ", "") == "": return True # separator 1 if cmd == "-": gef_print("") return True if cmd.startswith("-t"): gef_print(Color.boldify(cmd[2:].strip())) return True # separator 2 if cmd == "--": gef_print(titlify("", color="bold")) return True if cmd.startswith("--t"): gef_print(titlify(cmd[3:].strip(), color="bold", msg_color="bold")) return True # separator 3 if cmd == "---": gef_print(titlify("", color="bold", horizontal_line="=")) return True if cmd.startswith("---t"): gef_print(titlify(cmd[4:].strip(), color="bold", msg_color="bold", horizontal_line="=")) return True gef_print(titlify(cmd)) try: gdb.execute(cmd) except gdb.error as e: gef_print(e) return False # fail return True # Need not @parse_args because argparse can't stop interpreting options for user specified command. def do_invoke(self, argv): if len(argv) == 1 and argv[0] == "-h": self.usage() return commands = [] for arg in argv: if arg.endswith(";"): commands.append(arg.rstrip(";").lstrip(";")) if self.do_command(commands) is False: break commands = [] elif arg.startswith(";"): if self.do_command(commands) is False: break commands = [] commands.append(arg.lstrip(";")) elif arg == ";": if self.do_command(commands) is False: break commands = [] else: commands.append(arg) else: self.do_command(commands) return @register_command class TimeCommand(GenericCommand): """Measure the time of the GDB command.""" _cmdline_ = "time" _category_ = "01-c. Debugging Support - Basic Command Extension" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("cmd", metavar="GDB_CMD", help="gdb command.") parser.add_argument("arg", metavar="ARG", nargs="*", help="arguments of gdb command.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_COMMAND) return # Need not @parse_args because argparse can't stop interpreting options for user specified command. def do_invoke(self, argv): if len(argv) == 1 and argv[0] == "-h": self.usage() return start_time_real = time.perf_counter() start_time_proc = time.process_time() cmd = "" for c in argv: if "\\" in c or " " in c: cmd += " " + repr(c) else: cmd += " " + c cmd = cmd.strip() gef_print(titlify(cmd)) try: gdb.execute(cmd) except gdb.error: exc_type, exc_value, exc_traceback = sys.exc_info() gef_print(exc_value) return end_time_real = time.perf_counter() end_time_proc = time.process_time() gef_print(titlify("time elapsed")) gef_print("Real: {:.3f} s".format(end_time_real - start_time_real)) gef_print("CPU: {:.3f} s".format(end_time_proc - start_time_proc)) return @register_command class SaveOutputCommand(GenericCommand): """Save the command outputs.""" _cmdline_ = "saveo" _category_ = "07-f. Misc - Diff" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("cmd", metavar="GDB_CMD", help="gdb command.") parser.add_argument("arg", metavar="ARG", nargs="*", help="arguments of gdb command.") _syntax_ = parser.format_help() _note_ = [ "Saving the output of external commands is unsupported (e.g., pipe, !ls).", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_COMMAND) return # Need not @parse_args because argparse can't stop interpreting options for user specified command. def do_invoke(self, argv): if len(argv) == 1 and argv[0] == "-h": self.usage() return # get settings always_no_pager = Config.get_gef_setting("gef.always_no_pager") # parse command cmd = "" for c in argv: if "\\" in c or " " in c: cmd += " " + repr(c) else: cmd += " " + c cmd = cmd.strip() if not cmd: self.usage() return # do the command try: Config.set_gef_setting("gef.always_no_pager", True) # change temporarily current_output = Color.remove_color(gdb.execute(cmd, to_string=True)) Config.set_gef_setting("gef.always_no_pager", always_no_pager) # revert settings except gdb.error: Config.set_gef_setting("gef.always_no_pager", always_no_pager) # revert settings exc_type, exc_value, exc_traceback = sys.exc_info() gef_print(exc_value) return # remove clear_screen code if current_output.startswith("\x1b[H\x1b[2J"): current_output = current_output[7:] # save dloc = os.path.join(GEF_TEMP_DIR, "diff") if not os.path.exists(dloc): os.mkdir(dloc) tmp_fd, tmp_path = GefUtil.mkstemp(dir=dloc, suffix=".txt") os.fdopen(tmp_fd, "w").write(current_output) open(tmp_path[:-4] + ".cmd", "w").write(cmd) info("The output is saved to {:s}.(txt|cmd)".format(tmp_path[:-4])) # print gef_print(current_output, less=not always_no_pager) return @register_command class DiffOutputCommand(GenericCommand): """The base command to diff of the command outputs.""" _cmdline_ = "diffo" _category_ = "07-f. Misc - Diff" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("colordiff") subparsers.add_parser("git-diff") subparsers.add_parser("list") subparsers.add_parser("clear") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) super().__init__(prefix=prefix) self.add_setting("colordiff_option", "--left-column -y -W 200", "The option used by colordiff.") return def get_saved_files(self): dloc = os.path.join(GEF_TEMP_DIR, "diff") if not os.path.exists(dloc): return [] saved_files = [] for path in GefUtil.walk(dloc): if not path.endswith(".txt"): continue saved_files.append(path) return sorted(saved_files, key=lambda x:os.path.getmtime(x[:-4] + ".cmd")) @parse_args def do_invoke(self, args): self.usage() return @register_command class DiffOutputColordiffCommand(DiffOutputCommand): """Diff the two outputs by colordiff.""" _cmdline_ = "diffo colordiff" _category_ = "07-f. Misc - Diff" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("n1", metavar="N", type=int, help="first diff target got from `diffo list`.") parser.add_argument("n2", metavar="M", type=int, help="second diff target got from `diffo list`.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0 1 # diff between 0 and 1", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "You can check the available indexes with `diffo list`.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(prefix=False) return def make_diff(self, path1, path2): option = Config.get_gef_setting("diffo.colordiff_option") cmd = "{:s} {:s} '{:s}' '{:s}'".format(self.colordiff, option, path1, path2) result = subprocess.getoutput(cmd) return result @parse_args def do_invoke(self, args): try: self.colordiff = GefUtil.which("colordiff") except FileNotFoundError as e: err("{}".format(e)) return saved_files = self.get_saved_files() try: f1 = saved_files[args.n1] f2 = saved_files[args.n2] except IndexError: err("Out of index error") return if not os.path.exists(f1): err("Could not find {:s}".format(f1)) return if not os.path.exists(f2): err("Could not find {:s}".format(f2)) return output = self.make_diff(f1, f2) if output: gef_print(output, less=not args.no_pager) else: gef_print("No difference") return @register_command class DiffOutputGitDiffCommand(DiffOutputCommand): """Diff the two outputs by git.""" _cmdline_ = "diffo git-diff" _category_ = "07-f. Misc - Diff" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("n1", metavar="N", type=int, help="first diff target got from `diffo list`.") parser.add_argument("n2", metavar="M", type=int, help="second diff target got from `diffo list`.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0 1 # diff between 0 and 1", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "You can check the available indexes with `diffo list`.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(prefix=False) return def make_diff(self, path1, path2): cmd = "{:s} diff --color=always '{:s}' '{:s}'".format(self.git, path1, path2) result = subprocess.getoutput(cmd) return result @parse_args def do_invoke(self, args): try: self.git = GefUtil.which("git") except FileNotFoundError as e: err("{}".format(e)) return saved_files = self.get_saved_files() try: f1 = saved_files[args.n1] f2 = saved_files[args.n2] except IndexError: err("Out of index error") return if not os.path.exists(f1): err("Could not find {:s}".format(f1)) return if not os.path.exists(f2): err("Could not find {:s}".format(f2)) return output = self.make_diff(f1, f2) if output: gef_print(output, less=not args.no_pager) else: gef_print("No difference") return @register_command class DiffOutputListCommand(DiffOutputCommand): """List saved outputs.""" _cmdline_ = "diffo list" _category_ = "07-f. Misc - Diff" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): file_list = self.get_saved_files() max_path = max([len(fname) for fname in file_list] + [40]) fmt = "{:<3s} {:26s} {:{:d}s} {:<7s} {:s}" legend = ["#", "mtime", "path", max_path, "size", "command"] gef_print(GefUtil.make_legend(fmt.format(*legend))) for idx, path in enumerate(file_list): data = open(path, "rb").read() size = len(data) mtime = datetime.datetime.fromtimestamp(os.path.getmtime(path)) cmd = open(path[:-4] + ".cmd", "rb").read() cmd = String.bytes2str(cmd) gef_print("{:<3d} {} {:{:d}s} {:<7d} {:s}".format(idx, mtime, path, max_path, size, cmd)) return @register_command class DiffOutputClearCommand(DiffOutputCommand): """Clear all saved outputs.""" _cmdline_ = "diffo clear" _category_ = "07-f. Misc - Diff" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("n", metavar="N", type=int, nargs="*", help="index to be deleted.") parser.add_argument("--all", action="store_true", help="delete everything.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): if args.all: for path in self.get_saved_files(): os.unlink(path) os.unlink(path[:-4] + ".cmd") elif args.n: for i, path in enumerate(self.get_saved_files()): if i in args.n: os.unlink(path) os.unlink(path[:-4] + ".cmd") else: self.usage() return info("Removed") return @register_command class IiCommand(GenericCommand): """Shortcut `x/50i $pc` with opcode bytes.""" _cmdline_ = "ii" _category_ = "01-e. Debugging Support - Assemble" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the dump start address.") parser.add_argument("-l", "--length", type=AddressUtil.parse_address, default=50, help="the dump instruction length.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def ii(self, addr, N): try: res = read_memory(addr, N) except gdb.MemoryError: err("Memory read error") return if N >= 50 and res[0:1] * N == res: info("all targeted area is {:#04x}".format(res[0])) return # get instruction size try: res = gdb.execute("x/{:d}i {:#x}".format(N + 1, addr), to_string=True) except gdb.MemoryError: err("Memory read error") return addrs = [] for line in res.splitlines(): # [x64] # "=> 0x55555555aac0: endbr64" # " 0x55555555aac4: xor ebp,ebp" # [arm] # "=> 0x10340 <_start>: mov.w r11, #0" # " 0x10344 <_start+4>: mov.w lr, #0" r = re.search("^(?:=>| ) (0x[0-9a-f]+)", line) if r: addrs.append(int(r.group(1), 16)) insn_sizes = [(x, y - x) for x, y in zip(addrs[:-1], addrs[1:])] max_insn_width = max(x[1] for x in insn_sizes) * 2 # print for i, line in enumerate(res.splitlines()[:-1]): addr, size = insn_sizes[i] bytecode = read_memory(addr, size) bytecode_hex = "{:{:d}s}".format(bytecode.hex(), max_insn_width) line = line.rstrip() line = line.expandtabs(8) # get position to split # [x64] # "0x55555555aac0: endbr64" # "0x55555555aac4: xor ebp,ebp" # ^ # [arm] # "0x10340 <_start>: mov.w r11, #0" # "0x10344 <_start+4>: mov.w lr, #0" # ^ # Since it depends on the presence or absence of symbols, it must be calculated line by line each time. pos = None r = re.search("[: ]", line[3:]) if r: pos = 3 + r.span()[0] if pos is None: # somethinig is wrong gef_print(line) else: gef_print(line[:pos] + ": " + bytecode_hex + " " + line[pos:]) return @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): if args.location is None: location = current_arch.pc else: location = args.location self.ii(location, args.length) return @register_command class ConstGrepCommand(GenericCommand): """Grep for lines with #define in files under /usr/include.""" _cmdline_ = "constgrep" _category_ = "07-b. Misc - Search" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("pattern", metavar="GREP_PATTERN", help="filter by regex.") _syntax_ = parser.format_help() _example_ = [ "{0:s} '__NR_*'", ] _example_ = "\n".join(_example_).format(_cmdline_) def read_normalize(self, path): try: content = open(path, "rb").read() except (FileNotFoundError, IsADirectoryError): return None content = content.replace(b"\\\n", b"GEF_MARKER") content = content.replace(b"\t", b" ") for i in range(0x80, 0x100): content = content.replace(bytes([i]), b"") try: content = content.decode("UTF-8") except UnicodeDecodeError: err("Decode error: " + path) return None return content @parse_args def do_invoke(self, args): srcdir = "/usr/include" pattern = re.compile(r"^#define\s+\S*" + args.pattern) for path in GefUtil.walk(srcdir): content = self.read_normalize(path) if content is None: continue for line in content.splitlines(): if pattern.search(line): line = line.replace("GEF_MARKER", "\\\n") gef_print("{:s}: {:s}".format(Color.redify(path), line)) return @register_command class SlubDumpCommand(GenericCommand, BufferingOutput): """Dump SLUB free-list.""" _cmdline_ = "slub-dump" _category_ = "06-h. Qemu-system/KGDB Cooperation - Linux Allocator" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-hs", "--help-for-slab-virtual", action="store_true", help="show ASCII diagram for CONFIG_SLAB_VIRTUAL=y.") parser.add_argument("cache_name", metavar="SLUB_CACHE_NAME", nargs="*", help="filter by specific slub cache name.") parser.add_argument("-l", "--list", action="store_true", help="list all slub cache names.") parser.add_argument("-L", "--list-no-sort", action="store_true", help="list all slub cache names without sort.") parser.add_argument("--meta", action="store_true", help="display offset information.") parser.add_argument("--cpu", type=int, help="filter by specific cpu.") parser.add_argument("-R", "--reverse-walk", action="store_true", help="reverse order walk for slab_caches->list_head.") parser.add_argument("-s", "--simple", action="store_true", help="skip displaying layout and freelist.") parser.add_argument("-v", "--verbose", "--partial", action="store_true", help="dump with partial pages.") parser.add_argument("-vv", "--vverbose", "--node", action="store_true", help="dump with partial pages and node pages.") group = parser.add_mutually_exclusive_group(required=False) group.add_argument("--only-partial", action="store_true", help="dump only partial pages.") group.add_argument("--only-node", action="store_true", help="dump only node pages.") parser.add_argument("--skip-sheaf", action="store_true", help="skip to dump sheaf (6.18~).") parser.add_argument("--hexdump-used", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="hexdump `used chunks` if layout is resolved.") parser.add_argument("--hexdump-freed", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="hexdump `unused (freed) chunks` if layout is resolved.") parser.add_argument("--telescope-used", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="telescope `used chunks` if layout is resolved.") parser.add_argument("--telescope-freed", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="telescope `unused (freed) chunks` if layout is resolved.") parser.add_argument("--slub-debug-y", action="store_true", help="assumes `CONFIG_SLUB_DEBUG=y` and dumps kmem_cache_cpu->full slabs.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cached offset.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") parser.add_argument("--tlbflush-queue", action="store_true", help="dump `slub_tlbflush_queue` (x86-64 only && CONFIG_SLAB_VIRTUAL=y).") parser.add_argument("--skip-page2virt", action="store_true", help="[FOR DEVELOPER] used internally in gef, please don't use it.") parser.add_argument("--no-xor", action="store_true", help="[FOR DEVELOPER] skip xor to chunk->next when `kmem_cache.random` is falsely detected.") parser.add_argument("--no-byte-swap", action="store_true", default=None, help="[FOR DEVELOPER] skip byteswap to chunk->next when `kmem_cache.random` is falsely detected.") parser.add_argument("--offset-random", type=AddressUtil.parse_address, help="[FOR DEVELOPER] user-specified offsetof(kmem_cache, random) when `kmem_cache.random` is falsely detected.") parser.add_argument("--offset-node", type=AddressUtil.parse_address, help="[FOR DEVELOPER] user-specified offsetof(kmem_cache, node) when `kmem_cache.node` is falsely detected.") _syntax_ = parser.format_help() _example_ = [ "{0:s} kmalloc-256 # dump kmalloc-256 from all cpus", "{0:s} kmalloc-256 --cpu 1 # dump kmalloc-256 from cpu 1", "{0:s} kmalloc-256 --partial # show active pages and partial pages", "{0:s} kmalloc-256 --node # show active pages, partial pages and node pages", "{0:s} --list # list slub cache names", "{0:s} -vv --offset-node 0xc8 # user specified offsetof(kmem_cache, node)", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Simplified SLUB structure:", "", " +-kmem_cache----------+ +-kmem_cache--+ +-kmem_cache--+", " | cpu_slab |---+ | cpu_slab | | cpu_slab |", " | cpu_sheaves (6.18~) |---|-+ | cpu_sheaves | | cpu_sheaves |", " | flags | | | | flags | | flags |", " | size | | | | size | | size |", " | object_size | | | | object_size | | object_size |", " | offset | | | | offset | | offset |", " +-slab_caches-+ | name | | | | name | | name |", " ...<->| list_head |<->| list_head |<------->| list_head |<->| list_head |<-> ...", " +-------------+ | random | | | | random | | random |", " | node[] |-+ | | | node[] | | node[] |", " +---------------------+ | | | +-------------+ +-------------+", " | | |", " | | | [sheaf/barn (the fastest path)]", " +--------------------------------------------+ | | +-->+-slab_sheaf-+", " | +------------------------------------------+ | | | barn_list |", " | | +------------+ | | size |", " | | +-__per_cpu_offset-+ | | | objects[] |", " | +-----| cpu0_offset |------+------->+-slub_percpu_sheaves-+ | | ptr |->chunk", " | | | cpu1_offset | | main |---+ | ptr |->chunk", " | | | cpu2_offset | | spare |-->... | ... |", " | | | ... | +---------------------+ +------------+", " | | +------------------+", " | | [active page freelist (fast path)]", " | | +-chunk---+ +-chunk---+", " | | | ^ | | ^ |", " | | | |offset | | |offset |", " | | | v | | v |", " | | +-------------------------------->| next |->| next |->NULL", " | v | +---------+ +---------+", " | +-kmem_cache_cpu-+ |", " | | freelist |--+ [active page freelist (slow path)]", " | | page/slab |---->+-page/slab(active)--+ +-chunk---+ +-chunk---+", " | | partial |--+ | freelist |----+ | ^ | | ^ |", " | +----------------+ | | | | | |offset | | |offset |", " | | +------------------ -+ | | v | | v |", " | | +--->| next |->| next |->NULL", " | | +---------+ +---------+", " | |", " | | [partial page freelist]", " | +->+-page/slab(partial)-+ +-chunk---+ +-chunk---+", " | | freelist |----+ | ^ | | ^ |", " | | next |--+ | | |offset | | |offset |", " | +--------------------+ | | | v | | v |", " | | +--->| next |->| next |->NULL", " | +---------------------+ +---------+ +---------+", " | |", " | v [partial page freelist]", " | +-page/slab(partial)-+ +-chunk---+ +-chunk---+", " | | freelist |----+ | ^ | | ^ |", " | | next |--+ | | |offset | | |offset |", " | +--------------------+ | | | v | | v |", " | | +--->| next |->| next |->NULL", " | +---------------------+ +---------+ +---------+", " | |", " | v", " +--+ ...", " | [numa node partial page freelist]", " v +-page/slab(numa-node)+ +-chunk---+ +-chunk---+", " +-kmem_cache_node-+ | freelist |----+ | ^ | | ^ |", " | partial |---->| next |--+ | | |offset | | |offset |", " | (full) | +---------------------+ | | | v | | v |", " +---| barn (6.18~) | | +--->| next |->| next |->NULL", " | +-----------------+ +---------------------------+ +---------+ +---------+", " | | ... | |", " | | | | [numa node partial page freelist]", " | +-----------------+ | +-page/slab(numa-node)+ +-chunk---+ +-chunk---+", " | | | freelist |----+ | ^ | | ^ |", " | +->| next |--+ | | |offset | | |offset |", " | +---------------------+ | | | v | | v |", " | | +--->| next |->| next |->NULL", " | +---------------------------+ +---------+ +---------+", " | |", " +----+ v", " | ...", " v", " +-node_barn-----+ +-slab_sheaf-+ +-slab_sheaf-+", " | sheaves_full |<------->| barn_list |<-->| barn_list |<-->", " | sheaves_empty |<-->... | ... | | ... |", " +---------------+ +------------+ +------------+", "", "* `struct page` has been split into `struct page` and `struct slab` since kernel 5.17.", " The structure name used for SLUB has been changed to `struct slab`.", "* If all chunks in certain page (or slab) are in use, they will not be displayed by this command.", " This is because they cannot be reached by parsing from `slab_caches`.", " So use `slab-contains` (if you know the address) or `kvmmap` (if you want to see all slabs even if it takes time).", "* `slab_sheaf`/`barn` introduced in 6.18 is not used by default, but used by setting it when calling `kmem_cache_create`.", " `slab_sheaf.objects[]` is a stack that grows downwards and caches freed addresses.", " The top of the stack is represented by `slab_sheaf.size`.", "* To see the CONFIG_SLAB_VIRTUAL ASCII diagram, execute `slub-dump --help-for-slab-virtual`.", ] _note_ = "\n".join(_note_) _note2_ = [ "* A mitigation called CONFIG_SLAB_VIRTUAL was proposed in September 2023 to prevent cross-cache attacks.", " This config is not merged into mainline as of May 2025, but is used in KernelCTF@Google Security Research.", "* A unique feature of CONFIG_SLAB_VIRTUAL is that in addition to the existing SLUB structure,", " it also has a structure for managing released slab structures.", "", "Structures in `CONFIG_SLAB_VIRTUAL=y`", "- v6.1-based, v6.12-based", " +---slab----------+ +---slab----------+ +---slab----------+", " (Temporary Lists) | backing_folio | | backing_folio | | backing_folio |", " +-slub_tlbflush_queue-+ | oo | | oo | | oo |", " ...<->| list_head |<->| flush_list_elem |<-->| flush_list_elem |<->| flush_list_elem |<->...", " +---------------------+ | slab_list | | slab_list | | slab_list |", " | slab_cache |-+ | slab_cache | | slab_cache |", " | ... | | | ... | | ... |", " +-----------------+ | +-----------------+ +-----------------+", " |", " +-----------------------+", " |", " v", " +---kmem_cache-------+ +---kmem_cache-------+", " | cpu_slab |----+ | cpu_slab |", " | flags | | | flags |", " | size | | | size |", " | object_size | | | object_size |", " | offset | | | offset |", " | min | | | min |", " | oo | | | oo |", " | freed_slabs_normal |<---------+ | freed_slabs_normal |", " | freed_slabs_min |<------+ | | freed_slabs_min |", " +-slab_caches-+ | name | | | | | name |", " ...<->| list_head |<->| list_head |<------|--|->| list_head |<->...", " +-------------+ | random | | | | | random |", " | node[] |-+ | | | | node[] |", " +--------------------+ | | | | +--------------------+", " | | | |", " +-------------------------------------------+ | | | +---slab----+ +---slab----+", " | | | | | ... | | ... |", " | +-----------------------------------------+ | | | oo | | oo |", " | | | +-->| slab_list |<->| slab_list |<->...", " | | +-__per_cpu_offset-+ | | ... | | ... |", " | +-----| ... | | +-----------+ +-----------+", " | | +------------------+ |", " | v | +---slab----+ +---slab----+", " | +-kmem_cache_cpu-+ | | ... | | ... |", " | | ... | | | oo | | oo |", " | +----------------+ +----->| slab_list |<->| slab_list |<->...", " v | ... | | ... |", " +-kmem_cache_node-+ +-----------+ +-----------+", " | ... |", " +-----------------+", "", "", "- v6.6-based", " +-virtual_slab-+ +-virtual_slab-+ +-virtual_slab-+", " (Temporary Lists) | ... | | ... | | ... |", " +-slub_tlbflush_queue-+ | slab_cache |--+ | slab_cache | | slab_cache |", " ...<->| list_head |<->| slab_list |<-|->| slab_list |<->| slab_list |<->...", " +---------------------+ | oo | | | oo | | oo |", " | ... | | | ... | | ... |", " +--------------+ | +--------------+ +--------------+", " |", " +---------------------+", " |", " v", " +-kmem_cache------+ +-kmem_cache------+", " | cpu_slab |----+ | cpu_slab |", " | flags | | | flags |", " | size | | | size |", " | object_size | | | object_size |", " | offset | | | offset |", " | oo | | | oo |", " | min | | | min |", " | freed_slabs |<---------+ | freed_slabs |", " | freed_slabs_min |<------+ | | freed_slabs_min |", " | nr_freed_pages | | | | | nr_freed_pages |", " +-slab_caches-+ | name | | | | | name |", " ...<->| list_head |<->| list_head |<------|--|->| list_head |<->...", " +-------------+ | random | | | | | random |", " | node[] |-+ | | | | node[] |", " +-----------------+ | | | | +-----------------+", " | | | |", " +----------------------------------------+ | | | +-virtual_slab-+ +-virtual_slab-+", " | | | | | ... | | ... |", " | +--------------------------------------+ | +-->| slab_list |<->| slab_list |<->...", " | | | | oo | | oo |", " | | +-__per_cpu_offset-+ | | ... | | ... |", " | +-----| ... | | +--------------+ +--------------+", " | | +------------------+ |", " | v | +-virtual_slab-+ +-virtual_slab-+", " | +-kmem_cache_cpu-+ | | ... | | ... |", " | | ... | +----->| slab_list |<->| slab_list |<->...", " | +----------------+ | oo | | oo |", " v | ... | | ... |", " +-kmem_cache_node-+ +--------------+ +--------------+", " | ... |", " +-----------------+", "", "* The freed slab structure is initially connected to `slub_tlbflush_queue`.", " It is then reconnected to kmem_cache->freed_slabs_normal or freed_slabs_min or freed_slabs.", "* If oo_order(virtual_slab->slab.oo) == oo_order(kmem_cache->min),", " `slub_tlbflush_worker` uses `kmem_cache->freed_slabs_min` as freelist of pages for the `kmem_cache`.", " Otherwise, it uses `kmem_cache->freed_slabs`.", ] _note2_ = "\n".join(_note2_) @Cache.cache_until_next def parse_kmem_caches_for_initialize(self): seen = [self.slab_caches] current = self.slab_caches while True: current = read_int_from_memory(current) if current in seen: break seen.append(current) kmem_caches = seen[1:] # skip slab_caches itself return kmem_caches def resolve_kmem_cache_offset_list(self): """ struct kmem_cache { struct kmem_cache_cpu *cpu_slab; // In fact, the offset value, not the pointer struct lock_class_key { // CONFIG_LOCKDEP=y && 6.18 <= kernel union { // CONFIG_LOCKDEP=y && 6.18 <= kernel struct hlist_node hash_entry; // CONFIG_LOCKDEP=y && 6.18 <= kernel struct lockdep_subclass_key { // CONFIG_LOCKDEP=y && 6.18 <= kernel char __one_byte; // CONFIG_LOCKDEP=y && 6.18 <= kernel } __attribute__ ((__packed__)) subkeys[8]; // CONFIG_LOCKDEP=y && 6.18 <= kernel }; // CONFIG_LOCKDEP=y && 6.18 <= kernel } lock_key; // CONFIG_LOCKDEP=y && 6.18 <= kernel struct slub_percpu_sheaves __percpu *cpu_sheaves; // 6.18 <= kernel slab_flags_t flags; // unsigned int (+ padding 4 byte) unsigned long min_partial; unsigned int size; unsigned int object_size; struct reciprocal_value { // u32 m; // u8 sh1, sh2; // (+ padding 2 byte) } reciprocal_size; // if 5.9 <= kernel unsigned int offset; unsigned int cpu_partial; // if CONFIG_SLUB_CPU_PARTIAL=y unsigned int cpu_partial_slabs; // if CONFIG_SLUB_CPU_PARTIAL=y && 5.16 <= kernel struct kmem_cache_order_objects oo; struct kmem_cache_order_objects max; // if kernel < 5.19 struct kmem_cache_order_objects min; gfp_t allocflags; // unsigned int int refcount; void (*ctor)(void *); unsigned int inuse; unsigned int align; unsigned int red_left_pad; const char *name; struct list_head list; <-----> struct list_head <-----> struct list_head <-----> ... """ # fast path try: self.kmem_cache_offset_list = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache*)0).list") ) return except gdb.error: pass # slow path self.kmem_cache_offset_list = None kmem_caches = self.parse_kmem_caches_for_initialize() # This value should be at most 0x70 by default. However, cases using offset 0x98 have been observed. # This occurs when CONFIG_SLAB_VIRTUAL=y, which is not in the mainline but is introduced by some kernels. # Therefore, the search range is expanded. max_offset = 0x100 for candidate_offset in range(current_arch.ptrsize * 2, max_offset, current_arch.ptrsize): # backward search for the start of `struct kmem_cache` found = True seen = [] for kmem_cache in kmem_caches: val = read_int_from_memory(kmem_cache - candidate_offset) if val in [0, 0xffff_ffff, 0xffff_ffff_ffff_ffff]: found = False break if val in seen: found = False break else: seen.append(val) for cpuoff in self.cpu_offset: if not is_valid_addr(AddressUtil.align_address(val + cpuoff)): found = False break if found: self.kmem_cache_offset_list = candidate_offset return return def resolve_kmem_cache_offset_random(self): # fast path try: # find kmem_cache.random self.kmem_cache_offset_random = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache*)0).random") ) return except gdb.error: try: # kmem_cache exists but has no random member gdb.parse_and_eval("(struct kmem_cache*)0") self.kmem_cache_offset_random = None return except gdb.error: pass # slow path if self.args.no_xor: self.kmem_cache_offset_random = None return if self.args.offset_random is not None: self.kmem_cache_offset_random = self.args.offset_random return self.kmem_cache_offset_random = None # CONFIG_SLAB_FREELIST_HARDENED=n """ struct kmem_cache { ... struct list_head list; <-----> struct list_head <-----> struct list_head <-----> ... struct kobject kobj; // if CONFIG_SYSFS=y struct work_struct kobj_remove_work; // if CONFIG_SYSFS=y && kernel < 5.9 struct memcg_cache_params memcg_params; // if CONFIG_MEMCG=y && kernel < 5.9 unsigned int max_attr_size; // if CONFIG_MEMCG=y && kernel < 5.9 struct kset *memcg_kset; // if CONFIG_MEMCG=y && CONFIG_SYSFS=y && kernel < 5.9 unsigned long random; // if CONFIG_SLAB_FREELIST_HARDENED=y unsigned int remote_node_defrag_ratio; // if CONFIG_NUMA=y unsigned int *random_seq; // if CONFIG_SLAB_FREELIST_RANDOM=y struct kasan_cache { int alloc_meta_offset; int free_meta_offset; bool is_kmalloc; } kasan_info; // if CONFIG_KASAN=y unsigned int useroffset; // kernel < 6.2 || (6.2 <= kernel && CONFIG_HARDENED_USERCOPY=y) unsigned int usersize; // kernel < 6.2 || (6.2 <= kernel && CONFIG_HARDENED_USERCOPY=y) struct kmem_cache_node *node[MAX_NUMNODES]; }; """ kmem_caches = self.parse_kmem_caches_for_initialize() for i in range(2, 0x40): candidate_offset = current_arch.ptrsize * i found = True count = 0 seen = [] for kmem_cache in kmem_caches: if not is_valid_addr(kmem_cache + candidate_offset): found = False break val = read_int_from_memory(kmem_cache + candidate_offset) # random may happen to be a valid address, so some are acceptable if is_valid_addr(val): count += 1 if count >= 3: found = False break else: if val > 0xff_ffff: count -= 1 # The probability of the same random value appearing multiple times is negligible if val != 0: if val in seen: found = False break seen.append(val) if found: # Too few random numbers if len(seen) < 10: found = False # Occurrences of non-negative small integers are stochastically rare elif sum([0 < x < 0x10_0000 for x in seen]) >= 3: found = False # Occurrences of big integers are stochastically rare elif sum([0xffff_0000_0000_0000 < x <= 0xffff_ffff_ffff_ffff for x in seen]) >= 3: found = False # Occurrences of 0xXXXX000 are stochastically rare elif sum([x and (x & 0xfff) == 0 for x in seen]) >= 3: found = False if found: # search for `struct kmem_cache_node *node` or `unsigned int *random_seq` for i in range(1, 9): maybe_ptrs = [] for kmem_cache in kmem_caches: v = read_int_from_memory(kmem_cache + candidate_offset + current_arch.ptrsize * i) maybe_ptrs.append(v) # they should be at the same offset if all(is_valid_addr(p) for p in maybe_ptrs): break else: found = False if found: self.kmem_cache_offset_random = self.kmem_cache_offset_list + candidate_offset return return def resolve_kmem_cache_offset_node(self): # fast path try: self.kmem_cache_offset_node = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache*)0).node") ) return except gdb.error: pass # slow path if self.args.offset_node is not None: self.kmem_cache_offset_node = self.args.offset_node return self.kmem_cache_offset_node = None kversion = Kernel.kernel_version() """ struct kmem_cache { ... unsigned int object_size; ... struct list_head list; <-----> struct list_head <-----> struct list_head <-----> ... struct kobject kobj; // if CONFIG_SYSFS=y struct work_struct kobj_remove_work; // if CONFIG_SYSFS=y && kernel < 5.9 struct memcg_cache_params memcg_params; // if CONFIG_MEMCG=y && kernel < 5.9 unsigned int max_attr_size; // if CONFIG_MEMCG=y && kernel < 5.9 struct kset *memcg_kset; // if CONFIG_MEMCG=y && CONFIG_SYSFS=y && kernel < 5.9 unsigned long random; // if CONFIG_SLAB_FREELIST_HARDENED=y unsigned int remote_node_defrag_ratio; // if CONFIG_NUMA=y (<-- maybe 1000) unsigned int *random_seq; // if CONFIG_SLAB_FREELIST_RANDOM=y struct kasan_cache { int alloc_meta_offset; int free_meta_offset; bool is_kmalloc; } kasan_info; // if CONFIG_KASAN=y unsigned int useroffset; // kernel < 6.2 || (6.2 <= kernel && CONFIG_HARDENED_USERCOPY=y) unsigned int usersize; // kernel < 6.2 || (6.2 <= kernel && CONFIG_HARDENED_USERCOPY=y) struct kmem_cache_node *node[MAX_NUMNODES]; <-- this includes SPINLOCK_MAGIC if CONFIG_DEBUG_SPINLOCK=y } """ kmem_caches = self.parse_kmem_caches_for_initialize() # heuristic way 1 (SPINLOCK_MAGIC) if is_64bit(): # kmem_cache_node[0]->list_lock has SPINLOCK_MAGIC when CONFIG_DEBUG_SPINLOCK=y start_offset = self.kmem_cache_offset_list + current_arch.ptrsize * 2 # sizeof(kmem_cache.list) search_range = 0x100 if "5.9" <= kversion else 0x200 for candidate_offset in range(start_offset, start_offset + search_range, current_arch.ptrsize): kmem_cache_top = kmem_caches[0] - self.kmem_cache_offset_list x = read_int_from_memory(kmem_cache_top + candidate_offset) if not is_valid_addr(x): continue y = read_int_from_memory(x) if y != 0xdead_4ead_0000_0000: # SPINLOCK_MAGIC continue # found self.quiet_info("offset of node is found by heuristic way1") self.kmem_cache_offset_node = candidate_offset return # helper functions (for way2, way4) def get_next_valid_ptr_offset(addr, in_range=5): """Return the nearest valid pointer within a specified range.""" # Depending on the configuration, the offset where the address exists will vary, # so we need to find the closest valid address. for i in range(in_range): candidate_offset = current_arch.ptrsize * i v = read_int_from_memory(addr + candidate_offset) if is_valid_addr(v): return candidate_offset return None def is_random_seq(addr, N=8): # What random_seq points to is a rearrangement of sequential numbers of type u32. # Therefore, no two values will be the same. If the first some elements contain # the same value, we can determine that it is not random_seq but node[0]. # # kmem_cache_node example # 0xffff888003c40180|+0x0000|+000: 0xb7f638bb00000000 # 0xffff888003c40188|+0x0008|+001: 0x000000000000000a # 0xffff888003c40190|+0x0010|+002: 0xffffea000012da90 # 0xffff888003c40198|+0x0018|+003: 0xffffea0000118710 # 0xffff888003c401a0|+0x0020|+004: 0x0000000000000010 # 0xffff888003c401a8|+0x0028|+005: 0x0000000000000800 # random_seq example # 0xffff8f9d8104c400|+0x0000|+000: 0x00000e8000000b40 # 0xffff8f9d8104c408|+0x0008|+001: 0x00000f4000000080 # 0xffff8f9d8104c410|+0x0010|+002: 0x000000a000000ae0 # 0xffff8f9d8104c418|+0x0018|+003: 0x00000bc0000001e0 # 0xffff8f9d8104c420|+0x0020|+004: 0x0000020000000360 # 0xffff8f9d8104c428|+0x0028|+005: 0x0000024000000320 # random_seq another example # 0xffff89c3ce772240|+0x0000|+000: 0x00000000000019e0 # 0xffff89c3ce772248|+0x0008|+001: 0x00005a9000004da0 # 0xffff89c3ce772250|+0x0010|+002: 0x00000cf0000026d0 # 0xffff89c3ce772258|+0x0018|+003: 0x00006780000040b0 # 0xffff89c3ce772260|+0x0020|+004: 0x00000000000033c0 # 0xffff89c3ce772268|+0x0028|+005: 0x0000000000000000 # 0xffff89c3ce772270|+0x0030|+006: 0x0000000000000000 sizeof_uint32 = 4 data = read_memory(addr, sizeof_uint32 * N) data = slice_unpack(data, sizeof_uint32) if len(set(data)) != N: return False if any(x & 0x80000000 for x in data): return False return True # helper functions end # heuristic way 2 (remote_node_defrag_ratio == 1000) if is_64bit(): # Find the offset that has the initial value of 1000 for remote_node_defrag_ratio. # The first valid pointer encountered after that is either random_seq or node[0]. # We look at the contents to determine whether the pointer is random_seq. start_offset = self.kmem_cache_offset_list + current_arch.ptrsize * 2 # sizeof(kmem_cache.list) search_range = 0x100 if "5.9" <= kversion else 0x200 for candidate_offset in range(start_offset, start_offset + search_range, current_arch.ptrsize): # First, we search remote_node_defrag_ratio. remote_node_defrag_ratio_1000_count = 0 for kmem_cache in kmem_caches: kmem_cache_top = kmem_cache - self.kmem_cache_offset_list remote_node_defrag_ratio = read_int32_from_memory(kmem_cache_top + candidate_offset) if remote_node_defrag_ratio == 0x3e8: remote_node_defrag_ratio_1000_count += 1 if remote_node_defrag_ratio_1000_count < len(kmem_caches) // 10: # heuristic threshold: 10% continue offset_remote_node_defrag_ratio = candidate_offset offset_random_seq = offset_remote_node_defrag_ratio + current_arch.ptrsize # Check the value next to remote_node_defrag_ratio whether pointer or not. kmem_cache_0_top = kmem_caches[0] - self.kmem_cache_offset_list x = read_int_from_memory(kmem_cache_0_top + offset_random_seq) if is_valid_addr(x): # At this point, x is random_seq or node[0] if not is_random_seq(x): # x is not random_seq, but node[0] self.quiet_info("offset of node is found by heuristic way2-1") self.kmem_cache_offset_node = offset_random_seq return else: # x is random_seq, so skip it start_offset_node_search = offset_random_seq + current_arch.ptrsize else: # x is kasan_info or user_offset start_offset_node_search = offset_random_seq extend_offset = get_next_valid_ptr_offset(kmem_cache_0_top + start_offset_node_search) if extend_offset is not None: offset_node = start_offset_node_search + extend_offset y = read_int_from_memory(kmem_cache_0_top + offset_node) if is_valid_addr(y) and not is_random_seq(y): self.quiet_info("offset of node is found by heuristic way2-2") self.kmem_cache_offset_node = offset_node return # heuristic way 3 (relationship of user_offset, user_size, and object_size) # This method is valid for kernel < 6.2, or (CONFIG_HARDENED_USERCOPY=y and 6.2 <= kernel). start_offset = self.kmem_cache_offset_list + current_arch.ptrsize * 2 # sizeof(kmem_cache.list) search_range = 0x100 if "5.9" <= kversion else 0x200 for candidate_offset in range(start_offset, start_offset + search_range, current_arch.ptrsize): found = True user_offset_user_size_non_zero_flag = False for kmem_cache in kmem_caches: kmem_cache_top = kmem_cache - self.kmem_cache_offset_list # Check whether user_offset, user_size, and object_size satisfy some relationships user_offset = read_int32_from_memory(kmem_cache_top + candidate_offset) user_size = read_int32_from_memory(kmem_cache_top + candidate_offset + 4) object_size = read_int32_from_memory(kmem_cache_top + self.kmem_cache_offset_object_size) if user_offset == user_size == 0: continue user_offset_user_size_non_zero_flag = True if user_offset != 0 and user_size == 0: found = False break if object_size < user_size: found = False break # And check that the immediately following node is a valid address node_offset = candidate_offset + 4 + 4 node_addr = read_int_from_memory(kmem_cache_top + node_offset) if not is_valid_addr(node_addr): found = False break if user_offset_user_size_non_zero_flag is False: found = False if found: self.quiet_info("offset of node is found by heuristic way3") self.kmem_cache_offset_node = node_offset return # heuristic way 4 (detect random_seq) if is_64bit() and self.kmem_cache_offset_random: # user_offset and user_size probably don't exist. # remote_node_defrag_ratio does not exist either. # Search random_seq from random, then go like heuristic way 2. offset_random_seq = self.kmem_cache_offset_random + current_arch.ptrsize kmem_cache_0_top = kmem_caches[0] - self.kmem_cache_offset_list x = read_int_from_memory(kmem_cache_0_top + offset_random_seq) if is_valid_addr(x): # At this point, x is random_seq or node[0] if not is_random_seq(x): # x is not random_seq, but node[0] self.quiet_info("offset of node is found by heuristic way4-1") self.kmem_cache_offset_node = offset_random_seq return else: # x is random_seq, so skip it start_offset_node_search = offset_random_seq + current_arch.ptrsize else: # x is kasan_info or user_offset start_offset_node_search = offset_random_seq # not found extend_offset = get_next_valid_ptr_offset(kmem_cache_0_top + start_offset_node_search) if extend_offset is not None: offset_node = start_offset_node_search + extend_offset y = read_int_from_memory(kmem_cache_0_top + offset_node) if is_valid_addr(y) and not is_random_seq(y): self.quiet_info("offset of node is found by heuristic way4-2") self.kmem_cache_offset_node = offset_node return # heuristic way 5 (consecutive kmem_cache) # A kmem_cache may itself be allocated contiguously. # It may be possible to find a node from this relationship. # +-kmem_cache-+ # | ... | # ...-->| list |-->...(*) (1) detect consecutive kmem_cache # | ... | -------^ # | node | | (2) search this area # | (padding) | | # +-kmem_cache-+ -------v # | ... | # (*)...-->| list |-->... # | ... | # | node | # | (padding) | # +------------+ # Find the two nearest pairs and count the number of cases min_diff_pairs = [] min_diff = 0xffff_ffff_ffff_ffff for km1, km2 in itertools.combinations(kmem_caches, 2): diff = abs(km1 - km2) if diff < min_diff: min_diff_pairs = [(min(km1, km2), max(km1, km2))] min_diff = diff continue if diff == min_diff: min_diff_pairs.append((min(km1, km2), max(km1, km2))) continue # If there are enough such cases, we can determine that they are likely arranged consecutively if len(min_diff_pairs) >= 10: # Specifies the maximum traversal range for scanning node locations # Note that these are kmem_cache.list addresses km0_0, km0_1 = min_diff_pairs[0] km0_1_top = km0_1 - self.kmem_cache_offset_list km0_0_after_list = km0_0 + current_arch.ptrsize * 2 max_search_range = km0_1_top - km0_0_after_list # Check that the address is valid for all pairs for i in range(max_search_range // current_arch.ptrsize): candidate_offset = current_arch.ptrsize * (i + 1) found = True for _, m in min_diff_pairs: m_top = m - self.kmem_cache_offset_list x = read_int_from_memory(m_top - candidate_offset) if not is_valid_addr(x): found = False break if found: offset_node_from_after_list = max_search_range - candidate_offset offset_after_list = self.kmem_cache_offset_list + current_arch.ptrsize * 2 maxlen = len(list(itertools.combinations(kmem_caches, 2))) msg = "min_diff_pairs:{:d}/{:d}, ".format(len(min_diff_pairs), maxlen) msg += "min_diff:{:#x}".format(min_diff) self.quiet_info("offset of node is found by heuristic way5 ({:s})".format(msg)) self.kmem_cache_offset_node = offset_after_list + offset_node_from_after_list return return def resolve_for_CONFIG_SLAB_VIRTUAL(self): kversion = Kernel.kernel_version() # Feature: CONFIG_SLAB_VIRTUAL (this patchset is supported x86-64 only). # See https://lwn.net/Articles/944647/. if not is_x86_64(): self.slab_virtual_enabled = False elif kversion < "6.1": self.slab_virtual_enabled = False elif not Symbol.get_ksymaddr("slub_tlbflush_worker"): self.slab_virtual_enabled = False else: # Heuristic detection that CONFIG_SLAB_VIRTUAL is enabled or not from `struct kmem_cache`. # If enabled, `struct kmem_cache` has 2 doubly-link-lists above `kmem_cache->name`. # - kmem_cache->freed_slabs_normal (6.1.56 <= kernel) # - kmem_cache->freed_slabs (kernel < 6.1.56) # - kmem_cache->freed_slabs_min def has_freed_slabs_lists(freed_slabs_normal, freed_slabs_min): return is_double_link_list(freed_slabs_normal) and is_double_link_list(freed_slabs_min) kmem_caches = self.parse_kmem_caches_for_initialize() top = kmem_caches[0] - self.kmem_cache_offset_list offset_freed_slabs_normal = current_arch.ptrsize * 9 offset_freed_slabs_min = current_arch.ptrsize * 11 self.slab_virtual_enabled = has_freed_slabs_lists( top + offset_freed_slabs_normal, top + offset_freed_slabs_min, ) # cares CONFIG_SLUB_CPU_PARTIAL=n if not self.slab_virtual_enabled: offset_freed_slabs_normal = current_arch.ptrsize * 8 offset_freed_slabs_min = current_arch.ptrsize * 10 self.slab_virtual_enabled = has_freed_slabs_lists( top + offset_freed_slabs_normal, top + offset_freed_slabs_min, ) # parse kmem_cache for CONFIG_SLAB_VIRTUAL if self.slab_virtual_enabled: self.quiet_info("CONFIG_SLAB_VIRTUAL: detected") # 1. get global queue buffering freed slabs self.slub_tlbflush_queue = KernelAddressHeuristicFinder.get_slub_tlbflush_queue() if not self.slub_tlbflush_queue: return False self.quiet_info("slub_tlbflush_queue: {:#x}".format(self.slub_tlbflush_queue)) # 2. parse extra members of kmem_cache # - kmem_cache->nr_freed_pages (6.1-based, 6.12-based) or kmem_cache->virtual.nr_freed_pages (6.6-based) # - kmem_cache->freed_slabs_normal (6.1-based, 6.12-based) or kmem_cache->virtual.freed_slabs (6.6-based) # - kmem_cache->freed_slabs_min (6.1-based, 6.12-based) or kmem_cache->virtual.freed_slabs_min (6.6-based) # offsetof(kmem_cache, nr_freed_pages) if kversion < "6.1.56": self.kmem_cache_offset_nr_freed_pages = offset_freed_slabs_normal - current_arch.ptrsize * 1 else: self.kmem_cache_offset_nr_freed_pages = offset_freed_slabs_min + current_arch.ptrsize * 2 self.quiet_info("offsetof(kmem_cache, nr_freed_pages): {:#x}".format( self.kmem_cache_offset_nr_freed_pages, )) # offsetof(kmem_cache, freed_slabs_normal) self.kmem_cache_offset_freed_slabs_normal = offset_freed_slabs_normal self.quiet_info("offsetof(kmem_cache, freed_slabs_normal): {:#x}".format( self.kmem_cache_offset_freed_slabs_normal, )) # offsetof(kmem_cache, freed_slabs_min) self.kmem_cache_offset_freed_slabs_min = offset_freed_slabs_min self.quiet_info("offsetof(kmem_cache, freed_slabs_min): {:#x}".format( self.kmem_cache_offset_freed_slabs_min, )) else: if self.args.tlbflush_queue: warn("CONFIG_SLAB_VIRTUAL is not enabled (maybe), option `--tlbflush-queue` is ignored.") return def resolve_kmem_cache_node_offset_partial(self): # fast path try: self.kmem_cache_node_offset_partial = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache_node*)0).partial") ) return except gdb.error: pass # slow path if self.kmem_cache_offset_node is None: self.kmem_cache_node_offset_partial = None return self.kmem_cache_node_offset_partial = None kmem_caches = self.parse_kmem_caches_for_initialize() top = kmem_caches[0] - self.kmem_cache_offset_list node = read_int_from_memory(top + self.kmem_cache_offset_node) for i in range(1, 16): offset_partial = current_arch.ptrsize * i if is_double_link_list(node + offset_partial): self.kmem_cache_node_offset_partial = offset_partial return return def get_slub_percpu_sheaves(self, addr, cpu): cpu_sheaves = read_int_from_memory(addr + self.kmem_cache_offset_cpu_sheaves) if cpu_sheaves == 0: return None if len(self.cpu_offset) > 0: slub_percpu_sheaves = cpu_sheaves + self.cpu_offset[cpu] else: slub_percpu_sheaves = cpu_sheaves return AddressUtil.align_address(slub_percpu_sheaves) def resolve_slub_percpu_sheaves_offset_main(self): self.slub_percpu_sheaves_offset_main = None if self.kmem_cache_offset_cpu_sheaves is None: return # fast path try: self.slub_percpu_sheaves_offset_main = to_unsigned_long( gdb.parse_and_eval("&((struct slub_percpu_sheaves*)0).main") ) return except gdb.error: pass """ struct slub_percpu_sheaves { struct { #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map { struct lock_class_key *key; struct lock_class *class_cache[2]; const char *name; u8 wait_type_outer; u8 wait_type_inner; u8 lock_type; #ifdef CONFIG_LOCK_STAT int cpu; unsigned long ip; #endif } dep_map; struct task_struct *owner; #endif u8 acquired; } local_trylock_t lock; struct slab_sheaf *main; struct slab_sheaf *spare; struct slab_sheaf *rcu_free; }; """ # slow path kmem_caches = self.parse_kmem_caches_for_initialize() for kmem_cache in kmem_caches: kmem_cache_top = kmem_cache - self.kmem_cache_offset_list for cpu in range(self.ncpus): slub_percpu_sheaves = self.get_slub_percpu_sheaves(kmem_cache_top, cpu) if slub_percpu_sheaves is None: continue # CONFIG_DEBUG_LOCK_ALLOC=n acquired = read_int_from_memory(slub_percpu_sheaves) main = read_int_from_memory(slub_percpu_sheaves + current_arch.ptrsize) if acquired in [0, 1] and is_valid_addr(main): self.slub_percpu_sheaves_offset_main = current_arch.ptrsize return # CONFIG_DEBUG_LOCK_ALLOC=y for i in range(8): v = read_int_from_memory(slub_percpu_sheaves + current_arch.ptrsize * i) if v == kmem_cache: # owner self.slub_percpu_sheaves_offset_main = current_arch.ptrsize * (i + 2) return return def resolve_kmem_cache_node_offset_barn(self): self.kmem_cache_node_offset_barn = None if self.kmem_cache_offset_node is None: return # fast path try: self.kmem_cache_node_offset_barn = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache_node*)0).barn") ) return except gdb.error: pass """ struct kmem_cache_node { spinlock_t list_lock; unsigned long nr_partial; struct list_head partial; atomic_long_t nr_slabs; // if CONFIG_SLUB_DEBUG=y atomic_long_t total_objects; // if CONFIG_SLUB_DEBUG=y struct list_head full; // if CONFIG_SLUB_DEBUG=y struct node_barn *barn; // 6.18 <= kernel }; """ kmem_caches = self.parse_kmem_caches_for_initialize() for kmem_cache in kmem_caches: kmem_cache_top = kmem_cache - self.kmem_cache_offset_list for cpu in range(self.ncpus): # is kmem_cache enabled sheaves? slub_percpu_sheaves = self.get_slub_percpu_sheaves(kmem_cache_top, cpu) if slub_percpu_sheaves is None: continue # get kmem_cache_node kmem_cache_node_array = kmem_cache_top + self.kmem_cache_offset_node if not is_valid_addr(kmem_cache_node_array): continue kmem_cache_node = read_int_from_memory(kmem_cache_node_array) if not is_valid_addr(kmem_cache_node): # check 1st element is enough continue # search list_head for i in range(0x20): offset_candidate_list_head = current_arch.ptrsize * i if is_double_link_list(kmem_cache_node + offset_candidate_list_head): offset_candidate = offset_candidate_list_head + current_arch.ptrsize * 2 # search valid pointer which locates next to it if is_valid_addr_addr(kmem_cache_node + offset_candidate): self.kmem_cache_node_offset_barn = offset_candidate return return def resolve_node_barn_offset_sheaves_full(self): self.node_barn_offset_sheaves_full = None if self.kmem_cache_offset_node is None: return # fast path try: self.node_barn_offset_sheaves_full = to_unsigned_long( gdb.parse_and_eval("&((struct node_barn*)0).sheaves_full") ) return except gdb.error: pass """ struct node_barn { spinlock_t lock; struct list_head sheaves_full; struct list_head sheaves_empty; unsigned int nr_full; unsigned int nr_empty; }; """ # slow path kmem_caches = self.parse_kmem_caches_for_initialize() for kmem_cache in kmem_caches: kmem_cache_top = kmem_cache - self.kmem_cache_offset_list for cpu in range(self.ncpus): # is kmem_cache enabled sheaves? slub_percpu_sheaves = self.get_slub_percpu_sheaves(kmem_cache_top, cpu) if slub_percpu_sheaves is None: continue # get kmem_cache_node kmem_cache_node_array = kmem_cache_top + self.kmem_cache_offset_node if not is_valid_addr(kmem_cache_node_array): continue kmem_cache_node = read_int_from_memory(kmem_cache_node_array) if not is_valid_addr(kmem_cache_node): # check 1st element is enough continue # get barn barn = read_int_from_memory(kmem_cache_node + self.kmem_cache_node_offset_barn) if is_valid_addr(barn): # search list_head for i in range(0x20): offset_candidate = current_arch.ptrsize * i if is_double_link_list(barn + offset_candidate): self.node_barn_offset_sheaves_full = offset_candidate return return def resolve_sheaves(self): self.sheaves_enabled = False kversion = Kernel.kernel_version() if kversion < "6.18": return if self.kmem_cache_offset_cpu_sheaves is None: return if self.kmem_cache_offset_node is None: return # offsetof(slub_percpu_sheaves, main) self.resolve_slub_percpu_sheaves_offset_main() if self.slub_percpu_sheaves_offset_main is None: self.quiet_info("offsetof(slub_percpu_sheaves, main): Not found") return self.quiet_info("offsetof(slub_percpu_sheaves, main): {:#x}".format(self.slub_percpu_sheaves_offset_main)) # offsetof(kmem_cache_node, barn) self.resolve_kmem_cache_node_offset_barn() if self.kmem_cache_node_offset_barn is None: self.quiet_info("offsetof(kmem_cache_node, barn): Not found") return self.quiet_info("offsetof(kmem_cache_node, barn): {:#x}".format(self.kmem_cache_node_offset_barn)) # offsetof(node_barn, sheaves_full) self.resolve_node_barn_offset_sheaves_full() if self.node_barn_offset_sheaves_full is None: self.quiet_info("offsetof(node_barn, sheaves_full): Not found") return self.quiet_info("offsetof(node_barn, sheaves_full): {:#x}".format(self.node_barn_offset_sheaves_full)) # offsetof(slub_percpu_sheaves, spare) self.slub_percpu_sheaves_offset_spare = self.slub_percpu_sheaves_offset_main + current_arch.ptrsize """ struct slab_sheaf { union { struct rcu_head rcu_head; struct list_head barn_list; unsigned int capacity; /* only used for prefilled sheafs */ }; struct kmem_cache *cache; unsigned int size; int node; /* only used for rcu_sheaf */ void *objects[]; }; """ # offsetof(slab_sheaf, barn_list) self.slab_sheaf_offset_barn_list = 0 # offsetof(slab_sheaf, kmem_cache) self.slab_sheaf_offset_kmem_cache = self.slab_sheaf_offset_barn_list + current_arch.ptrsize * 2 # offsetof(slab_sheaf, size) self.slab_sheaf_offset_size = self.slab_sheaf_offset_kmem_cache + current_arch.ptrsize # offsetof(slab_sheaf, objects) self.slab_sheaf_offset_objects = self.slab_sheaf_offset_size + current_arch.ptrsize # offsetof(node_barn, sheaves_empty) self.node_barn_offset_sheaves_empty = self.node_barn_offset_sheaves_full + current_arch.ptrsize * 2 self.sheaves_enabled = True return # CONFIG_SLAB_VIRTUAL=n """ struct kmem_cache { struct kmem_cache_cpu *cpu_slab; // In fact, the offset value, not the pointer struct lock_class_key { // if CONFIG_LOCKDEP=y && 6.18 <= kernel union { // if CONFIG_LOCKDEP=y && 6.18 <= kernel struct hlist_node hash_entry; // if CONFIG_LOCKDEP=y && 6.18 <= kernel struct lockdep_subclass_key { // if CONFIG_LOCKDEP=y && 6.18 <= kernel char __one_byte; // if CONFIG_LOCKDEP=y && 6.18 <= kernel } __attribute__ ((__packed__)) subkeys[8]; // if CONFIG_LOCKDEP=y && 6.18 <= kernel }; // if CONFIG_LOCKDEP=y && 6.18 <= kernel } lock_key; // if CONFIG_LOCKDEP=y && 6.18 <= kernel struct slub_percpu_sheaves __percpu *cpu_sheaves; // if 6.18 <= kernel slab_flags_t flags; // unsigned int (+ padding 4 byte) unsigned long min_partial; unsigned int size; unsigned int object_size; struct reciprocal_value { // u32 m; // u8 sh1, sh2; // (+ padding 2 byte) } reciprocal_size; // if 5.9 <= kernel unsigned int offset; unsigned int cpu_partial; // if CONFIG_SLUB_CPU_PARTIAL=y unsigned int cpu_partial_slabs; // if CONFIG_SLUB_CPU_PARTIAL=y && 5.16 <= kernel struct kmem_cache_order_objects oo; struct kmem_cache_order_objects max; // if kernel < 5.19 struct kmem_cache_order_objects min; gfp_t allocflags; // unsigned int int refcount; void (*ctor)(void *); unsigned int inuse; unsigned int align; unsigned int red_left_pad; const char *name; struct list_head list; <-----> struct list_head <-----> struct list_head <-----> ... struct kobject kobj; // if CONFIG_SYSFS=y struct work_struct kobj_remove_work; // if CONFIG_SYSFS=y && kernel < 5.9 struct memcg_cache_params memcg_params; // if CONFIG_MEMCG=y && kernel < 5.9 unsigned int max_attr_size; // if CONFIG_MEMCG=y && kernel < 5.9 struct kset *memcg_kset; // if CONFIG_MEMCG=y && CONFIG_SYSFS=y && kernel < 5.9 unsigned long random; // if CONFIG_SLAB_FREELIST_HARDENED=y unsigned int remote_node_defrag_ratio; // if CONFIG_NUMA=y unsigned int *random_seq; // if CONFIG_SLAB_FREELIST_RANDOM=y struct kasan_cache { int alloc_meta_offset; int free_meta_offset; bool is_kmalloc; } kasan_info; // if CONFIG_KASAN=y unsigned int useroffset; // kernel < 6.2 || (6.2 <= kernel && CONFIG_HARDENED_USERCOPY=y) unsigned int usersize; // kernel < 6.2 || (6.2 <= kernel && CONFIG_HARDENED_USERCOPY=y) struct kmem_cache_node *node[MAX_NUMNODES]; }; struct kmem_cache_cpu { void **freelist; unsigned long tid; struct page *page; // if kernel < 5.17 struct page *partial; // if kernel < 5.17 && CONFIG_SLUB_CPU_PARTIAL=y struct slab *slab; // if 5.17 <= kernel struct slab *partial; // if 5.17 <= kernel && CONFIG_SLUB_CPU_PARTIAL=y local_lock_t lock; // if 5.15 <= kernel unsigned stat[NR_SLUB_STAT_ITEMS]; // if CONFIG_SLUB_STATS=y }; struct page { // if kernel < 4.18 unsigned long flags; union { }; // long void *freelist; unsigned inuse:16, objects:15, frozen:1; atomic_t _refcount; // if kernel < 4.16 struct page *next; int pages; // if 64bit else `short pages` int pobjects; // if 64bit else `short pobjects` struct kmem_cache *slab_cache; struct mem_cgroup *mem_cgroup; // if CONFIG_MEMCG=y void *virtual; // if CONFIG_WANT_PAGE_VIRTUAL=y void *shadow; // if CONFIG_KMEMCHECK=y && kernel < 4.14 int _last_cpuid; // if CONFIG_LAST_CPUPID_NOT_IN_PAGE_FLAGS=y }; struct page { // if 4.18 <= kernel < 5.17 unsigned long flags; struct page *next; int pages; // if 64bit else `short pages` int pobjects; // if 64bit else `short pobjects` struct kmem_cache *slab_cache; void *freelist; unsigned inuse:16, objects:15, frozen:1; union {}; // unsigned int atomic_t _refcount; unsigned long memcg_data; // if CONFIG_MEMCG=y && 5.10 <= kernel struct mem_cgroup *mem_cgroup; // if CONFIG_MEMCG=y && kernel < 5.10 void *virtual; // if CONFIG_WANT_PAGE_VIRTUAL=y int _last_cpuid; // if CONFIG_LAST_CPUPID_NOT_IN_PAGE_FLAGS=y }; struct slab { // if CONFIG_SLAB_VIRTUAL=n && 5.17 <= kernel < 6.2 unsigned long __page_flags; struct slab *next; int slabs; struct kmem_cache *slab_cache; void *freelist; unsigned inuse:16, objects:15, frozen:1; unsigned int __unused; atomic_t __page_refcount; unsigned long memcg_data; // if CONFIG_MEMCG=y }; struct slab { // if CONFIG_SLAB_VIRTUAL=n && 6.2 <= kernel < 6.10 unsigned long __page_flags; struct kmem_cache *slab_cache; struct slab *next; int slabs; void *freelist; unsigned inuse:16, objects:15, frozen:1; unsigned int __unused; atomic_t __page_refcount; unsigned long memcg_data; // if CONFIG_MEMCG=y }; struct slab { // if CONFIG_SLAB_VIRTUAL=n && 6.10 <= kernel unsigned long __page_flags; // if kernel < 6.18 memdesc_flags_t flags; // if 6.18 <= kernel struct kmem_cache *slab_cache; struct slab *next; int slabs; void *freelist; unsigned inuse:16, objects:15, frozen:1; unsigned int __page_type; atomic_t __page_refcount; unsigned long obj_exts; // if CONFIG_SLAB_OBJ_EXT=y }; struct kmem_cache_node { spinlock_t list_lock; unsigned long nr_partial; struct list_head partial; atomic_long_t nr_slabs; // if CONFIG_SLUB_DEBUG=y atomic_long_t total_objects; // if CONFIG_SLUB_DEBUG=y struct list_head full; // if CONFIG_SLUB_DEBUG=y struct node_barn *barn; // 6.18 <= kernel }; """ # CONFIG_SLAB_VIRTUAL=y """ struct kmem_cache { // if CONFIG_SLAB_VIRTUAL=y ... struct kmem_cache_order_objects min; // [ANNOTATION] struct kmem_cache_order_objects oo; // In kernel < 6.1.56, `min` and `oo` are swapped. unsigned long nr_freed_pages; // if CONFIG_SLAB_VIRTUAL=y && kernel < 6.1.56 struct list_head freed_slabs_normal; // if CONFIG_SLAB_VIRTUAL=y && kernel < 6.1.56 struct list_head freed_slabs_min; // if CONFIG_SLAB_VIRTUAL=y && kernel < 6.1.56 spinlock_t freed_slabs_lock; // if CONFIG_SLAB_VIRTUAL=y && kernel < 6.1.56 struct kmem_cache_virtual { // if CONFIG_SLAB_VIRTUAL=y && 6.1.56 <= kernel spinlock_t freed_slabs_lock; // if CONFIG_SLAB_VIRTUAL=y && 6.1.56 <= kernel struct list_head freed_slabs; // if CONFIG_SLAB_VIRTUAL=y && 6.1.56 <= kernel struct list_head freed_slabs_min; // if CONFIG_SLAB_VIRTUAL=y && 6.1.56 <= kernel unsigned long nr_freed_pages; // if CONFIG_SLAB_VIRTUAL=y && 6.1.56 <= kernel } virtual; // if CONFIG_SLAB_VIRTUAL=y && 6.1.56 <= kernel gfp_t allocflags; ... const char * name; struct list_head list; <-----> struct list_head <-----> struct list_head <-----> ... ... }; struct slab { // if CONFIG_SLAB_VIRTUAL=y && kernel < 6.6 struct slab *compound_slab_head; struct folio *backing_folio; struct kmem_cache_order_objects oo; spinlock_t slab_lists_lock; struct list_head flush_list_elem; unsigned long align_mask; atomic_t pinstate; struct slab *next; int slabs; struct kmem_cache *slab_cache; void *freelist; unsigned inuse:16, objects:15, frozen:1; unsigned int __unused; unsigned long memcg_data; // if CONFIG_MEMCG=y }; struct slab { // if CONFIG_SLAB_VIRTUAL=y && 6.6 <= kernel < 6.12 struct folio *backing_folio; struct kmem_cache *slab_cache; struct slab *next; int slabs; void *freelist; unsigned inuse:16, objects:15, frozen:1; struct kmem_cache_order_objects oo; spinlock_t slab_lock; unsigned long memcg_data; // if CONFIG_MEMCG=y } struct virtual_slab { // if CONFIG_SLAB_VIRTUAL=y && 6.6 <= kernel < 6.12 struct slab slab; struct virtual_slab *compound_slab_head; unsigned long align_mask; }; struct slab { // if CONFIG_SLAB_VIRTUAL=y && 6.12 <= kernel struct slab *compound_slab_head; struct folio *backing_folio; struct kmem_cache_order_objects oo; struct list_head flush_list_elem; unsigned long align_mask; spinlock_t slab_lock; struct kmem_cache *slab_cache; struct slab *next; int slabs; void *freelist; unsigned inuse:16, objects:15, frozen:1; unsigned long obj_exts // if CONFIG_SLAB_OBJ_EXT=y }; """ def initialize(self): if hasattr(self, "initialized") and self.initialized: if not self.args.meta and not self.args.rescan: return True kversion = Kernel.kernel_version() if not kversion: self.quiet_err("Failed to resolve kernel version") return False # resolve slab_caches self.slab_caches = KernelAddressHeuristicFinder.get_slab_caches() if self.slab_caches is None: self.quiet_err("Failed to resolve `slab_caches`") return False else: self.quiet_info("slab_caches: {:#x}".format(self.slab_caches)) # resolve __per_cpu_offset __per_cpu_offset = KernelAddressHeuristicFinder.get_per_cpu_offset() if __per_cpu_offset is None: self.quiet_info("__per_cpu_offset: Not found") self.cpu_offset = [] self.ncpus = 1 else: self.quiet_info("__per_cpu_offset: {:#x}".format(__per_cpu_offset)) self.cpu_offset = KernelCurrentCommand.get_each_cpu_offset(__per_cpu_offset) self.ncpus = len(self.cpu_offset) # offsetof(kmem_cache, list) self.resolve_kmem_cache_offset_list() if self.kmem_cache_offset_list is None: self.quiet_info("offsetof(kmem_cache, list): Not found") return False self.quiet_info("offsetof(kmem_cache, list): {:#x}".format(self.kmem_cache_offset_list)) # offsetof(kmem_cache, name) self.kmem_cache_offset_name = self.kmem_cache_offset_list - current_arch.ptrsize self.quiet_info("offsetof(kmem_cache, name): {:#x}".format(self.kmem_cache_offset_name)) # for CONFIG_SLAB_VIRTUAL self.resolve_for_CONFIG_SLAB_VIRTUAL() # offsetof(kmem_cache, cpu_slab) self.kmem_cache_offset_cpu_slab = 0 self.quiet_info("offsetof(kmem_cache, cpu_slab): {:#x}".format(self.kmem_cache_offset_cpu_slab)) # offsetof(kmem_cache, flags) if kversion < "6.18": self.kmem_cache_offset_flags = current_arch.ptrsize else: CONFIG_LOCKDEP = Symbol.get_ksymaddr("fs_reclaim_acquire") if CONFIG_LOCKDEP: self.kmem_cache_offset_flags = current_arch.ptrsize * 4 else: self.kmem_cache_offset_flags = current_arch.ptrsize * 2 self.quiet_info("offsetof(kmem_cache, flags): {:#x}".format(self.kmem_cache_offset_flags)) # offsetof(kmem_cache, cpu_sheaves) if kversion < "6.18": self.kmem_cache_offset_cpu_sheaves = None else: self.kmem_cache_offset_cpu_sheaves = self.kmem_cache_offset_flags - current_arch.ptrsize self.quiet_info("offsetof(kmem_cache, cpu_sheaves): {:#x}".format(self.kmem_cache_offset_cpu_sheaves)) # offsetof(kmem_cache, size) self.kmem_cache_offset_size = self.kmem_cache_offset_flags + current_arch.ptrsize * 2 self.quiet_info("offsetof(kmem_cache, size): {:#x}".format(self.kmem_cache_offset_size)) # offsetof(kmem_cache, object_size) self.kmem_cache_offset_object_size = self.kmem_cache_offset_size + 4 self.quiet_info("offsetof(kmem_cache, object_size): {:#x}".format(self.kmem_cache_offset_object_size)) # offsetof(kmem_cache, offset) if kversion < "5.9": self.kmem_cache_offset_offset = self.kmem_cache_offset_object_size + 4 else: self.kmem_cache_offset_offset = self.kmem_cache_offset_object_size + 4 + 8 self.quiet_info("offsetof(kmem_cache, offset): {:#x}".format(self.kmem_cache_offset_offset)) # offsetof(kmem_cache, red_left_pad) self.kmem_cache_offset_red_left_pad = self.kmem_cache_offset_name - current_arch.ptrsize self.quiet_info("offsetof(kmem_cache, red_left_pad): {:#x}".format(self.kmem_cache_offset_red_left_pad)) # offsetof(kmem_cache, random) self.resolve_kmem_cache_offset_random() if self.kmem_cache_offset_random is None: self.quiet_info("offsetof(kmem_cache, random): Not found") else: self.quiet_info("offsetof(kmem_cache, random): {:#x}".format(self.kmem_cache_offset_random)) # offsetof(kmem_cache, node) self.resolve_kmem_cache_offset_node() if self.kmem_cache_offset_node is None: self.quiet_info("offsetof(kmem_cache, node): Not found") else: self.quiet_info("offsetof(kmem_cache, node): {:#x}".format(self.kmem_cache_offset_node)) # offsetof(kmem_cache_cpu, freelist) self.kmem_cache_cpu_offset_freelist = 0 self.quiet_info("offsetof(kmem_cache_cpu, freelist): {:#x}".format(self.kmem_cache_cpu_offset_freelist)) # offsetof(kmem_cache_cpu, page or slab) self.kmem_cache_cpu_offset_page = current_arch.ptrsize * 2 self.quiet_info("offsetof(kmem_cache_cpu, {:s}): {:#x}".format( Kernel.slab_page_str(), self.kmem_cache_cpu_offset_page, )) # offsetof(kmem_cache_cpu, partial) self.kmem_cache_cpu_offset_partial = current_arch.ptrsize * 3 self.quiet_info("offsetof(kmem_cache_cpu, partial): {:#x}".format(self.kmem_cache_cpu_offset_partial)) # offsetof(page, next) / offsetof(slab, next) if self.slab_virtual_enabled: if kversion < "6.6": self.page_offset_next = current_arch.ptrsize * 7 elif kversion < "6.12": self.page_offset_next = current_arch.ptrsize * 2 else: # 6.12 self.page_offset_next = current_arch.ptrsize * 8 else: if kversion < "4.16" and is_32bit(): self.page_offset_next = current_arch.ptrsize * 5 elif kversion < "4.18": self.page_offset_next = current_arch.ptrsize * 4 elif kversion < "5.17": self.page_offset_next = current_arch.ptrsize elif kversion < "6.2": self.page_offset_next = current_arch.ptrsize else: self.page_offset_next = current_arch.ptrsize * 2 self.quiet_info("offsetof({:s}, next): {:#x}".format( Kernel.slab_page_str(), self.page_offset_next, )) # offsetof(page, freelist) / offsetof(slab, freelist) if self.slab_virtual_enabled: if kversion < "6.6": self.page_offset_freelist = current_arch.ptrsize * 10 elif kversion < "6.12": self.page_offset_freelist = current_arch.ptrsize * 4 else: # 6.12 self.page_offset_freelist = current_arch.ptrsize * 10 else: if kversion < "4.18": self.page_offset_freelist = current_arch.ptrsize * 2 elif kversion < "5.17": self.page_offset_freelist = current_arch.ptrsize * 4 elif kversion < "6.2": self.page_offset_freelist = current_arch.ptrsize * 4 else: self.page_offset_freelist = current_arch.ptrsize * 4 self.quiet_info("offsetof({:s}, freelist): {:#x}".format( Kernel.slab_page_str(), self.page_offset_freelist, )) # offsetof(page, slab_cache) / offsetof(slab, slab_cache) if self.slab_virtual_enabled: if kversion < "6.6": self.page_offset_slab_cache = current_arch.ptrsize * 9 elif kversion < "6.12": self.page_offset_slab_cache = current_arch.ptrsize else: # 6.12 self.page_offset_slab_cache = current_arch.ptrsize * 7 else: if kversion < "4.16" and is_32bit(): self.page_offset_slab_cache = current_arch.ptrsize * 7 elif kversion < "4.18": self.page_offset_slab_cache = current_arch.ptrsize * 6 elif kversion < "5.17": self.page_offset_slab_cache = current_arch.ptrsize * 3 elif kversion < "6.2": self.page_offset_slab_cache = current_arch.ptrsize * 3 else: self.page_offset_slab_cache = current_arch.ptrsize self.quiet_info("offsetof({:s}, slab_cache): {:#x}".format( Kernel.slab_page_str(), self.page_offset_slab_cache, )) # offsetof(page, inuse_objects_frozen) / offsetof(slab, inuse_objects_frozen) self.page_offset_inuse_objects_frozen = self.page_offset_freelist + current_arch.ptrsize self.quiet_info("offsetof({:s}, inuse_objects_frozen): {:#x}".format( Kernel.slab_page_str(), self.page_offset_inuse_objects_frozen, )) # parse extra members of `struct slab` for CONFIG_SLAB_VIRTUAL=y if self.slab_virtual_enabled: # offsetof(slab, flush_list_elem) # [ANNOTATION] # In 6.6-based implementation, the member `flush_list_elem` has been removed from `struct slab`, # however, `slub_tlbflush_queue` uses the member `slab_list` or `next` (which?) for the same purpose. # So, when CONFIG_SLAB_VIRTUAL=y and 6.6 or later, `flush_list_elem` means `next`. if kversion < "6.6": self.page_offset_flush_list_elem = current_arch.ptrsize * 3 elif kversion < "6.12": self.page_offset_flush_list_elem = current_arch.ptrsize * 2 else: # 6.12 self.page_offset_flush_list_elem = current_arch.ptrsize * 3 self.quiet_info("offsetof(slab, flush_list_elem): {:#x}".format(self.page_offset_flush_list_elem)) # offsetof(kmem_cache_node, partial) self.resolve_kmem_cache_node_offset_partial() if self.kmem_cache_node_offset_partial is None: self.quiet_info("offsetof(kmem_cache_node, partial): Not found") else: self.quiet_info("offsetof(kmem_cache_node, partial): {:#x}".format(self.kmem_cache_node_offset_partial)) # offsetof(kmem_cache_node, full) if self.kmem_cache_node_offset_partial is None: self.kmem_cache_node_offset_full = None else: self.kmem_cache_node_offset_full = self.kmem_cache_node_offset_partial + current_arch.ptrsize * 4 # for sheaves / barn self.resolve_sheaves() self.initialized = True return True @staticmethod def get_flags_str(flags_value): flags_dic = { 0x80000000: "__OBJECT_POISON", 0x40000000: "__CMPXCHG_DOUBLE", 0x20000000: "SLAB_SKIP_KFENCE", 0x10000000: "SLAB_NO_USER_FLAGS", 0x08000000: "SLAB_KASAN", 0x04000000: "SLAB_ACCOUNT", 0x02000000: "SLAB_FAILSLAB", 0x01000000: "SLAB_NOTRACK", 0x00800000: "SLAB_NOLEAKTRACE", 0x00400000: "SLAB_DEBUG_OBJECTS", 0x00200000: "SLAB_TRACE", 0x00100000: "SLAB_MEM_SPREAD", 0x00080000: "SLAB_TYPESAFE_BY_RCU", 0x00040000: "SLAB_PANIC", 0x00020000: "SLAB_RECLAIM_ACCOUNT", 0x00010000: "SLAB_STORE_USER", 0x00008000: "SLAB_CACHE_DMA32", 0x00004000: "SLAB_CACHE_DMA", 0x00002000: "SLAB_HWCACHE_ALIGN", 0x00001000: "SLAB_KMALLOC", 0x00000800: "SLAB_POISON", 0x00000400: "SLAB_RED_ZONE", 0x00000200: "SLAB_DEBUG_INITIAL", # kernel < v2.6.22 0x00000100: "SLAB_CONSISTENCY_CHECKS", } flags = [] for k, v in flags_dic.items(): if flags_value & k: flags.append(v) flags_str = " | ".join(flags) if flags_str == "": flags_str = "none" return flags_str def get_next_kmem_cache(self, addr, point_to_base=True): if point_to_base: addr += self.kmem_cache_offset_list if self.args.reverse_walk: return read_int_from_memory(addr) - self.kmem_cache_offset_list else: return read_int_from_memory(addr + current_arch.ptrsize) - self.kmem_cache_offset_list def get_name(self, addr): name_addr = read_int_from_memory(addr + self.kmem_cache_offset_name) return read_cstring_from_memory(name_addr) def get_random(self, addr): if self.kmem_cache_offset_random is None: return 0 else: return read_int_from_memory(addr + self.kmem_cache_offset_random) def get_kmem_cache_cpu(self, addr, cpu): cpu_slab = read_int_from_memory(addr + self.kmem_cache_offset_cpu_slab) if len(self.cpu_offset) > 0: kmem_cache_cpu = cpu_slab + self.cpu_offset[cpu] else: kmem_cache_cpu = cpu_slab return AddressUtil.align_address(kmem_cache_cpu) def page2virt(self, page, kmem_cache, freelist_fastpath=()): if self.slab_virtual_enabled: ret = gdb.execute("slab-virtual --quiet to_virt {:#x}".format(page["address"]), to_string=True) r = re.search(r"Virt: (\S+)", ret) if r: return int(r.group(1), 16) return None if not self.args.skip_page2virt: r = Kernel.page2virt(page["address"]) if r is not None: return r # set up for heuristic search from freelist freelist = list(freelist_fastpath) + page["freelist"] freelist = [x for x in freelist if isinstance(x, int) and x != 0] # ignore str and last 0 if not freelist: return None # heuristic detection pattern 1 # freed chunks are scattered and can be confirmed on each of the pages page_heads = [x & get_pagesize_mask_high() for x in freelist] uniq_page_heads = list(set(page_heads)) if page["num_pages"] == len(uniq_page_heads): return min(uniq_page_heads) # heuristic detection pattern 2 # if there is only one pattern with good alignment, use it # e.g., num_pages = 5 # 0xXXXX0000 # 0xXXXX1000 <----------------------------------- most_top_page ^ # 0xXXXX2000 ^| # 0xXXXX3000 <-- chunk in freelist (min_page) ^ ^|| # 0xXXXX4000 | known_num_pages ||| # 0xXXXX5000 <-- chunk in freelist (max_page) v ||v pattern 3 # 0xXXXX6000 |v pattern 2 # 0xXXXX7000 v pattern 1 chunk_size = kmem_cache["size"] min_page = min(freelist) & get_pagesize_mask_high() max_page = max(freelist) & get_pagesize_mask_high() known_num_pages = ((max_page - min_page) // get_pagesize()) + 1 unknown_num_pages = page["num_pages"] - known_num_pages most_top_page = min_page - (unknown_num_pages * get_pagesize()) candidate_top_pages = range(most_top_page, min_page + get_pagesize(), get_pagesize()) # alignment check for each candidate_top_pages valid_top_pages = [] for cand_top in candidate_top_pages: for chunk in freelist: # divisible? if (chunk - cand_top) % chunk_size != 0: break else: valid_top_pages.append(cand_top) # fast break if invalid if len(valid_top_pages) >= 2: break # confirm if there is only one valid pattern if len(valid_top_pages) == 1: return valid_top_pages[0] # not found return None def pointer_xor(self, addr, chunk, cache): def pattern1(addr, chunk, cache): return chunk ^ addr ^ cache["random"] def pattern2(addr, chunk, cache): return chunk ^ byteswap(addr) ^ cache["random"] if is_64bit(): shift_bits = 48 else: shift_bits = 24 if self.swap is False: chunk = pattern1(addr, chunk, cache) elif self.swap is True: chunk = pattern2(addr, chunk, cache) elif self.swap is None: # swap type is unknown, try heuristic check if pattern1(addr, chunk, cache) == 0: chunk = pattern1(addr, chunk, cache) self.swap = False elif (chunk >> shift_bits) == (cache["random"] >> shift_bits): chunk = pattern1(addr, chunk, cache) self.swap = False else: chunk = pattern2(addr, chunk, cache) self.swap = True return chunk def walk_freelist(self, chunk, kmem_cache): if self.args.simple: return [chunk] corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") freelist = [chunk] while chunk: try: addr = chunk + kmem_cache["offset"] chunk = read_int_from_memory(addr) # get next chunk except gdb.MemoryError: freelist.append("{:s}".format( Color.colorify("Corrupted (Memory access denied)", corrupted_msg_color), )) break if self.kmem_cache_offset_random is not None: # fix if randomized chunk = self.pointer_xor(addr, chunk, kmem_cache) if chunk % 8: freelist.append("{:#x}: {:s}".format( chunk, Color.colorify("Corrupted (Not aligned)", corrupted_msg_color), )) break if chunk in freelist: freelist.append("{:#x}: {:s}".format( chunk, Color.colorify("Corrupted (Loop detected)", corrupted_msg_color), )) break freelist.append(chunk) return freelist def walk_caches_active_page(self, cpu, kmem_cache): active_page = {} active_page["address"] = page = read_int_from_memory( kmem_cache["kmem_cache_cpu"][cpu]["address"] + self.kmem_cache_cpu_offset_page, ) if is_valid_addr(page): x = read_int_from_memory(page + self.page_offset_inuse_objects_frozen) active_page["inuse"] = x & 0xffff active_page["objects"] = (x >> 16) & 0x7fff active_page["frozen"] = (x >> 31) & 1 active_chunk = read_int_from_memory(page + self.page_offset_freelist) active_page["freelist"] = self.walk_freelist(active_chunk, kmem_cache) active_page["num_pages"] = ( kmem_cache["size"] * active_page["objects"] + get_pagesize_mask_low() ) // get_pagesize() active_page["virt_addr"] = self.page2virt( active_page, kmem_cache, kmem_cache["kmem_cache_cpu"][cpu]["freelist"] ) kmem_cache["kmem_cache_cpu"][cpu]["active_page"] = active_page return def walk_caches_partial_page(self, cpu, kmem_cache): kmem_cache["kmem_cache_cpu"][cpu]["partial_pages"] = [] current_partial_page = read_int_from_memory( kmem_cache["kmem_cache_cpu"][cpu]["address"] + self.kmem_cache_cpu_offset_partial, ) while True: partial_page = {} partial_page["address"] = current_partial_page if not is_valid_addr(current_partial_page): kmem_cache["kmem_cache_cpu"][cpu]["partial_pages"].append(partial_page) break x = read_int_from_memory(current_partial_page + self.page_offset_inuse_objects_frozen) partial_page["inuse"] = x & 0xffff partial_page["objects"] = (x >> 16) & 0x7fff if partial_page["objects"] == 0 or partial_page["inuse"] > partial_page["objects"]: break # something is wrong partial_page["frozen"] = (x >> 31) & 1 partial_chunk = read_int_from_memory(current_partial_page + self.page_offset_freelist) partial_page["freelist"] = self.walk_freelist(partial_chunk, kmem_cache) partial_page["num_pages"] = ( kmem_cache["size"] * partial_page["objects"] + get_pagesize_mask_low() ) // get_pagesize() partial_page["virt_addr"] = self.page2virt(partial_page, kmem_cache) kmem_cache["kmem_cache_cpu"][cpu]["partial_pages"].append(partial_page) next_partial_page = read_int_from_memory(current_partial_page + self.page_offset_next) if next_partial_page in [x["address"] for x in kmem_cache["kmem_cache_cpu"][cpu]["partial_pages"]]: break current_partial_page = next_partial_page return def walk_node_list(self, kmem_cache, kmem_cache_node, offset_list): # use different offsets node_page_list = [] node_page_head = kmem_cache_node + offset_list if not is_valid_addr(node_page_head): return node_page_list current_node_page = read_int_from_memory(node_page_head) while current_node_page != node_page_head: node_page = {} node_page["address"] = current_node_page - self.page_offset_next if not is_valid_addr(node_page["address"]): node_page_list.append(node_page) break x = read_int_from_memory(node_page["address"] + self.page_offset_inuse_objects_frozen) node_page["inuse"] = x & 0xffff node_page["objects"] = (x >> 16) & 0x7fff if node_page["objects"] == 0 or node_page["inuse"] > node_page["objects"]: break # something is wrong node_page["frozen"] = (x >> 31) & 1 node_chunk = read_int_from_memory(node_page["address"] + self.page_offset_freelist) node_page["freelist"] = self.walk_freelist(node_chunk, kmem_cache) node_page["num_pages"] = ( kmem_cache["size"] * node_page["objects"] + get_pagesize_mask_low() ) // get_pagesize() node_page["virt_addr"] = self.page2virt(node_page, kmem_cache) node_page_list.append(node_page) current_node_page = read_int_from_memory(node_page["address"] + self.page_offset_next) return node_page_list # for sheaf / barn def walk_node_barn_list(self, kmem_cache, kmem_cache_node): if not self.sheaves_enabled: return if self.args.skip_sheaf: return if not hasattr(kmem_cache, "node_barn"): kmem_cache["node_barn"] = [] def read_link_list(addr): seen = [] while is_valid_addr(addr): if addr in seen: break seen.append(addr) addr = read_int_from_memory(addr) if len(seen) >= 1: seen = seen[1:] # skip first return seen node_barn = {} node_barn["address"] = read_int_from_memory(kmem_cache_node + self.kmem_cache_node_offset_barn) if is_valid_addr(node_barn["address"]): # full node_barn["sheaves_full"] = [] sheaves_full = read_link_list(node_barn["address"] + self.node_barn_offset_sheaves_full) for addr in sheaves_full: sheaf = self.walk_sheaf(addr - self.slab_sheaf_offset_barn_list, kmem_cache["address"]) node_barn["sheaves_full"].append(sheaf) # empty node_barn["sheaves_empty"] = [] sheaves_empty = read_link_list(node_barn["address"] + self.node_barn_offset_sheaves_empty) for addr in sheaves_empty: sheaf = self.walk_sheaf(addr - self.slab_sheaf_offset_barn_list, kmem_cache["address"]) node_barn["sheaves_empty"].append(sheaf) kmem_cache["node_barn"].append(node_barn) return def walk_caches_node_page(self, cpu, kmem_cache): if not self.kmem_cache_offset_node: return if not self.kmem_cache_node_offset_partial: return kmem_cache["nodes_partial"] = [] if self.args.slub_debug_y: kmem_cache["nodes_full"] = [] kmem_cache_node_array = kmem_cache["address"] + self.kmem_cache_offset_node current_kmem_cache_node_ptr = kmem_cache_node_array while True: current_kmem_cache_node = read_int_from_memory(current_kmem_cache_node_ptr) if current_kmem_cache_node == 0: break if current_kmem_cache_node == current_kmem_cache_node_ptr: break if current_kmem_cache_node & 0b111: break # node list (partial) node_page_list_partial = self.walk_node_list( kmem_cache, current_kmem_cache_node, self.kmem_cache_node_offset_partial, ) kmem_cache["nodes_partial"].append(node_page_list_partial) # node list (full; exists when CONFIG_SLUB_DEBUG=y) if self.args.slub_debug_y and self.kmem_cache_node_offset_full: node_page_list_full = self.walk_node_list( kmem_cache, current_kmem_cache_node, self.kmem_cache_node_offset_full, ) kmem_cache["nodes_full"].append(node_page_list_full) # node barn self.walk_node_barn_list(kmem_cache, current_kmem_cache_node) # goto next current_kmem_cache_node_ptr += current_arch.ptrsize return # for CONFIG_SLAB_VIRTUAL def walk_slab_list(self, list_head, offset_next): slab_list = [] current_slab = read_int_from_memory(list_head) - offset_next while is_valid_addr(current_slab) and current_slab + offset_next != list_head: slab = {} slab["address"] = current_slab kmem_cache = read_int_from_memory(current_slab + self.page_offset_slab_cache) slab["slab_cache_name"] = self.get_name(kmem_cache) slab_list.append(slab) # next slab current_slab = read_int_from_memory(current_slab + offset_next) - offset_next return slab_list # for sheaf / barn def walk_sheaf(self, addr, kmem_cache_addr): sheaf = {} sheaf["address"] = addr if not is_valid_addr(sheaf["address"]): return sheaf sheaf["kmem_cache"] = read_int_from_memory(sheaf["address"] + self.slab_sheaf_offset_kmem_cache) assert sheaf["kmem_cache"] == kmem_cache_addr sheaf["size"] = read_int32_from_memory(sheaf["address"] + self.slab_sheaf_offset_size) sheaf["objects"] = [] for i in range(sheaf["size"]): v = read_int_from_memory(sheaf["address"] + self.slab_sheaf_offset_objects + current_arch.ptrsize * i) sheaf["objects"].append(v) return sheaf # for sheaf / barn def walk_cpu_sheaves(self, cpu, kmem_cache): if not self.sheaves_enabled: return if self.args.skip_sheaf: return if "cpu_sheaves" not in kmem_cache: kmem_cache["cpu_sheaves"] = {} # cpu_sheaves kmem_cache["cpu_sheaves"][cpu] = {} kmem_cache["cpu_sheaves"][cpu]["address"] = self.get_slub_percpu_sheaves(kmem_cache["address"], cpu) if kmem_cache["cpu_sheaves"][cpu]["address"] is None: return if self.slub_percpu_sheaves_offset_main is None: return # cpu_sheaves->main kmem_cache["cpu_sheaves"][cpu]["main"] = self.walk_sheaf( read_int_from_memory(kmem_cache["cpu_sheaves"][cpu]["address"] + self.slub_percpu_sheaves_offset_main), kmem_cache["address"], ) # cpu_sheaves->spare kmem_cache["cpu_sheaves"][cpu]["spare"] = self.walk_sheaf( read_int_from_memory(kmem_cache["cpu_sheaves"][cpu]["address"] + self.slub_percpu_sheaves_offset_spare), kmem_cache["address"], ) return def walk_caches(self, target_names, cpus): current_kmem_cache = self.get_next_kmem_cache(self.slab_caches, point_to_base=False) parsed_caches = [{"name": "slab_caches", "next": current_kmem_cache}] # first, parse kmem_cache while current_kmem_cache + self.kmem_cache_offset_list != self.slab_caches: kmem_cache = {} # parse member kmem_cache["name"] = self.get_name(current_kmem_cache) if target_names != [] and kmem_cache["name"] not in target_names: current_kmem_cache = self.get_next_kmem_cache(current_kmem_cache) continue kmem_cache["address"] = current_kmem_cache kmem_cache["flags"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_flags) kmem_cache["flags_str"] = SlubDumpCommand.get_flags_str(kmem_cache["flags"]) kmem_cache["size"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_size) kmem_cache["object_size"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_object_size) kmem_cache["offset"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_offset) kmem_cache["red_left_pad"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_red_left_pad) kmem_cache["random"] = self.get_random(current_kmem_cache) kmem_cache["next"] = self.get_next_kmem_cache(current_kmem_cache) # parse extra members for feat. CONFIG_SLAB_VIRTUAL if self.slab_virtual_enabled: kmem_cache["nr_freed_pages"] = read_int_from_memory(current_kmem_cache + self.kmem_cache_offset_nr_freed_pages) kmem_cache["freed_slabs_normal"] = self.walk_slab_list( current_kmem_cache + self.kmem_cache_offset_freed_slabs_normal, self.page_offset_next, ) kmem_cache["freed_slabs_min"] = self.walk_slab_list( current_kmem_cache + self.kmem_cache_offset_freed_slabs_min, self.page_offset_next, ) parsed_caches.append(kmem_cache) # goto next current_kmem_cache = kmem_cache["next"] # fast break if target_names != [] and not (self.args.list or self.args.list_no_sort): parsed_names = [x["name"] for x in parsed_caches] if all(t in parsed_names for t in target_names): break if self.args.list or self.args.list_no_sort: return parsed_caches # fast return # second, parse kmem_cache_cpu tqdm = GefUtil.get_tqdm(not self.args.quiet) for kmem_cache in tqdm(parsed_caches[1:], leave=False): # parsed_caches[0] is slab_caches, so skip # parse kmem_cache_cpu kmem_cache["kmem_cache_cpu"] = {} for cpu in cpus: kmem_cache["kmem_cache_cpu"][cpu] = {} kmem_cache["kmem_cache_cpu"][cpu]["address"] = self.get_kmem_cache_cpu(kmem_cache["address"], cpu) active_chunk_fast = read_int_from_memory( kmem_cache["kmem_cache_cpu"][cpu]["address"] + self.kmem_cache_cpu_offset_freelist, ) kmem_cache["kmem_cache_cpu"][cpu]["freelist"] = self.walk_freelist(active_chunk_fast, kmem_cache) # parse active if self.dump_target_active: self.walk_caches_active_page(cpu, kmem_cache) # parse cpu_sheaves self.walk_cpu_sheaves(cpu, kmem_cache) # parse partial if self.dump_target_partial: self.walk_caches_partial_page(cpu, kmem_cache) # parse node if self.dump_target_node: self.walk_caches_node_page(cpu, kmem_cache) return parsed_caches def dump_page_print_layout(self, tag, kmem_cache, page, freelist, freelist_fastpath): used_address_color = Config.get_gef_setting("theme.heap_chunk_address_used") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") if page["virt_addr"] is None: self.out.append(" layout: Failed to the get first page") return end_virt = page["virt_addr"] + page["num_pages"] * get_pagesize() start_addr = page["virt_addr"] + kmem_cache["red_left_pad"] if kmem_cache["red_left_pad"]: chunk_s = Color.colorify_hex(page["virt_addr"], used_address_color) self.out.append(" {:7s} {:#05x} {:s} ({:s})".format("layout:", 0, chunk_s, "never-used")) start_idx = 1 else: start_idx = 0 for idx, chunk in enumerate(range(start_addr, end_virt, kmem_cache["size"]), start=start_idx): if chunk in freelist_fastpath[:-1]: next_chunk = freelist_fastpath[freelist_fastpath.index(chunk) + 1] if isinstance(next_chunk, str): next_msg = "next: {:s}".format(next_chunk) else: next_msg = "next: {:#x}".format(next_chunk) chunk_s = Color.colorify_hex(chunk, freed_address_color) elif chunk in freelist[:-1]: next_chunk = freelist[freelist.index(chunk) + 1] if isinstance(next_chunk, str): next_msg = "next: {:s}".format(next_chunk) else: next_msg = "next: {:#x}".format(next_chunk) if tag == "active": next_msg += " (slow path)" chunk_s = Color.colorify_hex(chunk, freed_address_color) else: if page["objects"] <= idx: next_msg = "never-used" else: next_msg = "in-use" chunk_s = Color.colorify_hex(chunk, used_address_color) self.out.append(" {:7s} {:#05x} {:s} ({:s})".format( "layout:" if idx == 0 else "", idx, chunk_s, next_msg, )) # dump chunks if self.args.hexdump_used and next_msg == "in-use": peeked_data = read_memory(chunk, self.args.hexdump_used) h = hexdump(peeked_data, 0x10, base=chunk, unit=current_arch.ptrsize) self.out.append(h) if self.args.hexdump_freed and next_msg.startswith("next: "): peeked_data = read_memory(chunk, self.args.hexdump_freed) h = hexdump(peeked_data, 0x10, base=chunk, unit=current_arch.ptrsize) self.out.append(h) if self.args.telescope_used and next_msg == "in-use": n = self.args.telescope_used // current_arch.ptrsize for i in range(n): line = DereferenceCommand.pprint_dereferenced(chunk, i) self.out.append(line) if self.args.telescope_freed and next_msg.startswith("next: "): n = self.args.telescope_freed // current_arch.ptrsize for i in range(n): line = DereferenceCommand.pprint_dereferenced(chunk, i) self.out.append(line) return def dump_page_print_freelist(self, tag, kmem_cache, page, freelist, freelist_fastpath): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") def print_freelist(freelist): for chunk_addr in freelist: if page["virt_addr"] is not None: if chunk_addr == 0: continue if isinstance(chunk_addr, str): chunk_idx = "" msg = chunk_addr else: chunk_idx = (chunk_addr - page["virt_addr"]) // kmem_cache["size"] if chunk_idx < 0 or page["objects"] <= chunk_idx: chunk_idx = "" else: chunk_idx = "{:#05x}".format(chunk_idx) msg = Color.colorify_hex(chunk_addr, freed_address_color) self.out.append(" {:5s} {:s}".format(chunk_idx, msg)) else: if isinstance(chunk_addr, str): msg = chunk_addr else: msg = Color.colorify_hex(chunk_addr, freed_address_color) self.out.append(" {:s}".format(msg)) return if tag == "active": if freelist_fastpath == [] or freelist_fastpath == [0]: self.out.append(" freelist (fast path): (none)") else: self.out.append(" freelist (fast path):") print_freelist(freelist_fastpath) if freelist == [] or freelist == [0]: self.out.append(" freelist (slow path): (none)") else: self.out.append(" freelist (slow path):") print_freelist(freelist) else: if freelist == [] or freelist == [0]: self.out.append(" freelist: (none)") else: self.out.append(" freelist:") print_freelist(freelist) return def dump_page(self, page, kmem_cache, tag, freelist_fastpath=()): label_active_color = Config.get_gef_setting("theme.heap_label_active") label_inactive_color = Config.get_gef_setting("theme.heap_label_inactive") heap_page_color = Config.get_gef_setting("theme.heap_page_address") freelist_fastpath = list(freelist_fastpath) # page address if tag == "active": tag_s = Color.colorify("{:s} page".format(tag), label_active_color) else: tag_s = Color.colorify("{:s} page".format(tag), label_inactive_color) self.out.append(" {:s}: {:#x}".format(tag_s, page["address"])) # fast return if invalid if not is_valid_addr(page["address"]): return # print virtual address if page["virt_addr"] is None: self.out.append(" virtual address: ???") else: colored_virt_addr = Color.colorify_hex(page["virt_addr"], heap_page_color) self.out.append(" virtual address: {:s}".format(colored_virt_addr)) # print info self.out.append(" num pages: {:d}".format(page["num_pages"])) if self.args.simple: return freelist = page["freelist"] if tag == "active": freelist_len = len(set(freelist + freelist_fastpath)) - 1 # ignore last 0 inuse = page["objects"] - freelist_len else: inuse = page["inuse"] self.out.append(" in-use: {:d}/{:d}".format(inuse, page["objects"])) self.out.append(" frozen: {:d}".format(page["frozen"])) # print layout self.dump_page_print_layout(tag, kmem_cache, page, freelist, freelist_fastpath) # print freelist self.dump_page_print_freelist(tag, kmem_cache, page, freelist, freelist_fastpath) return # for CONFIG_SLAB_VIRTUAL def dump_slub_tlbflush_queue(self, parsed_slabs): chunk_label_color = Config.get_gef_setting("theme.heap_chunk_label") not_mapped_virt = Config.get_gef_setting("theme.address_valid_but_none") # dump self.out.append("slub_tlbflush_queue @ {:#x}".format(self.slub_tlbflush_queue)) for slab in parsed_slabs: slab_addr = slab["address"] self.out.append("") self.out.append(" slab: {:#x}".format(slab_addr)) self.out.append(" kmem_cache: {:s}".format(Color.colorify(slab["slab_cache_name"], chunk_label_color))) # virtual address is not mapped here virt = "{:#x}".format(self.page2virt_for_slab_virtual(slab_addr)) self.out.append(" virt: {:s}".format(Color.colorify(virt, not_mapped_virt))) return # for sheaf / barn def dump_sheaf(self, sheaf, tag): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") self.out.append(" {:s}: {:#x}".format(tag, sheaf["address"])) self.out.append(" size: {:d}".format(sheaf["size"])) if self.args.simple: return for i, chunk in enumerate(sheaf["objects"]): chunk_s = Color.colorify_hex(chunk, freed_address_color) if i == 0: self.out.append(" objects: {:#05x} {:s}".format(i, chunk_s)) elif i == len(sheaf["objects"]) - 1: self.out.append(" {:#05x} {:s} <- top".format(i, chunk_s)) else: self.out.append(" {:#05x} {:s}".format(i, chunk_s)) if self.args.hexdump_freed: peeked_data = read_memory(chunk, self.args.hexdump_freed) h = hexdump(peeked_data, 0x10, base=chunk, unit=current_arch.ptrsize) self.out.append(h) if self.args.telescope_freed: n = self.args.telescope_freed // current_arch.ptrsize for i in range(n): line = DereferenceCommand.pprint_dereferenced(chunk, i) self.out.append(line) return # for sheaf / barn def dump_sheaves(self, cpu_sheaves, kmem_cache): label_active_color = Config.get_gef_setting("theme.heap_label_active") label_inactive_color = Config.get_gef_setting("theme.heap_label_inactive") # main if "main" in cpu_sheaves: if cpu_sheaves["main"]["address"]: tag = Color.colorify("main sheaf", label_active_color) + " (fastest path)" self.dump_sheaf(cpu_sheaves["main"], tag) # spare if "spare" in cpu_sheaves: if cpu_sheaves["spare"]["address"]: tag = Color.colorify("spare sheaf", label_inactive_color) self.dump_sheaf(cpu_sheaves["spare"], tag) return def dump_caches(self, target_names, cpus, parsed_caches): chunk_label_color = Config.get_gef_setting("theme.heap_chunk_label") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") label_inactive_color = Config.get_gef_setting("theme.heap_label_inactive") self.out.append("slab_caches @ {:#x}".format(self.slab_caches)) for kmem_cache in parsed_caches[1:]: if target_names != [] and kmem_cache["name"] not in target_names: continue # dump kmem_cache metadata self.out.append("") self.out.append(" kmem_cache: {:#x}".format(kmem_cache["address"])) self.out.append(" name: {:s}".format(Color.colorify(kmem_cache["name"], chunk_label_color))) self.out.append(" flags: {:#x} ({:s})".format(kmem_cache["flags"], kmem_cache["flags_str"])) object_size_s = Color.colorify_hex(kmem_cache["object_size"], chunk_size_color) self.out.append(" object size: {:s} (chunk size: {:#x})".format(object_size_s, kmem_cache["size"])) self.out.append(" offset (next pointer in chunk): {:#x}".format(kmem_cache["offset"])) if self.kmem_cache_offset_random is not None: if self.args.no_xor is False: if self.swap is True: fmt = " random (xor key): {:#x} ^ byteswap(&chunk->next)" self.out.append(fmt.format(kmem_cache["random"])) else: fmt = " random (xor key): {:#x} ^ &chunk->next" self.out.append(fmt.format(kmem_cache["random"])) self.out.append(" red_left_pad: {:#x}".format(kmem_cache["red_left_pad"])) # dump freelist in kmem_cache only if CONFIG_SLAB_VIRTUAL=y if self.slab_virtual_enabled: nr_freed_pages = kmem_cache["nr_freed_pages"] self.out.append(" nr_freed_pages: {:#d}".format(nr_freed_pages)) # freed_slabs_normal/freed_slabs self.out.append(" freed_slabs_normal: {:#d}/{:#d}".format( len(kmem_cache["freed_slabs_normal"]), nr_freed_pages, )) for idx, slab in enumerate(kmem_cache["freed_slabs_normal"]): self.out.append(" {:#05x} {:#x}".format(idx, slab["address"])) # freed_slabs_min self.out.append(" freed_slabs_min: {:#d}/{:#d}".format( len(kmem_cache["freed_slabs_min"]), nr_freed_pages, )) for idx, slab in enumerate(kmem_cache["freed_slabs_min"]): self.out.append(" {:#05x} {:#x}".format(idx, slab["address"])) # dump each kmem_cache_cpu for cpu in cpus: self.out.append(" kmem_cache_cpu (cpu{:d}): {:#x}".format( cpu, kmem_cache["kmem_cache_cpu"][cpu]["address"], )) # dump active if self.dump_target_active: active_page = kmem_cache["kmem_cache_cpu"][cpu]["active_page"] freelist_fastpath = kmem_cache["kmem_cache_cpu"][cpu]["freelist"] self.dump_page(active_page, kmem_cache, "active", freelist_fastpath) # dump sheaves if "cpu_sheaves" in kmem_cache: cpu_sheaves = kmem_cache["cpu_sheaves"][cpu] self.dump_sheaves(cpu_sheaves, kmem_cache) # dump partial if self.dump_target_partial: printed_count = 0 for partial_page in kmem_cache["kmem_cache_cpu"][cpu]["partial_pages"]: self.dump_page(partial_page, kmem_cache, "partial") printed_count += 1 if printed_count > 1 : # included address == 0 self.out.append(" (end of the list)") # dump nodes if self.dump_target_node and "nodes_partial" in kmem_cache: for node_index, node_page_list_partial in enumerate(kmem_cache["nodes_partial"]): node_addr = read_int_from_memory( kmem_cache["address"] + self.kmem_cache_offset_node + current_arch.ptrsize * node_index, ) self.out.append(" kmem_cache_node[{:d}]: {:#x}".format(node_index, node_addr)) # node list (partial) printed_count = 0 for node_page in node_page_list_partial: self.dump_page(node_page, kmem_cache, "node") printed_count += 1 if printed_count == 0: self.out.append(" {:s}: (none)".format( Color.colorify("node pages", label_inactive_color), )) # node list (full; exists when CONFIG_SLUB_DEBUG=y) if "nodes_full" in kmem_cache: node_page_list_full = kmem_cache["nodes_full"][node_index] printed_count = 0 for node_page in node_page_list_full: self.dump_page(node_page, kmem_cache, "node (full)") printed_count += 1 if printed_count == 0: self.out.append(" {:s}: (none)".format( Color.colorify("node (full) pages", label_inactive_color), )) # barn list (6.18~) if "node_barn" in kmem_cache: for node_barn in kmem_cache["node_barn"]: if "shaves_full" in node_barn: for sheaves_full in node_barn["sheaves_full"]: self.dump_sheaf(sheaves_full, Color.colorify("node full sheaf", "underline")) if "shaves_empty" in node_barn: for sheaves_empty in node_barn["sheaves_empty"]: self.dump_sheaf(sheaves_empty, Color.colorify("node empty sheaf", "underline")) self.out.append(" next: {:#x}".format(kmem_cache["next"])) return def dump_names(self, parsed_caches): name_width = max(len(k["name"]) for k in parsed_caches[1:]) if not self.args.quiet: fmt = "{:<18s} {:<18s} {:" + str(name_width) + "s} {:20s}" legend = ["Object Size", "Chunk Size", "Name", "kmem_cache"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) if self.args.list_no_sort: target_caches = parsed_caches[1:] else: target_caches = sorted(parsed_caches[1:], key=lambda x: (x["object_size"], x["size"], x["name"])) for kmem_cache in target_caches: objsz = "{0:d} ({0:#x})".format(kmem_cache["object_size"]) chunksz = "{0:d} ({0:#x})".format(kmem_cache["size"]) chunk_name = kmem_cache["name"] address = kmem_cache["address"] self.out.append("{:18s} {:18s} {:{:d}s} {:#x}".format(objsz, chunksz, chunk_name, name_width, address)) return def slubwalk(self, target_names, cpu): if self.initialize() is False: self.quiet_err("Initialization failed") return if self.args.meta: return if self.args.list or self.args.list_no_sort: parsed_caches = self.walk_caches(target_names, cpus=None) self.dump_names(parsed_caches) return if cpu is None: target_cpus = list(range(self.ncpus)) else: if self.ncpus <= cpu: self.quiet_err("CPU number is invalid (valid range: {:d}-{:d})".format(0, self.ncpus - 1)) return target_cpus = [cpu] if self.args.tlbflush_queue: if self.slab_virtual_enabled: parsed_queue = self.walk_slab_list(self.slub_tlbflush_queue, self.page_offset_flush_list_elem) self.dump_slub_tlbflush_queue(parsed_queue) else: self.quiet_warn("CONFIG_SLAB_VIRTUAL is disabled. option `--tlbflush-queue` is ignored") return parsed_caches = self.walk_caches(target_names, target_cpus) self.dump_caches(target_names, target_cpus, parsed_caches) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): if args.help_for_slab_virtual: gef_print(self._note2_.strip()) return self.quiet_info("Wait for memory scan") allocator = Kernel.get_slab_type() if allocator == "SLUB": pass elif allocator == "SLUB_TINY": self.quiet_err("Unsupported; You should use `slub-tiny-dump`") return elif allocator == "SLAB": self.quiet_err("Unsupported; You should use `slab-dump`") return elif allocator == "SLOB": self.quiet_err("Unsupported; You should use `slob-dump`") return else: self.quiet_err("Unsupported: Unknown allocator") return # The slub-dump command is used by page2virt and kmagic to find vmemmap and sizeof(struct page). # Therefore, slub-dump itself may be called recursively (up to once) from slub-dump. # If a recursive call is made, various parameters held by self will be destroyed. # It's very tricky, but if we make sure to call page2virt first, # no further calls will be made and it will work without any problems. if not hasattr(self, "initialized"): if is_x86() or is_arm32(): if not args.skip_page2virt: args = self.args # backup gdb.execute("page2virt 0", to_string=True) # self.args will be overwritten. this is workaround. self.args = args # revert if args.no_byte_swap is None: self.swap = None else: self.swap = not args.no_byte_swap if args.no_xor or args.no_byte_swap: args.rescan = True if args.offset_random is not None or args.offset_node is not None: args.rescan = True # dump target self.dump_target_active = True self.dump_target_partial = args.verbose or args.vverbose self.dump_target_node = args.vverbose if args.only_partial: self.dump_target_active = False self.dump_target_partial = True self.dump_target_node = False elif args.only_node: self.dump_target_active = False self.dump_target_partial = False self.dump_target_node = True self.maps = None self.out = [] self.slubwalk(args.cache_name, args.cpu) self.print_output() return @register_command class SlubTinyDumpCommand(GenericCommand, BufferingOutput): """Dump SLUB-TINY free-list.""" _cmdline_ = "slub-tiny-dump" _category_ = "06-h. Qemu-system/KGDB Cooperation - Linux Allocator" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("cache_name", metavar="SLUB_CACHE_NAME", nargs="*", help="filter by specific slub cache name.") parser.add_argument("-l", "--list", action="store_true", help="list all slub cache names.") parser.add_argument("-L", "--list-no-sort", action="store_true", help="list all slub cache names without sort.") parser.add_argument("--meta", action="store_true", help="display offset information.") parser.add_argument("-R", "--reverse-walk", action="store_true", help="reverse order walk for slab_caches->list_head.") parser.add_argument("-s", "--simple", action="store_true", help="skip displaying layout and freelist.") parser.add_argument("--hexdump-used", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="hexdump `used chunks` if layout is resolved.") parser.add_argument("--hexdump-freed", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="hexdump `unused (freed) chunks` if layout is resolved.") parser.add_argument("--telescope-used", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="telescope `used chunks` if layout is resolved.") parser.add_argument("--telescope-freed", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="telescope `unused (freed) chunks` if layout is resolved.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cached offset.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") parser.add_argument("--skip-page2virt", action="store_true", help="[FOR DEVELOPER] used internally in gef, please don't use it.") _syntax_ = parser.format_help() _example_ = [ "{0:s} kmalloc-256 # dump kmalloc-256 from all cpus", "{0:s} --list # list slub cache names", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Simplified SLUB-TINY structure:", "", " +-kmem_cache--+ +-kmem_cache--+ +-kmem_cache--+", " | flags | | flags | | flags |", " | size | | size | | size |", " | object_size | | object_size | | object_size |", " | offset | | offset | | offset |", " +-slab_caches-+ | name | | name | | name |", " ...<->| list_head |<->| list_head |<--->| list_head |<->| list_head |<-> ...", " +-------------+ | node[] |--+ | node[] | | node[] |", " +-------------+ | +-------------+ +-------------+", " |", " +-------------------------------------+", " | [numa node partial page freelist]", " v +-slab-----------+ +-chunk---+ +-chunk---+", " +-kmem_cache_node-+ | freelist |----+ | ^ | | ^ |", " | partial |---->| next |--+ | | |offset | | |offset |", " +-----------------+ +----------------+ | | | v | | v |", " | ... | | +---->| next |->| next |->NULL", " +-----------------+ +----------------------+ +---------+ +---------+", " |", " | [numa node partial page freelist]", " | +-slab-----------+ +-chunk---+ +-chunk---+", " | | freelist |----+ | ^ | | ^ |", " +->| next |--+ | | |offset | | |offset |", " +----------------+ | | | v | | v |", " | +---->| next |->| next |->NULL", " +----------------------+ +---------+ +---------+", " |", " v", " ...", "* SLUB-TINY was introduced in kernel 6.2.", ] _note_ = "\n".join(_note_) @Cache.cache_until_next def parse_kmem_caches_for_initialize(self): seen = [self.slab_caches] current = self.slab_caches while True: current = read_int_from_memory(current) if current in seen: break seen.append(current) kmem_caches = seen[1:] # skip slab_caches itself return kmem_caches def resolve_kmem_cache_offset_list(self): # fast path try: self.kmem_cache_offset_list = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache*)0).list") ) return except gdb.error: pass # slow path kmem_caches = self.parse_kmem_caches_for_initialize() candidate = (0, -1) # (count, candidate_offset) for candidate_offset in range(current_arch.ptrsize * 2, 0x70, current_arch.ptrsize): # backward search for the start of `struct kmem_cache` count = 0 for kmem_cache in kmem_caches: val = read_int_from_memory(kmem_cache - candidate_offset) if val & 0x4000_0000: # __CMPXCHG_DOUBLE count += 1 if candidate[0] < count: candidate = (count, candidate_offset) self.kmem_cache_offset_list = candidate[1] return def resolve_kmem_cache_offset_node(self): # fast path try: self.kmem_cache_offset_node = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache*)0).node") ) return except gdb.error: pass # slow path self.kmem_cache_offset_node = None kmem_caches = self.parse_kmem_caches_for_initialize() start_offset = self.kmem_cache_offset_list + current_arch.ptrsize * 2 # sizeof(kmem_cache.list) for candidate_offset in range(start_offset, start_offset + 0x100, current_arch.ptrsize): # walk from list for heuristic search found = True for kmem_cache in kmem_caches: top = kmem_cache - self.kmem_cache_offset_list maybe_node = read_int_from_memory(top + candidate_offset) if not is_valid_addr(maybe_node): found = False break # skip node[0].{list_lock,nr_partial} and check node[0].partial.next if not is_valid_addr(maybe_node + current_arch.ptrsize * 2): found = False break # node[0].partial.next is slab # maybe_slab actually points to &slab.next, not to the beginning of the structure maybe_slab = read_int_from_memory(maybe_node + current_arch.ptrsize * 2) if not is_valid_addr(maybe_slab): found = False break # slab.next (it's actually the list_head) a = read_int_from_memory(maybe_slab) if not is_valid_addr(a): found = False break b = read_int_from_memory(maybe_slab + current_arch.ptrsize) if not is_valid_addr(b): found = False break # something is in linklist if a != maybe_node + current_arch.ptrsize * 2: # check slab->slab_cache c = read_int_from_memory(maybe_slab - current_arch.ptrsize) if c != top: found = False break if found: self.kmem_cache_offset_node = candidate_offset return return def resolve_kmem_cache_node_offset_partial(self): # fast path try: self.kmem_cache_node_offset_partial = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache_node*)0).partial") ) return except gdb.error: pass # slow path self.kmem_cache_node_offset_partial = None kmem_caches = self.parse_kmem_caches_for_initialize() node = read_int_from_memory(kmem_caches[0] - self.kmem_cache_offset_list + self.kmem_cache_offset_node) for i in range(2, 16): offset_partial = current_arch.ptrsize * i if is_double_link_list(node + offset_partial): self.kmem_cache_node_offset_partial = offset_partial return return """ struct kmem_cache { struct slub_percpu_sheaves __percpu *cpu_sheaves; // 6.18 <= kernel slab_flags_t flags; // unsigned int (+ padding 4 byte) unsigned long min_partial; unsigned int size; unsigned int object_size; struct reciprocal_value { // u32 m; // u8 sh1, sh2; // (+ padding 2 byte) } reciprocal_size; // unsigned int offset; unsigned int cpu_partial; // if CONFIG_SLUB_CPU_PARTIAL=y unsigned int cpu_partial_slabs; // if CONFIG_SLUB_CPU_PARTIAL=y struct kmem_cache_order_objects oo; struct kmem_cache_order_objects min; gfp_t allocflags; // unsigned int int refcount; void (*ctor)(void *); unsigned int inuse; unsigned int align; unsigned int red_left_pad; const char *name; struct list_head list; <-----> struct list_head <-----> struct list_head <-----> ... struct kobject { const char *name; struct list_head entry; struct kobject *parent; struct kset *kset; const struct kobj_type *ktype; struct kernfs_node *sd; struct kref kref; struct delayed_work release; // if CONFIG_DEBUG_KOBJECT_RELEASE=y unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; unsigned int state_remove_uevent_sent:1; unsigned int uevent_suppress:1; } kobj; // if CONFIG_SYSFS=y unsigned int remote_node_defrag_ratio; // if CONFIG_NUMA=y struct kasan_cache { int alloc_meta_offset; int free_meta_offset; bool is_kmalloc; } kasan_info; // if CONFIG_KASAN=y unsigned int useroffset; // if CONFIG_HARDENED_USERCOPY=y unsigned int usersize; // if CONFIG_HARDENED_USERCOPY=y struct kmem_cache_node *node[MAX_NUMNODES]; }; struct slab { unsigned long __page_flags; struct kmem_cache *slab_cache; struct slab *next; int slabs; void *freelist; unsigned inuse:16, objects:15, frozen:1; ... }; struct kmem_cache_node { spinlock_t list_lock; unsigned long nr_partial; struct list_head partial; atomic_long_t nr_slabs; // if CONFIG_SLUB_DEBUG=y atomic_long_t total_objects; // if CONFIG_SLUB_DEBUG=y struct list_head full; // if CONFIG_SLUB_DEBUG=y }; """ def initialize(self): if hasattr(self, "initialized") and self.initialized: if not self.args.meta and not self.args.rescan: return True kversion = Kernel.kernel_version() if not kversion: self.quiet_err("Failed to resolve kernel version") return False # resolve slab_caches self.slab_caches = KernelAddressHeuristicFinder.get_slab_caches() if self.slab_caches is None: self.quiet_err("Failed to resolve `slab_caches`") return False else: self.quiet_info("slab_caches: {:#x}".format(self.slab_caches)) # offsetof(kmem_cache, list) self.resolve_kmem_cache_offset_list() self.quiet_info("offsetof(kmem_cache, list): {:#x}".format(self.kmem_cache_offset_list)) # offsetof(kmem_cache, name) self.kmem_cache_offset_name = self.kmem_cache_offset_list - current_arch.ptrsize self.quiet_info("offsetof(kmem_cache, name): {:#x}".format(self.kmem_cache_offset_name)) # offsetof(kmem_cache, flags) if kversion < "6.18": self.kmem_cache_offset_flags = 0 else: self.kmem_cache_offset_flags = current_arch.ptrsize self.quiet_info("offsetof(kmem_cache, flags): {:#x}".format(self.kmem_cache_offset_flags)) # offsetof(kmem_cache, size) self.kmem_cache_offset_size = self.kmem_cache_offset_flags + current_arch.ptrsize * 2 self.quiet_info("offsetof(kmem_cache, size): {:#x}".format(self.kmem_cache_offset_size)) # offsetof(kmem_cache, object_size) self.kmem_cache_offset_object_size = self.kmem_cache_offset_size + 4 self.quiet_info("offsetof(kmem_cache, object_size): {:#x}".format(self.kmem_cache_offset_object_size)) # offsetof(kmem_cache, offset) self.kmem_cache_offset_offset = self.kmem_cache_offset_object_size + 4 + 8 self.quiet_info("offsetof(kmem_cache, offset): {:#x}".format(self.kmem_cache_offset_offset)) # offsetof(kmem_cache, red_left_pad) self.kmem_cache_offset_red_left_pad = self.kmem_cache_offset_name - current_arch.ptrsize self.quiet_info("offsetof(kmem_cache, red_left_pad): {:#x}".format(self.kmem_cache_offset_red_left_pad)) # offsetof(kmem_cache, node) self.resolve_kmem_cache_offset_node() if self.kmem_cache_offset_node is None: self.quiet_info("offsetof(kmem_cache, node): Not found") else: self.quiet_info("offsetof(kmem_cache, node): {:#x}".format(self.kmem_cache_offset_node)) # offsetof(slab, next) self.slab_offset_next = current_arch.ptrsize * 2 self.quiet_info("offsetof(slab, next): {:#x}".format(self.slab_offset_next)) # offsetof(slab, freelist) self.slab_offset_freelist = current_arch.ptrsize * 4 self.quiet_info("offsetof(slab, freelist): {:#x}".format(self.slab_offset_freelist)) # offsetof(slab, slab_cache) self.slab_offset_slab_cache = current_arch.ptrsize self.quiet_info("offsetof(slab, slab_cache): {:#x}".format(self.slab_offset_slab_cache)) # offsetof(slab, inuse_objects_frozen) self.slab_offset_inuse_objects_frozen = self.slab_offset_freelist + current_arch.ptrsize self.quiet_info("offsetof(slab, inuse_objects_frozen): {:#x}".format(self.slab_offset_inuse_objects_frozen)) # offsetof(kmem_cache_node, partial) self.resolve_kmem_cache_node_offset_partial() if self.kmem_cache_node_offset_partial is None: self.quiet_info("offsetof(kmem_cache_node, partial): Not found") return False else: self.quiet_info("offsetof(kmem_cache_node, partial): {:#x}".format(self.kmem_cache_node_offset_partial)) self.initialized = True return True def get_next_kmem_cache(self, addr, point_to_base=True): if point_to_base: addr += self.kmem_cache_offset_list if self.args.reverse_walk: return read_int_from_memory(addr) - self.kmem_cache_offset_list else: return read_int_from_memory(addr + current_arch.ptrsize) - self.kmem_cache_offset_list def get_name(self, addr): name_addr = read_int_from_memory(addr + self.kmem_cache_offset_name) return read_cstring_from_memory(name_addr) def page2virt(self, page, kmem_cache): if not self.args.skip_page2virt: ret = gdb.execute("page2virt {:#x}".format(page["address"]), to_string=True) r = re.search(r"Virt: (\S+)", ret) if r: return int(r.group(1), 16) # set up for heuristic search from freelist freelist = page["freelist"] freelist = [x for x in freelist if isinstance(x, int) and x != 0] # ignore str and last 0 if not freelist: return None # heuristic detection pattern 1 # freed chunks are scattered and can be confirmed on each of the pages page_heads = [x & get_pagesize_mask_high() for x in freelist] uniq_page_heads = list(set(page_heads)) if page["num_pages"] == len(uniq_page_heads): return min(uniq_page_heads) # heuristic detection pattern 2 # if there is only one pattern with good alignment, use it # e.g., num_pages = 5 # 0xXXXX0000 # 0xXXXX1000 <----------------------------------- most_top_page ^ # 0xXXXX2000 ^| # 0xXXXX3000 <-- chunk in freelist (min_page) ^ ^|| # 0xXXXX4000 | known_num_pages ||| # 0xXXXX5000 <-- chunk in freelist (max_page) v ||v pattern 3 # 0xXXXX6000 |v pattern 2 # 0xXXXX7000 v pattern 1 chunk_size = kmem_cache["size"] min_page = min(freelist) & get_pagesize_mask_high() max_page = max(freelist) & get_pagesize_mask_high() known_num_pages = ((max_page - min_page) // get_pagesize()) + 1 unknown_num_pages = page["num_pages"] - known_num_pages most_top_page = min_page - (unknown_num_pages * get_pagesize()) candidate_top_pages = range(most_top_page, min_page + get_pagesize(), get_pagesize()) # alignment check for each candidate_top_pages valid_top_pages = [] for cand_top in candidate_top_pages: for chunk in freelist: # divisible? if (chunk - cand_top) % chunk_size != 0: break else: valid_top_pages.append(cand_top) # fast break if invalid if len(valid_top_pages) >= 2: break # confirm if there is only one valid pattern if len(valid_top_pages) == 1: return valid_top_pages[0] # not found return None def walk_freelist(self, chunk, kmem_cache): if self.args.simple: return [chunk] corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") freelist = [chunk] while chunk: try: addr = chunk + kmem_cache["offset"] chunk = read_int_from_memory(addr) # get next chunk except gdb.MemoryError: freelist.append("{:s}".format( Color.colorify("Corrupted (Memory access denied)", corrupted_msg_color), )) break if chunk % 8: freelist.append("{:#x}: {:s}".format( chunk, Color.colorify("Corrupted (Not aligned)", corrupted_msg_color), )) break if chunk in freelist: freelist.append("{:#x}: {:s}".format( chunk, Color.colorify("Corrupted (Loop detected)", corrupted_msg_color), )) break freelist.append(chunk) return freelist def walk_caches_node_page(self, kmem_cache): kmem_cache["nodes"] = [] kmem_cache_node_array = kmem_cache["address"] + self.kmem_cache_offset_node current_kmem_cache_node_ptr = kmem_cache_node_array while True: current_kmem_cache_node = read_int_from_memory(current_kmem_cache_node_ptr) if current_kmem_cache_node == 0: break if current_kmem_cache_node == current_kmem_cache_node_ptr: break if current_kmem_cache_node & 0b111: break # node list node_page_list = [] node_page_head = current_kmem_cache_node + self.kmem_cache_node_offset_partial if not is_valid_addr(node_page_head): break current_node_page = read_int_from_memory(node_page_head) while current_node_page != node_page_head: node_page = {} node_page["address"] = current_node_page - self.slab_offset_next if not is_valid_addr(node_page["address"]): node_page_list.append(node_page) break x = read_int_from_memory(node_page["address"] + self.slab_offset_inuse_objects_frozen) node_page["inuse"] = x & 0xffff node_page["objects"] = (x >> 16) & 0x7fff if node_page["objects"] == 0 or node_page["inuse"] > node_page["objects"]: break # something is wrong node_page["frozen"] = (x >> 31) & 1 node_chunk = read_int_from_memory(node_page["address"] + self.slab_offset_freelist) node_page["freelist"] = self.walk_freelist(node_chunk, kmem_cache) node_page["num_pages"] = ( kmem_cache["size"] * node_page["objects"] + get_pagesize_mask_low() ) // get_pagesize() node_page["virt_addr"] = self.page2virt(node_page, kmem_cache) node_page_list.append(node_page) current_node_page = read_int_from_memory(node_page["address"] + self.slab_offset_next) kmem_cache["nodes"].append(node_page_list) # goto next current_kmem_cache_node_ptr += current_arch.ptrsize return def walk_caches(self, target_names): current_kmem_cache = self.get_next_kmem_cache(self.slab_caches, point_to_base=False) parsed_caches = [{"name": "slab_caches", "next": current_kmem_cache}] # first, parse kmem_cache while current_kmem_cache + self.kmem_cache_offset_list != self.slab_caches: kmem_cache = {} # parse member kmem_cache["name"] = self.get_name(current_kmem_cache) if target_names != [] and kmem_cache["name"] not in target_names: current_kmem_cache = self.get_next_kmem_cache(current_kmem_cache) continue kmem_cache["address"] = current_kmem_cache kmem_cache["flags"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_flags) kmem_cache["flags_str"] = SlubDumpCommand.get_flags_str(kmem_cache["flags"]) kmem_cache["size"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_size) kmem_cache["object_size"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_object_size) kmem_cache["offset"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_offset) kmem_cache["red_left_pad"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_red_left_pad) kmem_cache["next"] = self.get_next_kmem_cache(current_kmem_cache) parsed_caches.append(kmem_cache) # goto next current_kmem_cache = kmem_cache["next"] # fast break if target_names != [] and not (self.args.list or self.args.list_no_sort): parsed_names = [x["name"] for x in parsed_caches] if all(t in parsed_names for t in target_names): break if self.args.list or self.args.list_no_sort: return parsed_caches # fast return # second, parse node then update tqdm = GefUtil.get_tqdm(not self.args.quiet) for kmem_cache in tqdm(parsed_caches[1:], leave=False): # parsed_caches[0] is slab_caches, so skip # parse node self.walk_caches_node_page(kmem_cache) return parsed_caches def dump_page_print_layout(self, kmem_cache, page, freelist): used_address_color = Config.get_gef_setting("theme.heap_chunk_address_used") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") if page["virt_addr"] is None: self.out.append(" layout: Failed to the get first page") return end_virt = page["virt_addr"] + page["num_pages"] * get_pagesize() start_addr = page["virt_addr"] + kmem_cache["red_left_pad"] if kmem_cache["red_left_pad"]: chunk_s = Color.colorify_hex(page["virt_addr"], used_address_color) self.out.append(" {:7s} {:#05x} {:s} ({:s})".format("layout:", 0, chunk_s, "never-used")) start_idx = 1 else: start_idx = 0 for idx, chunk in enumerate(range(start_addr, end_virt, kmem_cache["size"]), start=start_idx): if chunk in freelist[:-1]: next_chunk = freelist[freelist.index(chunk) + 1] if isinstance(next_chunk, str): next_msg = "next: {:s}".format(next_chunk) else: next_msg = "next: {:#x}".format(next_chunk) chunk_s = Color.colorify_hex(chunk, freed_address_color) else: if page["objects"] <= idx: next_msg = "never-used" else: next_msg = "in-use" chunk_s = Color.colorify_hex(chunk, used_address_color) layout_msg = "layout:" if idx == 0 else "" self.out.append(" {:7s} {:#05x} {:s} ({:s})".format(layout_msg, idx, chunk_s, next_msg)) # dump chunks if self.args.hexdump_used and next_msg == "in-use": peeked_data = read_memory(chunk, self.args.hexdump_used) h = hexdump(peeked_data, 0x10, base=chunk, unit=current_arch.ptrsize) self.out.append(h) if self.args.hexdump_freed and next_msg.startswith("next: "): peeked_data = read_memory(chunk, self.args.hexdump_freed) h = hexdump(peeked_data, 0x10, base=chunk, unit=current_arch.ptrsize) self.out.append(h) if self.args.telescope_used and next_msg == "in-use": n = self.args.telescope_used // current_arch.ptrsize for i in range(n): line = DereferenceCommand.pprint_dereferenced(chunk, i) self.out.append(line) if self.args.telescope_freed and next_msg.startswith("next: "): n = self.args.telescope_freed // current_arch.ptrsize for i in range(n): line = DereferenceCommand.pprint_dereferenced(chunk, i) self.out.append(line) return def dump_page_print_freelist(self, kmem_cache, page, freelist): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") if freelist == [] or freelist == [0]: self.out.append(" freelist: (none)") return for idx, chunk_addr in enumerate(freelist): if page["virt_addr"] is not None: if chunk_addr == 0: continue if isinstance(chunk_addr, str): chunk_idx = "" msg = chunk_addr else: chunk_idx = (chunk_addr - page["virt_addr"]) // kmem_cache["size"] if chunk_idx < 0 or page["objects"] <= chunk_idx: chunk_idx = "" else: chunk_idx = "{:#05x}".format(chunk_idx) msg = Color.colorify_hex(chunk_addr, freed_address_color) freelist_msg = "freelist:" if idx == 0 else "" self.out.append(" {:9s} {:5s} {:s}".format(freelist_msg, chunk_idx, msg)) else: if isinstance(chunk_addr, str): msg = chunk_addr else: msg = Color.colorify_hex(chunk_addr, freed_address_color) freelist_msg = "freelist:" if idx == 0 else "" self.out.append(" {:9s} {:s}".format(freelist_msg, msg)) return def dump_page(self, page, kmem_cache, tag, freelist=None): label_active_color = Config.get_gef_setting("theme.heap_label_active") heap_page_color = Config.get_gef_setting("theme.heap_page_address") # page address tag_s = Color.colorify("{:s} page".format(tag), label_active_color) self.out.append(" {:s}: {:#x}".format(tag_s, page["address"])) # fast return if invalid if not is_valid_addr(page["address"]): return # for partial or node page if freelist is None: freelist = page["freelist"] # print virtual address if page["virt_addr"] is None: self.out.append(" virtual address: ???") else: colored_virt_addr = Color.colorify_hex(page["virt_addr"], heap_page_color) self.out.append(" virtual address: {:s}".format(colored_virt_addr)) # print info self.out.append(" num pages: {:d}".format(page["num_pages"])) if self.args.simple: return if tag == "active": freelist_len = len(freelist) - 1 # ignore last 0 inuse = page["objects"] - freelist_len else: inuse = page["inuse"] self.out.append(" in-use: {:d}/{:d}".format(inuse, page["objects"])) self.out.append(" frozen: {:d}".format(page["frozen"])) # print layout self.dump_page_print_layout(kmem_cache, page, freelist) # print freelist self.dump_page_print_freelist(kmem_cache, page, freelist) return def dump_caches(self, target_names, parsed_caches): chunk_label_color = Config.get_gef_setting("theme.heap_chunk_label") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") label_inactive_color = Config.get_gef_setting("theme.heap_label_inactive") self.out.append("slab_caches @ {:#x}".format(self.slab_caches)) for kmem_cache in parsed_caches[1:]: if target_names != [] and kmem_cache["name"] not in target_names: continue # dump kmem_cache metadata self.out.append("") self.out.append(" kmem_cache: {:#x}".format(kmem_cache["address"])) self.out.append(" name: {:s}".format(Color.colorify(kmem_cache["name"], chunk_label_color))) self.out.append(" flags: {:#x} ({:s})".format(kmem_cache["flags"], kmem_cache["flags_str"])) object_size_s = Color.colorify_hex(kmem_cache["object_size"], chunk_size_color) self.out.append(" object size: {:s} (chunk size: {:#x})".format(object_size_s, kmem_cache["size"])) self.out.append(" offset (next pointer in chunk): {:#x}".format(kmem_cache["offset"])) self.out.append(" red_left_pad: {:#x}".format(kmem_cache["red_left_pad"])) # dump nodes for node_index, node_page_list in enumerate(kmem_cache["nodes"]): node_addr = read_int_from_memory( kmem_cache["address"] + self.kmem_cache_offset_node + current_arch.ptrsize * node_index, ) self.out.append(" kmem_cache_node[{:d}]: {:#x}".format(node_index, node_addr)) printed_count = 0 for node_page in node_page_list: self.dump_page(node_page, kmem_cache, "node") printed_count += 1 if printed_count == 0: tag = Color.colorify("node pages", label_inactive_color) self.out.append(" {:s}: (none)".format(tag)) self.out.append(" next: {:#x}".format(kmem_cache["next"])) return def dump_names(self, parsed_caches): name_width = max(len(k["name"]) for k in parsed_caches[1:]) if not self.args.quiet: fmt = "{:<18s} {:<18s} {:" + str(name_width) + "s} {:20s}" legend = ["Object Size", "Chunk Size", "Name", "kmem_cache"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) if self.args.list_no_sort: target_caches = parsed_caches[1:] else: target_caches = sorted(parsed_caches[1:], key=lambda x: (x["object_size"], x["size"], x["name"])) for kmem_cache in target_caches: objsz = "{0:d} ({0:#x})".format(kmem_cache["object_size"]) chunksz = "{0:d} ({0:#x})".format(kmem_cache["size"]) chunk_name = kmem_cache["name"] address = kmem_cache["address"] self.out.append("{:18s} {:18s} {:{:d}s} {:#x}".format(objsz, chunksz, chunk_name, name_width, address)) return def slub_tiny_walk(self, target_names): if self.initialize() is False: self.quiet_err("Initialization failed") return if self.args.meta: return if self.args.list or self.args.list_no_sort: parsed_caches = self.walk_caches(target_names) self.dump_names(parsed_caches) return parsed_caches = self.walk_caches(target_names) self.dump_caches(target_names, parsed_caches) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") allocator = Kernel.get_slab_type() if allocator == "SLUB": self.quiet_err("Unsupported; You should use `slub-dump`") return elif allocator == "SLUB_TINY": pass elif allocator == "SLAB": self.quiet_err("Unsupported; You should use `slab-dump`") return elif allocator == "SLOB": self.quiet_err("Unsupported; You should use `slob-dump`") return else: self.quiet_err("Unsupported: Unknown allocator") return # The slub-tiny-dump command is used by page2virt and kmagic to find vmemmap and sizeof(struct page). # Therefore, slub-tiny-dump itself may be called recursively (up to once) from slub-tiny-dump. # If a recursive call is made, various parameters held by self will be destroyed. # It's very tricky, but if we make sure to call page2virt first, # no further calls will be made and it will work without any problems. if not hasattr(self, "initialized"): if is_x86() or is_arm32(): if not args.skip_page2virt: args = self.args # backup gdb.execute("page2virt 0", to_string=True) # self.args will be overwritten. this is workaround. self.args = args # revert self.maps = None self.out = [] self.slub_tiny_walk(args.cache_name) self.print_output() return @register_command class SlabDumpCommand(GenericCommand, BufferingOutput): """Dump SLAB free-list.""" _cmdline_ = "slab-dump" _category_ = "06-h. Qemu-system/KGDB Cooperation - Linux Allocator" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("cache_name", metavar="SLAB_CACHE_NAME", nargs="*", help="filter by specific slab cache name.") parser.add_argument("-l", "--list", action="store_true", help="list all slab cache names.") parser.add_argument("-L", "--list-no-sort", action="store_true", help="list all slab cache names without sort.") parser.add_argument("--meta", action="store_true", help="display offset information.") parser.add_argument("--cpu", type=int, help="filter by specific cpu.") parser.add_argument("-R", "--reverse-walk", action="store_true", help="reverse order walk for slab_caches->list_head.") parser.add_argument("-s", "--simple", action="store_true", help="skip displaying layout and freelist.") parser.add_argument("--skip-partial", action="store_true", help="skip displaying slabs_partial.") parser.add_argument("--skip-full", action="store_true", help="skip displaying slabs_full.") parser.add_argument("--skip-free", action="store_true", help="skip displaying slabs_free.") parser.add_argument("--hexdump-used", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="hexdump `used chunks` if layout is resolved.") parser.add_argument("--hexdump-freed", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="hexdump `unused (freed) chunks` if layout is resolved.") parser.add_argument("--telescope-used", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="telescope `used chunks` if layout is resolved.") parser.add_argument("--telescope-freed", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="telescope `unused (freed) chunks` if layout is resolved.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cached offset.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} kmalloc-256 # dump kmalloc-256 from all cpus", "{0:s} kmalloc-256 --cpu 1 # dump kmalloc-256 from cpu 1", "{0:s} --list # list slab cache names", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Simplified SLAB structure:", "\n" " +-kmem_cache--+ +-kmem_cache--+ +-kmem_cache--+", " | cpu_cache |---+ | cpu_cache | | cpu_cache |", " | limit | | | limit | | limit |", " | size | | | size | | size |", " | flags | | | flags | | flags |", " | num | | | num | | num |", " | gfporder | | | gfporder | | gfporder |", " +-slab_caches-+ | name | | | name | | name |", " ...<->| list_head |<->| list_head |<------->| list_head |<->| list_head |<-> ...", " +-------------+ | object_size | | | object_size | | object_size |", " | node[] |------+ | node[] | | node[] |", " +-------------+ | | +-------------+ +-------------+", " +-__per_cpu_offset-+ | |", " | cpu0_offset |--+----------------+ |", " | cpu1_offset | | |", " | cpu2_offset | | v +-page/slab-+ +-page/slab-+", " | ... | | +-kmem_cache_node-+ +---->| slab_list |--->| slab_list |-->...", " +------------------+ | | slabs_partial |------+ | freelist | | freelist |", " | | slabs_full |----->... | s_mem |-+ | s_mem |-+", " +-------------------+ | slabs_free |----->... | active | | | active | |", " | +-----------------+ +-----------+ | +-----------+ |", " v | |", " +-array_cache--------+ +-----------+ +-----------+", " | avail | | |", " | limit | v v", " | entry[] | +-chunk--+ +-chunk--+", " | freed_chunk_ptr |-------------------------------------->| | | |", " | freed_chunk_ptr |----------------------------+ +-chunk--+ +-chunk--+", " | freed_chunk_ptr | | | | | |", " | freed_chunk_ptr | | +-chunk--+ +-chunk--+", " | freed_chunk_ptr | +--------->| | | |", " | ... | +-...----+ +-...----+", " +--------------------+", "* `struct page` has been split into `struct page` and `struct slab` since kernel 5.17.", " The structure name used for SLAB has been changed to `struct slab`.", "* Chunks in array_cache are marked as in-use, even though they are actually reusable.", "* SLAB was removed in kernel 6.8.", ] _note_ = "\n".join(_note_) """ struct kmem_cache { struct array_cache __percpu *cpu_cache; // In fact, the offset value, not the pointer. if 3.18 <= kernel unsigned int batchcount; unsigned int limit; unsigned int shared; unsigned int size; struct reciprocal_value { u32 m; u8 sh1, sh2; } reciprocal_buffer_size; slab_flags_t flags; // unsigned int unsigned int num; unsigned int gfporder; gfp_t allocflags; // unsigned int size_t colour; unsigned int colour_off; struct kmem_cache *freelist_cache; // if kernel < 6.1 unsigned int freelist_size; void (*ctor)(void *obj); const char *name; struct list_head list; <-----> struct list_head <-----> struct list_head <-----> ... int refcount; int object_size; int align; unsigned long num_active; // if CONFIG_DEBUG_SLAB=y unsigned long num_allocations; // if CONFIG_DEBUG_SLAB=y unsigned long high_mark; // if CONFIG_DEBUG_SLAB=y unsigned long grown; // if CONFIG_DEBUG_SLAB=y unsigned long reaped; // if CONFIG_DEBUG_SLAB=y unsigned long errors; // if CONFIG_DEBUG_SLAB=y unsigned long max_freeable; // if CONFIG_DEBUG_SLAB=y unsigned long node_allocs; // if CONFIG_DEBUG_SLAB=y unsigned long node_frees; // if CONFIG_DEBUG_SLAB=y unsigned long node_overflow; // if CONFIG_DEBUG_SLAB=y atomic_t allochit; // if CONFIG_DEBUG_SLAB=y atomic_t allocmiss; // if CONFIG_DEBUG_SLAB=y atomic_t freehit; // if CONFIG_DEBUG_SLAB=y atomic_t freemiss; // if CONFIG_DEBUG_SLAB=y atomic_t store_user_clean; // if CONFIG_DEBUG_SLAB=y && CONFIG_DEBUG_SLAB_LEAK=y && 4.6 <= kernel < 5.2 int obj_offset; // if CONFIG_DEBUG_SLAB=y struct memcg_cache_params memcg_params; // if CONFIG_MEMCG=y && kernel < 5.9 struct kasan_cache kasan_info; // if CONFIG_KASAN=y && 4.6 <= kernel unsigned int *random_seq; // if CONFIG_SLAB_FREELIST_RANDOM=y && 4.7 <= kernel unsigned int useroffset; // if 4.16 <= kernel unsigned int usersize; // if 4.16 <= kernel struct kmem_cache_node *node[MAX_NUMNODES]; // if 3.18 <= kernel struct kmem_cache_node **node; // if kernel < 3.18 struct array_cache *array[NR_CPUS + MAX_NUMNODES]; // if kernel < 3.18 }; struct array_cache { unsigned int avail; unsigned int limit; unsigned int batchcount; unsigned int touched; spinlock_t lock; // if kernel < 3.17 void *entry[]; }; struct kmem_cache_node { raw_spinlock_t list_lock; struct list_head slabs_partial; struct list_head slabs_full; struct list_head slabs_free; unsigned long total_slabs; // if 4.10 <= kernel unsigned long free_slabs; // if 4.10 <= kernel unsigned long num_slabs; // if 4.9 <= kernel < 4.10 unsigned long free_objects; unsigned int free_limit; unsigned int colour_next; struct array_cache *shared; struct alien_cache **alien; unsigned long next_reap; int free_touched; }; struct page { // if kernel < 4.18 unsigned long flags; void *s_mem; void *freelist; unsigned int active; atomic_t refcount; // if kernel < 4.16 struct rcu_head rcu_head; struct kmem_cache *slab_cache; ... }; struct page { // if 4.18 <= kernel < 5.17 unsigned long flags; struct list_head slab_list; struct kmem_cache *slab_cache; void *freelist; void *s_mem; unsigned int active; ... }; struct slab { // if 5.17 <= kernel unsigned long __page_flags; struct kmem_cache *slab_cache; // if 6.2 <= kernel struct list_head slab_list; struct kmem_cache *slab_cache; // if kernel < 6.2 void *freelist; void *s_mem; unsigned int active; ... }; """ @Cache.cache_until_next def parse_kmem_caches_for_initialize(self): seen = [self.slab_caches] current = self.slab_caches while True: current = read_int_from_memory(current) if current in seen: break seen.append(current) kmem_caches = seen[1:] # skip slab_caches itself return kmem_caches def resolve_kmem_cache_offset_node(self): # fast path try: self.kmem_cache_offset_node = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache*)0).node") ) return except gdb.error: pass # slow path kversion = Kernel.kernel_version() if kversion < "4.16": self.kmem_cache_offset_node = self.kmem_cache_offset_object_size + 4 * 2 # heuristic could not use, so hard-coded return self.kmem_cache_offset_node = None kmem_caches = self.parse_kmem_caches_for_initialize() # Search heuristically using useroffset and usersize as markers start_offset = self.kmem_cache_offset_list + current_arch.ptrsize * 2 for candidate_offset in range(start_offset, start_offset + 0x100, 4): found = True for kmem_cache in kmem_caches: kmem_cache_top = kmem_cache - self.kmem_cache_offset_list user_offset = read_int32_from_memory(kmem_cache_top + candidate_offset) user_size = read_int32_from_memory(kmem_cache_top + candidate_offset + 4) object_size = read_int32_from_memory(kmem_cache_top + self.kmem_cache_offset_object_size) if user_offset == user_size == 0: continue if user_offset != 0 and user_size == 0: found = False break if object_size < user_size: found = False break node_addr_ptr = kmem_cache_top + candidate_offset + 4 + 4 node_addr_ptr = align_to_ptrsize(node_addr_ptr) node_addr = read_int_from_memory(node_addr_ptr) if not is_valid_addr(node_addr): found = False break if found: self.kmem_cache_offset_node = align_to_ptrsize(candidate_offset + 4 * 2) return return def resolve_kmem_cache_node_offset_slabs_partial(self): # fast path try: self.kmem_cache_node_offset_slabs_partial = to_unsigned_long( gdb.parse_and_eval("&((struct kmem_cache_node*)0).slabs_partial") ) return except gdb.error: pass # slow path kversion = Kernel.kernel_version() self.kmem_cache_node_offset_slabs_partial = None kmem_caches = self.parse_kmem_caches_for_initialize() # sizeof(raw_spinlock_t) can take many different values and must be determined heuristically. for candidate_offset in range(0, 0x80, current_arch.ptrsize): found = True for _kmem_cache in kmem_caches: kmem_cache = _kmem_cache - self.kmem_cache_offset_list if "3.18" <= kversion: kmem_cache_node_array = kmem_cache + self.kmem_cache_offset_node else: kmem_cache_node_array = read_int_from_memory(kmem_cache + self.kmem_cache_offset_node) kmem_cache_node_0 = read_int_from_memory(kmem_cache_node_array) # slabs_partial if not is_double_link_list(kmem_cache_node_0 + candidate_offset + current_arch.ptrsize * 0): found = False break # slabs_full if not is_double_link_list(kmem_cache_node_0 + candidate_offset + current_arch.ptrsize * 2): found = False break # slabs_free if not is_double_link_list(kmem_cache_node_0 + candidate_offset + current_arch.ptrsize * 4): found = False break if found: self.kmem_cache_node_offset_slabs_partial = candidate_offset return return def initialize(self): if hasattr(self, "initialized") and self.initialized: if not self.args.meta and not self.args.rescan: return True kversion = Kernel.kernel_version() if not kversion: self.quiet_err("Failed to resolve kernel version") return False # resolve slab_caches self.slab_caches = KernelAddressHeuristicFinder.get_slab_caches() if self.slab_caches is None: self.quiet_err("Failed to resolve `slab_caches`") return False else: self.quiet_info("slab_caches: {:#x}".format(self.slab_caches)) # resolve __per_cpu_offset __per_cpu_offset = KernelAddressHeuristicFinder.get_per_cpu_offset() if __per_cpu_offset is None: self.quiet_info("__per_cpu_offset: Not found") self.cpu_offset = [] self.ncpus = 1 else: self.quiet_info("__per_cpu_offset: {:#x}".format(__per_cpu_offset)) self.cpu_offset = KernelCurrentCommand.get_each_cpu_offset(__per_cpu_offset) self.ncpus = len(self.cpu_offset) # offsetof(kmem_cache, list) if kversion < "3.18": self.kmem_cache_offset_list = current_arch.ptrsize * 6 + 4 * 10 elif kversion < "6.1": self.kmem_cache_offset_list = current_arch.ptrsize * 7 + 4 * 10 else: self.kmem_cache_offset_list = current_arch.ptrsize * 4 + 4 * 12 self.quiet_info("offsetof(kmem_cache, list): {:#x}".format(self.kmem_cache_offset_list)) # offsetof(kmem_cache, name) self.kmem_cache_offset_name = self.kmem_cache_offset_list - current_arch.ptrsize self.quiet_info("offsetof(kmem_cache, name): {:#x}".format(self.kmem_cache_offset_name)) # offsetof(kmem_cache, size) if "3.18" <= kversion: self.kmem_cache_offset_size = current_arch.ptrsize + 4 * 3 else: self.kmem_cache_offset_size = 4 * 3 self.quiet_info("offsetof(kmem_cache, size): {:#x}".format(self.kmem_cache_offset_size)) # offsetof(kmem_cache, flags) self.kmem_cache_offset_flags = self.kmem_cache_offset_size + 4 * 3 self.quiet_info("offsetof(kmem_cache, flags): {:#x}".format(self.kmem_cache_offset_flags)) # offsetof(kmem_cache, num) self.kmem_cache_offset_num = self.kmem_cache_offset_flags + 4 self.quiet_info("offsetof(kmem_cache, num): {:#x}".format(self.kmem_cache_offset_num)) # offsetof(kmem_cache, gfporder) self.kmem_cache_offset_gfporder = self.kmem_cache_offset_num + 4 self.quiet_info("offsetof(kmem_cache, gfporder): {:#x}".format(self.kmem_cache_offset_gfporder)) # offsetof(kmem_cache, object_size) self.kmem_cache_offset_object_size = self.kmem_cache_offset_list + current_arch.ptrsize * 2 + 4 self.quiet_info("offsetof(kmem_cache, object_size): {:#x}".format(self.kmem_cache_offset_object_size)) # offsetof(kmem_cache, node) self.resolve_kmem_cache_offset_node() if self.kmem_cache_offset_node is None: self.quiet_info("offsetof(kmem_cache, node): Not found") return False else: self.quiet_info("offsetof(kmem_cache, node): {:#x}".format(self.kmem_cache_offset_node)) # offsetof(kmem_cache, cpu_cache) / offsetof(kmem_cache, array) if "3.18" <= kversion: self.kmem_cache_offset_cpu_cache = 0 self.quiet_info("offsetof(kmem_cache, cpu_cache): {:#x}".format(self.kmem_cache_offset_cpu_cache)) else: self.kmem_cache_offset_array = self.kmem_cache_offset_node + current_arch.ptrsize self.quiet_info("offsetof(kmem_cache, array): {:#x}".format(self.kmem_cache_offset_array)) # offsetof(page, next) / offsetof(slab, next) if kversion < "4.16": self.page_offset_next = current_arch.ptrsize * 3 + 4 * 2 elif kversion < "4.18": self.page_offset_next = current_arch.ptrsize * 3 + 4 elif kversion < "5.17": self.page_offset_next = current_arch.ptrsize elif kversion < "6.2": self.page_offset_next = current_arch.ptrsize else: self.page_offset_next = current_arch.ptrsize * 2 self.quiet_info("offsetof({:s}, next): {:#x}".format(Kernel.slab_page_str(), self.page_offset_next)) # offsetof(page, freelist) / offsetof(slab, freelist) if kversion < "4.18": self.page_offset_freelist = current_arch.ptrsize * 2 elif kversion < "5.17": self.page_offset_freelist = current_arch.ptrsize * 4 elif kversion < "6.2": self.page_offset_freelist = current_arch.ptrsize * 4 else: self.page_offset_freelist = current_arch.ptrsize * 4 self.quiet_info("offsetof({:s}, freelist): {:#x}".format(Kernel.slab_page_str(), self.page_offset_freelist)) # offsetof(page, slab_cache) / offsetof(slab, slab_cache) if kversion < "4.16" and is_32bit(): self.page_offset_slab_cache = current_arch.ptrsize * 7 elif kversion < "4.18": self.page_offset_slab_cache = current_arch.ptrsize * 6 elif kversion < "5.17": self.page_offset_slab_cache = current_arch.ptrsize * 3 elif kversion < "6.2": self.page_offset_slab_cache = current_arch.ptrsize * 3 else: self.page_offset_slab_cache = current_arch.ptrsize self.quiet_info("offsetof({:s}, slab_cache): {:#x}".format(Kernel.slab_page_str(), self.page_offset_slab_cache)) # offsetof(page, s_mem) / offsetof(slab, s_mem) if kversion < "4.18": self.page_offset_s_mem = current_arch.ptrsize elif kversion < "5.17": self.page_offset_s_mem = current_arch.ptrsize * 5 elif kversion < "6.2": self.page_offset_s_mem = 8 + current_arch.ptrsize * 5 else: self.page_offset_s_mem = 8 + current_arch.ptrsize * 5 self.quiet_info("offsetof({:s}, s_mem): {:#x}".format(Kernel.slab_page_str(), self.page_offset_s_mem)) # offsetof(page, active) / offsetof(slab, active) if kversion < "4.18": self.page_offset_active = current_arch.ptrsize * 3 elif kversion < "5.17": self.page_offset_active = current_arch.ptrsize * 6 elif kversion < "6.2": self.page_offset_active = current_arch.ptrsize * 6 else: self.page_offset_active = current_arch.ptrsize * 6 self.quiet_info("offsetof({:s}, active): {:#x}".format(Kernel.slab_page_str(), self.page_offset_active)) # offsetof(kmem_cache_node, slabs_partial) self.resolve_kmem_cache_node_offset_slabs_partial() if self.kmem_cache_node_offset_slabs_partial is None: self.quiet_info("offsetof(kmem_cache_node, slabs_partial): Not found") return False else: self.quiet_info("offsetof(kmem_cache_node, slabs_partial): {:#x}".format(self.kmem_cache_node_offset_slabs_partial)) # offsetof(kmem_cache_node, slabs_full) self.kmem_cache_node_offset_slabs_full = self.kmem_cache_node_offset_slabs_partial + current_arch.ptrsize * 2 self.quiet_info("offsetof(kmem_cache_node, slabs_full): {:#x}".format(self.kmem_cache_node_offset_slabs_full)) # offsetof(kmem_cache_node, slabs_free) self.kmem_cache_node_offset_slabs_free = self.kmem_cache_node_offset_slabs_full + current_arch.ptrsize * 2 self.quiet_info("offsetof(kmem_cache_node, slabs_free): {:#x}".format(self.kmem_cache_node_offset_slabs_free)) # offsetof(array_cache, avail) self.array_cache_offset_avail = 0 self.quiet_info("offsetof(array_cache, avail): {:#x}".format(self.array_cache_offset_avail)) # offsetof(array_cache, limit) self.array_cache_offset_limit = 4 self.quiet_info("offsetof(array_cache, limit): {:#x}".format(self.array_cache_offset_limit)) # offsetof(array_cache, entry) if "3.17" <= kversion: self.array_cache_offset_entry = 4 * 4 else: sizeof_raw_spinlock_t = self.kmem_cache_node_offset_slabs_partial self.array_cache_offset_entry = 4 * 4 + sizeof_raw_spinlock_t self.quiet_info("offsetof(array_cache, entry): {:#x}".format(self.array_cache_offset_entry)) self.initialized = True return True def get_next_kmem_cache(self, addr, point_to_base=True): if point_to_base: addr += self.kmem_cache_offset_list if self.args.reverse_walk: return read_int_from_memory(addr) - self.kmem_cache_offset_list else: return read_int_from_memory(addr + current_arch.ptrsize) - self.kmem_cache_offset_list def get_name(self, addr): name_addr = read_int_from_memory(addr + self.kmem_cache_offset_name) return read_cstring_from_memory(name_addr) def get_array_cache_cpu(self, addr, cpu): kversion = Kernel.kernel_version() if "3.18" <= kversion: cpu_cache = read_int_from_memory(addr + self.kmem_cache_offset_cpu_cache) if len(self.cpu_offset) > 0: # __percpu return AddressUtil.align_address(cpu_cache + self.cpu_offset[cpu]) else: # not __percpu return cpu_cache else: return read_int_from_memory(addr + self.kmem_cache_offset_array + current_arch.ptrsize * cpu) def walk_array_cache(self, array_cache, cpu, kmem_cache): if self.args.simple: return [] freelist = [] entry = array_cache + self.array_cache_offset_entry end = entry + kmem_cache["array_cache"][cpu]["avail"] * current_arch.ptrsize for current in range(entry, end, current_arch.ptrsize): chunk = read_int_from_memory(current) freelist.append(chunk) return freelist def walk_node_list(self, node_page_head, current_node_page, kmem_cache): kversion = Kernel.kernel_version() node_page_list = [] seen = [] # avoid infinity loop while current_node_page != node_page_head: if current_node_page in seen: break seen.append(current_node_page) node_page = {} node_page["address"] = current_node_page - self.page_offset_next if not is_valid_addr(node_page["address"]): node_page_list.append(node_page) break node_page["s_mem"] = read_int_from_memory(node_page["address"] + self.page_offset_s_mem) node_page["s_mem_base"] = node_page["s_mem"] & get_pagesize_mask_high() if not self.args.simple: freelist_addr = read_int_from_memory(node_page["address"] + self.page_offset_freelist) if is_valid_addr(freelist_addr): active = read_int32_from_memory(node_page["address"] + self.page_offset_active) if "3.15" <= kversion: freelist_byteseq = read_memory(freelist_addr, kmem_cache["objperslab"]) node_page["freelist"] = list(freelist_byteseq[active:]) else: freelist_intseq = read_memory(freelist_addr, kmem_cache["objperslab"] * 4) node_page["freelist"] = slice_unpack(freelist_intseq, current_arch.ptrsize)[active:] else: node_page["freelist"] = [] node_page_list.append(node_page) current_node_page = read_int_from_memory(node_page["address"] + self.page_offset_next) return node_page_list def walk_caches(self, target_names, cpus): kversion = Kernel.kernel_version() current_kmem_cache = self.get_next_kmem_cache(self.slab_caches, point_to_base=False) parsed_caches = [{"name": "slab_caches", "next": current_kmem_cache}] # first, parse kmem_cache while current_kmem_cache + self.kmem_cache_offset_list != self.slab_caches: kmem_cache = {} # parse member kmem_cache["name"] = self.get_name(current_kmem_cache) if target_names != [] and kmem_cache["name"] not in target_names: current_kmem_cache = self.get_next_kmem_cache(current_kmem_cache) continue kmem_cache["address"] = current_kmem_cache kmem_cache["flags"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_flags) kmem_cache["flags_str"] = SlubDumpCommand.get_flags_str(kmem_cache["flags"]) kmem_cache["size"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_size) kmem_cache["object_size"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_object_size) kmem_cache["objperslab"] = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_num) gfporder = read_int32_from_memory(current_kmem_cache + self.kmem_cache_offset_gfporder) kmem_cache["pagesperslab"] = 1 << gfporder kmem_cache["next"] = self.get_next_kmem_cache(current_kmem_cache) parsed_caches.append(kmem_cache) # goto next current_kmem_cache = kmem_cache["next"] # fast break if target_names != [] and not (self.args.list or self.args.list_no_sort): parsed_names = [x["name"] for x in parsed_caches] if all(t in parsed_names for t in target_names): break if self.args.list or self.args.list_no_sort: return parsed_caches # second, parse array_cache and node tqdm = GefUtil.get_tqdm(not self.args.quiet) for kmem_cache in tqdm(parsed_caches[1:], leave=False): # parsed_caches[0] is slab_caches, so skip # parse array_cache kmem_cache["array_cache"] = {} kmem_cache["array_cache"]["freelist_all"] = [] for cpu in cpus: kmem_cache["array_cache"][cpu] = {} if not is_valid_addr(self.get_array_cache_cpu(kmem_cache["address"], cpu)): continue kmem_cache["array_cache"][cpu]["address"] = array_cache = self.get_array_cache_cpu(kmem_cache["address"], cpu) kmem_cache["array_cache"][cpu]["avail"] = read_int32_from_memory(array_cache + self.array_cache_offset_avail) kmem_cache["array_cache"][cpu]["limit"] = read_int32_from_memory(array_cache + self.array_cache_offset_limit) kmem_cache["array_cache"][cpu]["freelist"] = self.walk_array_cache(array_cache, cpu, kmem_cache) kmem_cache["array_cache"]["freelist_all"].extend(kmem_cache["array_cache"][cpu]["freelist"]) # parse node kmem_cache["nodes"] = [] if "3.18" <= kversion: kmem_cache_node_array = kmem_cache["address"] + self.kmem_cache_offset_node else: kmem_cache_node_array = read_int_from_memory(kmem_cache["address"] + self.kmem_cache_offset_node) current_kmem_cache_node_ptr = kmem_cache_node_array while True: # 3.18 or after: node is array (node[MAX_NUMNODES]), so need loop until invalid address current_kmem_cache_node = read_int_from_memory(current_kmem_cache_node_ptr) if not is_valid_addr(current_kmem_cache_node): break slabs_list = {} node_page_head = current_kmem_cache_node + self.kmem_cache_node_offset_slabs_partial if is_valid_addr(node_page_head): current_node_page = read_int_from_memory(node_page_head) slabs_list["slabs_partial"] = self.walk_node_list(node_page_head, current_node_page, kmem_cache) node_page_head = current_kmem_cache_node + self.kmem_cache_node_offset_slabs_full if is_valid_addr(node_page_head): current_node_page = read_int_from_memory(node_page_head) slabs_list["slabs_full"] = self.walk_node_list(node_page_head, current_node_page, kmem_cache) node_page_head = current_kmem_cache_node + self.kmem_cache_node_offset_slabs_free if is_valid_addr(node_page_head): current_node_page = read_int_from_memory(node_page_head) slabs_list["slabs_free"] = self.walk_node_list(node_page_head, current_node_page, kmem_cache) kmem_cache["nodes"].append(slabs_list) if kversion < "3.18": # 3.17 or before: node is single element (**node), so skip loop break current_kmem_cache_node_ptr += current_arch.ptrsize return parsed_caches def dump_page(self, page, kmem_cache, tag): heap_page_color = Config.get_gef_setting("theme.heap_page_address") label_inactive_color = Config.get_gef_setting("theme.heap_label_inactive") used_address_color = Config.get_gef_setting("theme.heap_chunk_address_used") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") # page address tag_s = Color.colorify(tag, label_inactive_color) self.out.append(" {:s}: {:#x}".format(tag_s, page["address"])) # fast return if invalid if not is_valid_addr(page["address"]): return # print virtual address colored_s_mem_base = Color.colorify_hex(page["s_mem_base"], heap_page_color) self.out.append(" virtual address (s_mem & ~0xfff): {:s}".format(colored_s_mem_base)) # print info self.out.append(" num pages: {:d}".format(kmem_cache["pagesperslab"])) colour_off = page["s_mem"] - page["s_mem_base"] self.out.append(" colour offset: {:#x}".format(colour_off)) if self.args.simple: return # print layout freelist = page["freelist"] end_virt = page["s_mem_base"] + kmem_cache["pagesperslab"] * get_pagesize() if colour_off: chunk_s = Color.colorify_hex(page["s_mem_base"], used_address_color) self.out.append(" {:7s} ---- {:s} ({:s})".format("layout:", chunk_s, "never-used")) for idx, chunk in enumerate(range(page["s_mem"], end_virt, kmem_cache["size"])): if idx in freelist: idxidx = freelist.index(idx) if idxidx == len(freelist) - 1: next_msg = "next: None" else: next_idx = freelist[idxidx + 1] next_msg = "next: {:#x}".format(next_idx) chunk_s = Color.colorify_hex(chunk, freed_address_color) elif "array_cache" in kmem_cache and chunk in kmem_cache["array_cache"]["freelist_all"]: next_msg = "in-use (array_cache)" chunk_s = Color.colorify_hex(chunk, freed_address_color) else: if kmem_cache["objperslab"] <= idx: next_msg = "never-used" else: next_msg = "in-use" chunk_s = Color.colorify_hex(chunk, used_address_color) self.out.append(" {:7s} {:#04x} {:s} ({:s})".format( "layout:" if idx == 0 else "", idx, chunk_s, next_msg, )) # dump chunks if self.args.hexdump_used and next_msg == "in-use": peeked_data = read_memory(chunk, self.args.hexdump_used) h = hexdump(peeked_data, 0x10, base=chunk, unit=current_arch.ptrsize) self.out.append(h) if self.args.hexdump_freed and next_msg.startswith(("next: ", "in-use (array_cache)")): peeked_data = read_memory(chunk, self.args.hexdump_freed) h = hexdump(peeked_data, 0x10, base=chunk, unit=current_arch.ptrsize) self.out.append(h) if self.args.telescope_used and next_msg == "in-use": n = self.args.telescope_used // current_arch.ptrsize for i in range(n): line = DereferenceCommand.pprint_dereferenced(chunk, i) self.out.append(line) if self.args.telescope_freed and next_msg.startswith(("next: ", "in-use (array_cache)")): n = self.args.telescope_freed // current_arch.ptrsize for i in range(n): line = DereferenceCommand.pprint_dereferenced(chunk, i) self.out.append(line) # print freelist if freelist == []: self.out.append(" freelist: (none)") else: for i, idx in enumerate(freelist): chunk = page["s_mem"] + kmem_cache["size"] * idx msg = Color.colorify_hex(chunk, freed_address_color) self.out.append(" {:9s} {:#04x} {:s}".format("freelist:" if i == 0 else "", idx, msg)) return def dump_array_cache(self, cpu, kmem_cache): label_active_color = Config.get_gef_setting("theme.heap_label_active") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") tag_s = Color.colorify("array_cache (cpu{:d})".format(cpu), label_active_color) if "array_cache" not in kmem_cache: self.out.append(" {:s}: (none)".format(tag_s)) return if "address" not in kmem_cache["array_cache"][cpu]: self.out.append(" {:s}: (none)".format(tag_s)) return self.out.append(" {:s}: {:#x}".format(tag_s, kmem_cache["array_cache"][cpu]["address"])) self.out.append(" avail: {:d}".format(kmem_cache["array_cache"][cpu]["avail"])) self.out.append(" limit: {:d}".format(kmem_cache["array_cache"][cpu]["limit"])) if self.args.simple: return freelist = kmem_cache["array_cache"][cpu]["freelist"] if freelist == []: self.out.append(" entry: (none)") else: for idx, f in enumerate(freelist): if not is_valid_addr(f): break msg = Color.colorify_hex(f, freed_address_color) self.out.append(" {:6s} {:s}".format("entry:" if idx == 0 else "", msg)) return def dump_caches(self, target_names, cpus, parsed_caches): chunk_label_color = Config.get_gef_setting("theme.heap_chunk_label") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") label_inactive_color = Config.get_gef_setting("theme.heap_label_inactive") self.out.append("slab_caches @ {:#x}".format(self.slab_caches)) for kmem_cache in parsed_caches[1:]: if target_names != [] and kmem_cache["name"] not in target_names: continue # dump kmem_cache metadata self.out.append("") self.out.append(" kmem_cache: {:#x}".format(kmem_cache["address"])) self.out.append(" name: {:s}".format(Color.colorify(kmem_cache["name"], chunk_label_color))) self.out.append(" flags: {:#x} ({:s})".format(kmem_cache["flags"], kmem_cache["flags_str"])) object_size_s = Color.colorify_hex(kmem_cache["object_size"], chunk_size_color) self.out.append(" object size: {:s} (chunk size: {:#x})".format(object_size_s, kmem_cache["size"])) self.out.append(" object per slab: {:#x}".format(kmem_cache["objperslab"])) self.out.append(" pages per slab: {:#x}".format(kmem_cache["pagesperslab"])) # dump array_cache for cpu in cpus: self.dump_array_cache(cpu, kmem_cache) # dump nodes if len(kmem_cache["nodes"]) == 0: self.out.append(" {:s}: (none)".format(Color.colorify("node pages", label_inactive_color))) else: for node_index, slabs_list in enumerate(kmem_cache["nodes"]): node_addr = read_int_from_memory( kmem_cache["address"] + self.kmem_cache_offset_node + current_arch.ptrsize * node_index, ) self.out.append(" kmem_cache_node[{:d}]: {:#x}".format(node_index, node_addr)) if not self.args.skip_partial and "slabs_partial" in slabs_list: if len(slabs_list["slabs_partial"]) == 0: tag = Color.colorify("node[{:d}].slabs_partial".format(node_index), label_inactive_color) self.out.append(" {:s}: (none)".format(tag)) else: for node_page in slabs_list["slabs_partial"]: self.dump_page(node_page, kmem_cache, tag="node[{:d}].slabs_partial".format(node_index)) if not self.args.skip_full and "slabs_full" in slabs_list: if len(slabs_list["slabs_full"]) == 0: tag = Color.colorify("node[{:d}].slabs_full".format(node_index), label_inactive_color) self.out.append(" {:s}: (none)".format(tag)) else: for node_page in slabs_list["slabs_full"]: self.dump_page(node_page, kmem_cache, tag="node[{:d}].slabs_full".format(node_index)) if not self.args.skip_free and "slabs_free" in slabs_list: if len(slabs_list["slabs_free"]) == 0: tag = Color.colorify("node[{:d}].slabs_free".format(node_index), label_inactive_color) self.out.append(" {:s}: (none)".format(tag)) else: for node_page in slabs_list["slabs_free"]: self.dump_page(node_page, kmem_cache, tag="node[{:d}].slabs_free".format(node_index)) self.out.append(" next: {:#x}".format(kmem_cache["next"])) return def dump_names(self, parsed_caches): name_width = max(len(k["name"]) for k in parsed_caches[1:]) if not self.args.quiet: fmt = "{:<18s} {:<18s} {:" + str(name_width) + "s} {:20s}" legend = ["Object Size", "Chunk Size", "Name", "kmem_cache"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) if self.args.list_no_sort: target_caches = parsed_caches[1:] else: target_caches = sorted(parsed_caches[1:], key=lambda x: (x["object_size"], x["size"], x["name"])) for kmem_cache in target_caches: objsz = "{0:d} ({0:#x})".format(kmem_cache["object_size"]) chunksz = "{0:d} ({0:#x})".format(kmem_cache["size"]) chunk_name = kmem_cache["name"] address = kmem_cache["address"] self.out.append("{:18s} {:18s} {:{:d}s} {:#x}".format(objsz, chunksz, chunk_name, name_width, address)) return def slabwalk(self, target_names, cpu): if self.initialize() is False: self.quiet_err("Initialization failed") return if self.args.meta: return if self.args.list or self.args.list_no_sort: parsed_caches = self.walk_caches(target_names, cpus=None) self.dump_names(parsed_caches) return if cpu is None: target_cpus = list(range(self.ncpus)) else: if self.ncpus <= cpu: self.quiet_err("CPU number is invalid (valid range: {:d}-{:d})".format(0, self.ncpus - 1)) return target_cpus = [cpu] parsed_caches = self.walk_caches(target_names, target_cpus) self.dump_caches(target_names, target_cpus, parsed_caches) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") allocator = Kernel.get_slab_type() if allocator == "SLUB": self.quiet_err("Unsupported; You should use `slub-dump`") return elif allocator == "SLUB_TINY": self.quiet_err("Unsupported; You should use `slub-tiny-dump`") return elif allocator == "SLAB": pass elif allocator == "SLOB": self.quiet_err("Unsupported; You should use `slob-dump`") return else: self.quiet_err("Unsupported: Unknown allocator") return self.maps = None self.out = [] self.slabwalk(args.cache_name, args.cpu) self.print_output() return @register_command class SlobDumpCommand(GenericCommand, BufferingOutput): """Dump SLOB free-list.""" _cmdline_ = "slob-dump" _category_ = "06-h. Qemu-system/KGDB Cooperation - Linux Allocator" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("cache_name", metavar="SLOB_CACHE_NAME", nargs="*", help="filter by specific slob cache name (need -v option).") parser.add_argument("-l", "--list", action="store_true", help="list all slob cache names.") parser.add_argument("-L", "--list-no-sort", action="store_true", help="list all slob cache names without sort.") parser.add_argument("--meta", action="store_true", help="display offset information.") parser.add_argument("-R", "--reverse-walk", action="store_true", help="reverse order walk for slab_caches->list_head.") parser.add_argument("-s", "--simple", action="store_true", help="skip showing freelist.") parser.add_argument("--large", action="store_true", help="display only free_slob_large.") parser.add_argument("--medium", action="store_true", help="display only free_slob_medium.") parser.add_argument("--small", action="store_true", help="display only free_slob_small.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cached offset.") parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose mode (print kmem_cache).") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} kmalloc-256 # dump kmalloc-256 kmem_cache and all freelists", "{0:s} --list # list slob cache names", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Simplified SLOB structure:", "", " +-kmem_cache--+ +-kmem_cache--+ +-kmem_cache--+", " | object_size | | object_size | | object_size |", " | size | | size | | size |", " | flags | | flags | | flags |", " +-slab_caches-+ | name | | name | | name |", " ...<->| list_head |<->| list_head |<->| list_head |<->| list_head |<-> ...", " +-------------+ +-------------+ +-------------+ +-------------+", "* slab_caches is not used when traversing the freelist", "", " +-free_slob_large--+ +-page/slab-----+ +-page/slab-----+", " | list_head |<---------+ | freelist |-----+ | freelist |", " +-free_slob_medium-+ | | units (total) | | | units (total) |", " | list_head |-->... +-->| list_head |<----|---->| list_head |<->...", " +-free_slob_small--+ +---------------+ | +---------------+", " | list_head |-->... |", " +------------------+ +-------------+", " small : size < 0x100 |", " medium: 0x100 <= size < 0x400 | +-chunk-----+ +-chunk-----+", " large : 0x400 <= size < 0x1000 +-->| units |-->| -offset |-->...", "* size is only judged when first inserted, | offset | +-----------+", " so divided remainder is stay on. +-----------+ (when units=1, stored negative offset)", "", "* `struct page` has been split into `struct page` and `struct slab` since kernel 5.17.", " The structure name used for SLOB has been changed to `struct slab`.", "* SLOB was removed in kernel 6.4.", ] _note_ = "\n".join(_note_) """ struct kmem_cache { unsigned int object_size; unsigned int size; unsigned int align; slab_flags_t flags; // unsigned int unsigned int useroffset; // if 4.16 <= kernel unsigned int usersize; // if 4.16 <= kernel const char *name; int refcount; void (*ctor)(void *); struct list_head list; }; struct page { // if kernel < 4.18 unsigned long flags; void *__unused_1; void *freelist; int units; atomic_t refcount; // if kernel < 4.16 struct list_head lru; ... }; struct page { // if 4.18 <= kernel < 5.17 unsigned long flags; struct list_head lru; struct kmem_cache *__unused_1; void *freelist; void *__unused_2; int units; ... }; struct slab { // if 5.17 <= kernel unsigned long __page_flags; struct list_head slab_list; void *__unused_1; void *freelist long units; unsigned int __unused_2; }; """ def initialize(self): if hasattr(self, "initialized") and self.initialized: if not self.args.meta and not self.args.rescan: return True kversion = Kernel.kernel_version() if not kversion: self.quiet_err("Failed to resolve kernel version") return False # resolve slab_caches self.slab_caches = KernelAddressHeuristicFinder.get_slab_caches() if self.slab_caches is None: self.quiet_err("Failed to resolve `slab_caches`") return False else: self.quiet_info("slab_caches: {:#x}".format(self.slab_caches)) # resolve global freelists self.free_slob_large = Symbol.get_ksymaddr("free_slob_large") if self.free_slob_large is None: self.quiet_err("Failed to resolve `free_slob_large`") return False else: self.quiet_info("free_slob_large: {:#x}".format(self.free_slob_large)) self.free_slob_medium = Symbol.get_ksymaddr("free_slob_medium") if self.free_slob_medium is None: self.quiet_err("Failed to resolve `free_slob_medium`") return False else: self.quiet_info("free_slob_medium: {:#x}".format(self.free_slob_medium)) self.free_slob_small = Symbol.get_ksymaddr("free_slob_small") if self.free_slob_small is None: self.quiet_err("Failed to resolve `free_slob_small`") return False else: self.quiet_info("free_slob_small: {:#x}".format(self.free_slob_small)) # offsetof(kmem_cache, list) if kversion < "4.16": self.kmem_cache_offset_list = current_arch.ptrsize * 3 + 4 * 4 else: self.kmem_cache_offset_list = current_arch.ptrsize * 3 + 4 * 6 self.quiet_info("offsetof(kmem_cache, list): {:#x}".format(self.kmem_cache_offset_list)) # offsetof(kmem_cache, name) self.kmem_cache_offset_name = self.kmem_cache_offset_list - current_arch.ptrsize * 3 self.quiet_info("offsetof(kmem_cache, name): {:#x}".format(self.kmem_cache_offset_name)) # offsetof(kmem_cache, object_size) self.kmem_cache_offset_object_size = 0 self.quiet_info("offsetof(kmem_cache, object_size): {:#x}".format(self.kmem_cache_offset_object_size)) # offsetof(kmem_cache, size) self.kmem_cache_offset_size = 4 self.quiet_info("offsetof(kmem_cache, size): {:#x}".format(self.kmem_cache_offset_size)) # offsetof(kmem_cache, flags) self.kmem_cache_offset_flags = 4 * 3 self.quiet_info("offsetof(kmem_cache, flags): {:#x}".format(self.kmem_cache_offset_flags)) # offsetof(page, next) / offsetof(slab, next) if kversion < "4.16": self.page_offset_next = current_arch.ptrsize * 3 + 4 * 2 elif kversion < "4.18": self.page_offset_next = current_arch.ptrsize * 4 elif kversion < "5.17": self.page_offset_next = current_arch.ptrsize else: self.page_offset_next = current_arch.ptrsize self.quiet_info("offsetof({:s}, next): {:#x}".format(Kernel.slab_page_str(), self.page_offset_next)) # offsetof(page, freelist) / offsetof(slab, freelist) if kversion < "4.18": self.page_offset_freelist = current_arch.ptrsize * 2 elif kversion < "5.17": self.page_offset_freelist = current_arch.ptrsize * 4 else: self.page_offset_freelist = current_arch.ptrsize * 4 self.quiet_info("offsetof({:s}, freelist): {:#x}".format(Kernel.slab_page_str(), self.page_offset_freelist)) # offsetof(page, units) / offsetof(slab, units) if kversion < "4.18": self.page_offset_units = current_arch.ptrsize * 3 elif kversion < "5.17": self.page_offset_freelist = current_arch.ptrsize * 6 else: self.page_offset_freelist = current_arch.ptrsize * 5 self.quiet_info("offsetof({:s}, units): {:#x}".format(Kernel.slab_page_str(), self.page_offset_units)) self.initialized = True return True def get_next_kmem_cache(self, addr, point_to_base=True): if point_to_base: addr += self.kmem_cache_offset_list if self.args.reverse_walk: return read_int_from_memory(addr) - self.kmem_cache_offset_list else: return read_int_from_memory(addr + current_arch.ptrsize) - self.kmem_cache_offset_list def get_name(self, addr): name_addr = read_int_from_memory(addr + self.kmem_cache_offset_name) return read_cstring_from_memory(name_addr) def walk_freelist(self, head, page): if self.args.simple: return [] freelist = [] current = head while True: base = current & get_pagesize_mask_high() units = struct.unpack("+-kmem_cache_cpu-+", "| ... | | page/slab |--+", "+------------+ | freelist | |", " ^ +----------------+ | <---virt/page translate--->", " | v", " | +-page/slab---+ +-0x1000-page-+ <--base (named by GEF)", " +-------------------------| slab_cache | | chunk |", " | +-page/slab---+ | ... |", " +-------------------------| slab_cache | +-0x1000-page-+", " | +-page/slab---+ | chunk |", " +-------------------------| slab_cache | | chunk | <--user specified address", " +-------------+ | ... |", " +-0x1000-page-+", " | chunk |", " | ... |", " +-------------+", ] _note_ = "\n".join(_note_) def initialize(self): if hasattr(self, "initialized") and self.initialized: return True cmd = {"SLUB": "slub-dump", "SLAB": "slab-dump", "SLUB_TINY": "slub-tiny-dump"}[self.allocator] res = gdb.execute("{:s} --meta".format(cmd), to_string=True) r = re.search(r"offsetof\((?:page|slab), slab_cache\): (0x\S+)", res) if not r: return False self.page_offset_slab_cache = int(r.group(1), 16) if self.args.verbose: info("offsetof({:s}, slab_cache): {:#x}".format(Kernel.slab_page_str(), self.page_offset_slab_cache)) r = re.search(r"offsetof\((?:page|slab), next\): (0x\S+)", res) if not r: return False self.page_offset_next = int(r.group(1), 16) if self.args.verbose: info("offsetof({:s}, next): {:#x}".format(Kernel.slab_page_str(), self.page_offset_next)) r = re.search(r"offsetof\(kmem_cache, name\): (0x\S+)", res) if not r: return False self.kmem_cache_offset_name = int(r.group(1), 16) if self.args.verbose: info("offsetof(kmem_cache, name): {:#x}".format(self.kmem_cache_offset_name)) r = re.search(r"offsetof\(kmem_cache, size\): (0x\S+)", res) if not r: return False self.kmem_cache_offset_size = int(r.group(1), 16) if self.args.verbose: info("offsetof(kmem_cache, size): {:#x}".format(self.kmem_cache_offset_size)) r = re.search(r"offsetof\(kmem_cache, object_size\): (0x\S+)", res) if not r: return False self.kmem_cache_offset_object_size = int(r.group(1), 16) if self.args.verbose: info("offsetof(kmem_cache, object_size): {:#x}".format(self.kmem_cache_offset_object_size)) # for aligned check if self.allocator in ["SLUB", "SLUB_TINY"]: r = re.search(r"offsetof\(kmem_cache, red_left_pad\): (0x\S+)", res) if not r: return False self.kmem_cache_offset_red_left_pad = int(r.group(1), 16) if self.args.verbose: info("offsetof(kmem_cache, red_left_pad): {:#x}".format(self.kmem_cache_offset_red_left_pad)) if self.allocator == "SLAB": r = re.search(r"offsetof\((?:page|slab), s_mem\): (0x\S+)", res) if not r: return False self.page_offset_s_mem = int(r.group(1), 16) if self.args.verbose: info("offsetof({:s}, s_mem): {:#x}".format(Kernel.slab_page_str(), self.page_offset_s_mem)) # for slab-virtual if is_x86_64(): r = re.search(r"offsetof\(kmem_cache, freed_slabs_min\): (0x\S+)", res) if r: self.slab_virtual_enabled = True if self.args.verbose: info("offsetof(kmem_cache, freed_slabs_min): {:#x}".format(int(r.group(1), 16))) else: self.slab_virtual_enabled = False else: self.slab_virtual_enabled = False # for num of pages if self.allocator in ["SLUB", "SLUB_TINY"]: r = re.search(r"offsetof\((?:page|slab), inuse_objects_frozen\): (0x\S+)", res) if not r: return False self.page_offset_inuse_objects_frozen = int(r.group(1), 16) if self.args.verbose: info("offsetof({:s}, inuse_objects_frozen): {:#x}".format( Kernel.slab_page_str(), self.page_offset_inuse_objects_frozen, )) elif self.allocator == "SLAB": r = re.search(r"offsetof\(kmem_cache, gfporder\): (0x\S+)", res) if not r: return False self.kmem_cache_offset_gfporder = int(r.group(1), 16) if self.args.verbose: info("offsetof(kmem_cache, gfporder): {:#x}".format(self.kmem_cache_offset_gfporder)) self.initialized = True return True def virt2page_wrapper(self, vaddr): if self.slab_virtual_enabled: ret = gdb.execute("slab-virtual --quiet from_virt {:#x}".format(vaddr), to_string=True) r = re.search(r"Slab: (\S+)", ret) else: ret = gdb.execute("virt2page {:#x}".format(vaddr), to_string=True) r = re.search(r"Page: (\S+)", ret) if r: return int(r.group(1), 16) return None def check_slab_dump(self, target_addr, slab_cache_name): def get_freed_addresses(res): freed_addresses = [] res = Color.remove_color(res) in_freelist_section = 0 for line in res.splitlines(): line = line.rstrip() if not in_freelist_section: if re.match(r"^ (freelist|objects:)", line): in_freelist_section = 1 elif re.match(r"^ entry:", line): in_freelist_section = 2 elif in_freelist_section == 1: if not line.startswith(" 0x"): in_freelist_section = 0 elif in_freelist_section == 2: if not line.startswith(" 0x"): in_freelist_section = 0 if in_freelist_section == 1: r = re.search(r"0x\S+ (0x\S+)", line) if r: freed_chunk = int(r.group(1), 16) freed_addresses.append(freed_chunk) elif in_freelist_section == 2: r = re.search(r"(0x\S+)", line) if r: freed_chunk = int(r.group(1), 16) freed_addresses.append(freed_chunk) return freed_addresses if self.allocator == "SLUB": res = gdb.execute("slub-dump --node --no-pager --quiet {:s}".format(slab_cache_name), to_string=True) elif self.allocator == "SLUB_TINY": res = gdb.execute("slub-tiny-dump --no-pager --quiet {:s}".format(slab_cache_name), to_string=True) elif self.allocator == "SLAB": res = gdb.execute("slab-dump --no-pager --quiet {:s}".format(slab_cache_name), to_string=True) else: return freed_addresses = get_freed_addresses(res) freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") used_address_color = Config.get_gef_setting("theme.heap_chunk_address_used") if target_addr in freed_addresses: gef_print("status: {:s} (found in freelist)".format(Color.colorify("freed", freed_address_color))) else: gef_print("status: {:s} (not found in freelist)".format(Color.colorify("in-use", used_address_color))) return def slab_contains(self): current = self.args.address & get_pagesize_mask_high() chunk_label_color = Config.get_gef_setting("theme.heap_chunk_label") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") try: while True: page = self.virt2page_wrapper(current) if page is None: self.quiet_err("Invalid address") return self.quiet_print("{:s}: {:#x}".format(Kernel.slab_page_str(), page)) page_next = read_int_from_memory(page + self.page_offset_next) if page_next & 1: current -= get_pagesize() self.quiet_warn("Detected invalid value, continue exploring...") continue kmem_cache = read_int_from_memory(page + self.page_offset_slab_cache) if kmem_cache == 0: self.quiet_err("This address is not managed by slab (kmem_cache=0)") return self.quiet_print("kmem_cache: {:#x}".format(kmem_cache)) if (kmem_cache & get_pagesize_mask_high()) == 0xdead_0000_0000_0000: current -= get_pagesize() self.quiet_warn("Detected invalid value, continue exploring...") continue if kmem_cache & 1: current -= get_pagesize() self.quiet_warn("Detected invalid value, continue exploring...") continue self.quiet_print("base: {:#x}".format(current)) break except (gdb.MemoryError, ZeroDivisionError): self.quiet_err("Memory read error") return try: slab_cache_name_ptr = read_int_from_memory(kmem_cache + self.kmem_cache_offset_name) slab_cache_name = read_cstring_from_memory(slab_cache_name_ptr) if slab_cache_name is None: self.quiet_err("This address is not managed by slab (slab_cache_name=\"\")") return slab_cache_size = read_int32_from_memory(kmem_cache + self.kmem_cache_offset_size) slab_cache_object_size = read_int32_from_memory(kmem_cache + self.kmem_cache_offset_object_size) if self.allocator in ["SLUB", "SLUB_TINY"]: red_left_pad = read_int_from_memory(kmem_cache + self.kmem_cache_offset_red_left_pad) color_offset = 0 x = read_int_from_memory(page + self.page_offset_inuse_objects_frozen) objects = (x >> 16) & 0x7fff num_pages = (slab_cache_size * objects + get_pagesize_mask_low()) // get_pagesize() else: red_left_pad = 0 s_mem = read_int_from_memory(page + self.page_offset_s_mem) color_offset = s_mem & 0xfff gfporder = read_int32_from_memory(kmem_cache + self.kmem_cache_offset_gfporder) num_pages = 1 << gfporder # `inuse` is not displayed because it is not a reliable reference value. # The value of `slab->inuse` also includes the number of chunks registered in `kmem_cache_cpu->freelist` etc. # However, what the user actually wants is the number of chunks that are truly in use, # excluding those accounted for by `kmem_cache_cpu->freelist`. # Accurately deriving that value is non-trivial. msg = ("name: {:s} object_size: {:s} (chunk_size: {:#x}) num_pages: {:#x}".format( Color.colorify(slab_cache_name, chunk_label_color), Color.colorify_hex(slab_cache_object_size, chunk_size_color), slab_cache_size, num_pages, )) aligned = True if (self.args.address - (red_left_pad + color_offset) - current) % slab_cache_size != 0: msg += " " + Color.redify("(unaligned?)") aligned = False gef_print(msg) # resolve freelist and print chunk status (in-use or freed) if aligned: self.check_slab_dump(self.args.address, slab_cache_name) except (gdb.MemoryError, ZeroDivisionError): self.quiet_err("Memory read error") return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") if not hasattr(self, "initialized"): self.initialized = False if args.rescan: self.initialized = False if not hasattr(self, "allocator"): self.allocator = Kernel.get_slab_type() if self.allocator not in ["SLUB", "SLUB_TINY", "SLAB"]: self.quiet_err("Unsupported: SLOB, Unknown allocator") return ret = self.initialize() if not ret: self.quiet_err("Failed to initialize") return self.slab_contains() return @register_command class KmemCacheAliasCommand(GenericCommand, BufferingOutput): """Resolve the slab cache (kmem_cache) alias.""" _cmdline_ = "kmem-cache-alias" _category_ = "06-h. Qemu-system/KGDB Cooperation - Linux Allocator" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("names", nargs="*", help="filter by specific cache name(s) (substring match).") parser.add_argument("-s", "--sort-by-size", action="store_true", help="sort by object size.") parser.add_argument("-m", "--merged-only", action="store_true", help="show only merged caches grouped by physical cache.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _note_ = [ "This command requires CONFIG_SYSFS=y.", ] _note_ = "\n".join(_note_) def parse_rb_node(self, rb_node): if not rb_node or not is_valid_addr(rb_node): return [] right = read_int_from_memory(rb_node + current_arch.ptrsize * 1) & ~1 # remove RB_BLACK left = read_int_from_memory(rb_node + current_arch.ptrsize * 2) & ~1 # remove RB_BLACK ret = [rb_node] if right: ret += self.parse_rb_node(right) if left: ret += self.parse_rb_node(left) return ret def initialize(self): if hasattr(self, "initialized") and self.initialized: return True self.slab_kset = KernelAddressHeuristicFinder.get_slab_kset() if self.slab_kset is None: self.quiet_err("Could not find slab_kset") return False self.quiet_info("slab_kset: {:#x}".format(self.slab_kset)) """ struct kset { struct list_head list; spinlock_t list_lock; struct kobject { const char *name; // -> "slab" struct list_head entry; struct kobject *parent; struct kset *kset; const struct kobj_type *ktype; struct kernfs_node *sd; ... } kobj; const struct kset_uevent_ops *uevent_ops; } __randomize_layout; """ kset = read_int_from_memory(self.slab_kset) for i in range(0x10): if not is_valid_addr(kset + current_arch.ptrsize * i): continue name_ptr = read_int_from_memory(kset + current_arch.ptrsize * i) name = read_cstring_from_memory(name_ptr) if name != "slab": continue if not is_double_link_list(kset + current_arch.ptrsize * (i + 1)): continue # found self.offset_kobj_sd = current_arch.ptrsize * (i + 6) self.quiet_info("offsetof(kset, kobj.sd): {:#x}".format(self.offset_kobj_sd)) break else: self.quiet_err("Could not find offsetof(kset, kobj.sd)") return False """ struct kernfs_node { atomic_t count; atomic_t active; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif struct kernfs_node __rcu *__parent; const char __rcu *name; struct rb_node { unsigned long __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; } __attribute__((aligned(sizeof(long)))) rb; const void *ns; unsigned int hash; unsigned short flags; // v6.9~ umode_t mode; // unsigned short, v6.9~ union { struct kernfs_elem_dir { unsigned long subdirs; struct rb_root children; struct kernfs_root *root; } dir; struct kernfs_elem_symlink { struct kernfs_node *target_kn; } symlink; struct kernfs_elem_attr attr; }; u64 id; void *priv; struct kernfs_iattrs *iattr; struct rcu_head rcu; }; """ sd = read_int_from_memory(kset + self.offset_kobj_sd) for i in range(0x20): if not is_valid_addr(sd + current_arch.ptrsize * i): continue name_ptr = read_int_from_memory(sd + current_arch.ptrsize * i) name = read_cstring_from_memory(name_ptr) if name != "slab": continue # found self.offset_name = current_arch.ptrsize * i self.offset_rb = self.offset_name + current_arch.ptrsize self.offset_union = self.offset_rb + current_arch.ptrsize * 5 subdirs = read_int_from_memory(sd + self.offset_union) if subdirs == 0 or subdirs > 0x1000: # subdirs should be small positive number (~0x200), at most, it should not be 0x1000. # so there exists padding or flags||mode if not. self.offset_union += current_arch.ptrsize self.offset_dir_children = self.offset_union + current_arch.ptrsize self.offset_symlink_target_kn = self.offset_union self.quiet_info("offsetof(kernfs_node, name): {:#x}".format(self.offset_name)) self.quiet_info("offsetof(kernfs_node, rb): {:#x}".format(self.offset_rb)) self.quiet_info("offsetof(kernfs_node, dir.children): {:#x}".format(self.offset_dir_children)) self.quiet_info("offsetof(kernfs_node, symlink.target_kn): {:#x}".format(self.offset_symlink_target_kn)) break else: self.quiet_err("Could not find offsetof(kernfs_node, name)") return False self.initialized = True return True def parse_kmem_cache_alias(self): # get children_root kset = read_int_from_memory(self.slab_kset) sd = read_int_from_memory(kset + self.offset_kobj_sd) children_root = read_int_from_memory(sd + self.offset_dir_children) # parse alias_groups = {} nodes = self.parse_rb_node(children_root) for node in nodes: node = node - self.offset_rb name_ptr = read_int_from_memory(node + self.offset_name) name = read_cstring_from_memory(name_ptr) target_kn = read_int_from_memory(node + self.offset_symlink_target_kn) alias = "-" if is_valid_addr(target_kn): alias_ptr = read_int_from_memory(target_kn + self.offset_name) alias = read_cstring_from_memory(alias_ptr) # rare case: L2TP!IPv6 -> L2TP/IPv6 if "!" in name: name = name.replace("!", "/") alias_groups[name] = { "alias": alias, "slab_cache_name": None, "name": name, "object_size": 0, "chunk_size": 0, } # parse slub-dump cmd = {"SLUB": "slub-dump", "SLUB_TINY": "slub-tiny-dump"}[self.allocator] res = gdb.execute("{:s} --list --no-pager --quiet".format(cmd), to_string=True) used_names = [] for line in res.splitlines(): r = re.search(r"(\d+)\s+\(0x\S+\)\s+(\d+)\s+\(0x\S+\)\s+(\S+)\s+0x\S+$", line) object_size = int(r.group(1)) chunk_size = int(r.group(2)) name = r.group(3) used_names.append([object_size, chunk_size, name]) # identify the actual slab_cache name in use for object_size, chunk_size, slab_cache_name in used_names: original_slab_cache_name = slab_cache_name # In older kernels, the slab_cache name may include the process name, # such as "kmalloc-512(342:serial-getty@ttyAMA0.service)". # To support this, anything after the parentheses is ignored. if slab_cache_name not in alias_groups: if "(" in slab_cache_name: slab_cache_name = slab_cache_name[:slab_cache_name.find("(")] # skip if not found if slab_cache_name not in alias_groups: self.quiet_err("Could not find key: {:s}".format(original_slab_cache_name)) continue for k in alias_groups.keys(): # already resolved if alias_groups[k]["slab_cache_name"]: continue if alias_groups[k]["alias"] == "-": if k == alias_groups[slab_cache_name]["alias"]: # k: ":0000256" -> "-" # slab_cache_name: "key_jar" -> ":0000256" alias_groups[k]["slab_cache_name"] = slab_cache_name alias_groups[k]["object_size"] = object_size alias_groups[k]["chunk_size"] = chunk_size elif k == slab_cache_name: # k: "kmalloc-256" -> "-" # slab_cache_name: "kmalloc-256" -> "-" alias_groups[k]["slab_cache_name"] = slab_cache_name alias_groups[k]["object_size"] = object_size alias_groups[k]["chunk_size"] = chunk_size else: if alias_groups[k]["alias"] == alias_groups[slab_cache_name]["alias"]: # k: "key_jar" -> ":0000256" # slab_cache_name: "key_jar" -> ":0000256" alias_groups[k]["slab_cache_name"] = slab_cache_name alias_groups[k]["object_size"] = object_size alias_groups[k]["chunk_size"] = chunk_size return alias_groups def make_output_merged(self, alias_groups): chunk_label_color = Config.get_gef_setting("theme.heap_chunk_label") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") # group entries by their Physical Cache Name merged_groups = {} for name, info in alias_groups.items(): if not info["slab_cache_name"]: continue phys_name = info["slab_cache_name"] if phys_name not in merged_groups: merged_groups[phys_name] = [] entry = info.copy() entry["logical_name"] = name merged_groups[phys_name].append(entry) # list keys if self.args.names: target_phys_groups = set() # Check every physical group for phys_name, children in merged_groups.items(): matched_group = False for child in children: child_name = child["logical_name"] for filter_name in self.args.names: if filter_name in child_name: target_phys_groups.add(phys_name) matched_group = True break if matched_group: break # found a match in this group, move to next one if not target_phys_groups: self.out.append("No caches found matching filter '{}'.".format(", ".join(self.args.names))) return # Only process the unique set of matching groups keys_to_process = [k for k in merged_groups.keys() if k in target_phys_groups] else: keys_to_process = list(merged_groups.keys()) # sort by keys if self.args.sort_by_size: sorted_phys_names = sorted(keys_to_process, key=lambda k: (merged_groups[k][0]["object_size"] if merged_groups[k] else 0)) else: sorted_phys_names = sorted(keys_to_process) # print self.out.append(titlify("Merged Slab Caches")) found_merge = False for phys_name in sorted_phys_names: children = merged_groups[phys_name] # MERGE-ONLY LOGIC: # We want to hide groups that are just [PhysicalOwner, SysfsID] # We count how many "real" aliases exist. # A "real" alias is one that is NOT the physical owner AND NOT the sysfs group ID (alias == "-") real_aliases = 0 keep_this_group = False for child in children: # If alias is "-" it's the sysfs group ID (e.g. :0000064) # If logical_name == phys_name, it's the physical owner if child["alias"] != "-" and child["logical_name"] != phys_name: real_aliases += 1 # filtering by name if self.args.names: for filter_name in self.args.names: if child["logical_name"] in filter_name: keep_this_group = True break if real_aliases == 0 and not keep_this_group: continue found_merge = True # print header header = "{:s} (Object size: {:s}, Chunk size: {:#x})".format( Color.colorify(phys_name, chunk_label_color), Color.colorify_hex(children[0]["object_size"], chunk_size_color), children[0]["chunk_size"], ) self.out.append(header) # print as tree children.sort(key=lambda x: x["logical_name"]) for i, child in enumerate(children): # If non-ASCII characters are included, they will affect # the processing of dev/update-syscalls/update-syscalls.py and # other programs for developing, so they should be written in hexadecimal. if i == len(children) - 1: tree_char = b"\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 ".decode() else: tree_char = b"\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ".decode() # coloring name_str = child["logical_name"] already_colored = False # Highlight the specific match if filtering is active if self.args.names: for filter_name in self.args.names: if filter_name in name_str: name_str = Color.colorify(name_str, "bold underline orange") already_colored = True break if name_str == phys_name: if not already_colored: name_str = Color.colorify(name_str, "green") line = tree_char + name_str + " (Physical Owner)" elif child["alias"] == "-": line = tree_char + Color.colorify(name_str, "gray") + " (Sysfs Group)" else: line = tree_char + name_str self.out.append(line) self.out.append("") if not found_merge: self.out.append("No interesting merges found (1:1 mappings only).") return def make_output(self, alias_groups): fmt = "{:16s} {:16s} {:30s} {:12s} {:s}" legend = ["Object Size", "Chunk Size", "Name", "Alias", "slab_cache name"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # sort by keys if self.args.sort_by_size: sorted_alias_groups = sorted(alias_groups.items(), key=lambda x: (x[1]["object_size"], x[1]["chunk_size"], x[1]["name"])) else: sorted_alias_groups = sorted(alias_groups.items()) found = False # print for name, v in sorted_alias_groups: # filtering by name if self.args.names: for filter_name in self.args.names: if filter_name in name: break if filter_name in v["alias"]: break if filter_name in v["slab_cache_name"]: break else: continue # print flat found = True if v["object_size"] == 0: self.out.append("{:16s} {:16s} {:30s} {:12s} {:s}".format( "-", "-", name, v["alias"], "", )) else: object_size = "{0:d} ({0:#x})".format(v["object_size"]) chunk_size = "{0:d} ({0:#x})".format(v["chunk_size"]) self.out.append("{:16s} {:16s} {:30s} {:12s} {:s}".format( object_size, chunk_size, name, v["alias"], v["slab_cache_name"], )) if self.args.names and not found: self.out.append("No caches found matching filters") return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return if kversion < "3.14": self.quiet_err("Unsupported before v3.14") return if not hasattr(self, "allocator"): self.allocator = Kernel.get_slab_type() if self.allocator not in ["SLUB", "SLUB_TINY"]: self.quiet_err("Unsupported: SLAB, SLOB, Unknown allocator") return ret = self.initialize() if not ret: self.quiet_err("Failed to initialize") return self.out = [] alias_groups = self.parse_kmem_cache_alias() if self.args.merged_only: self.make_output_merged(alias_groups) else: self.make_output(alias_groups) self.print_output() return @register_command class BuddyDumpCommand(GenericCommand, BufferingOutput): """Dump the zone of the page allocator (buddy allocator) free-list.""" _cmdline_ = "buddy-dump" _category_ = "06-h. Qemu-system/KGDB Cooperation - Linux Allocator" _aliases_ = ["zone-dump", "pcplist"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-z", "--zone", dest="zone_filter", action="append", choices=["DMA", "DMA32", "Normal", "HighMem", "Movable", "Device"], help="filter by specified zone name.") parser.add_argument("-o", "--order", dest="order_filter", action="append", type=int, help="filter by specified order.") parser.add_argument("-m", "--mtype", dest="mtype_filter", action="append", type=int, help="filter by specified mtype.") parser.add_argument("-p", "--pcp-index", dest="pcp_index_filter", action="append", type=int, help="filter by specified per-cpu index.") parser.add_argument("-P", "--only-pcp", action="store_true", help="dump only per-cpu pages.") parser.add_argument("-F", "--skip-pcp", action="store_true", help="skip dumping per-cpu pages (dump only free_area).") parser.add_argument("--cpu", action="append", type=int, help="filter by specific cpu for per-cpu pages.") parser.add_argument("-s", "--sort", action="store_true", help="sort by page address instead of link list order of each size. overrides -c to 0.") parser.add_argument("-S", "--sort-verbose", action="store_true", help="enable --sort and add used area. filtered areas are treated as used. overrides -c to 0.") parser.add_argument("-Q", "--skip-phys", action="store_true", help="skip virt -> phys translation.") parser.add_argument("-M", "--use-physmap", action="store_true", help="use physmap for virt -> phys translation to speed up (when KGDB mode, x64/arm64 only).") parser.add_argument("--MIGRATE_PCPTYPES", type=int, choices=[3, 4], default=3, help="use specify value; linux: 3, android: 4 (2023~).") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-c", "--count", metavar="N", type=AddressUtil.parse_address, default=5, help="max entries to read per list (default: %(default)s, 0=unlimited). -s/-S/-v/-vv override this to 0.") parser.add_argument("-v", "--verbose", action="store_true", help="show all entries for non-sort mode. equivalent to -c 0.") parser.add_argument("-vv", "--vverbose", action="store_true", help="show empty entries too for non-sort mode. overrides -c to 0.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -z DMA32", "{0:s} -o 1 -o 2", "{0:s} --only-pcp --pcp-index 0 --cpu 0", "{0:s} --sort-verbose", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Simplified buddy allocator structure:", "", " +-node_data[MAX_NUMNODES]-+", " | *pglist_data (node 0) |--+", " | *pglist_data (node 1) | |", " | *pglist_data (node 2) | |", " | ... | |", " +-------------------------+ |", " |", " +--------------------------+", " |", " v", " +-pglist_data------------------------------+", " | node_zones[MAX_NR_ZONES] |", " | +-node_zones[0]----------------------+ | +--->+-per_cpu_pages--------+", " | | ... | | | | ... |", " | | per_cpu_pageset |-----+ | lists[NR_PCP_LISTS] | +-page-----+", " | | ... | | | +-lists[0]-------+ | | flags |", " | | name | | | | next |----->| lru.next |->...", " | | ... | | | | prev | | | lru.prev |", " | | free_area[MAX_ORDER] | | | +-lists[1]-------+ | | ... |", " | | +-free_area[0]----------------+ | | | | ... | | +----------+", " | | | free_list[MIGRATE_TYPES] | | | | +----------------+ |", " | | | +-free_list[0]----------+ | | | +----------------------+", " | | | | next |---------+", " | | | | prev | | | | |", " | | | +-free_list[1]----------+ | | | | +-page-----+ +-page-----+ +-page-----+", " | | | | ... | | | | | | flags | | flags | | flags |", " | | | +-----------------------+ | | | +--->| lru.next |--->| lru.next |--->| lru.next |->...", " | | | nr_free | | | | lru.prev | | lru.prev | | lru.prev |", " | | +-free_area[1]----------------+ | | | ... | | ... | | ... |", " | | | ... | | | +----------+ +----------+ +----------+", " | | +-----------------------------+ | |", " | +-node_zones[1]----------------------+ |", " | | ... | |", " | +------------------------------------+ |", " | ... |", " +------------------------------------------+", "", "You can combine this result with information of in-use space. Try using `kvmmap` command.", ] _note_ = "\n".join(_note_) def resolve_zone_offset_name(self): # fast path try: self.offset_name = to_unsigned_long(gdb.parse_and_eval("&((struct zone*)0).name")) self.sizeof_zone = to_unsigned_long(gdb.parse_and_eval("sizeof(struct zone)")) return except gdb.error: pass # slow path current = self.nodes[0] name_offsets = [] while len(name_offsets) < 2: val = read_int_from_memory(current) name = read_cstring_from_memory(val) if name in ["DMA", "DMA32", "Normal", "HighMem", "Movable", "Device"]: offset = current - self.nodes[0] name_offsets.append(offset) current += current_arch.ptrsize self.offset_name = name_offsets[0] self.sizeof_zone = name_offsets[1] - name_offsets[0] return def resolve_zone_offset_per_cpu_pageset(self): # fast path kversion = Kernel.kernel_version() try: if kversion < "5.14": self.offset_per_cpu_pageset = to_unsigned_long(gdb.parse_and_eval("&((struct zone*)0).pageset")) else: self.offset_per_cpu_pageset = to_unsigned_long(gdb.parse_and_eval("&((struct zone*)0).per_cpu_pageset")) return except gdb.error: pass # slow path if "3.12" <= kversion: current = self.nodes[0] while current < self.nodes[0] + self.offset_name: val = read_int_from_memory(current) if is_valid_addr(val): offset_zone_pgdat = current - self.nodes[0] self.offset_per_cpu_pageset = offset_zone_pgdat + current_arch.ptrsize return current += current_arch.ptrsize self.offset_per_cpu_pageset = None return for i in range(1, 100): candidate_offset = current_arch.ptrsize * i val = read_int_from_memory(self.nodes[0] + candidate_offset) if val == 0 or val < 0x100: continue if self.cpu_offset is None: if not is_valid_addr(val): continue # found self.offset_per_cpu_pageset = candidate_offset return else: if not is_valid_addr(self.cpu_offset[0] + val): continue x = read_memory(self.cpu_offset[0] + val, 0x40) if set(x) == {0}: continue # found self.offset_per_cpu_pageset = candidate_offset return self.offset_per_cpu_pageset = None return def resolve_per_cpu_pages_offset_lists(self, per_cpu_pageset): """ struct per_cpu_pageset { // ~v5.13 struct per_cpu_pages pcp; ... } struct per_cpu_pages { spinlock_t lock; // v5.14~ int count; int high; int batch; short free_factor; // v5.14~ #ifdef CONFIG_NUMA short expire; // v5.14~ #endif struct list_head lists[NR_PCP_LISTS]; // v5.14~ struct list_head lists[MIGRATE_PCPTYPES]; // ~v5.13 } ____cacheline_aligned_in_smp; """ # fast path try: self.offset_lists = to_unsigned_long(gdb.parse_and_eval("&((struct per_cpu_pages*)0).lists")) return except gdb.error: pass # slow path current = align_to_ptrsize(per_cpu_pageset + 4 * 3) # count, high, batch while not is_double_link_list(current): # search for list_head current += current_arch.ptrsize self.offset_lists = current - per_cpu_pageset return def resolve_NR_PCP_LISTS(self, per_cpu_pageset): # GEF detects MIGRATE_PCPTYPES and NR_PCP_LISTS using the same logic. # It will retain them as NR_PCP_LISTS regardless of version. # fast path try: self.NR_PCP_LISTS = to_unsigned_long(gdb.parse_and_eval( "sizeof(((struct per_cpu_pages*)0).lists) / sizeof(((struct per_cpu_pages*)0).lists[0])", )) return except gdb.error: # In some environments, the size of list_head is not saved, resulting in division by 0. try: t = gdb.lookup_type("struct per_cpu_pages") f = next(x for x in t.fields() if x.name == "lists") lo, hi = f.type.range() self.NR_PCP_LISTS = hi - lo return except gdb.error: pass # slow path current = per_cpu_pageset + self.offset_lists while is_double_link_list(current): # search for not list_head current += current_arch.ptrsize * 2 self.NR_PCP_LISTS = ((current - per_cpu_pageset) - self.offset_lists) // (current_arch.ptrsize * 2) return def resolve_MAX_NR_ZONES(self): # fast path try: self.MAX_NR_ZONES = to_unsigned_long(gdb.parse_and_eval( "sizeof(((struct zone*)0).lowmem_reserve) / sizeof(((struct zone*)0).lowmem_reserve[0])", )) return except gdb.error: pass # slow path self.MAX_NR_ZONES = 0 for i in range(6): zone = self.nodes[0] + self.sizeof_zone * i name_ptr = read_int_from_memory(zone + self.offset_name) name = read_cstring_from_memory(name_ptr) if not name: break self.MAX_NR_ZONES += 1 return def resolve_zone_offset_free_area(self): """ struct free_area { struct list_head free_list[MIGRATE_TYPES]; unsigned long nr_free; }; """ # fast path try: self.offset_free_area = to_unsigned_long(gdb.parse_and_eval("&((struct zone*)0).free_area")) return except gdb.error: pass # slow path kversion = Kernel.kernel_version() if "3.12" <= kversion: current = self.nodes[0] + self.offset_name + current_arch.ptrsize else: current = self.nodes[0] while True: # search for list_head if is_double_link_list(current): break current += current_arch.ptrsize self.offset_free_area = current - self.nodes[0] return def resolve_MIGRATE_TYPES(self): # fast path try: self.MIGRATE_TYPES = to_unsigned_long(gdb.parse_and_eval( "sizeof(((struct free_area*)0).free_list) / sizeof(((struct free_area*)0).free_list[0])", )) return except gdb.error: pass # slow path current = free_area = self.nodes[0] + self.offset_free_area while True: val = read_int_from_memory(current) if not is_valid_addr(val): break current += current_arch.ptrsize offset_nr_free = current - free_area sizeof_list_head = current_arch.ptrsize * 2 self.MIGRATE_TYPES = offset_nr_free // sizeof_list_head return def resolve_migratetype_names(self): if self.args.MIGRATE_PCPTYPES == 3: """ const char * const migratetype_names[MIGRATE_TYPES] = { "Unmovable", "Movable", "Reclaimable", "HighAtomic", #ifdef CONFIG_CMA "CMA", #endif #ifdef CONFIG_MEMORY_ISOLATION "Isolate", #endif }; """ kversion = Kernel.kernel_version() if self.MIGRATE_TYPES == 4: if "4.4" <= kversion: self.migrate_types = [ "Unmovable", "Movable", "Reclaimable", "HighAtomic", ] else: self.migrate_types = [ "Unmovable", "Reclaimable", "Movable", "Reserve", ] elif self.MIGRATE_TYPES == 5: if "4.4" <= kversion: self.migrate_types = [ "Unmovable", "Movable", "Reclaimable", "HighAtomic", "Isolate", ] else: self.migrate_types = [ "Unmovable", "Reclaimable", "Movable", "Reserve", "Isolate", ] elif self.MIGRATE_TYPES == 6: if "4.4" <= kversion: self.migrate_types = [ "Unmovable", "Movable", "Reclaimable", "HighAtomic", "Contiguous", # CONFIG_CMA needs CONFIG_MEMORY_ISOLATION, so there is only this pattern "Isolate", ] else: self.migrate_types = [ "Unmovable", "Reclaimable", "Movable", "Reserve", "Contiguous", # CONFIG_CMA needs CONFIG_MEMORY_ISOLATION, so there is only this pattern "Isolate", ] else: err("MIGRATE_TYPES: {:#x}".format(self.MIGRATE_TYPES)) raise self.MIGRATE_PCPTYPES = 3 elif self.args.MIGRATE_PCPTYPES == 4: # https://android.googlesource.com/kernel/common/+/433445e9a160%5E%21/#F1 """ const char * const migratetype_names[MIGRATE_TYPES] = { "Unmovable", "Movable", "Reclaimable", #ifdef CONFIG_CMA "CMA", #endif "HighAtomic", #ifdef CONFIG_MEMORY_ISOLATION "Isolate", #endif }; """ if self.MIGRATE_TYPES == 4: self.migrate_types = [ "Unmovable", "Movable", "Reclaimable", "HighAtomic", ] elif self.MIGRATE_TYPES == 5: self.migrate_types = [ "Unmovable", "Movable", "Reclaimable", "HighAtomic", "Isolate", ] elif self.MIGRATE_TYPES == 6: self.migrate_types = [ "Unmovable", "Movable", "Reclaimable", "Contiguous", # CONFIG_CMA needs CONFIG_MEMORY_ISOLATION, so there is only this pattern "HighAtomic", "Isolate", ] else: err("MIGRATE_TYPES: {:#x}".format(self.MIGRATE_TYPES)) raise self.MIGRATE_PCPTYPES = 4 return def resolve_MAX_ORDER(self): # fast path try: self.MAX_ORDER = to_unsigned_long(gdb.parse_and_eval( "sizeof(((struct zone*)0).free_area) / sizeof(((struct zone*)0).free_area[0])", )) return except gdb.error: pass # slow path current = free_area = self.nodes[0] + self.offset_free_area while True: ok = True for i in range(self.MIGRATE_TYPES): if not is_double_link_list(current + current_arch.ptrsize * (i * 2)): ok = False break if not ok: break current += self.sizeof_free_area self.MAX_ORDER = (current - free_area) // self.sizeof_free_area return def initialize(self): if hasattr(self, "initialized") and self.initialized: return True # per_cpu_offset __per_cpu_offset = KernelAddressHeuristicFinder.get_per_cpu_offset() if __per_cpu_offset is None: self.cpu_offset = None else: self.cpu_offset = KernelCurrentCommand.get_each_cpu_offset(__per_cpu_offset) # search for node_data node_data = KernelAddressHeuristicFinder.get_node_data() if node_data: self.quiet_info("node_data: {:#x}".format(node_data)) # parse each node (*pglist_data) self.nodes = [] current = node_data while True: node = read_int_from_memory(current) if not is_valid_addr(node): break self.nodes.append(node) current += current_arch.ptrsize else: first_node = KernelAddressHeuristicFinder.get_node_data0() if first_node: self.quiet_info("first_node: {:#x}".format(first_node)) self.nodes = [first_node] else: self.quiet_err("Failed to resolve node_data or first_node") return False self.quiet_info("num of nodes: {:d}".format(len(self.nodes))) assert len(self.nodes) > 0 """ typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES]; ... }; struct zone { // v3.12, v3.14~ unsigned long _watermark[NR_WMARK]; // v5.0~ unsigned long watermark_boost; // v5.0~ unsigned long watermark[NR_WMARK]; // ~v4.20 unsigned long nr_reserved_highatomic; // v4.4~ unsigned long nr_free_highatomic; // v6.12~ long lowmem_reserve[MAX_NR_ZONES]; #ifdef CONFIG_NEED_MULTIPLE_NODES // v5.10 #ifdef CONFIG_NUMA // ~v5.9, v5.11~ int node; #endif unsigned int inactive_ratio; // ~v4.7 struct pglist_data *zone_pgdat; struct per_cpu_pageset __percpu *pageset; // ~v5.13 struct per_cpu_pages __percpu *per_cpu_pageset; // v5.14~ struct per_cpu_zonestat __percpu *per_cpu_zonestats; // v5.14~ ... const char *name; #ifdef CONFIG_MEMORY_ISOLATION unsigned long nr_isolate_pageblock; #endif #ifdef CONFIG_MEMORY_HOTPLUG seqlock_t span_seqlock; #endif int initialized; // v4.9~ wait_queue_head_t *wait_table; // ~v4.8 unsigned long wait_table_hash_nr_entries; // ~v4.8 unsigned long wait_table_bits; // ~v4.8 ZONE_PADDING(_pad1_) spinlock_t lock; // ~v3.19 struct free_area free_area[MAX_ORDER]; ... }; struct zone { // ~v3.11, v3.13 unsigned long watermark[NR_WMARK]; unsigned long percpu_drift_mark; unsigned long lowmem_reserve[MAX_NR_ZONES]; unsigned long dirty_balance_reserve; // v3.3~ #ifdef CONFIG_NUMA int node; unsigned long min_unmapped_pages; unsigned long min_slab_pages; #endif struct per_cpu_pageset __percpu *pageset; spinlock_t lock; int all_unreclaimable; #if defined CONFIG_COMPACTION || defined CONFIG_CMA bool compact_blockskip_flush; // v3.7~ unsigned long compact_cached_free_pfn; // v3.6~ unsigned long compact_cached_migrate_pfn; // v3.7~ #endif #ifdef CONFIG_MEMORY_HOTPLUG seqlock_t span_seqlock; #endif #ifdef CONFIG_CMA unsigned long min_cma_pages; // v3.4~v3.7 #endif struct free_area free_area[MAX_ORDER]; ... const char *name; }; static char * const zone_names[MAX_NR_ZONES] = { #ifdef CONFIG_ZONE_DMA "DMA", #endif #ifdef CONFIG_ZONE_DMA32 "DMA32", #endif "Normal", #ifdef CONFIG_HIGHMEM "HighMem", #endif "Movable", #ifdef CONFIG_ZONE_DEVICE "Device", // v4.3~ #endif }; """ # zone->name, sizeof(struct zone) self.resolve_zone_offset_name() self.quiet_info("offsetof(zone, name): {:#x}".format(self.offset_name)) self.quiet_info("sizeof(zone): {:#x}".format(self.sizeof_zone)) # zone->per_cpu_pageset self.resolve_zone_offset_per_cpu_pageset() if self.offset_per_cpu_pageset is None: self.quiet_err("Failed to resolve per_cpu_pageset") return False else: self.quiet_info("offsetof(zone, per_cpu_pageset): {:#x}".format(self.offset_per_cpu_pageset)) # per_cpu_pageset->lists if __per_cpu_offset is None: per_cpu_pageset = read_int_from_memory(self.nodes[0] + self.offset_per_cpu_pageset) else: per_cpu_pageset = read_int_from_memory(self.nodes[0] + self.offset_per_cpu_pageset) + self.cpu_offset[0] per_cpu_pageset = AddressUtil.align_address(per_cpu_pageset) self.resolve_per_cpu_pages_offset_lists(per_cpu_pageset) self.quiet_info("offsetof(per_cpu_pages, lists): {:#x}".format(self.offset_lists)) # NR_PCP_LISTS self.resolve_NR_PCP_LISTS(per_cpu_pageset) self.quiet_info("NR_PCP_LISTS: {:d}".format(self.NR_PCP_LISTS)) # MAX_NR_ZONES self.resolve_MAX_NR_ZONES() self.quiet_info("MAX_NR_ZONES: {:d}".format(self.MAX_NR_ZONES)) # zone->free_area self.resolve_zone_offset_free_area() self.quiet_info("offsetof(zone, free_area): {:#x}".format(self.offset_free_area)) # MIGRATE_TYPES self.resolve_MIGRATE_TYPES() self.quiet_info("MIGRATE_TYPES: {:d}".format(self.MIGRATE_TYPES)) # migratetype_names self.resolve_migratetype_names() # sizeof(free_area) sizeof_list_head = current_arch.ptrsize * 2 self.sizeof_free_area = sizeof_list_head * self.MIGRATE_TYPES + current_arch.ptrsize self.quiet_info("sizeof(free_area): {:#x}".format(self.sizeof_free_area)) # MAX_ORDER self.resolve_MAX_ORDER() self.quiet_info("MAX_ORDER: {:d}".format(self.MAX_ORDER)) """ struct page { // v5.18~ unsigned long flags; union { struct { union { struct list_head lru; ... }; struct page { // v4.18~v5.17 unsigned long flags; union { struct { struct list_head lru; ... }; struct page { // v3.1~v4.17 unsigned long flags; union { }; // ptrsize union { }; // ptrsize union { }; // 8 bytes union { struct list_head lru; ... }; """ # page->lru kversion = Kernel.kernel_version() if "4.18" <= kversion: self.offset_lru = current_arch.ptrsize else: self.offset_lru = current_arch.ptrsize * 3 + 8 self.initialized = True return True class Entry: def __init__(self, page, size, is_highmem, args, cpu_num=None): self.page = page self.size = size self.is_highmem = is_highmem self.args = args self.cpu_num = cpu_num return def get_virt_phys_str(self): virt_str = "???" phys_str = "???" if self.is_highmem: return virt_str, phys_str heap_page_color = Config.get_gef_setting("theme.heap_page_address") align = AddressUtil.get_format_address_width() virt = Kernel.page2virt(self.page) phys = None if virt: if self.args.skip_phys: pass elif self.args.use_physmap: if is_x86_64(): physmap = KernelAddressHeuristicFinder.get_PAGE_OFFSET() elif is_arm64(): physmap = KernelAddressHeuristicFinder.consts().physmap_base if physmap is not None: phys = virt - physmap else: phys = PageMap.v2p_from_map(virt, BuddyDumpCommand.maps) if virt is not None: virt_str = "{:#0{:d}x}-{:#0{:d}x}".format(virt, align, virt + self.size, align) virt_str = Color.colorify(virt_str, heap_page_color) if phys is not None: phys_str = "{:#0{:d}x}-{:#0{:d}x}".format(phys, align, phys + self.size, align) return virt_str, phys_str def __str__(self): chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") align = AddressUtil.get_format_address_width() page_str = Color.colorify("{:#0{:d}x}".format(self.page, align), freed_address_color) size_str = Color.colorify("{:#08x}".format(self.size), chunk_size_color) virt_str, phys_str = self.get_virt_phys_str() if self.cpu_num is not None: msg = " page:{:s} size:{:s} virt:{:s} phys:{:s} (pcp, cpu={:d})".format( page_str, size_str, virt_str, phys_str, self.cpu_num, ) else: msg = " page:{:s} size:{:s} virt:{:s} phys:{:s}".format( page_str, size_str, virt_str, phys_str, ) return msg @Cache.cache_this_session def get_pageblock_order(self): # for 5.14 ~ 6.10 kversion = Kernel.kernel_version() if not ("5.14" <= kversion < "6.10"): return None PMD_SHIFT = KernelAddressHeuristicFinder.consts().PMD_SHIFT PAGE_SHIFT = KernelAddressHeuristicFinder.consts().PAGE_SHIFT HPAGE_SHIFT = PMD_SHIFT HUGETLB_PAGE_ORDER = HPAGE_SHIFT - PAGE_SHIFT CONFIG_HUGETLB_PAGE = bool( Symbol.get_ksymaddr("hugetlb_fault") or Symbol.get_ksymaddr("hugetlbfs_read_iter") ) # CONFIG_HUGETLB_PAGE_SIZE_VARIABLE is ia64 or ppc only, so ignored if "5.14" <= kversion < "5.18": if CONFIG_HUGETLB_PAGE: return HUGETLB_PAGE_ORDER else: return self.MAX_ORDER - 1 elif "5.18" <= kversion < "6.8": if CONFIG_HUGETLB_PAGE: return min(HUGETLB_PAGE_ORDER, self.MAX_ORDER - 1) else: return self.MAX_ORDER - 1 else: # 6.8 <= kversion < "6.10" MAX_PAGE_ORDER = self.MAX_ORDER - 1 if CONFIG_HUGETLB_PAGE: return min(HUGETLB_PAGE_ORDER, MAX_PAGE_ORDER) else: return MAX_PAGE_ORDER def dump_pcp_entry(self, list_i, i, cpu_num, is_highmem): PAGE_ALLOC_COSTLY_ORDER = 3 NR_LOWORDER_PCP_LISTS = (self.MIGRATE_PCPTYPES * (PAGE_ALLOC_COSTLY_ORDER + 1)) if i < NR_LOWORDER_PCP_LISTS: # for normal case order = i // self.MIGRATE_PCPTYPES mtype = i % self.MIGRATE_PCPTYPES mtype_str = self.migrate_types[mtype] else: # for CONFIG_TRANSPARENT_HUGEPAGE kversion = Kernel.kernel_version() if kversion < "5.14": raise elif "5.14" <= kversion < "6.0": # indices 12..14 are "base=4 + migratetype" base = PAGE_ALLOC_COSTLY_ORDER + 1 mtype = i - self.MIGRATE_PCPTYPES * base # 0,1,2 if 0 <= mtype < self.MIGRATE_PCPTYPES: mtype_str = self.migrate_types[mtype] else: mtype_str = "THP_UNKNOWN" order = self.get_pageblock_order() elif ("6.0" <= kversion < "6.1") or ("6.2" <= kversion < "6.6.37") or ("6.7" <= kversion < "6.9"): # 1 slot mtype = i - NR_LOWORDER_PCP_LISTS if i == NR_LOWORDER_PCP_LISTS: mtype_str = "THP" else: mtype_str = "THP_UNKNOWN" order = self.get_pageblock_order() elif ("6.1" <= kversion < "6.2") or ("6.6.37" <= kversion < "6.7") or ("6.9" <= kversion < "6.10"): # 2 slots thp_i = i - NR_LOWORDER_PCP_LISTS mtype = thp_i if 0 <= thp_i < 2: mtype_str = "THP_MOVABLE" if thp_i == 1 else "THP_OTHER" else: mtype_str = "THP_UNKNOWN" order = self.get_pageblock_order() else: # 6.10~ # 2 slots thp_i = i - NR_LOWORDER_PCP_LISTS mtype = thp_i if 0 <= thp_i < 2: mtype_str = "THP_MOVABLE" if thp_i == 1 else "THP_OTHER" else: mtype_str = "THP_UNKNOWN" HPAGE_PMD_SHIFT = KernelAddressHeuristicFinder.consts().PMD_SHIFT PAGE_SHIFT = KernelAddressHeuristicFinder.consts().PAGE_SHIFT HPAGE_PMD_ORDER = HPAGE_PMD_SHIFT - PAGE_SHIFT order = HPAGE_PMD_ORDER # size info PAGE_SIZE = KernelAddressHeuristicFinder.consts().PAGE_SIZE size = PAGE_SIZE * (2 ** order) chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") size_str = Color.colorify("{:#08x}".format(size), chunk_size_color) # make title pcp_title = " pcp_index: {:d}, order: {:d} ({:s} bytes), mtype: {:d} (={:s})".format( i, order, size_str, mtype, mtype_str, ) entries = [] # filtering if self.args.mtype_filter and mtype not in self.args.mtype_filter: return pcp_title, entries, bool(len(entries)) if self.args.order_filter and order not in self.args.order_filter: return pcp_title, entries, bool(len(entries)) # fast check current = read_int_from_memory(list_i) if not is_valid_addr(current): return pcp_title, entries, bool(len(entries)) # parse pcp entries MAX_ENTRIES = self.args.count while current != list_i: page = current - self.offset_lru entry = self.Entry(page, size, is_highmem, self.args, cpu_num=cpu_num) entries.append(entry) if MAX_ENTRIES and len(entries) >= MAX_ENTRIES: entries.append(None) # sentinel for "..." break current = read_int_from_memory(current) return pcp_title, entries, bool(len(entries)) def dump_pcp(self, zone, is_highmem): # list pageset per_cpu_pageset = read_int_from_memory(zone + self.offset_per_cpu_pageset) if self.cpu_offset is None: per_cpu_pageset = [per_cpu_pageset] else: per_cpu_pageset = [AddressUtil.align_address(cpuoff + per_cpu_pageset) for cpuoff in self.cpu_offset] # parse each cpu tqdm = GefUtil.get_tqdm(not self.args.quiet) sizeof_list_head = current_arch.ptrsize * 2 pcp_all_entries = {} for cpu_num, pcp in tqdm(enumerate(per_cpu_pageset), leave=False, desc="cpu", total=len(per_cpu_pageset)): if self.args.cpu and cpu_num not in self.args.cpu: continue # parse each pcp list pcp_entries = [] for i in tqdm(range(self.NR_PCP_LISTS), leave=False, desc="pcplist"): if self.args.pcp_index_filter and i not in self.args.pcp_index_filter: continue lists_i = pcp + self.offset_lists + sizeof_list_head * i res = self.dump_pcp_entry(lists_i, i, cpu_num, is_highmem) pcp_entries.append(res) pcp_all_entries[cpu_num] = pcp_entries return pcp_all_entries def dump_free_list(self, free_list, mtype, size, is_highmem): # make title mtype_title = " mtype: {:d} (={:s})".format(mtype, self.migrate_types[mtype]) entries = [] # fast check current = read_int_from_memory(free_list) if not is_valid_addr(current): return mtype_title, entries, bool(len(entries)) # parse free list MAX_ENTRIES = self.args.count while current != free_list: page = current - self.offset_lru entry = self.Entry(page, size, is_highmem, self.args) entries.append(entry) if MAX_ENTRIES and len(entries) >= MAX_ENTRIES: entries.append(None) # sentinel for "..." break current = read_int_from_memory(current) return mtype_title, entries, bool(len(entries)) def dump_free_area(self, free_area, order, is_highmem): # size info PAGE_SIZE = KernelAddressHeuristicFinder.consts().PAGE_SIZE size = PAGE_SIZE * (2 ** order) chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") size_str = Color.colorify_hex(size, chunk_size_color) order_title = "order: {:d} ({:s} bytes)".format(order, size_str) # prase free area tqdm = GefUtil.get_tqdm(not self.args.quiet) sizeof_list_head = current_arch.ptrsize * 2 free_lists = [] has_any = False for mtype in tqdm(range(self.MIGRATE_TYPES), leave=False, desc="mtype"): if self.args.mtype_filter and mtype not in self.args.mtype_filter: continue free_list = free_area + sizeof_list_head * mtype res = self.dump_free_list(free_list, mtype, size, is_highmem) has_any |= res[2] free_lists.append(res) return order_title, free_lists, has_any def dump_zone(self, zone, is_highmem=False): zone_entry = {} # parse pcp if not self.args.skip_pcp: zone_entry["per_cpu_pageset"] = self.dump_pcp(zone, is_highmem) # parse free_area tqdm = GefUtil.get_tqdm(not self.args.quiet) if not self.args.only_pcp: free_area_entries = [] for order in tqdm(range(self.MAX_ORDER), leave=False, desc="order"): if self.args.order_filter and order not in self.args.order_filter: continue free_area_i = zone + self.offset_free_area + self.sizeof_free_area * order res = self.dump_free_area(free_area_i, order, is_highmem) free_area_entries.append(res) zone_entry["free_area"] = free_area_entries return zone_entry def dump_node(self, node): tqdm = GefUtil.get_tqdm(not self.args.quiet) zone_entries = [] for i in tqdm(range(self.MAX_NR_ZONES), leave=False, desc="zone"): zone = node + self.sizeof_zone * i name_ptr = read_int_from_memory(zone + self.offset_name) name = read_cstring_from_memory(name_ptr) if self.args.zone_filter and name not in self.args.zone_filter: continue title = "zone[{:d}] @ {:#x} ({:s})".format(i, zone, name) is_highmem = name == "HighMem" res = self.dump_zone(zone, is_highmem=is_highmem) zone_entries.append([title, res]) return zone_entries def make_output_for_sort(self, node_entries): # get all etnries all_entries = [] for _, zone_entries in node_entries: for _, zone_entry in zone_entries: if "per_cpu_pageset" in zone_entry: for _, pcp_all_entries in zone_entry["per_cpu_pageset"].items(): for _, pcp_entries, has_any in pcp_all_entries: if not has_any: continue for entry in pcp_entries: all_entries.append(entry) if "free_area" in zone_entry: for _, free_lists, has_any in zone_entry["free_area"]: if not has_any: continue for _, free_list, has_any2 in free_lists: if not has_any2: continue for entry in free_list: all_entries.append(entry) # sort all_entries = sorted(all_entries, key=lambda e: e.page) # make output prev_virt = None prev_size = None first = True align = AddressUtil.get_format_address_width() tqdm = GefUtil.get_tqdm(not self.args.quiet) for entry in tqdm(all_entries, leave=False): # for simple sort if not self.args.sort_verbose: self.out.append(str(entry)) continue # for verbose sort (filling the gap) # add used area if calculable virt = Kernel.page2virt(entry.page) if first: if virt is not None: phys = None if self.args.skip_phys: pass elif self.args.use_physmap: if is_x86_64(): physmap = KernelAddressHeuristicFinder.get_PAGE_OFFSET() elif is_arm64(): physmap = KernelAddressHeuristicFinder.consts().physmap_base if physmap is not None: phys = virt - physmap else: phys = PageMap.v2p_from_map(virt, BuddyDumpCommand.maps) if phys is not None: self.out.append(" used:{:{:d}s} size:{:#08x}".format("", align, phys)) first = False else: if isinstance(virt, int) and isinstance(prev_virt, int): if prev_virt + prev_size != virt: diff = virt - (prev_virt + prev_size) self.out.append(" used:{:{:d}s} size:{:#08x}".format("", align, diff)) # add free area self.out.append(str(entry)) prev_virt = virt prev_size = entry.size return def make_output(self, node_entries): tqdm = GefUtil.get_tqdm(not self.args.quiet) for node_title, zone_entries in tqdm(node_entries, leave=False, desc="node"): self.out.append(titlify(node_title)) for zone_title, zone_entry in tqdm(zone_entries, leave=False, desc="zone"): self.out.append(titlify(zone_title)) if "per_cpu_pageset" in zone_entry: self.out.append(titlify("per_cpu_pageset")) for cpu_num, pcp_all_entries in tqdm(zone_entry["per_cpu_pageset"].items(), leave=False, desc="cpu"): self.out.append("cpu: {:d}".format(cpu_num)) for pcp_title, pcp_entries, has_any in tqdm(pcp_all_entries, leave=False, desc="pcplist"): if not has_any and not self.args.vverbose: continue self.out.append(pcp_title) for i, entry in enumerate(pcp_entries): if self.args.count and i >= self.args.count: self.out.append(" ...") break self.out.append(str(entry)) if "free_area" in zone_entry: self.out.append(titlify("free_area")) for order_title, free_lists, has_any in tqdm(zone_entry["free_area"], leave=False, desc="order"): if not has_any and not self.args.vverbose: continue self.out.append(order_title) for mtype_title, free_list, has_any2 in tqdm(free_lists, leave=False, desc="mtype"): if not has_any2 and not self.args.vverbose: continue self.out.append(mtype_title) for i, entry in enumerate(free_list): if self.args.count and i >= self.args.count: self.out.append(" ...") break self.out.append(str(entry)) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): kversion = Kernel.kernel_version() if kversion < "3.1": self.quiet_err("Unsupported before v3.1") return if self.args.use_physmap: if not (is_x86_64() or is_arm64()): self.quiet_err("Unsupported architecture") return # parse args if args.rescan: self.initialized = False self.args.sort = args.sort_verbose or args.sort self.args.verbose = args.vverbose or args.verbose if self.args.sort or self.args.verbose: self.args.count = 0 # initialize self.quiet_info("Wait for memory scan") if not self.initialize(): return # do not use cache if not self.args.skip_phys and not self.args.use_physmap: BuddyDumpCommand.maps = PageMap.get_page_maps(None) if BuddyDumpCommand.maps is None: self.quiet_err("Failed to resolve maps") return # dump node_entries = [] tqdm = GefUtil.get_tqdm(not self.args.quiet) for i, node in tqdm(enumerate(self.nodes), leave=False, total=len(self.nodes), desc="node"): title = "node[{:d}] @ {:#x}".format(i, node) res = self.dump_node(node) node_entries.append([title, res]) self.quiet_info("Parse OK, making output...") # print self.out = [] if self.args.sort: self.make_output_for_sort(node_entries) else: self.make_output(node_entries) self.print_output(check_terminal_size=True) return @register_command class KernelPipeCommand(GenericCommand, BufferingOutput): """Dump pipe information.""" _cmdline_ = "kpipe" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-i", "--inode-filter", type=AddressUtil.parse_address, default=[], action="append", help="filter by specific struct inode.") parser.add_argument("-f", "--file-filter", type=AddressUtil.parse_address, default=[], action="append", help="filter by specific struct file.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -q", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "", "Simplified pipe structure:", "", "+-task_struct-+ +->+-files_struct-+ +->+-fdtable---+ +->+-files*[]----+ +->+-file------+", "| ... | | | ... | | | max_fds | | | [0] |--+ | ... |", "| files |--+ | fdt |--+ | fd |--+ | ... | | f_path |", "| ... | | ... | | ... | | [max_fds-1] | | dentry |---+", "+-------------+ +--------------+ +-----------+ +-------------+ | ... | |", " +-----------+ |", " |", "+----------------------------------------------------------------------------------------------+", "|", "| +-dentry---+ +->+-inode-----+ +->+-pipe_inode_info--------+ +->+-pipe_buffer-+", "| | ... | | | ... | | | ... | | | page |--->page", "+->| d_inode |--+ | i_pipe |--+ | head, tail, (v5.5~) | | | offset |", " | ... | | ... | | max_usage, (v5.5~) | | | len |", " +----------+ +-----------+ | ring_size, (v5.5~) | | | ... |", " | nrbuf, curbuf, (~v5.4) | | +-------------+", " | buffers (~v5.4) | | | page |--->page", " | ... | | | offset |", " | bufs |--+ | len |", " | ... | | ... |", " +------------------------+ +-------------+", " | ... |", " +-------------+", ] _note_ = "\n".join(_note_) def initialize(self, pipe_files): if hasattr(self, "initialized") and self.initialized: return True # inode->i_pipe offset_i_pipe = self.get_offset_i_pipe(pipe_files) if not offset_i_pipe: self.quiet_err("Could not find inode->i_pipe") return False self.offset_i_pipe = offset_i_pipe self.quiet_info("offsetof(inode, i_pipe): {:#x}".format(self.offset_i_pipe)) # pipe_inode_info->bufs offset_bufs = self.get_offset_bufs(pipe_files) if not offset_bufs: self.quiet_err("Could not find pipe_inode_info->bufs") return False self.offset_bufs = offset_bufs self.quiet_info("offsetof(pipe_inode_info, bufs): {:#x}".format(self.offset_bufs)) kversion = Kernel.kernel_version() if "5.5" <= kversion: # pipe_inode_info->{head,tail,max_usage,ring_size} ret = self.get_offset_head_or_nrbuf(pipe_files) if ret is None: self.quiet_err("Could not find pipe_inode_info->head") return False self.offset_head = ret self.quiet_info("offsetof(pipe_inode_info, head): {:#x}".format(self.offset_head)) self.offset_tail = self.offset_head + 4 self.quiet_info("offsetof(pipe_inode_info, tail): {:#x}".format(self.offset_tail)) self.offset_max_usage = self.offset_tail + 4 self.quiet_info("offsetof(pipe_inode_info, max_usage): {:#x}".format(self.offset_max_usage)) self.offset_ring_size = self.offset_max_usage + 4 self.quiet_info("offsetof(pipe_inode_info, ring_size): {:#x}".format(self.offset_ring_size)) else: # pipe_inode_info->{nrbuf,curbuf,buffers} ret = self.get_offset_head_or_nrbuf(pipe_files) if ret is None: self.quiet_err("Could not find pipe_inode_info->nrbuf") return False self.offset_nrbuf = ret self.quiet_info("offsetof(pipe_inode_info, nrbuf): {:#x}".format(self.offset_nrbuf)) self.offset_curbuf = self.offset_nrbuf + 4 self.quiet_info("offsetof(pipe_inode_info, curbuf): {:#x}".format(self.offset_curbuf)) self.offset_buffers = self.offset_curbuf + 4 self.quiet_info("offsetof(pipe_inode_info, buffers): {:#x}".format(self.offset_buffers)) # pipe_buffer->{page, offset, len, flags} """ struct pipe_buffer { struct page *page; unsigned int offset, len; const struct pipe_buf_operations *ops; unsigned int flags; unsigned long private; }; """ self.offset_page = 0 self.quiet_info("offsetof(pipe_buffer, page): {:#x}".format(self.offset_page)) self.offset_offset = current_arch.ptrsize self.quiet_info("offsetof(pipe_buffer, offset): {:#x}".format(self.offset_offset)) self.offset_len = self.offset_offset + 4 self.quiet_info("offsetof(pipe_buffer, len): {:#x}".format(self.offset_len)) self.offset_flags = self.offset_len + 4 + current_arch.ptrsize self.quiet_info("offsetof(pipe_buffer, flags): {:#x}".format(self.offset_flags)) self.sizeof_pipe_buffer = align_to_ptrsize(self.offset_flags + 4) + current_arch.ptrsize self.quiet_info("sizeof(pipe_buffer): {:#x}".format(self.sizeof_pipe_buffer)) self.initialized = True return True def get_offset_i_pipe(self, pipe_files): """ struct inode { ... struct list_head i_lru; struct list_head i_sb_list; struct list_head i_wb_list; ... #if defined(CONFIG_IMA) || defined(CONFIG_FILE_LOCKING) atomic_t i_readcount; #endif const struct file_operations *i_fop; // ~v5.1 union { // v5.2~ const struct file_operations *i_fop; // v5.2~ void (*free_inode)(struct inode *); // v5.2~ }; // v5.2~ struct file_lock_context *i_flctx; struct address_space i_data; #ifdef CONFIG_QUOTA // ~v3.18 struct dquot *i_dquot[MAXQUOTAS]; // ~v3.18 // MAXQUOTAS=2 #endif // ~v3.18 struct list_head i_devices; // ~v6.13 union { // v6.14~ struct list_head i_devices; // v6.14~ int i_linklen; // v6.14~ }; // v6.14~ union { struct pipe_inode_info *i_pipe; <-- here struct block_device *i_bdev; struct cdev *i_cdev; char *i_link; unsigned i_dir_seq; }; ... }; """ # plan 1 inode = pipe_files[0][1] for i in range(0x100): # search three list_head if not is_double_link_list(inode + current_arch.ptrsize * (i + 0)): continue if not is_double_link_list(inode + current_arch.ptrsize * (i + 2)): continue if not is_double_link_list(inode + current_arch.ptrsize * (i + 4)): continue # search i_pipe for j in range(i + 6, 0x100): if not is_double_link_list(inode + current_arch.ptrsize * (j + 0)): # i_devices continue if not is_valid_addr_addr(inode + current_arch.ptrsize * (j + 2)): # i_pipe continue if is_double_link_list(inode + current_arch.ptrsize * (j + 2)): # i_pipe continue i_pipe = read_int_from_memory(inode + current_arch.ptrsize * (j + 2)) # count 0x10 and 0x01 value count_0x10 = 0 count_0x01 = 0 for k in range(0x20): v = read_int32_from_memory(i_pipe + 4 * k) if v == 0x01: count_0x01 += 1 if v == 0x10: count_0x10 += 1 if count_0x10 > 1 or count_0x01 > 3: return current_arch.ptrsize * (j + 2) # plan 2 for i in range(0x100): v = read_int_from_memory(inode + current_arch.ptrsize * i) # i_pipe is valid addr if v < 0x10000 or not is_valid_addr(v): continue # skip invalid chunk ret = Kernel.get_slab_contains(v) if ret is None: continue # pipe_inode_info is allocated from kmalloc-192 (x64) or kmalloc-256 (arm64). # sometimes it is allocated from kmalloc-512, kmalloc-128, kmalloc-96 and kmalloc-64. # Other candidates found are kmalloc-2k, kmalloc-1024 and inode_cache (these are false positives), # so these should be excluded. if re.search(r"kmalloc(-cg)?-(64|96|128|192|256|512)", ret): return current_arch.ptrsize * i return None def get_offset_bufs(self, pipe_files): """ [v5.5~] struct pipe_inode_info { struct mutex mutex; wait_queue_head_t rd_wait, wr_wait; // v5.6~ wait_queue_head_t wait; // ~v5.5 unsigned int head; unsigned int tail; unsigned int max_usage; unsigned int ring_size; #ifdef CONFIG_WATCH_QUEUE // v5.8~ bool note_loss; // v5.8~ #endif // v5.8~ unsigned int nr_accounted; // v5.8~ unsigned int readers; unsigned int writers; unsigned int files; unsigned int r_counter; unsigned int w_counter; bool poll_usage; // v5.10~ struct page *tmp_page; struct fasync_struct *fasync_readers; struct fasync_struct *fasync_writers; struct pipe_buffer *bufs; <-- here struct user_struct *user; #ifdef CONFIG_WATCH_QUEUE // v5.8~ struct watch_queue *watch_queue; // v5.8~ #endif // v5.8~ }; [~v5.4] struct pipe_inode_info { struct mutex mutex; // v3.10~ wait_queue_head_t wait; unsigned int nrbufs, curbuf, buffers; unsigned int readers; unsigned int writers; unsigned int files; // v3.10~ unsigned int waiting_writers; unsigned int r_counter; unsigned int w_counter; struct page *tmp_page; struct fasync_struct *fasync_readers; struct fasync_struct *fasync_writers; struct inode *inode; // ~v3.9 struct pipe_buffer *bufs; <-- here struct user_struct *user; // v3.10, v3.12, v3.14, v3.16, v3.18, v4.1, v4.4~ }; struct pipe_buffer { struct page *page; unsigned int offset, len; const struct pipe_buf_operations *ops; // allow NULL unsigned int flags; unsigned long private; }; """ # plan 1 seen = [] for _file, inode in pipe_files: if inode in seen: continue seen.append(inode) pipe_inode_info = read_int_from_memory(inode + self.offset_i_pipe) for i in range(0x40): offset_bufs = current_arch.ptrsize * i if not is_valid_addr_addr(pipe_inode_info + offset_bufs): continue if is_double_link_list(pipe_inode_info + offset_bufs): continue if is_double_link_list(pipe_inode_info + offset_bufs - current_arch.ptrsize): continue # bufs bufs = read_int_from_memory(pipe_inode_info + offset_bufs) if is_64bit(): if not is_valid_addr_addr(bufs + current_arch.ptrsize * 0): # page continue len_ = read_int32_from_memory(bufs + current_arch.ptrsize * 1 + 4) # len if len_ == 0 or is_valid_addr_addr(bufs + current_arch.ptrsize * 1): # offset||len continue ops = read_int_from_memory(bufs + current_arch.ptrsize * 2) # ops if ops != 0 and not is_valid_addr(ops): continue if is_valid_addr_addr(bufs + current_arch.ptrsize * 3): # flags continue if is_valid_addr_addr(bufs + current_arch.ptrsize * 4): # private continue else: if not is_valid_addr_addr(bufs + current_arch.ptrsize * 0): # page continue if is_valid_addr_addr(bufs + current_arch.ptrsize * 1): # offset continue len_ = read_int_from_memory(bufs + current_arch.ptrsize * 2) # len if len_ == 0 or is_valid_addr_addr(len_): continue ops = read_int_from_memory(bufs + current_arch.ptrsize * 3) # ops if ops != 0 and not is_valid_addr(ops): continue if is_valid_addr_addr(bufs + current_arch.ptrsize * 4): # flags continue if is_valid_addr_addr(bufs + current_arch.ptrsize * 5): # private continue # found self.quiet_info("offset of bufs is found by heuristic way1") return offset_bufs # plan 2 kversion = Kernel.kernel_version() inode = pipe_files[0][1] pipe_inode_info = read_int_from_memory(inode + self.offset_i_pipe) for i in range(0x80): if not is_valid_addr(pipe_inode_info + current_arch.ptrsize * i): break v = read_int_from_memory(pipe_inode_info + current_arch.ptrsize * i) # bufs is valid addr if v < 0x10000 or not is_valid_addr(v): continue # bufs is not self if v == pipe_inode_info: continue # skip invalid chunk ret = Kernel.get_slab_contains(v) if ret is None: continue # pipe_inode_info is allocated from kmalloc-1k (x64) or kmalloc-512 (x86) if re.search(r"kmalloc(-cg)?-(1k|1024|512)", ret): self.quiet_info("offset of bufs is found by heuristic way2-1") return current_arch.ptrsize * i # before v5.5, pipe_buffer is allocated not from slub, but `user` is allocated from slub. if kversion < "5.5" and "uid_cache" in ret: self.quiet_info("offset of bufs is found by heuristic way2-2") return current_arch.ptrsize * (i - 1) return None def get_offset_head_or_nrbuf(self, pipe_files): inode = pipe_files[0][1] pipe_inode_info = read_int_from_memory(inode + self.offset_i_pipe) for i in range(3, 0x40): if is_64bit(): # head||tail/nrbuf||curbuf is not address v1 = read_int_from_memory(pipe_inode_info + current_arch.ptrsize * i) if is_valid_addr(v1): continue # head/nrbuf is too large v1_32 = read_int32_from_memory(pipe_inode_info + current_arch.ptrsize * i) if v1_32 > 0x100: continue # max_usage||ring_size/buffers||readers is not address v2 = read_int_from_memory(pipe_inode_info + current_arch.ptrsize * (i + 1)) if is_valid_addr(v2): continue # max_usage/buffers is too large or zero v2_32 = read_int32_from_memory(pipe_inode_info + current_arch.ptrsize * (i + 1)) if v2_32 > 0x100 or v2_32 == 0: continue return current_arch.ptrsize * i else: # head/nrbuf is not address v1 = read_int_from_memory(pipe_inode_info + current_arch.ptrsize * i) if is_valid_addr(v1): continue # head/nrbuf is too large v1_32 = read_int32_from_memory(pipe_inode_info + current_arch.ptrsize * i) if v1_32 > 0x100: continue # max_usage/buffers is not address v2 = read_int_from_memory(pipe_inode_info + current_arch.ptrsize * (i + 2)) if is_valid_addr(v2): continue # max_usage/buffers is too large or zero v2_32 = read_int32_from_memory(pipe_inode_info + current_arch.ptrsize * (i + 2)) if v2_32 > 0x100 or v2_32 == 0: continue return current_arch.ptrsize * i return None def get_pipe_files(self): # struct file of pipe ret = gdb.execute("ktask --quiet --no-pager --user-process-only --print-fd", to_string=True) pipe_files = [] for line in ret.splitlines(): m = re.search(r"\d+\s+(0x\S+) 0x\S+ (0x\S+) pipe:\[\d+\]", line) if not m: continue file = int(m.group(1), 16) inode = int(m.group(2), 16) pipe_files.append((file, inode)) if pipe_files: self.quiet_info("Num of pipe: {:d}".format(len({x[1] for x in pipe_files}))) return pipe_files def get_flags_str(self, flags_value): flags_dic = { 0x01: "PIPE_BUF_FLAG_LRU", 0x02: "PIPE_BUF_FLAG_ATOMIC", 0x04: "PIPE_BUF_FLAG_GIFT", 0x08: "PIPE_BUF_FLAG_PACKET", 0x10: "PIPE_BUF_FLAG_CAN_MERGE", 0x20: "PIPE_BUF_FLAG_WHOLE", 0x40: "PIPE_BUF_FLAG_LOSS", } flags = [] for k, v in flags_dic.items(): if flags_value & k: flags.append(v) flags_str = " | ".join(flags) if flags_str == "": flags_str = "none" return flags_str def dump_pipe(self, pipe_files): heap_page_color = Config.get_gef_setting("theme.heap_page_address") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") used_address_color = Config.get_gef_setting("theme.heap_chunk_address_used") kversion = Kernel.kernel_version() inodes = {} for file, inode in pipe_files: inodes[inode] = inodes.get(inode, []) + [file] for inode, files in inodes.items(): if self.args.inode_filter and inode not in self.args.inode_filter: continue if self.args.file_filter and not (set(self.args.file_filter) & set(files)): continue related_files = ", ".join(["{:#x}".format(x) for x in files]) self.out.append("inode: {:#x} (related struct file: {:s})".format(inode, related_files)) pipe_inode_info = read_int_from_memory(inode + self.offset_i_pipe) self.out.append(" pipe_inode_info: {:#x}".format(pipe_inode_info)) pipe_buffer = read_int_from_memory(pipe_inode_info + self.offset_bufs) self.out.append(" pipe_buffer: {:#x}".format(pipe_buffer)) if "5.5" <= kversion: head = read_int32_from_memory(pipe_inode_info + self.offset_head) tail = read_int32_from_memory(pipe_inode_info + self.offset_tail) max_usage = read_int32_from_memory(pipe_inode_info + self.offset_max_usage) ring_size = read_int32_from_memory(pipe_inode_info + self.offset_ring_size) self.out.append(" head: {:d}, tail: {:d}, max: {:d}, ring_size: {:d}".format( head, tail, max_usage, ring_size, )) else: nrbuf = read_int32_from_memory(pipe_inode_info + self.offset_nrbuf) curbuf = read_int32_from_memory(pipe_inode_info + self.offset_curbuf) buffers = read_int32_from_memory(pipe_inode_info + self.offset_buffers) self.out.append(" nrbuf: {:d}, curbuf: {:d}, buffers: {:d}".format( nrbuf, curbuf, buffers, )) head = curbuf + nrbuf tail = curbuf max_usage = buffers used_range = [x % max_usage for x in range(tail, head)] for idx in range(max_usage): base = pipe_buffer + self.sizeof_pipe_buffer * idx page = read_int_from_memory(base + self.offset_page) offset = read_int32_from_memory(base + self.offset_offset) len_ = read_int32_from_memory(base + self.offset_len) flags = read_int32_from_memory(base + self.offset_flags) virt = Kernel.page2virt(page) if idx in used_range: status = Color.colorify("used", used_address_color) else: status = Color.colorify("free", freed_address_color) if head % max_usage == idx: head_marker = "head" else: head_marker = " " if tail % max_usage == idx: tail_marker = "tail" else: tail_marker = " " out = " {:s} {:s} {:s} [{:02d}] page: {:#x}, ".format( head_marker, tail_marker, status, idx, page, ) if virt: colored_virt = Color.colorify_hex(virt, heap_page_color) out += "(virt: {:s}), ".format(colored_virt) out += "offset: {:#x}, len: {:#x}, flags: {:#x} ({:s})".format( offset, len_, flags, self.get_flags_str(flags), ) self.out.append(out) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") allocator = Kernel.get_slab_type() if allocator not in ["SLUB", "SLUB_TINY", "SLAB"]: err("Unsupported: SLOB, Unknown allocator") return # init kinfo = Kernel.get_kernel_layout() if kinfo.has_none: self.quiet_err("The kernel .text area could not be determined correctly") return pipe_files = self.get_pipe_files() if not pipe_files: self.quiet_info("Nothing to dump") return ret = self.initialize(pipe_files) if ret is False: return # dump self.out = [] self.dump_pipe(pipe_files) self.print_output(check_terminal_size=True) return @register_command class KernelBpfCommand(GenericCommand, BufferingOutput): """Dump the BPF information.""" _cmdline_ = "kbpf" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-p", "--only-progs", action="store_true", help="print progs only.") parser.add_argument("-m", "--only-maps", action="store_true", help="print maps only.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose mode.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -q", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "", "Simplified bpf structure:", "", "+-prog_idr----+ +--->+-xa_node----------+ +--------->+-bpf_prog-------------+", "| idr_rt | | | shift | | | ... |", "| xa_lock | | | ... | | | type |", "| xa_flags | | | count | | | expected_attach_type |", "| xa_head |---+ | ... | | | len |", "| idr_base | | slots[0] |---+ | jited_len |", "| idr_next | | slots[1] |--->xa_node | tag[8] |", "+-------------+ | ... | or | ... |", " | slots[15 or 63] | bpf_prog | bpf_func |---> BPF-code", " | ... | | ... |", " +------------------+ | aux |", " | ... |", " +----------------------+", "", "+-map_idr-----+ +--->+-xa_node----------+ +--------->+-bpf_array------------+", "| idr_rt | | | shift | | | map |", "| xa_lock | | | ... | | | ... |", "| xa_flags | | | count | | | map_type |", "| xa_head |---+ | ... | | | key_size |", "| idr_base | | slots[0] |---+ | value_size |", "| idr_next | | slots[1] |--->xa_node | max_entries |", "+-------------+ | ... | or | ... |", " | slots[15 or 63] | bpf_array | elem_size |", " | ... | | index_mask |", " +------------------+ | ... |", " +----------------------+", " | value[0] |", " | value[1] |", " | ... |", " | value[max_entries-1] |", " +----------------------+", ] _note_ = "\n".join(_note_) def parse_xarray(self, ptr, root=False): if ptr == 0: return [] ptr &= ~3 # untagged if root: node = read_int_from_memory(ptr + self.offset_xa_head) return self.parse_xarray(node) shift = read_int8_from_memory(ptr + self.offset_shift) count = read_int8_from_memory(ptr + self.offset_count) slots = ptr + self.offset_slots elems = [] for i in range(64): # 16 or 64 x = read_int_from_memory(slots + current_arch.ptrsize * i) if x == 0: continue if shift: elems += self.parse_xarray(x) else: elems.append(x) count -= 1 if count == 0: break return elems def initialize(self): # get global address prog_idr = KernelAddressHeuristicFinder.get_prog_idr() if not prog_idr: return False self.quiet_info("prog_idr: {:#x}".format(prog_idr)) map_idr = KernelAddressHeuristicFinder.get_map_idr() if not map_idr: return False self.quiet_info("map_idr: {:#x}".format(map_idr)) kversion = Kernel.kernel_version() """ struct xarray { spinlock_t xa_lock; gfp_t xa_flags; void __rcu *xa_head; }; """ # idr->idr_rt->xa_head base = prog_idr + 4 * 2 max_sizeof_idr = abs(prog_idr - map_idr) for i in range(20): pos = base + current_arch.ptrsize * i if (pos - prog_idr) >= max_sizeof_idr: continue x = read_int_from_memory(pos) if not is_valid_addr(x): continue if (x & 2) != 2: # tag continue y = read_cstring_from_memory(x) if y and len(y) > 8 or y == "bpf": continue z = read_int_from_memory(x) if is_valid_addr(z): continue self.offset_xa_head = pos - prog_idr self.quiet_info("offsetof(xarray, xa_head): {:#x}".format(self.offset_xa_head)) break else: err("Could not find xa_head. (maybe uninitialized?)") return False """ struct xa_node { unsigned char shift; unsigned char offset; unsigned char count; unsigned char nr_values; struct xa_node __rcu *parent; struct xarray *array; union { struct list_head private_list; struct rcu_head rcu_head; }; void __rcu *slots[XA_CHUNK_SIZE]; union { unsigned long tags[XA_MAX_MARKS][XA_MARK_LONGS]; unsigned long marks[XA_MAX_MARKS][XA_MARK_LONGS]; }; }; """ # xa_node->{shift,count,slots} self.offset_shift = 0 self.offset_count = 2 self.offset_slots = current_arch.ptrsize * 5 self.quiet_info("offsetof(xa_node, shift): {:#x}".format(self.offset_shift)) self.quiet_info("offsetof(xa_node, count): {:#x}".format(self.offset_count)) self.quiet_info("offsetof(xa_node, slots): {:#x}".format(self.offset_slots)) # parse progs, maps try: progs = self.parse_xarray(prog_idr, root=True) self.quiet_info("Num of progs: {:#x}".format(len(progs))) maps = self.parse_xarray(map_idr, root=True) self.quiet_info("Num of maps: {:#x}".format(len(maps))) except gdb.MemoryError: self.quiet_err("Not found") return False """ struct bpf_prog { u16 pages; u16 jited:1, jit_requested:1, gpl_compatible:1, cb_access:1, dst_needed:1, blinded:1, is_func:1, kprobe_override:1, has_callchain_buf:1, enforce_expected_attach_type:1, call_get_stack:1; enum bpf_prog_type type; enum bpf_attach_type expected_attach_type; u32 len; u32 jited_len; u8 tag[BPF_TAG_SIZE]; // 8 byte struct bpf_prog_stats __percpu *stats; // v5.12~ int __percpu *active; // v5.12~ unsigned int (*bpf_func)(const void *ctx, const struct bpf_insn *insn); // v5.12~ struct bpf_prog_aux *aux; struct sock_fprog_kern *orig_prog; unsigned int (*bpf_func)(const void *ctx, const struct bpf_insn *insn); // ~v5.11 const struct bpf_insn *insn); struct sock_filter insns[0]; struct bpf_insn insnsi[]; }; """ # bpf_prog->{type,expected_attach_type,len,jited_len,tag,aux} self.offset_prog_type = 4 self.offset_expected_attach_type = self.offset_prog_type + 4 self.offset_len = self.offset_expected_attach_type + 4 self.offset_jited_len = self.offset_len + 4 self.offset_tag = self.offset_jited_len + 4 if "5.12" <= kversion: self.offset_aux = align_to_ptrsize(self.offset_tag + 8) + current_arch.ptrsize * 3 self.offset_bpf_func = self.offset_aux - current_arch.ptrsize else: self.offset_aux = align_to_ptrsize(self.offset_tag + 8) self.offset_bpf_func = self.offset_aux + current_arch.ptrsize * 2 self.offset_orig_prog = self.offset_aux + current_arch.ptrsize self.quiet_info("offsetof(bpf_prog, type): {:#x}".format(self.offset_prog_type)) self.quiet_info("offsetof(bpf_prog, expected_attach_type): {:#x}".format(self.offset_expected_attach_type)) self.quiet_info("offsetof(bpf_prog, len): {:#x}".format(self.offset_len)) self.quiet_info("offsetof(bpf_prog, jited_len): {:#x}".format(self.offset_jited_len)) self.quiet_info("offsetof(bpf_prog, tag): {:#x}".format(self.offset_tag)) self.quiet_info("offsetof(bpf_prog, aux): {:#x}".format(self.offset_aux)) self.quiet_info("offsetof(bpf_prog, bpf_func): {:#x}".format(self.offset_bpf_func)) self.quiet_info("offsetof(bpf_prog, orig_prog): {:#x}".format(self.offset_orig_prog)) try: self.seccomp_tools_command = [GefUtil.which("ceccomp"), "disasm", "-c", "always"] self.quiet_info("ceccomp is found") except FileNotFoundError: try: self.seccomp_tools_command = [GefUtil.which("seccomp-tools"), "disasm"] self.quiet_info("seccomp-tools is found") if is_arm32(): self.quiet_warn("`seccomp-tools` is not supported on ARM32. " "Consider using `ceccomp` instead, as it supports ARM32.") self.quiet_info("GEF uses `capstone-disassemble bpf_func`") self.seccomp_tools_command = None except FileNotFoundError: self.quiet_info("Could not find ceccomp or seccomp-tools, GEF uses `capstone-disassemble bpf_func`") self.seccomp_tools_command = None if maps: """ struct bpf_map { const struct bpf_map_ops *ops ____cacheline_aligned; struct bpf_map *inner_map_meta; #ifdef CONFIG_SECURITY void *security; #endif enum bpf_map_type map_type; u32 key_size; u32 value_size; u32 max_entries; ... }; """ # bpf_map->{map_type,key_size,value_size,max_entries} cand = read_int_from_memory(maps[0] + current_arch.ptrsize * 2) if cand == 0 or is_valid_addr(cand): self.offset_map_type = current_arch.ptrsize * 3 else: self.offset_map_type = current_arch.ptrsize * 2 self.offset_key_size = self.offset_map_type + 4 self.offset_value_size = self.offset_key_size + 4 self.offset_max_entries = self.offset_value_size + 4 self.quiet_info("offsetof(bpf_map, map_type): {:#x}".format(self.offset_map_type)) self.quiet_info("offsetof(bpf_map, key_size): {:#x}".format(self.offset_key_size)) self.quiet_info("offsetof(bpf_map, value_size): {:#x}".format(self.offset_value_size)) self.quiet_info("offsetof(bpf_map, max_entries): {:#x}".format(self.offset_max_entries)) """ struct bpf_array { struct bpf_map map; u32 elem_size; u32 index_mask; struct bpf_array_aux *aux; union { char value[0] __aligned(8); void *ptrs[0] __aligned(8); void __percpu *pptrs[0] __aligned(8); }; }; """ # bpf_array->union_array value_size = read_int32_from_memory(maps[0] + self.offset_value_size) value_size_aligned_8 = align(value_size, 8) max_entries = read_int32_from_memory(maps[0] + self.offset_max_entries) k = 1 while k < max_entries: k <<= 1 index_mask = k - 1 sizeof_cache_line = 0x40 # ? base = maps[0] + sizeof_cache_line * 3 for i in range(100): pos = base + current_arch.ptrsize * i x = read_int32_from_memory(pos) y = read_int32_from_memory(pos + 4) if x == value_size_aligned_8 and y == index_mask: self.offset_union_array = (pos - maps[0]) + 4 * 2 + current_arch.ptrsize self.quiet_info("offsetof(bpf_array, union_array): {:#x}".format(self.offset_union_array)) break else: return False return progs, maps def dump_bpf_progs(self, progs): self.out.append(titlify("prog_idr")) fmt = "{:3s} {:18s} {:23s} {:24s} {:18s} {:18s} {:18s} {:9s} {:18s}" legend = ["#", "bpf_prog", "bpf_prog_type", "bpf_attach_type", "tag", "bpf_prog_aux", "bpf_func", "jited_len", "orig_prog"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) defined_prog_types = [ "UNSPEC", "SOCKET_FILTER", "KPROBE", "SCHED_CLS", "SCHED_ACT", "TRACEPOINT", "XDP", "PERF_EVENT", "CGROUP_SKB", "CGROUP_SOCK", "LWT_IN", "LWT_OUT", "LWT_XMIT", "SOCK_OPS", "SK_SKB", "CGROUP_DEVICE", "SK_MSG", "RAW_TRACEPOINT", "CGROUP_SOCK_ADDR", "LWT_SEG6LOCAL", "LIRC_MODE2", "SK_REUSEPORT", "FLOW_DISSECTOR", "CGROUP_SYSCTL", "RAW_TRACEPOINT_WRITABLE", "CGROUP_SOCKOPT", "TRACING", "STRUCT_OPS", "EXT", "LSM", "SK_LOOKUP", ] defined_attach_types = [ "CGROUP_INET_INGRESS", "CGROUP_INET_EGRESS", "CGROUP_INET_SOCK_CREATE", "CGROUP_SOCK_OPS", "SK_SKB_STREAM_PARSER", "SK_SKB_STREAM_VERDICT", "CGROUP_DEVICE", "SK_MSG_VERDICT", "CGROUP_INET4_BIND", "CGROUP_INET6_BIND", "CGROUP_INET4_CONNECT", "CGROUP_INET6_CONNECT", "CGROUP_INET4_POST_BIND", "CGROUP_INET6_POST_BIND", "CGROUP_UDP4_SENDMSG", "CGROUP_UDP6_SENDMSG", "LIRC_MODE2", "FLOW_DISSECTOR", "CGROUP_SYSCTL", "CGROUP_UDP4_RECVMSG", "CGROUP_UDP6_RECVMSG", "CGROUP_GETSOCKOPT", "CGROUP_SETSOCKOPT", "TRACE_RAW_TP", "TRACE_FENTRY", "TRACE_FEXIT", "MODIFY_RETURN", "LSM_MAC", "TRACE_ITER", "CGROUP_INET4_GETPEERNAME", "CGROUP_INET6_GETPEERNAME", "CGROUP_INET4_GETSOCKNAME", "CGROUP_INET6_GETSOCKNAME", "XDP_DEVMAP", "CGROUP_INET_SOCK_RELEASE", "XDP_CPUMAP", "SK_LOOKUP", "XDP", ] fmt = "{:<3d} {:#018x} {:23s} {:24s} {:#018x} {:#018x} {:#018x} {:<#9x} {:#018x}" for i, prog in enumerate(progs): bpf_type = read_int32_from_memory(prog + self.offset_prog_type) bpf_attach_type = read_int32_from_memory(prog + self.offset_expected_attach_type) jited_len = read_int32_from_memory(prog + self.offset_jited_len) orig_prog = read_int_from_memory(prog + self.offset_orig_prog) t1 = defined_prog_types[bpf_type] t2 = defined_attach_types[bpf_attach_type] tag = read_int64_from_memory(prog + self.offset_tag) aux = read_int_from_memory(prog + self.offset_aux) bpf_func = read_int_from_memory(prog + self.offset_bpf_func) self.out.append(fmt.format(i, prog, t1, t2, tag, aux, bpf_func, jited_len, orig_prog)) if self.args.verbose: # dump func if self.seccomp_tools_command and is_valid_addr(orig_prog): # use seccomp-tools or ceccomp cnt = read_int16_from_memory(orig_prog) prog = read_int_from_memory(orig_prog + current_arch.ptrsize) data = read_memory(prog, cnt * 8) tmp_fd, tmp_path = GefUtil.mkstemp(prefix="kbpf") os.fdopen(tmp_fd, "wb").write(data) ret = GefUtil.gef_execute_external( self.seccomp_tools_command + [tmp_path], as_list=True, ) self.out.extend(ret) os.unlink(tmp_path) elif is_valid_addr(bpf_func): try: __import__("capstone") # use capstone data = read_memory(bpf_func, jited_len) dump_count = 0 for insn in Disasm.capstone_disassemble(bpf_func, jited_len, code=data.hex()): msg = insn.colored_text(10) self.out.append(msg) dump_count += insn.size if dump_count >= jited_len: break except ImportError: ret = gdb.execute("x/40i {:#x}".format(bpf_func), to_string=True).rstrip() self.out.append(ret) self.out.append("...") else: self.err_add_out("Memory read error") self.out.append(titlify("")) return def dump_bpf_maps(self, maps): self.out.append(titlify("map_idr")) fmt = "{:3s} {:18s} {:21s} {:10s} {:10s} {:10s} {:18s}" legend = ["#", "bpf_map", "bpf_map_type", "key_size", "value_size", "max_ents", "array"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) defined_map_types = [ "UNSPEC", "HASH", "ARRAY", "PROG_ARRAY", "PERF_EVENT_ARRAY", "PERCPU_HASH", "PERCPU_ARRAY", "STACK_TRACE", "CGROUP_ARRAY", "LRU_HASH", "LRU_PERCPU_HASH", "LPM_TRIE", "ARRAY_OF_MAPS", "HASH_OF_MAPS", "DEVMAP", "SOCKMAP", "CPUMAP", "XSKMAP", "SOCKHASH", "CGROUP_STORAGE", "REUSEPORT_SOCKARRAY", "PERCPU_CGROUP_STORAGE", "QUEUE", "STACK", "SK_STORAGE", "DEVMAP_HASH", "STRUCT_OPS", "RINGBUF", "INODE_STORAGE", ] fmt = "{:<3d} {:#018x} {:21s} {:#010x} {:#010x} {:#010x} {:#018x}" for i, m in enumerate(maps): map_type = read_int32_from_memory(m + self.offset_map_type) t1 = defined_map_types[map_type] key_size = read_int32_from_memory(m + self.offset_key_size) val_size = read_int32_from_memory(m + self.offset_value_size) max_ents = read_int32_from_memory(m + self.offset_max_entries) union_array = m + self.offset_union_array self.out.append(fmt.format(i, m, t1, key_size, val_size, max_ents, union_array)) if self.verbose: if map_type == 2: # ARRAY res = gdb.execute("dereference -n {:#x} {:#x}".format(union_array, max_ents), to_string=True) self.out.append(res.rstrip()) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return if kversion < "4.20": # xarray is introduced from 4.20 self.quiet_err("Unsupported before v4.20") return stv_bpf_ret = gdb.execute("syscall-table-view -f bpf --quiet --no-pager", to_string=True) if "bpf" not in stv_bpf_ret: self.quiet_err("bpf syscall is unimplemented") return elif "invalid bpf" in stv_bpf_ret: self.quiet_err("bpf syscall is disabled") return # init ret = self.initialize() if ret is False: self.quiet_err("Failed to initialize") return progs, maps = ret # dump self.out = [] if not args.only_maps: self.dump_bpf_progs(progs) if not args.only_progs: self.dump_bpf_maps(maps) self.print_output(check_terminal_size=True) return @register_command class KernelIpcsCommand(GenericCommand, BufferingOutput): """Dump IPCs information (System V semaphore, message queue and shared memory).""" _cmdline_ = "kipcs" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-v", "--verbose", action="store_true", help="dump the beginning of msg_msg.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _note_ = [ "This command requires CONFIG_RANDSTRUCT=n.", "", "Simplified ipc structure:", "", "+-task_struct-+ +-->+-nsproxy--+ +-->+-ipc_namespace-+", "| ... | | | ... | | | ... |", "| nsproxy |--+ | ipc_ns |--+ | ids[0] (sem) |", "| ... | | ... | | ... |", "+-------------+ +----------+ | ipcs_idr |", " | xa_head |-->xarray-->+-sem_array-+", " | ... | | ... |", " | ids[1] (msg) | +-----------+", " | ... |", " | ipcs_idr |", " | xa_head |-->xarray-->+-msg_queue-+", " | ... | | ... |", " | ids[2] (shm) | +-----------+", " | ... |", " | ipcs_idr |", " | xa_head |-->xarray-->+-shmid_kernel-+", " | ... | | ... |", " | ... | +--------------+", " +---------------+", ] _note_ = "\n".join(_note_) def get_all_ipc_ns(self): res = gdb.execute("ktask --print-namespace --user-process-only --no-pager --quiet", to_string=True) r = re.findall(r"nsproxy->ipc_ns\s+(0x\S+)", res) ipc_ns_list = [] # do not use `set()` because the order is important. for x in r: x = int(x, 16) if x not in ipc_ns_list: ipc_ns_list.append(x) return ipc_ns_list def initialize(self, ipc_ns_list): if hasattr(self, "initialized") and self.initialized: return True if ipc_ns_list == []: return False if ipc_ns_list == [0]: err("Could not find valid ipc_ns (maybe CONFIG_SYSVIPC=n)") return False # ipc_namespace """ struct ipc_namespace { refcount_t count; // ~5.10 struct ipc_ids { int in_use; unsigned short seq; struct rw_semaphore { atomic_long_t count; atomic_long_t owner; #ifdef CONFIG_RWSEM_SPIN_ON_OWNER struct optimistic_spin_queue osq; #endif raw_spinlock_t wait_lock; struct list_head wait_list; #ifdef CONFIG_DEBUG_RWSEMS void *magic; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif } rwsem; struct idr { struct radix_tree_root { // =struct xarray spinlock_t xa_lock; gfp_t xa_flags; void __rcu *xa_head; } idr_rt; unsigned int idr_base; unsigned int idr_next; } ipcs_idr; int max_idx; int last_idx; #ifdef CONFIG_CHECKPOINT_RESTORE int next_id; #endif struct rhashtable key_ht; } ids[3]; ... """ kversion = Kernel.kernel_version() if "5.11" <= kversion: self.offset_ids = 0 else: self.offset_ids = current_arch.ptrsize self.quiet_info("offsetof(ipc_namespace, ids): {:#x}".format(self.offset_ids)) # offsetof(ipc_ids, ipcs_idr.idr_rt.xa_head): tagged valid pointer is `xa_head`. # sizeof(ids[0]): find two `xa_head` and calculate the distance. init_ipc_ns = ipc_ns_list[0] found = [] first_xa_flags = None for i in range(6, 120): base = self.offset_ids + current_arch.ptrsize * i """ [x64 before using IPC] 0xffffffff8b1841f8|+0x0038|+007: 0x0080000400000000 <- xa_lock, xa_flags 0xffffffff8b184200|+0x0040|+008: 0x0000000000000000 <- xa_head 0xffffffff8b184208|+0x0048|+009: 0x0000000000000000 <- idr_base, idr_next [x64 after using IPC] 0xffffffff8b1841f8|+0x0038|+007: 0x0080000400000000 0xffffffff8b184200|+0x0040|+008: 0xffff9250c7dc2002 -> 0x0000000000000001 0xffffffff8b184208|+0x0048|+009: 0x0000000100000000 [x64 after deleting IPC] 0xffffffff8b1841f8|+0x0038|+007: 0x0080000400000000 0xffffffff8b184200|+0x0040|+008: 0x0000000000000000 0xffffffff8b184208|+0x0048|+009: 0x0000000100000000 [x86 before using IPC] 0xc1b4e104|+0x0024|+009: 0x00000000 <- xa_lock 0xc1b4e108|+0x0028|+010: 0x00800004 <- xa_flags 0xc1b4e10c|+0x002c|+011: 0x00000000 <- xa_head 0xc1b4e110|+0x0030|+012: 0x00000000 <- idr_base 0xc1b4e114|+0x0034|+013: 0x00000000 <- idr_next [x86 after using IPC] 0xc1b4e104|+0x0024|+009: 0x00000000 0xc1b4e108|+0x0028|+010: 0x00800004 0xc1b4e10c|+0x002c|+011: 0xc2c67392 -> 0x00000001 0xc1b4e110|+0x0030|+012: 0x00000000 0xc1b4e114|+0x0034|+013: 0x00000001 [x86 after deleting IPC] 0xc1b4e104|+0x0024|+009: 0x00000000 0xc1b4e108|+0x0028|+010: 0x00800004 0xc1b4e10c|+0x002c|+011: 0x00000000 0xc1b4e110|+0x0030|+012: 0x00000000 0xc1b4e114|+0x0034|+013: 0x00000001 """ # xa_flags x = read_int_from_memory(init_ipc_ns + base - current_arch.ptrsize) if x == 0 or is_valid_addr(x): continue if first_xa_flags is not None and first_xa_flags != x: continue # xa_head y = read_int_from_memory(init_ipc_ns + base) if y: if not is_valid_addr(y): continue if y & 0x2 != 0x2: # xa_head is NULL or tagged address continue # idr_base, idr_next if is_32bit(): z1 = read_int_from_memory(init_ipc_ns + base + current_arch.ptrsize) # idr_base z2 = read_int_from_memory(init_ipc_ns + base + current_arch.ptrsize * 2) # idr_next if is_valid_addr(z1) or is_valid_addr(z2): continue if y and z1 == 0 and z2 == 0: continue else: z = read_int_from_memory(init_ipc_ns + base + current_arch.ptrsize) # idr_base, idr_next if is_valid_addr(z): # idr_base, idr_next continue if y and z == 0: continue # first found if not hasattr(self, "offset_xa_head"): self.offset_xa_head = base - self.offset_ids first_xa_flags = x # found found.append(base - self.offset_ids) # exit loop? if len(found) >= 2: self.sizeof_ipc_ids = found[1] - found[0] break else: self.quiet_err("Could not find ipc_namespace->ids[0].ipcs_idr.idr_rt.xa_head") self.quiet_err("Not recognized sizeof(struct ipc_ids)") self.quiet_err("Maybe CONFIG_SYSVIPC=n") return False self.quiet_info("offsetof(ipc_ids, ipcs_idr.idr_rt.xa_head): {:#x}".format(self.offset_xa_head)) self.quiet_info("sizeof(struct ipc_ids): {:#x}".format(self.sizeof_ipc_ids)) # xa_node """ struct xa_node { unsigned char shift; unsigned char offset; unsigned char count; unsigned char nr_values; struct xa_node __rcu *parent; struct xarray *array; union { struct list_head private_list; struct rcu_head rcu_head; }; void __rcu *slots[XA_CHUNK_SIZE]; union { unsigned long tags[XA_MAX_MARKS][XA_MARK_LONGS]; unsigned long marks[XA_MAX_MARKS][XA_MARK_LONGS]; }; }; """ # xa_node->{shift,count,slots} self.offset_shift = 0 self.offset_count = 2 self.offset_slots = current_arch.ptrsize * 5 # kern_ipc_perm """ struct kern_ipc_perm { spinlock_t lock; bool deleted; int id; key_t key; kuid_t uid; kgid_t gid; kuid_t cuid; kgid_t cgid; umode_t mode; unsigned long seq; void *security; struct rhash_head khtnode; struct rcu_head rcu; refcount_t refcount; } ____cacheline_aligned_in_smp __randomize_layout; """ self.offset_id = 4 + 4 self.offset_key = self.offset_id + 4 self.offset_uid = self.offset_key + 4 self.offset_gid = self.offset_uid + 4 self.offset_mode = self.offset_gid + 4 + 4 + 4 self.initialized = True return True def parse_xarray(self, ptr, root=False): if ptr == 0: return [] ptr &= ~3 # untagged if root: # ptr is &ipc_ids[i] node = read_int_from_memory(ptr + self.offset_xa_head) return self.parse_xarray(node) shift = read_int8_from_memory(ptr + self.offset_shift) count = read_int8_from_memory(ptr + self.offset_count) slots = ptr + self.offset_slots elems = [] for i in range(64): # 16 or 64 x = read_int_from_memory(slots + current_arch.ptrsize * i) if x == 0: continue if shift: elems += self.parse_xarray(x) else: elems.append(x) count -= 1 if count == 0: break return elems def dump_ipc_sem_ids(self, ipc_ids_ptr): """ struct sem_array { struct kern_ipc_perm sem_perm; time64_t sem_ctime; struct list_head pending_alter; struct list_head pending_const; struct list_head list_id; int sem_nsems; ... } __randomize_layout; """ self.out.append(titlify("Semaphore Arrays")) fmt = "{:18s} {:5s} {:10s} {:4s} {:4s} {:5s} {:s}" legend = ["sem_array", "semid", "key", "uid", "gid", "perms", "nsems"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) elems = self.parse_xarray(ipc_ids_ptr, root=True) for e in elems: semid = read_int32_from_memory(e + self.offset_id) key = read_int32_from_memory(e + self.offset_key) uid = read_int32_from_memory(e + self.offset_uid) gid = read_int32_from_memory(e + self.offset_gid) mode = read_int16_from_memory(e + self.offset_mode) if not hasattr(self, "offset_sem_nsems"): for i in range(1, 64): # search pending_alter, pending_const, list_id base = self.offset_mode + current_arch.ptrsize * i addrs = [read_int_from_memory(e + base + current_arch.ptrsize * j) for j in range(6)] if all(is_valid_addr(x) for x in addrs): # found self.offset_sem_nsems = base + current_arch.ptrsize * 6 break if hasattr(self, "offset_sem_nsems"): nsems = read_int_from_memory(e + self.offset_sem_nsems) self.out.append("{:#018x} {:<5d} {:#010x} {:<4d} {:<4d} {:#5o} {:d}".format( e, semid, key, uid, gid, mode, nsems, )) else: self.out.append("{:#018x} {:<5d} {:#010x} {:<4d} {:<4d} {:#5o} {:s}".format( e, semid, key, uid, gid, mode, "?", )) return def dump_ipc_msg_ids(self, ipc_ids_ptr): """ struct msg_queue { struct kern_ipc_perm q_perm; time64_t q_stime; time64_t q_rtime; time64_t q_ctime; unsigned long q_cbytes; unsigned long q_qnum; unsigned long q_qbytes; struct pid *q_lspid; struct pid *q_lrpid; struct list_head q_messages; <--> msg_msg.m_list struct list_head q_receivers; struct list_head q_senders; } __randomize_layout; struct msg_msg { struct list_head m_list; long m_type; size_t m_ts; /* message text size */ struct msg_msgseg *next; void *security; }; """ self.out.append(titlify("Message Queues")) fmt = "{:18s} {:5s} {:10s} {:4s} {:4s} {:5s} {:10s} {:s}" legend = ["msg_queue", "msqid", "key", "uid", "gid", "perms", "used-bytes", "messages"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) elems = self.parse_xarray(ipc_ids_ptr, root=True) for e in elems: msqid = read_int32_from_memory(e + self.offset_id) key = read_int32_from_memory(e + self.offset_key) uid = read_int32_from_memory(e + self.offset_uid) gid = read_int32_from_memory(e + self.offset_gid) mode = read_int16_from_memory(e + self.offset_mode) if not hasattr(self, "offset_q_cbytes"): for i in range(1, 64): # search q_messages, q_receivers, q_senders base = self.offset_mode + current_arch.ptrsize * i addrs = [read_int_from_memory(e + base + current_arch.ptrsize * j) for j in range(6)] if all(is_valid_addr(x) for x in addrs): x = read_int_from_memory(e + base + current_arch.ptrsize * 6) if not is_valid_addr(x): # found self.offset_q_cbytes = base - current_arch.ptrsize * 5 self.offset_q_qnum = base - current_arch.ptrsize * 4 self.offset_q_messages = base break if hasattr(self, "offset_q_cbytes"): q_cbytes = read_int_from_memory(e + self.offset_q_cbytes) q_qnum = read_int_from_memory(e + self.offset_q_qnum) self.out.append("{:#018x} {:<5d} {:#010x} {:<4d} {:<4d} {:#5o} {:<#10x} {:+-resource--------+", "| start | | | start |", "| end | | | end |", "| name | | | name |", "| flags | | | flags |", "| desc | | | desc |", "| parent |-------+ | parent |", "| sibling |--> resource | sibling |", "| child |--> resource | child |", "+-----------------+ +-----------------+", "", "Simplified iomem structure:", "", "+-iomem_resource--+ +-------->+-resource--------+", "| start | | | start |", "| end | | | end |", "| name | | | name |", "| flags | | | flags |", "| desc | | | desc |", "| parent |-------+ | parent |", "| sibling |--> resource | sibling |", "| child |--> resource | child |", "+-----------------+ +-----------------+", ] _note_ = "\n".join(_note_) def dump_resource(self, addr): if not is_valid_addr(addr): return [] if addr in self.seen: return [] self.seen.append(addr) if not hasattr(self, "sizeof_resource_size_t"): name_ptr = read_int_from_memory(addr + 0x8 * 2) # sizeof(resource_size_t) == 8 if name_ptr and is_valid_addr(name_ptr): name = read_cstring_from_memory(name_ptr) if name in ["PCI IO", "PCI mem"]: self.sizeof_resource_size_t = 0x8 if not hasattr(self, "sizeof_resource_size_t"): name_ptr = read_int_from_memory(addr + 0x4 * 2) # sizeof(resource_size_t) == 4 if name_ptr and is_valid_addr(name_ptr): name = read_cstring_from_memory(name_ptr) if name in ["PCI IO", "PCI mem"]: self.sizeof_resource_size_t = 0x4 if not hasattr(self, "sizeof_resource_size_t"): err("Not recognized sizeof(resource_size_t)") return [] """ struct resource { resource_size_t start; // 4 or 8 resource_size_t end; // 4 or 8 const char *name; unsigned long flags; unsigned long desc; // v4.5~ struct resource *parent, *sibling, *child; }; """ if self.sizeof_resource_size_t == 8: start = read_int64_from_memory(addr) end = read_int64_from_memory(addr + 8) elif self.sizeof_resource_size_t == 4: start = read_int32_from_memory(addr) end = read_int32_from_memory(addr + 4) name = read_cstring_from_memory(read_int_from_memory(addr + self.sizeof_resource_size_t * 2)) flags = read_int_from_memory(addr + self.sizeof_resource_size_t * 2 + current_arch.ptrsize) ret = [(addr, start, end, name, flags)] kversion = Kernel.kernel_version() if "4.5" <= kversion: parent = read_int_from_memory(addr + self.sizeof_resource_size_t * 2 + current_arch.ptrsize * 3) ret += self.dump_resource(parent) sibling = read_int_from_memory(addr + self.sizeof_resource_size_t * 2 + current_arch.ptrsize * 4) ret += self.dump_resource(sibling) child = read_int_from_memory(addr + self.sizeof_resource_size_t * 2 + current_arch.ptrsize * 5) ret += self.dump_resource(child) else: parent = read_int_from_memory(addr + self.sizeof_resource_size_t * 2 + current_arch.ptrsize * 2) ret += self.dump_resource(parent) sibling = read_int_from_memory(addr + self.sizeof_resource_size_t * 2 + current_arch.ptrsize * 3) ret += self.dump_resource(sibling) child = read_int_from_memory(addr + self.sizeof_resource_size_t * 2 + current_arch.ptrsize * 4) ret += self.dump_resource(child) return ret @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") self.out = [] # ioport ioport_resource = KernelAddressHeuristicFinder.get_ioport_resource() if not ioport_resource: err("Could not find ioport_resource") else: info("ioport_resource: {:#x}".format(ioport_resource)) self.seen = [] resources = self.dump_resource(ioport_resource) if resources: name_width = max(len(res[3]) for res in resources) else: name_width = 4 self.out.append(titlify("I/O-port")) fmt = "{:18s} {:17s} {:{:d}s} {:s}" legend = ["resource", "I/O address", "name", name_width, "flags"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for addr, start, end, name, flags in sorted(resources, key=lambda x: x[1]): self.out.append("{:#018x} {:#08x}-{:#08x} {:{:d}s} {:#010x} ({:s})".format( addr, start, end, name, name_width, flags, KernelPciDeviceCommand.get_flags_str(flags), )) # iomem iomem_resource = KernelAddressHeuristicFinder.get_iomem_resource() if not iomem_resource: err("Could not find iomem_resource") else: info("iomem_resource: {:#x}".format(iomem_resource)) self.seen = [] resources = self.dump_resource(iomem_resource) if resources: name_width = max(len(res[3]) for res in resources) else: name_width = 4 self.out.append(titlify("I/O-memory")) fmt = "{:18s} {:37s} {:{:d}s} {:s}" legend = ["resource", "Physical address", "name", name_width, "flags"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for addr, start, end, name, flags in sorted(resources, key=lambda x: x[1]): self.out.append("{:#018x} {:#018x}-{:#018x} {:{:d}s} {:#010x} ({:s})".format( addr, start, end, name, name_width, flags, KernelPciDeviceCommand.get_flags_str(flags), )) self.print_output(check_terminal_size=True) return @register_command class KernelDmaBufCommand(GenericCommand, BufferingOutput): """Dump DMA-BUF information.""" _cmdline_ = "kdmabuf" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _note_ = [ "Simplified DMA-BUF structure:", "", " +-dma_buf-----+ +-dma_buf-----+", " | size | | size |", " | file | | file |", " | ... | | ... |", " | exp_name | | exp_name |", " | name | | name |", "+---------+ | ... | | ... |", "| db_list |----->| list_node |----->| list_node |-->...", "+---------+ | priv |--+ | priv |", " | ... | | | ... |", " +-------------+ | +-------------+", " |", " +----------------------------+", " |", " +--->+-system_heap_buffer-+ +-->+-scatterlist--+", " | ... | | | page_link |----->+------+", " | len | | | offset | | page |", " | sg_table | | | length | +------+", " | sgl |--+ | ... |", " | ... | +--------------+", " | ... | | page_link |-->page", " +--------------------+ | offset | or", " | length | scatterlist", " | ... |", " +--------------+", " | ... |", " +--------------+", ] _note_ = "\n".join(_note_) def initialize(self): self.db_list = KernelAddressHeuristicFinder.get_db_list() if self.db_list is None: err("Could not find db_list (maybe DMA_SHARED_BUFFER=n)") return False self.quiet_info("db_list: {:#x}".format(self.db_list)) first_dma_buf = read_int_from_memory(self.db_list) if first_dma_buf == self.db_list: warn("Nothing to dump") return False """ struct dma_buf { size_t size; struct file *file; struct list_head attachments; const struct dma_buf_ops *ops; struct mutex lock; // ~v6.2 unsigned vmapping_counter; struct iosys_map { union { void __iomem *vaddr_iomem; void *vaddr; }; bool is_iomem; } vmap_ptr; const char *exp_name; const char *name; spinlock_t name_lock; struct module *owner; struct list_head list_node; void *priv; <-- struct system_heap_buffer* struct dma_resv *resv; wait_queue_head_t poll; ... } [v6.4 x64 example] 0xffff8880135f8a00|+0x0000|+000: 0x0000000000001000 // size 0xffff8880135f8a08|+0x0008|+001: 0xffff888000f85800 -> 0x0000000000000000 // file 0xffff8880135f8a10|+0x0010|+002: 0xffff8880135f8a10 -> [loop detected] // attachments 0xffff8880135f8a18|+0x0018|+003: 0xffff8880135f8a10 -> [loop detected] 0xffff8880135f8a20|+0x0020|+004: 0xffffffff83e79d00 -> 0x0000000000000000 // ops 0xffff8880135f8a28|+0x0028|+005: 0x0000000000000000 // vmapping_counter 0xffff8880135f8a30|+0x0030|+006: 0x0000000000000000 // vmap_ptr.vaddr_iomem 0xffff8880135f8a38|+0x0038|+007: 0x0000000000000000 // vmap_ptr.is_iomem 0xffff8880135f8a40|+0x0040|+008: 0xffffffff8482754e -> 0x6e006d6574737973 ('system'?) // exp_name 0xffff8880135f8a48|+0x0048|+009: 0x0000000000000000 // name 0xffff8880135f8a50|+0x0050|+010: 0xdead4ead00000000 // name_lock 0xffff8880135f8a58|+0x0058|+011: 0x00000000ffffffff 0xffff8880135f8a60|+0x0060|+012: 0xffffffffffffffff 0xffff8880135f8a68|+0x0068|+013: 0xffffffff883cc330 <__key.7> -> 0x0000000000000000 0xffff8880135f8a70|+0x0070|+014: 0x0000000000000000 0xffff8880135f8a78|+0x0078|+015: 0x0000000000000000 0xffff8880135f8a80|+0x0080|+016: 0xffffffff847790c3 -> '&dmabuf->name_lock' 0xffff8880135f8a88|+0x0088|+017: 0x0000000000000200 0xffff8880135f8a90|+0x0090|+018: 0x0000000000000000 // owner 0xffff8880135f8a98|+0x0098|+019: 0xffff8880135f8c98 -> 0xffffffff883cc360 -> [loop detected] // list_node 0xffff8880135f8aa0|+0x00a0|+020: 0xffffffff883cc360 -> 0xffff8880135f8a98 -> 0xffff8880135f8c98 -> ... 0xffff8880135f8aa8|+0x00a8|+021: 0xffff88800f978600 -> ... // priv 0xffff8880135f8ab0|+0x00b0|+022: 0xffff8880135f8b58 -> 0x0000000000000000 0xffff8880135f8ab8|+0x00b8|+023: 0xdead4ead00000000 0xffff8880135f8ac0|+0x00c0|+024: 0x00000000ffffffff 0xffff8880135f8ac8|+0x00c8|+025: 0xffffffffffffffff """ for i in range(1, 50): a = read_int_from_memory(first_dma_buf - current_arch.ptrsize * (i + 4)) # size b = read_int_from_memory(first_dma_buf - current_arch.ptrsize * (i + 3)) # file c = read_int_from_memory(first_dma_buf - current_arch.ptrsize * (i + 2)) # attachments e = read_int_from_memory(first_dma_buf - current_arch.ptrsize * (i + 0)) # ops # size check if a == 0 or (is_valid_addr(a) and AddressUtil.is_msb_on(a)): continue # file check if not is_valid_addr(b): continue # attachments check if not is_double_link_list(c): continue # ops check if not is_valid_addr(e): continue self.offset_list_node = current_arch.ptrsize * (i + 4) self.quiet_info("offsetof(dma_buf, list_node): {:#x}".format(self.offset_list_node)) break else: err("Could not find dma_buf->list_node") return False # dma_buf->{size,file,priv} self.offset_size = 0 self.offset_file = current_arch.ptrsize self.offset_priv = self.offset_list_node + current_arch.ptrsize * 2 self.quiet_info("offsetof(dma_buf, size): {:#x}".format(self.offset_size)) self.quiet_info("offsetof(dma_buf, file): {:#x}".format(self.offset_file)) self.quiet_info("offsetof(dma_buf, priv): {:#x}".format(self.offset_priv)) # dma_buf->{exp_name,name} for i in range(1, 50): top = first_dma_buf - self.offset_list_node x = read_int_from_memory(top + current_arch.ptrsize * i) s = read_cstring_from_memory(x) if s and len(s) >= 3: self.offset_exp_name = current_arch.ptrsize * i self.offset_name = current_arch.ptrsize * (i + 1) self.quiet_info("offsetof(dma_buf, exp_name): {:#x}".format(self.offset_exp_name)) self.quiet_info("offsetof(dma_buf, name): {:#x}".format(self.offset_name)) break else: err("Could not find dma_buf->{exp_name,name}") return False """ struct system_heap_buffer { struct dma_heap *heap; struct list_head attachments; struct mutex lock; unsigned long len; struct sg_table { struct scatterlist *sgl; unsigned int nents; unsigned int orig_nents; } sg_table; int vmap_cnt; void *vaddr; }; """ # system_heap_buffer->sg_table size = read_int_from_memory(first_dma_buf - self.offset_list_node + self.offset_size) priv = read_int_from_memory(first_dma_buf - self.offset_list_node + self.offset_priv) for i in range(50): x = read_int_from_memory(priv + current_arch.ptrsize * i) if x == size: self.offset_sg_table = current_arch.ptrsize * (i + 1) break else: err("Could not find system_heap_buffer->sg_table") return False self.quiet_info("offsetof(system_heap_buffer, sg_table): {:#x}".format(self.offset_sg_table)) return True def dump_sgl(self, sg): while True: page_link = read_int_from_memory(sg) # check if chain if page_link & 1: # SG_CHAIN sg = page_link & ~3 continue # output page, phys, virt page = page_link & ~3 phys = None phys_str = "???" ret = gdb.execute("page2phys {:#x}".format(page), to_string=True) r = re.search(r"Page: \S+ -> Phys: (\S+)", ret) if r: phys = int(r.group(1), 16) phys_str = "{:#018x}".format(phys) virt_str = "???" if phys: r = Kernel.p2v(phys) if r: r = [hex(x) for x in r if AddressUtil.is_msb_on(x)] virt_str = ",".join(r) offset = read_int32_from_memory(sg + current_arch.ptrsize) length = read_int32_from_memory(sg + current_arch.ptrsize + 4) self.out.append(" page: {:#018x} offset: {:#010x} length: {:#010x} phys: {:18s} virt: {:s}".format( page, offset, length, phys_str, virt_str, )) # check if end if page_link & 2: # SG_END: break # calc sizeof(scatterlist) then go to next """ struct scatterlist { unsigned long page_link; unsigned int offset; unsigned int length; dma_addr_t dma_address; #ifdef CONFIG_NEED_SG_DMA_LENGTH unsigned int dma_length; #endif #ifdef CONFIG_PCI_P2PDMA unsigned int dma_flags; #endif }; """ sg += current_arch.ptrsize + 4 * 2 + current_arch.ptrsize if not is_valid_addr(read_int_from_memory(sg)): sg += current_arch.ptrsize if not is_valid_addr(read_int_from_memory(sg)): sg += current_arch.ptrsize return def dump_db_list(self): fmt = "{:18s} {:18s} {:16s} {:16s} {:18s} {:18s}" legend = ["dma_buf", "size", "exp_name", "name", "file", "priv"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) seen = [self.db_list] current = read_int_from_memory(self.db_list) while True: if not is_valid_addr(current): break if current in seen: break seen.append(current) # calc top dma_buf = current - self.offset_list_node # size, file, priv size = read_int_from_memory(dma_buf + self.offset_size) file = read_int_from_memory(dma_buf + self.offset_file) priv = read_int_from_memory(dma_buf + self.offset_priv) # exp_name exp_name_p = read_int_from_memory(dma_buf + self.offset_exp_name) exp_name = read_cstring_from_memory(exp_name_p) # name name_p = read_int_from_memory(dma_buf + self.offset_name) if is_valid_addr(name_p): name = read_cstring_from_memory(name_p) else: name = "" # dump self.out.append("{:#018x} {:#018x} {:16s} {:16s} {:#018x} {:#018x}".format( dma_buf, size, exp_name, name, file, priv, )) # dump sgl sgl = read_int_from_memory(priv + self.offset_sg_table) self.dump_sgl(sgl) # go to next current = read_int_from_memory(current) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return if kversion < "5.11": err("Unsupported before v5.11") return ret = gdb.execute("ksymaddr-remote --quiet --no-pager dma_heap", to_string=True) if not ret: err("This kernel does not support DMA-BUF") return ret = self.initialize() if ret is False: return self.out = [] self.dump_db_list() self.print_output(check_terminal_size=True) return @register_command class KernelIrqCommand(GenericCommand, BufferingOutput): """Dump IRQ (interrupt request) information.""" _cmdline_ = "kirq" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose mode.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _note_ = [ "Simplified irq structure:", "", "+-irq_desc_tree(~6.5)-+ +--->+-xa_node---------+ +--->+-irq_desc----+", "| xa_lock | | | shift | | | ... |", "| xa_flags | | | ... | | | irq_data |", "| xa_head |---+ | count | | | ... |", "+---------------------+ | ... | | | irq |", " | slots[0] |---+ | ... |", " | slots[1] | ^ | ... |", " | ... | | | action |", " | slots[15 or 63] | | | handler |", " | ... | | | ... |", " +-----------------+ | | name |", " | | ... |", "+-sparce_irq(6.5~)-+ +-->+-maple_node------+ | | ... |", "| ... | | | ... | | +-------------+", "| ma_root |---+ | mr64|ma64|alloc | |", "| ... | | ... | |", "+------------------+ | slot[] |-------+", " +-----------------+", ] _note_ = "\n".join(_note_) def parse_xarray(self, ptr, root=False): if ptr == 0: return [] ptr &= ~3 # untagged if root: node = read_int_from_memory(ptr + self.offset_xa_head) return self.parse_xarray(node) shift = read_int8_from_memory(ptr + self.offset_shift) count = read_int8_from_memory(ptr + self.offset_count) slots = ptr + self.offset_slots elems = [] for i in range(64): # 16 or 64 x = read_int_from_memory(slots + current_arch.ptrsize * i) if x == 0: continue if shift: elems += self.parse_xarray(x) else: elems.append(x) count -= 1 if count == 0: break return elems class MapleTree: """Linux v6.5 introduces maple_tree to irq. This is a simple parser.""" MT_FLAGS_HEIGHT_MASK = 0x7c MT_FLAGS_HEIGHT_OFFSET = 0x02 MAPLE_NODE_TYPE_SHIFT = 0x03 MAPLE_NODE_TYPE_MASK = 0x0f MAPLE_NODE_POINTER_MASK = 0xff MAPLE_DENSE = 0 MAPLE_LEAF_64 = 1 MAPLE_RANGE_64 = 2 MAPLE_ARANGE_64 = 3 def __init__(self, ptr): kversion = Kernel.kernel_version() # ____cacheline_aligned_in_smp attribute, spinlock_t and lockdep_map_p can be different size # in each environment or situation, so search heuristically. for i in range(0x10): x = read_int_from_memory(ptr + current_arch.ptrsize * i) """ [x64 v6.4.2] 0xffff8bedc104db00|+0x0000|+000: 0x0000000000000000 // union 0xffff8bedc104db08|+0x0008|+001: 0xffff8bedc1a6601e // ma_root 0xffff8bedc104db10|+0x0010|+002: 0x000000000000030b // ma_flags [x64 v6.6.1] 0xffff972801b78a38|+0x0040|+008: 0x0000000000000000 // (the end of cacheline?) 0xffff972801b78a40|+0x0040|+008: 0x0000030b00000000 // ma_flags || union 0xffff972801b78a48|+0x0048|+009: 0xffff972801b0cc1e // ma_root """ if is_valid_addr(x) and (x & 0xff) in [0x1e, 0x0e]: offset_ma_root = current_arch.ptrsize * i if kversion < "6.6": offset_ma_flags = offset_ma_root + current_arch.ptrsize else: offset_ma_flags = offset_ma_root - 4 if is_64bit() and read_int32_from_memory(ptr + offset_ma_flags) == 0: offset_ma_flags = offset_ma_root - 8 break else: raise self.ma_root_raw = read_int_from_memory(ptr + offset_ma_root) self.ma_flags = read_int_from_memory(ptr + offset_ma_flags) self.max_depth = (self.ma_flags & self.MT_FLAGS_HEIGHT_MASK) >> self.MT_FLAGS_HEIGHT_OFFSET if is_64bit(): self.MAPLE_NODE_SLOTS = 31 self.MAPLE_RANGE64_SLOTS = 16 self.MAPLE_ARANGE64_SLOTS = 10 self.MAPLE_ALLOC_SLOTS = self.MAPLE_NODE_SLOTS - 1 self.maple_range_64_offset_slot = current_arch.ptrsize * self.MAPLE_RANGE64_SLOTS self.maple_arange_64_offset_slot = current_arch.ptrsize * self.MAPLE_ARANGE64_SLOTS self.maple_alloc_offset_slot = current_arch.ptrsize * 2 else: self.MAPLE_NODE_SLOTS = 63 self.MAPLE_RANGE64_SLOTS = 32 self.MAPLE_ARANGE64_SLOTS = 21 self.MAPLE_ALLOC_SLOTS = self.MAPLE_NODE_SLOTS - 2 self.maple_range_64_offset_slot = current_arch.ptrsize * self.MAPLE_RANGE64_SLOTS self.maple_arange_64_offset_slot = current_arch.ptrsize * self.MAPLE_ARANGE64_SLOTS self.maple_alloc_offset_slot = current_arch.ptrsize * 3 self.seen = set() self.iters = self.parse_node(self.ma_root_raw, 1) return def parse_node(self, entry, depth): if entry in self.seen: return self.seen.add(entry) if self.max_depth < depth: return pointer = entry & ~(self.MAPLE_NODE_POINTER_MASK) node_type = (entry >> self.MAPLE_NODE_TYPE_SHIFT) & self.MAPLE_NODE_TYPE_MASK if node_type == self.MAPLE_DENSE: slot_top = pointer + self.maple_alloc_offset_slot for i in range(self.MAPLE_ALLOC_SLOTS): slot = read_int_from_memory(slot_top + current_arch.ptrsize * i) if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: if is_valid_addr(slot): yield slot elif node_type == self.MAPLE_LEAF_64: slot_top = pointer + self.maple_range_64_offset_slot for i in range(self.MAPLE_RANGE64_SLOTS): slot = read_int_from_memory(slot_top + current_arch.ptrsize * i) if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: if is_valid_addr(slot): yield slot elif node_type == self.MAPLE_RANGE_64: slot_top = pointer + self.maple_range_64_offset_slot for i in range(self.MAPLE_RANGE64_SLOTS): slot = read_int_from_memory(slot_top + current_arch.ptrsize * i) if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self.parse_node(slot, depth + 1) elif node_type == self.MAPLE_ARANGE_64: slot_top = pointer + self.maple_arange_64_offset_slot for i in range(self.MAPLE_ARANGE64_SLOTS): slot = read_int_from_memory(slot_top + current_arch.ptrsize * i) if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self.parse_node(slot, depth + 1) return def initialize(self): if hasattr(self, "initialized") and self.initialized: return True kversion = Kernel.kernel_version() if kversion < "6.5": self.irq_desc_tree = KernelAddressHeuristicFinder.get_irq_desc_tree() if self.irq_desc_tree is None: self.quiet_err("Could not find irq_desc_tree") return False for i in range(0, 10): # xa_head x = read_int_from_memory(self.irq_desc_tree + current_arch.ptrsize * i) if not x: continue if not is_valid_addr(x): continue if x & 0x2 != 0x2: # xa_head is NULL or tagged address continue self.offset_xa_head = current_arch.ptrsize * i self.quiet_info("offsetof(xarray, xa_head): {:#x}".format(self.offset_xa_head)) break else: self.quiet_err("Could not find xa_head. (maybe uninitialized?)") return False # xa_node """ struct xa_node { unsigned char shift; unsigned char offset; unsigned char count; unsigned char nr_values; struct xa_node __rcu *parent; struct xarray *array; union { struct list_head private_list; struct rcu_head rcu_head; }; void __rcu *slots[XA_CHUNK_SIZE]; union { unsigned long tags[XA_MAX_MARKS][XA_MARK_LONGS]; unsigned long marks[XA_MAX_MARKS][XA_MARK_LONGS]; }; }; """ # xa_node->{shift,count,slots} self.offset_shift = 0 self.offset_count = 2 self.offset_slots = current_arch.ptrsize * 5 descs = self.parse_xarray(self.irq_desc_tree, root=True) else: # "6.5" <= kversion self.sparse_irqs = KernelAddressHeuristicFinder.get_sparse_irqs() if self.sparse_irqs is None: self.quiet_err("Could not find sparse_irqs") return False descs = list(self.MapleTree(self.sparse_irqs).iters) if not descs: self.quiet_err("Could not find any valid irq_desc") return False # irq_desc->{irq,action} """ struct irq_desc { struct irq_common_data { unsigned int __private state_use_accessors; #ifdef CONFIG_NUMA unsigned int node; #endif void *handler_data; struct msi_desc *msi_desc; #ifdef CONFIG_SMP cpumask_var_t affinity; #endif #ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK cpumask_var_t effective_affinity; #endif #ifdef CONFIG_GENERIC_IRQ_IPI unsigned int ipi_offset; #endif } irq_common_data; struct irq_data { u32 mask; unsigned int irq; unsigned long hwirq; struct irq_common_data *common; struct irq_chip *chip; struct irq_domain *domain; #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY struct irq_data *parent_data; #endif void *chip_data; } irq_data; unsigned int __percpu *kstat_irqs; irq_flow_handler_t handle_irq; struct irqaction *action; unsigned int status_use_accessors; unsigned int core_internal_state__do_not_mess_with_it; ... }; """ if is_x86(): desc = descs[0] else: # ARM may have invalid descs[irq=0] desc = descs[-1] self.quiet_info("desc: {:#x}".format(desc)) for i in range(100): x = read_int_from_memory(desc + current_arch.ptrsize * i) if x == desc: if is_32bit(): self.offset_irq = current_arch.ptrsize * i - 8 else: self.offset_irq = current_arch.ptrsize * i - 12 # for padding self.quiet_info("offsetof(irq_desc, irq_data.irq): {:#x}".format(self.offset_irq)) break else: self.quiet_err("Could not find irq_desc->irq_data.irq") return False ofs_irq = align_to_ptrsize(self.offset_irq + 4 * 2) for i in range(100): x = read_int_from_memory(desc + ofs_irq + current_arch.ptrsize * i) y = read_int_from_memory(desc + ofs_irq + current_arch.ptrsize * (i + 1)) if not is_valid_addr(x) and not is_valid_addr(y): ofs_action_candidate = ofs_irq + current_arch.ptrsize * i - current_arch.ptrsize action_candidate = read_int_from_memory(desc + ofs_action_candidate) if is_valid_addr_addr(action_candidate): self.offset_action = ofs_action_candidate self.quiet_info("offsetof(irq_desc, action): {:#x}".format(self.offset_action)) break else: self.quiet_err("Could not find irq_desc->action") return False # irqaction->{handler,name} """ struct irqaction { irq_handler_t handler; void *dev_id; void __percpu *percpu_dev_id; struct irqaction *next; irq_handler_t thread_fn; struct task_struct *thread; struct irqaction *secondary; unsigned int irq; unsigned int flags; unsigned long thread_flags; unsigned long thread_mask; const char *name; struct proc_dir_entry *dir; } ____cacheline_internodealigned_in_smp; """ self.offset_handler = 0 self.quiet_info("offsetof(irqaction, handler): {:#x}".format(self.offset_handler)) action = read_int_from_memory(desc + self.offset_action) for i in range(100): x = read_int_from_memory(action + current_arch.ptrsize * i) if not is_valid_addr(x): continue s = read_cstring_from_memory(x) if s and len(s) >= 4: self.offset_name = current_arch.ptrsize * i self.quiet_info("offsetof(irqaction, name): {:#x}".format(self.offset_name)) break else: self.quiet_err("Could not find irqaction->name") return False return True def dump_irq(self): kversion = Kernel.kernel_version() if kversion < "6.5": descs = self.parse_xarray(self.irq_desc_tree, root=True) else: descs = list(self.MapleTree(self.sparse_irqs).iters) entries = {} for desc in descs: irq = read_int32_from_memory(desc + self.offset_irq) action = read_int_from_memory(desc + self.offset_action) if action == 0: entries[irq] = [desc, action, None, None] else: handler = read_int_from_memory(action + self.offset_handler) name_ptr = read_int_from_memory(action + self.offset_name) name = read_cstring_from_memory(name_ptr) or "???" entries[irq] = [desc, action, handler, name] fmt = "{:3s} {:18s} {:18s} {:24s} {:18s}" legend = ["irq", "irq_desc", "action", "name", "handler"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for i in range(256): if i in entries: desc, action, handler, name = entries[i] if action: symbol = Symbol.get_symbol_string(handler, nosymbol_string=" ") self.out.append("{:3d} {:#018x} {:#018x} {:24s} {:#018x}{:s}".format( i, desc, action, name, handler, symbol, ).rstrip()) else: self.out.append("{:3d} {:#018x} {:18s} {:24s} {:18s}".format( i, desc, "unused", "-", "-", ).rstrip()) else: if self.args.verbose: self.out.append("{:3d} {:18s} {:18s} {:24s} {:18s}".format( i, "unused", "unused", "-", "-", ).rstrip()) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion is None: err("Could not find Linux kernel") return if kversion < "4.20": # xarray is introduced from 4.20 self.quiet_err("Unsupported before v4.20") return ret = self.initialize() if ret is False: return self.out = [] self.dump_irq() self.print_output(check_terminal_size=True) return @register_command class KernelNetDeviceCommand(GenericCommand, BufferingOutput): """Dump net device information.""" _cmdline_ = "knetdev" _category_ = "06-g. Qemu-system/KGDB Cooperation - Linux Advanced" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _note_ = [ "Simplified net_device structure:", "", " +-net_device--------+ +-net_device--------+", " | ... (v6.8~) | | ... (v6.8~) |", "+-init_net------+ | name[] | | name[] |", "| ... | | ... | | ... |", "| dev_base_head |--->| dev_list |--->| dev_list |--->...", "| ... | | ... | | ... |", "+---------------+ +-------------------+ +-------------------+", ] _note_ = "\n".join(_note_) def initialize(self): if hasattr(self, "initialized") and self.initialized: return True # init_net self.init_net = KernelAddressHeuristicFinder.get_init_net() if self.init_net is None: self.quiet_err("Could not find init_net") return False self.quiet_info("init_net: {:#x}".format(self.init_net)) """ struct net { ... struct list_head dev_base_head; ... }; struct net_device { char name[IFNAMSIZ]; ... struct list_head dev_list; // dev_base_head points here struct list_head napi_list; struct list_head unreg_list; struct list_head close_list; struct list_head ptype_all; struct list_head ptype_specific; // ~v6.7 ... }; """ # net->dev_base_head for i in range(0x100): candidate_offset = current_arch.ptrsize * i addr = self.init_net + candidate_offset if not is_double_link_list(addr): continue cand_netdev = read_int_from_memory(addr) if not is_double_link_list(cand_netdev + current_arch.ptrsize * 2): # napi_list continue if not is_double_link_list(cand_netdev + current_arch.ptrsize * 4): # unreg_list continue if not is_double_link_list(cand_netdev + current_arch.ptrsize * 6): # close_list continue if not is_double_link_list(cand_netdev + current_arch.ptrsize * 8): # ptype_all continue break # found else: self.quiet_err("Could not find net->dev_base_head") return False self.offset_dev_base_head = candidate_offset self.quiet_info("offsetof(net, dev_base_head): {:#x}".format(self.offset_dev_base_head)) # net_device->dev_list netdev_dev_list = read_int_from_memory(self.init_net + self.offset_dev_base_head) for i in range(0x20): candidate_offset = current_arch.ptrsize * i if read_cstring_from_memory(netdev_dev_list - candidate_offset) == "lo": break else: self.quiet_err("Could not find net_device->dev_list") return False self.offset_dev_list = candidate_offset self.quiet_info("offsetof(net_device, dev_list): {:#x}".format(self.offset_dev_list)) return True def dump_net(self): fmt = "{:18s} {:s}" legend = ["net_device", "name"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # `struct net_device` is a very complex struct, and detecting the offset of its members is very difficult. # My best effort is to detect only names and addresses. head = current = self.init_net + self.offset_dev_base_head while True: current = read_int_from_memory(current) if current == head: break netdev = current - self.offset_dev_list name = read_cstring_from_memory(netdev) self.out.append("{:#018x} {:s}".format(netdev, name)) kversion = Kernel.kernel_version() if "6.8" <= kversion: info("In kernel 6.8 and later, the order of the members of `struct net_device` has changed significantly") info("Please note that the address detected as `net_device` is precisely the address of &net_device.name") return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") ret = self.initialize() if ret is False: return self.out = [] self.dump_net() self.print_output(check_terminal_size=True) return @register_command class VmallocDumpCommand(GenericCommand, BufferingOutput): """Dump vmalloc used list and freed list.""" _cmdline_ = "vmalloc-dump" _category_ = "06-h. Qemu-system/KGDB Cooperation - Linux Allocator" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("--only-used", action="store_true", help="display only used area.") parser.add_argument("--only-freed", action="store_true", help="display only freed area.") parser.add_argument("--meta", action="store_true", help="display offset information.") parser.add_argument("--hexdump-used", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="hexdump `used chunks` if layout is resolved.") parser.add_argument("--telescope-used", metavar="SIZE", type=lambda x: int(x, 16), default=0, help="telescope `used chunks` if layout is resolved.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -q", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Simplified vmalloc structure:" "", " +-vmap_area--+", " | va_start |", "(~v6.8) | va_end |", "+---------------------+ | ... |", "| vmap_area_list |--->| list |--->...", "+---------------------+ | ... |", " | vm |---->+-vm_struct--+", " | ... | | ... |", " +------------+ | flags |", " | ... |", " +------------+", " +-vmap_area--+", " | va_start |", "(v5.2~) | va_end |", "+---------------------+ | ... |", "| free_vmap_area_list |--->| list |--->...", "+---------------------+ | ... |", " +------------+", ] _note_ = "\n".join(_note_) def initialize(self): if hasattr(self, "initialized") and self.initialized: if not self.args.meta and not self.args.rescan: return True """ struct vmap_area { unsigned long va_start; unsigned long va_end; unsigned long subtree_max_size; // v5.2.0~v5.2.21 unsigned long flags; // ~v5.3 struct rb_node { unsigned long __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; } rb_node; struct list_head list; union { // v5.4~ unsigned long subtree_max_size; // v5.4~ struct vm_struct *vm; // v5.4~ struct llist_node purge_list; // v5.4~v5.10 }; // v5.4~ struct llist_node purge_list; // v4.7~v5.3 struct list_head purge_list; // ~v4.7 struct vm_struct *vm; // ~v5.3 unsigned long flags; // v6.3~ }; struct vm_struct { struct vm_struct *next; void *addr; unsigned long size; unsigned long flags; struct page **pages; #ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC // v5.13~ unsigned int page_order; // v5.13~ #endif // v5.13~ unsigned int nr_pages; phys_addr_t phys_addr; const void *caller; unsigned long requested_size; // v6.12~ }; """ kversion = Kernel.kernel_version() if kversion and kversion < "6.9": self.vmap_area_list = KernelAddressHeuristicFinder.get_vmap_area_list() if not self.vmap_area_list: self.quiet_err("Could not find vmap_area_list") else: self.quiet_info("vmap_area_list: {:#x}".format(self.vmap_area_list)) else: self.vmap_area_list = None if kversion and "5.2" <= kversion: self.free_vmap_area_list = KernelAddressHeuristicFinder.get_free_vmap_area_list() if not self.free_vmap_area_list: self.quiet_err("Could not find free_vmap_area_list") else: self.quiet_info("free_vmap_area_list: {:#x}".format(self.free_vmap_area_list)) else: self.free_vmap_area_list = None if not self.vmap_area_list and not self.free_vmap_area_list: return False # vmap_area->list if kversion and "5.4" <= kversion: self.offset_list = current_arch.ptrsize * 5 elif kversion and "5.2" <= kversion: self.offset_list = current_arch.ptrsize * 7 else: self.offset_list = current_arch.ptrsize * 6 self.quiet_info("offsetof(vmap_area, list): {:#x}".format(self.offset_list)) # vmap_area->vm if kversion and "5.4" <= kversion: self.offset_vm = self.offset_list + current_arch.ptrsize * 2 elif kversion and "4.7" <= kversion: self.offset_vm = self.offset_list + current_arch.ptrsize * 3 else: self.offset_vm = self.offset_list + current_arch.ptrsize * 4 self.quiet_info("offsetof(vmap_area, vm): {:#x}".format(self.offset_vm)) # vm_struct->flags self.offset_flags = current_arch.ptrsize * 3 self.quiet_info("offsetof(vm_struct, flags): {:#x}".format(self.offset_flags)) self.initialized = True return True def parse_vmap_area_list(self, head, used): if head is None or not is_valid_addr(head): return [] seen = [head] current = read_int_from_memory(head) idx = 0 areas = [] while True: if current in seen: break seen.append(current) vmap_area = current - self.offset_list va_start = read_int_from_memory(vmap_area) va_end = read_int_from_memory(vmap_area + current_arch.ptrsize) va_size = va_end - va_start flags = None if used: vm = read_int_from_memory(vmap_area + self.offset_vm) if is_valid_addr(vm): flags = read_int_from_memory(vm + self.offset_flags) else: flags = 0 areas.append([used, va_start, va_end, va_size, flags]) try: current = read_int_from_memory(current) except gdb.MemoryError: break idx += 1 return areas def get_flags(self, flags_value): flags_dic = { 0x00000001: "VM_IOREMAP", 0x00000002: "VM_ALLOC", 0x00000004: "VM_MAP", 0x00000008: "VM_USERMAP", 0x00000010: "VM_DMA_COHERENT", 0x00000020: "VM_UNINITIALIZED", 0x00000040: "VM_NO_GUARD", 0x00000080: "VM_KASAN", 0x00000100: "VM_FLUSH_RESET_PERMS", 0x00000200: "VM_MAP_PUT_PAGES", } flags = [] for k, v in flags_dic.items(): if flags_value & k: flags.append(v) return "|".join(flags) def dump_areas(self, areas): fmt = "{:4s} {:6s} {:37s} {:18s} {:s}" legend = ["#", "state", "virtual address", "size", "flags"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) used_address_color = Config.get_gef_setting("theme.heap_chunk_address_used") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") for idx, (used, va_start, va_end, va_size, flags) in enumerate(areas): size_str = "{:<#18x}".format(va_size) size_str = Color.colorify(size_str, chunk_size_color) virt_str = "{:#018x}-{:#018x}".format(va_start, va_end) if used: virt_str = Color.colorify(virt_str, used_address_color) state = "in-use" flags_str = self.get_flags(flags) flags_str = flags_str.rstrip() if not flags_str: flags_str = "-" else: virt_str = Color.colorify(virt_str, freed_address_color) state = "freed" flags_str = "-" self.out.append("{:<4d} {:6s} {:s} {:s} {:s}".format(idx, state, virt_str, size_str, flags_str)) # dump chunks if self.args.hexdump_used and used: try: peeked_data = read_memory(va_start, self.args.hexdump_used) h = hexdump(peeked_data, 0x10, base=va_start, unit=current_arch.ptrsize) self.out.append(h) except Exception: pass if self.args.telescope_used and used: n = self.args.telescope_used // current_arch.ptrsize for i in range(n): try: line = DereferenceCommand.pprint_dereferenced(va_start, i) self.out.append(line) except Exception: pass return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") ret = self.initialize() if ret is False: return if self.args.meta: return self.out = [] areas = [] kversion = Kernel.kernel_version() # parse used list if not args.only_freed: if kversion and kversion < "6.9": areas += self.parse_vmap_area_list(self.vmap_area_list, used=True) # parse freed list if not args.only_used: if kversion and "5.2" <= kversion: areas += self.parse_vmap_area_list(self.free_vmap_area_list, used=False) areas = sorted(areas, key=lambda x:x[1]) self.dump_areas(areas) self.print_output() return @register_command class KtypesCommand(GenericCommand, BufferingOutput): """Display kernel type information from /sys/kernel/btf/vmlinux.""" _cmdline_ = "ktypes" _category_ = "06-e. Qemu-system/KGDB Cooperation - Linux Symbol/Type" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = [ "This command requires CONFIG_DEBUG_INFO_BTF=y.", "CONFIG_KALLSYMS_ALL=y is not required.", ] _note_ = "\n".join(_note_) def check_command(self): try: GefUtil.which("bpftool") if is_x86(): GefUtil.which("gcc") elif is_arm64(): GefUtil.which("aarch64-linux-gnu-gcc") elif is_arm32(): GefUtil.which("arm-linux-gnueabihf-gcc") except FileNotFoundError as e: err("{}".format(e)) return False return True def get_base_name(self): if not hasattr(__gef_command_instances__["ksymaddr-remote"], "kernel_version"): gdb.execute("ksymaddr-remote --no-pager GEF_DUMMY_STRING", to_string=True) if not hasattr(__gef_command_instances__["ksymaddr-remote"], "kernel_version"): err("Could not find kernel version") return None ks = __gef_command_instances__["ksymaddr-remote"] h = hashlib.sha256(String.str2bytes(ks.version_string)).hexdigest()[-16:] major, minor, patch = ks.kernel_version base_name = os.path.join(GEF_TEMP_DIR, "ktypes-{:d}.{:d}.{:d}-{:s}".format(major, minor, patch, h)) return base_name def get_btf_addr(self): start = Symbol.get_ksymaddr("__start_BTF") if start is None: return None end = Symbol.get_ksymaddr("__stop_BTF") return start, end - start def build_header_file(self): base_path = self.get_base_name() if base_path is None: return None raw_path = base_path + ".raw" header_path = base_path + ".h" # use cache if not self.args.rescan: if os.path.exists(header_path) and os.path.getsize(header_path) > 0: return header_path # get address of /sys/kernel/btf/vmlinux addr_size = self.get_btf_addr() if addr_size is None: err("Could not find /sys/kernel/btf/vmlinux") return None # read /sys/kernel/btf/vmlinux try: content = read_memory(*addr_size) except gdb.MemoryError: err("Memory read error") return None # save it open(raw_path, "wb").write(content) # raw -> vmlinux.h os.system("{!r} btf dump file {!r} format c > {!r}".format(GefUtil.which("bpftool"), raw_path, header_path)) return header_path @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): if not self.check_command(): return header_path = self.build_header_file() if header_path is None: warn("This kernel may be CONFIG_DEBUG_INFO_BTF=n") return content = open(header_path, "r").read() self.out = [] self.out.extend(content.splitlines()) self.print_output(check_terminal_size=True) return @register_command class KtypesLoadCommand(KtypesCommand): """Load kernel type information from /sys/kernel/btf/vmlinux.""" _cmdline_ = "ktypes-load" _category_ = "06-e. Qemu-system/KGDB Cooperation - Linux Symbol/Type" _aliases_ = ["kt-load"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") _syntax_ = parser.format_help() def build_obj_file(self, header_path): source_path = header_path[:-2] + ".c" obj_path = source_path[:-2] # use cache if not self.args.rescan: if os.path.exists(obj_path) and os.path.getsize(obj_path) > 0: return obj_path # copy vmlinux.h to vmlinux.c open(source_path, "wb").write(open(header_path, "rb").read()) try: if is_x86_64(): gcc, opt = GefUtil.which("gcc"), "" elif is_x86_32(): gcc, opt = GefUtil.which("gcc"), "-m32" elif is_arm64(): gcc, opt = GefUtil.which("aarch64-linux-gnu-gcc"), "" elif is_arm32(): gcc, opt = GefUtil.which("arm-linux-gnueabihf-gcc"), "" except FileNotFoundError as e: err("{}".format(e)) return None # build with debug types cmd = "{!r} {:s} -std=c11 -g -O0 -fno-eliminate-unused-debug-types -c {!r} -o {!r}".format( gcc, opt, source_path, obj_path, ) info(cmd) os.system(cmd) if not os.path.exists(obj_path): return None return obj_path @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): if not self.check_command(): return header_path = self.build_header_file() if header_path is None: warn("This kernel may be CONFIG_DEBUG_INFO_BTF=n") return obj_path = self.build_obj_file(header_path) if obj_path is None: err("Failed to build") return info(obj_path) gdb.execute("file {:s}".format(obj_path), to_string=True) info("Kernel types are loaded successfully") return @register_command class KsymaddrRemoteCommand(GenericCommand, BufferingOutput): """Resolve kernel symbols from kallsyms table.""" # Thanks to https://github.com/marin-m/vmlinux-to-elf _cmdline_ = "ksymaddr-remote" _category_ = "06-e. Qemu-system/KGDB Cooperation - Linux Symbol/Type" _aliases_ = ["ks"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("keyword", metavar="KEYWORD", nargs="*", help="filter by specific symbol name.") parser.add_argument("-t", "--type", action="append", default=[], help="filter by symbol type.") parser.add_argument("-e", "--exact", action="store_true", help="use exact match.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-s", "--smart", action="store_true", help="filter __pfx_*, __ksymtab_*, etc.") parser.add_argument("--vmlinux-file", help="force use your vmlinux file which includes symbols.") parser.add_argument("-I", "--ignore-loaded-vmlinux", action="store_true", help="force skip parsing loaded vmlinux.") parser.add_argument("--print-saved-config", action="store_true", help="print saved (cached) config contents.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="enable verbose mode.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} commit_creds prepare_kernel_cred # OR search", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "GEF caches offset information for parsing kallsyms to speed up this command.", "Each cache is used based on kernel version strings.", "In other words, in cases where the kernel version is exactly the same and", "the CONFIG is slightly different, the offset will be applied incorrectly.", "In this case, rescan with `ks -rv` or clear the cache with `gef reset-cache --hard`.", ] _note_ = "\n".join(_note_) def __init__(self, *args, **kwargs): super().__init__() """ # Do not use dict; There are cases where multiple symbols with the same name exist. # cat /proc/kallsyms |grep set_is_seen ffffffff812326e0 t set_is_seen ffffffff81d58900 t set_is_seen ffffffff81d5cab0 t set_is_seen # """ self.kallsyms = [] return def get_loaded_vmlinux_path(self): if self.args.ignore_loaded_vmlinux: return None # Check `nm` first for later use (in parse_vmlinux) try: GefUtil.which(Config.get_gef_setting("gef.nm_command")) except FileNotFoundError as e: self.quiet_err("{}".format(e)) return None # check vmlinux for inf in gdb.inferiors(): if not hasattr(inf, "progspace"): continue if not hasattr(inf.progspace, "filename"): continue filename = str(inf.progspace.filename) if not os.path.exists(filename): continue # Currently, the filename in vmlinux is hard-coded if "vmlinux" not in os.path.basename(filename).lower(): continue # it has symbol? try: elf = Elf(filename) if elf.get_shdr(".symtab"): return filename except Exception: continue return None def parse_vmlinux(self, filename): # read symbols try: nm = GefUtil.which(Config.get_gef_setting("gef.nm_command")) except FileNotFoundError as e: self.quiet_err("{}".format(e)) return result = GefUtil.gef_execute_external([nm, filename], as_list=True) # distinctive addresses to use for rebasing if is_x86(): target = [ "asm_exc_divide_error", # 5.8~ "divide_error", # 3.0 ~ 5.7 ] elif is_arm64() or is_arm32(): target = [ "vectors", # 3.7~ ] elif is_riscv64() or is_riscv32(): target = [ "handle_exception", # 4.19~ ] else: raise # parse symbol tmp_kallsyms = [] target_found = {} for line in result: try: addr, typ, name = line.split() addr = int(addr, 16) typ = typ.strip() name = name.strip() except ValueError: continue tmp_kallsyms.append([addr, name, typ]) if name in target: target_found["handler"] = addr # rebase if target_found: text_base_hint = Kernel.get_kernel_base_hint() if text_base_hint: diff = text_base_hint - target_found["handler"] if diff & get_pagesize_mask_low() == 0: self.kallsyms = [] for addr, name, typ in tmp_kallsyms: # don't rebase per-cpu offset if addr >= 0x4000_0000: # This value is the lowest boundary between ARM32 kernel and userland. addr += diff self.kallsyms.append([addr, name, typ]) return # fail, use as is self.kallsyms = tmp_kallsyms return def get_token_table(self): # Parse symbol name tokens tokens = [] position = self.offset_kallsyms_token_table for _ in range(256): token = "" while self.kernel_img[position]: token += chr(self.kernel_img[position]) position += 1 position += 1 tokens.append(token) assert len(tokens) == 256 return tokens def read_kallsyms(self): if self.kallsyms: # resolved already return tokens = self.get_token_table() symbol_names = [] position = self.offset_kallsyms_names for _ in range(self.num_symbols): # read token length length = self.kernel_img[position] position += 1 # check if big symbol (6.1~) if self.kernel_version >= (6, 1, 0): if length & 0x80: low = length & 0x7f high = self.kernel_img[position] position += 1 length = (high << 7) | low # make symbol_name symbol_name = "" for _ in range(length): symbol_token_index = self.kernel_img[position] symbol_token = tokens[symbol_token_index] position += 1 symbol_name += symbol_token symbol_names.append(symbol_name) for addr, name in zip(self.kernel_addresses, symbol_names): try: self.kallsyms.append([addr, name[1:], name[0]]) except IndexError: pass return def print_kallsyms(self, keywords, types, smart): if is_32bit(): fmt = "{:#010x} {:s} {:s}" else: fmt = "{:#018x} {:s} {:s}" if types: types = [t.lower() for t in types] kallsyms = [entry for entry in self.kallsyms if entry[2].lower() in types] else: kallsyms = self.kallsyms if smart: ignore_list = ( "__pfx_", # prefix symbols for function padding "__kstrtab_", "__ksymtab_", "__kcrctab_", "__tpstrtab_", # tracepoint "__initcall__", "__traceiter_", "__tracepoint_", "__probestub_", "__already_done.", "__flags.", "__func__.", "__key.", "__mkey.", "__msg.", "__print_once.", "__quirk.", "__warned.", "__wkey.", "___done.", "___once_key.", "___tp_str.", "__compound_literal.", "__SCT__tp_func_", "__SCK__tp_func_", "__TRACE_SYSTEM_", ) kallsyms = [entry for entry in kallsyms if not entry[1].startswith(ignore_list)] self.out = [] if not keywords: for addr, symbol, typ in kallsyms: self.out.append(fmt.format(addr, typ, symbol)) elif self.args.exact: for addr, symbol, typ in kallsyms: if symbol in keywords: self.out.append(fmt.format(addr, typ, symbol)) else: for addr, symbol, typ in kallsyms: text = fmt.format(addr, typ, symbol) for k in keywords: if k in text: # not only symbol search, but also address search self.out.append(text) break return def get_kernel_version_triplet(self, version_number): major = int(version_number.split(".")[0]) minor = int(version_number.split(".")[1]) if len(version_number.split(".")) == 2: patch = 0 else: patch = int(version_number.split(".")[2]) return (major, minor, patch) def get_kernel_version(self): # don't use Kernel.kernel_version, since it refers ksymaddr-remote r = re.search(rb"Linux version (\d+\.[\d.]*\d)[ -~]+", self.kernel_img) if r is None: self.verbose_err("Could not find the kernel version") return False self.version_string = r.group(0) self.version_string_offset = r.span()[0] self.verbose_info("linux_banner: {:#x}".format(self.ro_base + self.version_string_offset)) version_number = r.group(1).decode("ascii") self.kernel_version = self.get_kernel_version_triplet(version_number) # The important thing for parsing is whether it is 6.1.42 or later. # However, some distributions may have a patch version of 0. # e.g., debian 6.1.119-1 -> 6.1.0-28-amd64 # In this case, special processing is required to find a different version string. if self.kernel_version[:2] == (6, 1): # e.g., Linux version 6.1.0-28-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) 12.2.0, # GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian 6.1.119-1 (2024-11-22) r = re.findall(rb" 6\.1\.(\d+)", self.version_string) for patch_version in r: patch_version = int(patch_version) if patch_version >= 42: self.kernel_version = (self.kernel_version[0], self.kernel_version[1], patch_version) break return True def get_cfg_name(self): h = hashlib.sha256(String.str2bytes(self.version_string)).hexdigest()[-16:] major, minor, patch = self.kernel_version cfg_file_name = os.path.join(GEF_TEMP_DIR, "ksymaddr-remote-{:d}.{:d}.{:d}-{:s}.cfg".format(major, minor, patch, h)) return cfg_file_name def save_config(self, param_name): cfg_file_name = self.get_cfg_name() config = configparser.ConfigParser() if os.path.exists(cfg_file_name): config.read(cfg_file_name) else: config["parameters"] = {} if param_name in config["parameters"]: return config["parameters"][param_name] = str(getattr(self, param_name)) with open(cfg_file_name, "w") as cfg_file: config.write(cfg_file) return def get_saved_config(self, param_names): if self.args.rescan: return False cfg_file_name = self.get_cfg_name() if not os.path.exists(cfg_file_name): return False config = configparser.ConfigParser() config.read(cfg_file_name) for param_name in param_names: if param_name not in config["parameters"]: return False for param_name in param_names: param_value = int(config["parameters"][param_name]) setattr(self, param_name, param_value) return True def print_saved_config(self): if hasattr(self, "version_string"): cfg_file_name = self.get_cfg_name() info("path: {:s}".format(cfg_file_name)) if os.path.exists(cfg_file_name): gef_print(titlify("content (hexlified)")) config = configparser.ConfigParser() config.read(cfg_file_name) for param_name in config["parameters"]: param_value = config["parameters"][param_name] try: param_value = int(param_value) gef_print("{:s} = {:#x}".format(param_name, param_value)) except ValueError: gef_print("{:s} = {:s}".format(param_name, param_value)) return err("Could not find cached config (Run the `ksymaddr-remote` command at least once)") return def find_kallsyms_token_table(self): ret = self.get_saved_config(["offset_kallsyms_token_table"]) if ret: self.verbose_info("kallsyms_token_table: {:#x}".format(self.ro_base + self.offset_kallsyms_token_table)) return True """ [Search strategy] - kallsyms_token_table has unique sequences like "30 00 31 00 32 00 33 00 34 00 35 00 36 00 37 00 38 00 39 00". - We search for it from .rodata area, then search backwards for invalid characters to get the top. [Positional relationship] - ... - kallsyms_token_table - ... [Sample values for 64bit] gef> hexdump -n byte kallsyms_token_table 0xffffffff8b2b51b0: 65 75 00 77 5f 00 61 64 64 00 64 5f 5f 66 75 6e | eu.w_.add.d__fun | 0xffffffff8b2b51c0: 63 5f 5f 00 74 70 5f 66 75 6e 63 00 33 32 00 6e | c__.tp_func.32.n | 0xffffffff8b2b51d0: 61 00 66 66 00 69 70 00 78 65 6e 00 70 72 00 73 | a.ff.ip.xen.pr.s | 0xffffffff8b2b51e0: 65 74 00 63 70 75 00 49 44 00 65 64 00 53 43 00 | et.cpu.ID.ed.SC. | 0xffffffff8b2b51f0: 66 72 65 00 76 65 5f 00 70 6f 00 78 5f 00 5f 73 | fre.ve_.po.x_._s | 0xffffffff8b2b5200: 68 00 2e 31 00 62 6c 00 6d 65 6d 00 5f 72 65 67 | h..1.bl.mem._reg | 0xffffffff8b2b5210: 00 74 5f 5f 00 6c 6f 63 6b 00 62 5f 00 72 5f 5f | .t__.lock.b_.r__ | 0xffffffff8b2b5220: 6b 73 74 72 74 61 62 6e 73 00 66 75 6e 63 5f 5f | kstrtabns.func__ | 0xffffffff8b2b5230: 00 69 6e 74 5f 00 72 65 73 00 74 72 61 63 65 00 | .int_.res.trace. | 0xffffffff8b2b5240: 70 61 72 00 2e 30 00 64 65 76 65 6e 74 5f 00 6d | par..0.devent_.m | 0xffffffff8b2b5250: 75 00 61 63 70 69 5f 00 6d 70 00 73 74 61 00 64 | u.acpi_.mp.sta.d | 0xffffffff8b2b5260: 65 62 75 67 00 5f 5f 5f 00 62 75 67 00 6f 75 00 | ebug.___.bug.ou. | 0xffffffff8b2b5270: 5f 73 74 61 00 77 72 69 74 00 2e 00 67 72 6f 00 | _sta.writ...gro. | 0xffffffff8b2b5280: 30 00 31 00 32 00 33 00 34 00 35 00 36 00 37 00 | 0.1.2.3.4.5.6.7. | <- here unique seqs 0xffffffff8b2b5290: 38 00 39 00 72 63 00 77 61 00 63 61 6c 00 75 70 | 8.9.rc.wa.cal.up | 0xffffffff8b2b52a0: 5f 00 45 5f 00 67 65 5f 00 6d 61 70 00 41 00 42 | _.E_.ge_.map.A.B | [Sample values for 32bit] gef> hexdump -n byte kallsyms_token_table 0xc6e58efc: 54 52 41 43 45 5f 53 59 53 00 41 43 45 5f 53 59 | TRACE_SYS.ACE_SY | 0xc6e58f0c: 53 00 5f 53 59 53 00 54 45 4d 00 41 43 45 00 69 | S._SYS.TEM.ACE.i | 0xc6e58f1c: 67 00 70 6f 69 6e 74 5f 00 62 75 00 75 74 5f 00 | g.point_.bu.ut_. | # codespell:ignore 0xc6e58f2c: 5f 53 59 00 54 52 00 5f 73 79 00 72 65 61 64 00 | _SY.TR._sy.read. | 0xc6e58f3c: 66 5f 00 75 6c 00 62 6c 00 61 6c 6c 6f 63 00 74 | f_.ul.bl.alloc.t | 0xc6e58f4c: 6c 00 63 6c 00 65 79 00 61 74 61 00 70 63 00 5f | l.cl.ey.ata.pc._ | 0xc6e58f5c: 65 6e 00 76 65 72 00 54 45 00 64 74 72 61 63 65 | en.ver.TE.dtrace | # codespell:ignore 0xc6e58f6c: 5f 65 76 65 6e 74 5f 00 61 70 00 61 74 65 00 74 | _event_.ap.ate.t | 0xc6e58f7c: 6e 00 41 43 00 6d 73 00 72 61 77 5f 00 5f 63 6f | n.AC.ms.raw_._co | 0xc6e58f8c: 00 73 74 72 00 6d 6f 00 67 69 73 74 65 72 00 69 | .str.mo.gister.i | 0xc6e58f9c: 70 00 63 6f 6e 00 67 69 73 00 69 6e 69 74 00 66 | p.con.gis.init.f | 0xc6e58fac: 75 6e 63 00 65 5f 73 00 75 74 00 5f 73 68 00 70 | unc.e_s.ut._sh.p | 0xc6e58fbc: 6f 00 61 6c 6c 00 2e 00 66 73 5f 00 30 00 31 00 | o.all...fs_.0.1. | 0xc6e58fcc: 32 00 33 00 34 00 35 00 36 00 37 00 38 00 39 00 | 2.3.4.5.6.7.8.9. | <- here unique seqs 0xc6e58fdc: 6b 5f 00 5f 63 68 00 72 69 74 00 61 63 70 69 00 | k_._ch.rit.acpi. | 0xc6e58fec: 5f 63 6f 6e 00 65 78 74 34 00 61 6d 00 41 00 42 | _con.ext4.am.A.B | """ # first, search for unique bytes seq_to_find = b"0\x001\x002\x003\x004\x005\x006\x007\x008\x009\x00" seq_to_avoid = [b":\0", b"\0\0", b"\0\1", b"\0\2", b"ASCII\0"] target_pattern = seq_to_find + b"(?!" + b"|".join(seq_to_avoid) + b")" unique_bytes_offset = [] for r in re.finditer(target_pattern, self.kernel_img): unique_bytes_offset.append(r.span()) # (start_pos, end_pos) if len(unique_bytes_offset) == 0: self.verbose_err("Could not find kallsyms_token_table (0 candidate)") return False if len(unique_bytes_offset) > 1: # strict check for i, offsets in enumerate(unique_bytes_offset.copy()): self.verbose_info("unique_bytes {:d}: {:#x}".format(i, self.ro_base + offsets[0])) follow = self.kernel_img[offsets[1]:offsets[1] + 1] if not follow.isalnum() and follow not in [b"_", b"."]: unique_bytes_offset.remove(offsets) # re-check if len(unique_bytes_offset) == 0: self.verbose_err("Could not find kallsyms_token_table (0 candidate)") return False if len(unique_bytes_offset) > 1: self.verbose_err("Could not find kallsyms_token_table (multiple candidates)") return False position = unique_bytes_offset[0][0] self.verbose_info("unique_bytes: {:#x}".format(self.ro_base + position)) # second, backward search for the top position -= 1 if position < 0: self.verbose_err("Could not find kallsyms_token_table (failed to get top: before '0')") return False if self.kernel_img[position] != 0: self.verbose_err("Unexpected byte before '0' entry in kallsyms_token_table") return False num_tokens_back = ord('0') # 48 max_token_len = 50 # find the beginning of a kallsyms_token_table for _ in range(num_tokens_back): # find the beginning of a token for _ in range(max_token_len): position -= 1 if position < 0: self.verbose_err("Could not find kallsyms_token_table (out of range while walking tokens)") return False b = self.kernel_img[position] if b == 0 or b > ord('z'): break else: # max_token_len exceeded self.verbose_err("This structure is not a kallsyms_token_table (token too long)") return False position += 1 position += -position % 4 self.offset_kallsyms_token_table = position self.save_config("offset_kallsyms_token_table") self.verbose_info("kallsyms_token_table: {:#x}".format(self.ro_base + self.offset_kallsyms_token_table)) return True def find_kallsyms_token_index(self): ret = self.get_saved_config(["offset_kallsyms_token_index"]) if ret: self.verbose_info("kallsyms_token_index: {:#x}".format(self.ro_base + self.offset_kallsyms_token_index)) return True """ [Search strategy] - Find the index where the string appears in kallsyms_token_table. - Find where that index is arranged like a table. [Positional relationship] - ... - kallsyms_token_table - kallsyms_token_index - ... [Sample values for 64bit] gef> hexdump -n word kallsyms_token_index 0xffffffff8b2b5540: 0x0000 0x0003 0x0006 0x000a 0x0014 0x001c 0x001f 0x0022 | ..............". | 0xffffffff8b2b5550: 0x0025 0x0028 0x002c 0x002f 0x0033 0x0037 0x003a 0x003d | %.(.,./.3.7.:.=. | 0xffffffff8b2b5560: 0x0040 0x0044 0x0048 0x004b 0x004e 0x0052 0x0055 0x0058 | @.D.H.K.N.R.U.X. | 0xffffffff8b2b5570: 0x005c 0x0061 0x0065 0x006a 0x006d 0x007a 0x0081 0x0086 | ..a.e.j.m.z..... | 0xffffffff8b2b5580: 0x008a 0x0090 0x0094 0x0097 0x009f 0x00a2 0x00a8 0x00ab | ................ | 0xffffffff8b2b5590: 0x00af 0x00b5 0x00b9 0x00bd 0x00c0 0x00c5 0x00ca 0x00cc | ................ | 0xffffffff8b2b55a0: 0x00d0 0x00d2 0x00d4 0x00d6 0x00d8 0x00da 0x00dc 0x00de | ................ | 0xffffffff8b2b55b0: 0x00e0 0x00e2 0x00e4 0x00e7 0x00ea 0x00ee 0x00f2 0x00f5 | ................ | [Sample values for 32bit] gef> hexdump -n word kallsyms_token_index 0xc6e59274: 0x0000 0x000a 0x0012 0x0017 0x001b 0x001f 0x0022 0x0029 | ............".). | 0xc6e59284: 0x002c 0x0030 0x0034 0x0037 0x003b 0x0040 0x0043 0x0046 | ,.0.4.7.;.@.C.F. | 0xc6e59294: 0x0049 0x004f 0x0052 0x0055 0x0058 0x005c 0x005f 0x0063 | I.O.R.U.X..._.c. | 0xc6e592a4: 0x0067 0x006a 0x0078 0x007b 0x007f 0x0082 0x0085 0x0088 | g.j.x.{......... | 0xc6e592b4: 0x008d 0x0091 0x0095 0x0098 0x009f 0x00a2 0x00a6 0x00aa | ................ | 0xc6e592c4: 0x00af 0x00b4 0x00b8 0x00bb 0x00bf 0x00c2 0x00c6 0x00c8 | ................ | 0xc6e592d4: 0x00cc 0x00ce 0x00d0 0x00d2 0x00d4 0x00d6 0x00d8 0x00da | ................ | 0xc6e592e4: 0x00dc 0x00de 0x00e0 0x00e3 0x00e7 0x00eb 0x00f0 0x00f5 | ................ | """ # create expected kallsyms_token_index byte sequqence position = self.offset_kallsyms_token_table seq_token_table_head = self.kernel_img[position:position + 256] token_offsets = [p16(0)] pos = 0 while True: pos = seq_token_table_head.find(b"\0", pos + 1) if pos == -1: break token_offsets.append(p16(pos + 1)) seq_to_find = b"".join(token_offsets) # search for it from memory position = self.kernel_img.find(seq_to_find, self.offset_kallsyms_token_table) if position == -1: self.verbose_err("Could not find kallsyms_token_index (0 candidate)") return False self.offset_kallsyms_token_index = position self.save_config("offset_kallsyms_token_index") self.verbose_info("kallsyms_token_index: {:#x}".format(self.ro_base + self.offset_kallsyms_token_index)) return True def find_kallsyms_markers(self): # determines the size of table elements depended on kernel version. if self.kernel_version < (4, 20, 0): # kallsyms_markers is unsigned long[] self.kallsyms_markers_table_element_size = current_arch.ptrsize else: # kallsyms_markers is unsigned int[] self.kallsyms_markers_table_element_size = 4 if self.kernel_version >= (6, 1, 42) and self.kernel_version < (6, 9, 0): ret = self.get_saved_config([ "offset_kallsyms_token_markers", "offset_kallsyms_seqs_of_names", ]) if ret: self.verbose_info("kallsyms_markers: {:#x}".format(self.ro_base + self.offset_kallsyms_markers)) self.verbose_info("kallsyms_seqs_of_names: {:#x}".format(self.ro_base + self.offset_kallsyms_seqs_of_names)) return True else: ret = self.get_saved_config([ "offset_kallsyms_token_markers", ]) if ret: self.verbose_info("kallsyms_markers: {:#x}".format(self.ro_base + self.offset_kallsyms_markers)) return True """ [Search strategy] - From kallsyms_token_table, search backwards for 0x00000000. - For kernel v6.1.42~v6.8, there is kallsyms_seqs_of_names between kallsyms_markers and kallsyms_token_table, so this should be skipped. [Positional relationship] ... - kallsyms_markers - kallsyms_seqs_of_names (v6.1.42~v6.8) - kallsyms_token_table - kallsyms_token_index ... - kallsyms_seqs_of_names (v6.9~) ... [Sample values for 64bit ~v6.1.41] gef> hexdump -n dword kallsyms_markers 0xffffffff8b2b4b48: 0x00000000 0x00000ab0 0x000016d3 0x00002316 | .............#.. | <- kallsyms_markers 0xffffffff8b2b4b58: 0x00002f38 0x00003cf8 0x00004c4c 0x000059c8 | 8/...<..LL...Y.. | 0xffffffff8b2b4b68: 0x0000664b 0x00007316 0x00008119 0x00008f2e | Kf...s.......... | 0xffffffff8b2b4b78: 0x00009cc6 0x0000a9ff 0x0000b687 0x0000c20f | ................ | ... 0xffffffff8b2b5180: 0x0013fa80 0x001404c6 0x00140f67 0x00141a29 | ........g...)... | 0xffffffff8b2b5190: 0x0014240e 0x00142e25 0x00143a5f 0x0014442a | .$..%..._:..*D.. | 0xffffffff8b2b51a0: 0x00144e55 0x001458d8 0x00146338 0x00000000 | UN...X..8c...... | 0xffffffff8b2b51b0: 0x77007565 0x6461005f 0x5f640064 0x6e75665f | eu.w_.add.d__fun | <- kallsyms_token_table [Sample values for 64bit v6.1.42~] gef> hexdump -n dword kallsyms_markers 0xffffffff8d5fcde0: 0x00000000 0x00000b55 0x000017bb 0x000024c3 | ....U........$.. | <- kallsyms_markers 0xffffffff8d5fcdf0: 0x000030c1 0x00003dca 0x00004983 0x000058aa | .0...=...I...X.. | 0xffffffff8d5fce00: 0x00006785 0x0000760e 0x0000828d 0x00009160 | .g...v......`... | 0xffffffff8d5fce10: 0x00009efa 0x0000ab20 0x0000b73a 0x0000c37d | .... ...:...}... | ... 0xffffffff8d5fd790: 0x00212755 0x002131b8 0x00213af8 0x00214558 | U'!..1!..:!.XE!. | 0xffffffff8d5fd7a0: (*)0x01069d01 0x9d01019d 0x029d0100 0x02039d01 | ................ | <- kallsyms_seqs_of_names (*) 0xffffffff8d5fd7b0: 0xa400291c 0x10a50024 0x0154a500 0xaa0116aa | .)..$.....T..... | 0xffffffff8d5fd7c0: 0x87610214 0x01274902 0xb201cbaf 0xbbbc01c9 | ..a..I'......... | [Sample values for 32bit v6.1.42~] gef> hexdump -n dword kallsyms_markers 0xc6e0dc98: 0x00000000 0x00000c61 0x0000188f 0x00002641 | ....a.......A&.. | <- kallsyms_markers 0xc6e0dca8: 0x00003492 0x000041a7 0x00004e6b 0x00005ace | .4...A..kN...Z.. | 0xc6e0dcb8: 0x0000691b 0x00007703 0x00008411 0x00008fc1 | .i...w.......... | 0xc6e0dcc8: 0x00009c98 0x0000a8ea 0x0000b719 0x0000c4dd | ................ | ... 0xc6e0e2c8: 0x0014f2fa 0x0014fbeb 0x00150653 0x00634401(*) | ........S....Dc. | <- kallsyms_seqs_of_names (*) 0xc6e0e2d8: 0xd90030d9 0x8bd30032 0x0189d300 0x6a01e97f | .0..2..........j | 0xc6e0e2e8: 0x31d90063 0x0007e100 0xd600b7e1 0xd1d900cb | c..1............ | 0xc6e0e2f8: 0x0083dd00 0xd90004e9 0x85ef00ab 0x00bfdb00 | ................ | """ # kallsyms_markers[0] is 0. seq_to_find = b"\0" * self.kallsyms_markers_table_element_size # ignore the 0 immediately above kallsyms_token_table. position = self.offset_kallsyms_token_table - 1 if len(self.kernel_img) <= position: return False while position > 0 and self.kernel_img[position] == 0: position -= 1 # aligned search for it from memory while position > 0: needle = self.kernel_img.rfind(seq_to_find, 0, position) if needle == -1: self.verbose_err("Could not find kallsyms_markers") return False # check alignment align_diff = needle % self.kallsyms_markers_table_element_size if align_diff == 0: position = needle break # ok else: position = needle + self.kallsyms_markers_table_element_size - align_diff # not aligned, so retry if self.kernel_version >= (6, 1, 42) and self.kernel_version < (6, 9, 0): # kallsyms_seqs_of_names was introduced in kernel 6.1.42 # In this case, we may find kallsyms_seqs_of_names instead of kallsyms_markers, # so we should search back through memory again. # # kallsyms_markers (1) we want to find this # kallsyms_seqs_of_names (3) this may have been found, try the backward search again # kallsyms_token_table (2) backward search from here # kallsyms_token_index # while position > 0: # the first some values of kallsyms_markers should be a small number. # if not, detected kallsyms_markers is incorrect and we'll go back further. first_10_elements = self.kernel_img[position + self.kallsyms_markers_table_element_size:] first_10_elements = first_10_elements[:self.kallsyms_markers_table_element_size * 10] first_10_elements = slice_unpack(first_10_elements, self.kallsyms_markers_table_element_size) if all((x & 0xfff_0000) == 0 for x in first_10_elements): break needle = self.kernel_img.rfind(seq_to_find, 0, position) if needle == -1: self.verbose_err("Could not find kallsyms_markers") return False # check alignment align_diff = needle % self.kallsyms_markers_table_element_size if align_diff == 0: position = needle else: position = needle + self.kallsyms_markers_table_element_size - align_diff if position <= 0: self.verbose_err("Could not find kallsyms_markers") return False self.offset_kallsyms_markers = position self.save_config("offset_kallsyms_markers") self.verbose_info("kallsyms_markers: {:#x}".format(self.ro_base + self.offset_kallsyms_markers)) if self.kernel_version >= (6, 1, 42) and self.kernel_version < (6, 9, 0): # locate kallsyms_seqs_of_names to get the table size of kallsyms_markers (used after). # # kallsyms_markers (1) we found this # kallsyms_seqs_of_names (2) to know the end of kallsyms_markers, we need to find this # kallsyms_token_table # kallsyms_token_index # position = self.offset_kallsyms_markers + self.kallsyms_markers_table_element_size # check MSB of the element of kallsyms_markers while self.kernel_img[position + (self.kallsyms_markers_table_element_size - 1)] == 0: a = u32(self.kernel_img[position - self.kallsyms_markers_table_element_size:position]) # prev b = u32(self.kernel_img[position:position + self.kallsyms_markers_table_element_size]) # current # kallsyms_markers are monotonically increasing. # and it doesn't increase very dramatically. if a > b or b - a > 0x10_0000: break position += self.kallsyms_markers_table_element_size self.offset_kallsyms_seqs_of_names = position self.save_config("offset_kallsyms_seqs_of_names") self.verbose_info("kallsyms_seqs_of_names: {:#x}".format(self.ro_base + self.offset_kallsyms_seqs_of_names)) return True def find_kallsyms_names(self): ret = self.get_saved_config(["offset_kallsyms_names"]) if ret: return True """ [Search strategy] - From kallsyms_markers, go back as far as we can definitively go back. - This address is not accurate. [Positional relationship] ... - kallsyms_names (this is not accurate address in this step) - kallsyms_markers - kallsyms_seqs_of_names (v6.1.42~v6.8) - kallsyms_token_table - kallsyms_token_index ... - kallsyms_seqs_of_names (v6.9~) ... [Sample values for 64bit] gef> hexdump -n qword kallsyms_names 0xffffffff8b16e610: 0x0cf3ec0e78b6410a 0xf370ff4109fe61cb | .A.x.....a..A.p. | <- kallsyms_names 0xffffffff8b16e620: 0x0c410774722cbdeb 0xa8410df67ef4285f | ..,rt.A._(.~..A. | 0xffffffff8b16e630: 0x936bed62d8632c71 0x925f0c4107f67ef4 | q,c.b.k..~..A._. | 0xffffffff8b16e640: 0xfb646741067772f1 0x706563a4410add86 | .rw.Agd....A.cep | ... 0xffffffff8b2b4b20: 0x616bd977fc6d7364 0x738df5ff440e61f6 | dsm.w.ka.a.D...s | 0xffffffff8b2b4b30: 0x6765625fbfe87263 0x63738df5ff440cf5 | cr.._beg..D...sc | 0xffffffff8b2b4b40: 0x000064ee5fbfe872 0x00000ab000000000 | r.._.d.......... | <- kallsyms_markers 0xffffffff8b2b4b50: 0x00002316000016d3 0x00003cf800002f38 | .....#..8/...<.. | [Sample values for 32bit] gef> hexdump -n qword kallsyms_names 0xc6cbce58: 0x335fd57472fb5c08 0x54039974f9540432 | ...rt._32.T.t..T | <- kallsyms_names 0xc6cbce68: 0x63ff72fb5c0799a6 0xd57472fb5c0a30a1 | .....r.c.0...rt. | 0xc6cbce78: 0x177407b6f932335f 0xa0ca0aa1f3796669 | _32...t.ify..... | 0xc6cbce88: 0xd7f49b2d63ecc37f 0x2d63ecc37fa0ca0a | ...c-.........c- | ... 0xc6e0dc78: 0x3a7262fe620d105f 0x10ff67ef796ccf65 | _..b.br:e.ly.g.. | 0xc6e0dc88: 0x62fe420964164203 0x0000ec6d699b6b72 | .B.d.B.brk.im... | 0xc6e0dc98: 0x00000c6100000000 0x000026410000188f | ....a.......A&.. | <- kallsyms_markers 0xc6e0dca8: 0x000041a700003492 0x00005ace00004e6b | .4...A..kN...Z.. | """ # take the last element of kallsyms_marker if hasattr(self, "offset_kallsyms_seqs_of_names"): # maybe 6.1.42~6.8 kallsyms_markers_end = self.offset_kallsyms_seqs_of_names else: kallsyms_markers_end = self.offset_kallsyms_token_table kallsyms_markers_data = self.kernel_img[self.offset_kallsyms_markers:kallsyms_markers_end] kallsyms_markers_entries = slice_unpack(kallsyms_markers_data, self.kallsyms_markers_table_element_size) kallsyms_markers_last_entry = list(filter(None, kallsyms_markers_entries))[-1] # filter 0, maybe padding # go back that number of bytes position = self.offset_kallsyms_markers position -= kallsyms_markers_last_entry position += -position % self.kallsyms_markers_table_element_size if position <= 0: self.verbose_err("Could not find kallsyms_names") return False # This value is provisional. It will be corrected in the next process (=find_kallsyms_num_syms). self.offset_kallsyms_names = position self.verbose_info("kallsyms_names: {:#x} (candidate)".format(self.ro_base + self.offset_kallsyms_names)) return True def find_kallsyms_num_syms(self): ret = self.get_saved_config([ "num_symbols", "offset_kallsyms_names", "offset_kallsyms_num_syms", ]) if ret: self.verbose_info("num_symbols: {:#x}".format(self.num_symbols)) self.verbose_info("kallsyms_names: {:#x}".format(self.ro_base + self.offset_kallsyms_names)) self.verbose_info("kallsyms_num_syms: {:#x}".format(self.ro_base + self.offset_kallsyms_num_syms)) return True """ [Search strategy] - From candidate address of kallsyms_names, search backwards to the top of what can be correctly interpreted as kallsyms_names. [Positional relationship] ... - kallsyms_num_syms - kallsyms_names (to be fixed in this step) - kallsyms_markers - kallsyms_seqs_of_names (v6.1.42~v6.8) - kallsyms_token_table - kallsyms_token_index ... - kallsyms_seqs_of_names (v6.9~) ... [Sample values for 64bit] gef> hexdump -n qword kallsyms_num_syms 0xffffffff8b16e608: 0x000000000001982b 0x0cf3ec0e78b6410a | +........A.x.... | 0xffffffff8b16e618: 0xf370ff4109fe61cb 0x0c410774722cbdeb | .a..A.p...,rt.A. | 0xffffffff8b16e628: 0xa8410df67ef4285f 0x936bed62d8632c71 | _(.~..A.q,c.b.k. | 0xffffffff8b16e638: 0x925f0c4107f67ef4 0xfb646741067772f1 | .~..A._..rw.Agd. | [Sample values for 32bit] gef> hexdump -n dword kallsyms_num_syms 0xc6cbce54: 0x00018eb8 0x72fb5c08 0x335fd574 0xf9540432 | .......rt._32.T. | 0xc6cbce64: 0x54039974 0x5c0799a6 0x63ff72fb 0x5c0a30a1 | t..T.....r.c.0.. | 0xc6cbce74: 0xd57472fb 0xf932335f 0x177407b6 0xf3796669 | .rt._32...t.ify. | """ token_table = self.get_token_table() possible_symbol_types = "-?ABCDGINPRSTUVWabcdginprstuvw" # from `man nm` dp = [] position = self.offset_kallsyms_names # kallsyms_names should be aligned. # This optimization is based on experience and is applied for now. position += -position % current_arch.ptrsize while True: if position < 0: self.verbose_err("Could not find kallsyms_names") return False # Do some types of checks. # 1: check the token type is likely or not. token_index = self.kernel_img[position + 1] symbol_type = token_table[token_index][0] if symbol_type not in possible_symbol_types: position -= current_arch.ptrsize continue # 2: check the table (kallsyms_names) entirely. # Each element of kallsyms_names consists of {number of tokens, tokens[number of tokens]}. # tokens[0][0] is symbol type. # # The following is an example of last elements of kallsyms_names. # gef> x/24xb 0xffffffffb46b4b48-0x10 # 0xffffffffb46b4b38: 0xf5 0x0c* 0x44 0xff 0xf5 0x8d 0x73 0x63 (*: start of last valid elements) # 0xffffffffb46b4b40: 0x72 0xe8 0xbf 0x5f 0xee 0x64* 0x00** 0x00 (*: end of last valid elements, **: end marker) # 0xffffffffb46b4b48: 0x00* 0x00 0x00 0x00 0xb0 0x0a 0x00 0x00 (*: start of kallsyms_markers) # # 0x0c: number of tokens # gef> pi GCI["ksymaddr-remote"].get_token_table()[0x44] # 'D' (= symbol type) # gef> pi GCI["ksymaddr-remote"].get_token_table()[0xff] # '__' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0xf5] # 'in' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0x8d] # 'it_' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0x73] # 's' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0x63] # 'c' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0x72] # 'r' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0xe8] # 'at' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0xbf] # 'ch' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0x5f] # '_' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0xee] # 'en' # gef> pi GCI["ksymaddr-remote"].get_token_table()[0x64] # 'd' # (=`__init_scratch_end`) # # Finally, 0x00(**) is following, this is the marker that represents the end of kallsyms_names. # This can be interpreted that the size of element is 0. # # However, this 0x00 may not exist. # gef> x/16xb 0xffffffffadefc1d8-0x8 # 0xffffffffadefc1d0: 0x12 0x65 0x05* 0xbf 0x65 0xaf 0x74 0xa5** (*/**: start/end of last valid elements) # 0xffffffffadefc1d8: 0x00* 0x00 0x00 0x00 0xb2 0x0b 0x00 0x00 (*: start of kallsyms_markers) # Even in this case, the first byte of kallsyms_markers is always 0, so we use it. # # Check that this structure is correct or not, using bottom-up DP. # dp[i] contains num_syms as interpreted from `kallsyms_makers - i` as the start of kallsyms_names. # dp[i] == -1 means invalid. range_start = position range_end = self.offset_kallsyms_markers range_end -= len(dp) # shortcut the already checked results. for pos in range(range_end, range_start - 1, -1): symbol_size = self.kernel_img[pos] is_big_symbol = False # default # check if big symbol (6.1~) if self.kernel_version >= (6, 1, 0): if symbol_size & 0x80: low = symbol_size & 0x7f high = self.kernel_img[pos + 1] symbol_size = (high << 7) | low is_big_symbol = True # 0xffffffffb46b4b38: 0xf5 0x0c 0x44 0xff 0xf5 0x8d 0x73 0x63 # 0xffffffffb46b4b40: 0x72 0xe8 0xbf 0x5f 0xee 0x64 0x00* 0x00* # dp[2]=0 dp[1]=0 # 0xffffffffb46b4b48: 0x00* 0x00 0x00 0x00 0xb0 0x0a 0x00 0x00 # dp[0]=0 if symbol_size == 0: dp.append(0) # maybe it is a last entry continue # 0xffffffffb46b4b38: 0xf5 0x0c 0x44 0xff 0xf5 0x8d 0x73 0x63 # 0xffffffffb46b4b40: 0x72 0xe8 0xbf 0x5f 0xee 0x64* 0x00 0x00 # dp[3]=-1 dp[2]=0 dp[1]=0 # 0xffffffffb46b4b48: 0x00* 0x00 0x00 0x00 0xb0 0x0a 0x00 0x00 # dp[0]=0 dp_len = len(dp) if is_big_symbol: dp_len = len(dp) - 1 if symbol_size >= dp_len: dp.append(-1) # exceed the kallsyms_markers continue # 0xffffffffb46b4b38: 0xf5 0x0c* 0x44 0xff 0xf5 0x8d 0x73 0x63 # dp[f]=1 dp[e]=-1 dp[d]=-1 dp[c]=-1 dp[b]=-1 dp[a]=-1 dp[9]=-1 # 0xffffffffb46b4b40: 0x72 0xe8 0xbf 0x5f 0xee 0x64 0x00** 0x00 # dp[8]=-1 dp[7]=-1 dp[6]=-1 dp[5]=-1 dp[4]=-1 dp[3]=-1 dp[2]=0 dp[1]=0 # 0xffffffffb46b4b48: 0x00* 0x00 0x00 0x00 0xb0 0x0a 0x00 0x00 # dp[0]=0 # when we see 0x0c(*), next element is 0x00(**). # In this case, here, len(dp) == 15 (dp[15] does not exist, but dp[14] exists). # dp[-(0xc + 1)] is dp[2]. d[2] is 0, not -1, so dp[15] is valid. If dp[2] is -1, dp[15] is invalid. offset_of_next_element = -symbol_size - 1 if is_big_symbol: offset_of_next_element -= 1 if dp[offset_of_next_element] == -1: dp.append(-1) continue # seems to be okay, append valid dp dp.append(dp[offset_of_next_element] + 1) num_symbols = dp[-1] if num_symbols < 256: # It is judged as NG because there are too few symbols. position -= current_arch.ptrsize continue # 3: Find num_symbols from memory. if self.kallsyms_markers_table_element_size == 4: seq_to_find = p32(num_symbols) elif self.kallsyms_markers_table_element_size == 8: seq_to_find = p64(num_symbols) # Depending on the environment, there are many zero padding after seq_to_find (=kallsyms_num_syms). # This is probably because each variable is aligned in units of 256 bytes. MAX_ALIGNMENT = 256 start = max(0, position - MAX_ALIGNMENT) needle = self.kernel_img.rfind(seq_to_find, start, position) if needle == -1: position -= current_arch.ptrsize continue # it seems ok. self.offset_kallsyms_names = position self.offset_kallsyms_num_syms = needle break self.save_config("offset_kallsyms_names") self.save_config("offset_kallsyms_num_syms") self.num_symbols = num_symbols self.save_config("num_symbols") self.verbose_info("num_symbols: {:#x}".format(self.num_symbols)) self.verbose_info("kallsyms_names: {:#x}".format(self.ro_base + self.offset_kallsyms_names)) self.verbose_info("kallsyms_num_syms: {:#x}".format(self.ro_base + self.offset_kallsyms_num_syms)) return True def find_kallsyms_offsets(self): """ [Search strategy] - ~v6.3 - From kallsyms_num_syms, go back by num_symbols element sizes. - num_symbols offsets are stored, so get them. - v6.4~ - From kallsyms_token_index + 0x200, num_symbols offsets are stored, so get them. [Positional relationship] - ... - kallsyms_offsets (v4.6~v6.3, CONFIG_KALLSYMS_BASE_RELATIVE=y) - kallsyms_relative_base (v4.6~v6.3, CONFIG_KALLSYMS_BASE_RELATIVE=y) - kallsyms_num_syms - kallsyms_names - kallsyms_markers - kallsyms_seqs_of_names (v6.1.42~v6.8) - kallsyms_token_table - kallsyms_token_index - kallsyms_offsets (v6.4~, CONFIG_KALLSYMS_BASE_RELATIVE=y) - kallsyms_relative_base (v6.4~, CONFIG_KALLSYMS_BASE_RELATIVE=y) - kallsyms_seqs_of_names (v6.9~) - ... [Sample values for 64bit ~v6.3, CONFIG_KALLSYMS_ABSOLUTE_PERCPU=n (use positive offset)] gef> hexdump -n dword kallsyms_offsets 0xffffffff8b108550: 0x00000000 0x00000000 0x00001000 0x00002000 | ............. .. | 0xffffffff8b108560: 0x00006000 0x0000b000 0x0000c000 0x00018000 | .`.............. | 0xffffffff8b108570: 0x00019000 0x00019008 0x00019010 0x00019020 | ............ ... | 0xffffffff8b108580: 0x00019420 0x00019440 0x00019448 0x00019450 | ...@...H...P... | [Sample values for 64bit ~v6.3, CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y (use negative offset)] gef> hexdump -n dword kallsyms_offsets 0xffffffffa72854b0: 0xffffffff 0xffffffff 0xffffffff 0xffffffbf | ................ | 0xffffffffa72854c0: 0xffffffba 0xfffffeef 0xfffffdef 0xfffffddf | ................ | 0xffffffffa72854d0: 0xfffffdcf 0xfffffa1f 0xfffff9cf 0xfffff9bf | ................ | 0xffffffffa72854e0: 0xfffff99f 0xfffff8ff 0xfffff76f 0xfffff73f | ........o...?... | [Sample values for 32bit ~v6.3, CONFIG_KALLSYMS_ABSOLUTE_PERCPU=n (use positive offset)] gef> hexdump -n dword kallsyms_offsets 0xc6c59370: 0x00000000 0x00000000 0x00000000 0x00000070 | ............p... | 0xc6c59380: 0x00000080 0x000001d8 0x000002e0 0x00000320 | ............ ... | 0xc6c59390: 0x00000360 0x000003a8 0x000003e8 0x000004a8 | `............... | 0xc6c593a0: 0x000005a8 0x0000066c 0x0000073c 0x000007ac | ....l...<....... | [Sample values for 64bit v6.4~, CONFIG_KALLSYMS_ABSOLUTE_PERCPU=n (use positive offset)] gef> hexdump -n word kallsyms_token_index 0xffffffff844fa178: 0x0000 0x0003 0x0006 0x000a 0x0010 0x0013 0x0016 0x0019 | ................ | 0xffffffff844fa188: 0x001d 0x0029 0x002d 0x0030 0x0034 0x0037 0x003b 0x003e | ..).-.0.4.7.;.>. | 0xffffffff844fa198: 0x0041 0x0056 0x005a 0x005e 0x0061 0x0064 0x0067 0x006a | A.V.Z.^.a.d.g.j. | ... 0xffffffff844fa358: 0x0386 0x0389 0x038c 0x038f 0x0392 0x0395 0x0398 0x039b | ................ | 0xffffffff844fa368: 0x039e 0x03a1 0x03a5 0x03a8 0x03ab 0x03ae 0x03b1 0x03b4 | ................ | gef> hexdump -n dword kallsyms_offset 0xffffffff844fa378: 0x00000000 0x00000000 0x00001000 0x00002000 | ............. .. | 0xffffffff844fa388: 0x00006000 0x0000b000 0x0000c000 0x00014000 | .`...........@.. | ... 0xffffffff8461e44c: 0xf89effff 0xf89dffff 0xf89d9fff 0xf89d9fff | ................ | 0xffffffff8461e45c: 0x00000000 0x81000000 0xffffffff 0x02fa0e02 | ................ | relative_base_address: 0xffffffff81000000 [Sample values for 64bit v6.4~, CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y (use negative offset)] gef> hexdump -n word kallsyms_token_index 0xffffffff86744a38: 0x0000 0x0004 0x000c 0x0010 0x0014 0x0017 0x001b 0x0020 | .............. . | 0xffffffff86744a48: 0x002d 0x0034 0x0039 0x003d 0x0042 0x0045 0x0048 0x004b | -.4.9.=.B.E.H.K. | 0xffffffff86744a58: 0x004f 0x0053 0x005d 0x0060 0x0064 0x0067 0x006b 0x0072 | O.S.].`.d.g.k.r. | ... 0xffffffff86744c18: 0x0338 0x033b 0x033e 0x0341 0x0349 0x034c 0x034f 0x0357 | 8.;.>.A.I.L.O.W. | 0xffffffff86744c28: 0x035a 0x035d 0x0360 0x0363 0x0369 0x036e 0x0372 0x0375 | Z.].`.c.i.n.r.u. | gef> hexdump -n dword kallsyms_offset 0xffffffff86744c38: 0xffffffff 0xffffffff 0xffffffff 0xffffffaf | ................ | 0xffffffff86744c48: 0xffffffaa 0xfffffe9f 0xfffffe8f 0xfffffd8f | ................ | ... 0xffffffff8677ed5c: 0xff09237f 0xff09218f 0xff09217f 0xff0920a9 | .#...!...!... .. | 0xffffffff8677ed6c: 0x00000000 0x85c00000 0xffffffff 0x00f0d800 | ................ | relative_base_address: 0xffffffff85c00000 """ # const values if Endian.is_big_endian(): endianness_marker = ">" endian_str = "big" else: endianness_marker = "<" endian_str = "little" offset_byte_size = 4 address_byte_size = current_arch.ptrsize # get relative_base_address if self.kernel_version < (6, 4, 0): # ignore the 0 immediately above offset_kallsyms_num_syms. position = self.offset_kallsyms_num_syms while True: previous_word = self.kernel_img[position - address_byte_size:position] if previous_word != b"\0" * address_byte_size: break position -= address_byte_size # Go backward by num_symbols. position -= address_byte_size # read from kallsyms_relative_base relative_base_address = int.from_bytes(self.kernel_img[position:position + address_byte_size], endian_str) if relative_base_address and (relative_base_address & get_pagesize_mask_low()) == 0: """ some environment has invalid address as relative_base_address. so don't use the logic of is_valid_addr(relative_base_address). gef> hexdump -n qword 0xffffafc5c2adb260-0x10 0x20 0xffffafc5c2adb250: 0xffffafc5c1750000 0x0000000000028193 | ..u............. | 0xffffafc5c2adb260: 0x6474107414bc5404 0x6c7463be6270d277 | .T..t.tdw.pb.ctl | gef> x/16xg 0xffffafc5c1750000 0xffffafc5c1750000: Cannot access memory at address 0xffffafc5c1750000 """ while True: previous_word = self.kernel_img[position - offset_byte_size:position] if previous_word != b"\0" * offset_byte_size: break position -= offset_byte_size position -= self.num_symbols * offset_byte_size else: # kernel_version >= (6, 4): position = self.offset_kallsyms_token_index + 0x200 position_relative_base = align(position + self.num_symbols * offset_byte_size, current_arch.ptrsize) relative_base_address_data = self.kernel_img[position_relative_base:position_relative_base + address_byte_size] if len(relative_base_address_data) == 0: self.verbose_err("kernel_img is not long enough.") return False relative_base_address = int.from_bytes(relative_base_address_data, endian_str) if not (relative_base_address and (relative_base_address & get_pagesize_mask_low()) == 0): return True # Getting here means that the relative_address and position have been detected correctly. self.verbose_info("relative_base_address: {:#x}".format(relative_base_address)) # Try to parse addresses or offsets. fmt = "{:s}{:d}i".format(endianness_marker, self.num_symbols) # signed int kallsyms_offsets_data = self.kernel_img[position:position + self.num_symbols * offset_byte_size] ksym_offsets = struct.unpack(fmt, kallsyms_offsets_data) # Check the ratio of the negative value number_of_negative_items = len([offset for offset in ksym_offsets if offset < 0]) if number_of_negative_items / len(ksym_offsets) >= 0.5: # the case CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y. kernel_addresses = [] for offset in ksym_offsets: if offset < 0: x = relative_base_address - 1 - offset kernel_addresses.append(x) else: kernel_addresses.append(offset) else: # the case CONFIG_KALLSYMS_ABSOLUTE_PERCPU=n. kernel_addresses = [] for offset in ksym_offsets: x = offset + relative_base_address kernel_addresses.append(x) # Check the ratio of the null value. number_of_null_items = kernel_addresses.count(0) if number_of_null_items / len(kernel_addresses) >= 0.2: return True # It seems ok. self.offset_kallsyms_addresses_or_offsets = position self.kernel_addresses = kernel_addresses self.verbose_info("kallsyms_offsets: {:#x}".format(self.ro_base + self.offset_kallsyms_addresses_or_offsets)) return True def find_kallsyms_addresses(self): """ [Search strategy] - From kallsyms_num_syms, go back by num_symbols element sizes. - num_symbols addresses are stored, so get them. [Positional relationship] - ... - kallsyms_addresses (~v6.3, CONFIG_KALLSYMS_BASE_RELATIVE=n) - kallsyms_num_syms - kallsyms_names - kallsyms_markers - kallsyms_seqs_of_names (v6.1.42~v6.8) - kallsyms_token_table - kallsyms_token_index - kallsyms_addresses (v6.4~?, CONFIG_KALLSYMS_BASE_RELATIVE=n) # Unimplemented, as this pattern has not been observed yet. - kallsyms_seqs_of_names (v6.9~) - ... [Sample values for 64bit ~v6.3] gef> hexdump -n qword kallsyms_addresses 0xffffffff81ae3cb8: 0x0000000000000000 0x0000000000000000 | ................ | 0xffffffff81ae3cc8: 0x0000000000004000 0x0000000000009000 | .@.............. | ... 0xffffffff81ae4588: 0xffffffff81000000 0xffffffff81000000 | ................ | 0xffffffff81ae4598: 0xffffffff81000110 0xffffffff810001a9 | ................ | [Sample values for 32bit ~v6.3] gef> hexdump -n dword kallsyms_addresses 0xc1940888: 0xc1000000 0xc1000000 0xc10000bc 0xc10000cc | ................ | 0xc1940898: 0xc10000ed 0xc1000165 0xc10001e7 0xc1000239 | ....e.......9... | 0xc19408a8: 0xc1000283 0xc10002c1 0xc10002d0 0xc1000302 | ................ | 0xc19408b8: 0xc1000328 0xc100032f 0xc1000338 0xc1000338 | (.../...8...8... | """ # const values if Endian.is_big_endian(): endianness_marker = ">" else: endianness_marker = "<" address_byte_size = current_arch.ptrsize # ignore the 0 immediately above offset_kallsyms_num_syms. position = self.offset_kallsyms_num_syms while True: previous_word = self.kernel_img[position - address_byte_size:position] if previous_word != b"\0" * address_byte_size: break position -= address_byte_size # Go backward by num_symbols. position -= self.num_symbols * address_byte_size # Try to parse addresses. if address_byte_size == 8: fmt = "{:s}{:d}Q".format(endianness_marker, self.num_symbols) else: fmt = "{:s}{:d}I".format(endianness_marker, self.num_symbols) kallsyms_addresses_data = self.kernel_img[position:position + self.num_symbols * address_byte_size] self.kernel_addresses = struct.unpack(fmt, kallsyms_addresses_data) self.offset_kallsyms_addresses_or_offsets = position self.verbose_info("kallsyms_addresses: {:#x}".format(self.ro_base + self.offset_kallsyms_addresses_or_offsets)) return True def initialize(self): ret = self.get_kernel_version() if not ret: return False if self.args.rescan: cfg_file_name = self.get_cfg_name() if os.path.exists(cfg_file_name): os.remove(cfg_file_name) else: # the case of both kernel version string are same, but offset are different. current_version_string_offset = self.version_string_offset # keep current if self.get_saved_config(["version_string_offset"]): # load temporarily if self.version_string_offset != current_version_string_offset: cfg_file_name = self.get_cfg_name() os.remove(cfg_file_name) self.version_string_offset = current_version_string_offset # set current again ret = self.find_kallsyms_token_table() if not ret: return False ret = self.find_kallsyms_token_index() if not ret: return False ret = self.find_kallsyms_markers() if not ret: return False ret = self.find_kallsyms_names() if not ret: return False ret = self.find_kallsyms_num_syms() if not ret: return False self.offset_kallsyms_addresses_or_offsets = None if self.kernel_version >= (4, 6): # On modern kernels, first check the case CONFIG_KALLSYMS_BASE_RELATIVE=y. ret = self.find_kallsyms_offsets() if not ret: return False if not self.offset_kallsyms_addresses_or_offsets: # the case CONFIG_KALLSYMS_BASE_RELATIVE=n. self.find_kallsyms_addresses() self.save_config("version_string") self.save_config("version_string_offset") self.save_config("ro_size") return True def arm64_fast_path(self): if not is_in_kernel(): self.quiet_info("Use slow path") return False # This path is more faster because it does not use pagewalk. # Instead of finding ro_base from pagewalk results, it finds ro_base by scanning the kernel version. # It is especially beneficial for ARM64, due to extensive pagetables and pagewalk take a long time. # It may work on other architectures but limited to ARM64 as others gain little. # First, search for the kernel version string from $pc. # It is located at around top of ro_base, and ro_base is aligned by 0x10000. current = (current_arch.pc & ~0xffff) + 0x10000 # Starting address to brute force ro_base step_size = 0x10000 while True: try: # As kernel version string is near the top of ro_base, it is enough to check the first page. candidate_rodata = read_memory(current, get_pagesize()) except gdb.MemoryError: # reached to the end of ro_base self.quiet_info("Use slow path") return False # '\n\0' is needed to avoid false positives in the dmesg buffer. r = re.search(rb"Linux version (\d+\.[\d.]*\d)[ -~]+\n\0", candidate_rodata) if r: # Found kernel version string version_string_address = current + r.span()[0] self.version_string = r.group(0)[:-2] version_number = r.group(1).decode("ascii") self.kernel_version = self.get_kernel_version_triplet(version_number) break current += step_size # We can make the hash from the kernel version string and load from saved config. # At this point, following two parameters are enough. ret = self.get_saved_config([ "version_string_offset", "ro_size", ]) if not ret: self.quiet_info("Use slow path") return False # Restore ro_base with considering kASLR. self.quiet_info("Use fast path") self.ro_base = version_string_address - self.version_string_offset self.verbose_info("ro_base: {:#x}-{:#x}".format(self.ro_base, self.ro_base + self.ro_size)) # doit self.kernel_img = read_memory(self.ro_base, self.ro_size) ret = self.initialize() if not ret: self.quiet_info("Use slow path") return False self.read_kallsyms() return True def parse_kallsyms(self): if self.kallsyms: return True # use cache # Fast path when reattaching GDB after executing this command once (ARM64 only). if is_arm64(): ret = self.arm64_fast_path() if ret: return True # Slow path try: kinfo = Kernel.get_kernel_layout() if kinfo.has_none: return False except gdb.MemoryError: self.quiet_err("Memory read error") return False if not kinfo.rwx: # On modern kernels, ro_size can be trusted, so it only tries to parse once. self.ro_base = kinfo.ro_base self.ro_size = kinfo.ro_size self.kernel_img = read_memory(self.ro_base, self.ro_size) self.verbose_info("ro_base: {:#x}-{:#x}".format(self.ro_base, self.ro_base + self.ro_size)) ret = self.initialize() if not ret: return False else: # Older kernel that has the RWX attribute don't trust ro_size. # Very large values of ro_size can be detected. # It will take a long time to parse if you just use it. # Gradually increasing ro_size while searching can speed up several times. self.ro_base = kinfo.ro_base # This value is a heuristic threshold derived through testing across numerous kernel images. # Unless there is a compelling reason, do not modify it. base_size = 0x10_0000 step = 0x10_0000 for candidate_size in range(base_size, kinfo.ro_size, step): self.ro_size = candidate_size self.kernel_img = read_memory(self.ro_base, self.ro_size) self.verbose_info("ro_base: {:#x}-{:#x}".format(self.ro_base, self.ro_base + self.ro_size)) ret = self.initialize() if ret: # found break else: # not found return False # here, we got all offsets to read kallsyms self.read_kallsyms() return True def parse_main(self): if self.kallsyms: return True # specified vmlinux parse if self.args.vmlinux_file: if not os.path.exists(self.args.vmlinux_file): self.quiet_err("Could not find vmlinux file") return False self.quiet_info("Parse from file: {!s}".format(self.args.vmlinux_file)) self.parse_vmlinux(self.args.vmlinux_file) return True # loaded vmlinux parse loaded_vmlinux = self.get_loaded_vmlinux_path() if loaded_vmlinux: self.quiet_info("Parse from file: {!s}".format(loaded_vmlinux)) self.parse_vmlinux(loaded_vmlinux) return True # normal parse self.quiet_info("Wait for memory scan") ret = self.parse_kallsyms() if ret: return True # failed, but if args.rescan was not specified originally if not self.args.rescan: self.quiet_info("Try to rescan (ignore cached config)") self.kallsyms = [] ret = self.parse_kallsyms() if ret: return True self.quiet_err("Failed to parse") return False @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64", "RISCV32", "RISCV64")) def do_invoke(self, args): if args.print_saved_config: self.print_saved_config() return if self.args.rescan: self.kallsyms = [] ret = self.parse_main() if not ret: return self.print_kallsyms(args.keyword, args.type, args.smart) self.print_output(check_terminal_size=True) return @register_command class VmlinuxToElfApplyCommand(GenericCommand): """Apply symbol from kallsyms in memory using vmlinux-to-elf.""" _cmdline_ = "vmlinux-to-elf-apply" _category_ = "06-e. Qemu-system/KGDB Cooperation - Linux Symbol/Type" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--rescan", action="store_true", help="force applying again. (default: reuse vmlinux-to-elf-dump-memory.elf if exists)") _syntax_ = parser.format_help() @staticmethod def dump_kernel_elf(rescan=False): """Dump the kernel from the memory, then apply vmlinux-to-elf to create symboled ELF.""" # check try: vmlinux2elf = GefUtil.which("vmlinux-to-elf") except FileNotFoundError as e: err("{}".format(e)) return None # resolve kversion for saved file name kversion = Kernel.kernel_version() h = hashlib.sha256(String.str2bytes(kversion.version_string)).hexdigest()[-16:] dumped_mem_file = os.path.join(GEF_TEMP_DIR, "dump-memory-{:s}.raw".format(h)) symboled_vmlinux_file = os.path.join(GEF_TEMP_DIR, "dump-memory-{:s}.elf".format(h)) # check if it can be reused if (not rescan) and os.path.exists(symboled_vmlinux_file) and os.path.getsize(symboled_vmlinux_file) > 0: info("A previously used file found, will be reused") return symboled_vmlinux_file # resolve text_base, ro_base kinfo = Kernel.get_kernel_layout() if None in (kinfo.text_base, kinfo.text_size, kinfo.ro_base, kinfo.ro_size): err("Failed to resolve") return None gef_print("kernel base: {:#x}-{:#x} ({:#x} bytes)".format(kinfo.text_base, kinfo.text_end, kinfo.text_size)) gef_print("kernel rodata: {:#x}-{:#x} ({:#x} bytes)".format(kinfo.ro_base, kinfo.ro_end, kinfo.ro_size)) info("Start memory dump") # rodata size detection may be inaccurate on kernels with RWX attribute regions. # Since they tend to be very large, we put a size cap on the rodata to make it faster. if kinfo.rwx: # The number 0x400000 has no basis. fixed_ro_base_size = min(0x40_0000, kinfo.ro_size) else: fixed_ro_base_size = kinfo.ro_size # delete old file if os.path.exists(dumped_mem_file): gef_print("Delete old {:s}".format(dumped_mem_file)) os.unlink(dumped_mem_file) if os.path.exists(symboled_vmlinux_file): gef_print("Delete old {:s}".format(symboled_vmlinux_file)) os.unlink(symboled_vmlinux_file) # delete files related to old file for f in os.listdir(GEF_TEMP_DIR): if os.path.basename(dumped_mem_file) in f: remove_file = os.path.join(GEF_TEMP_DIR, f) gef_print("Delete old {:s}".format(remove_file)) os.unlink(remove_file) if os.path.basename(symboled_vmlinux_file) in f: remove_file = os.path.join(GEF_TEMP_DIR, f) gef_print("Delete old {:s}".format(remove_file)) os.unlink(remove_file) # dump text start = kinfo.text_base end = start + kinfo.text_size gef_print("Dumping .text area: {:#x} - {:#x}".format(start, end)) try: gdb.execute("dump memory {} {:#x} {:#x}".format(dumped_mem_file, start, end), to_string=True) except gdb.MemoryError: err("Memory read error. Make sure the context is in supervisor mode / Ring-0") return None # dump sparse text_end = kinfo.text_end unmapped_size = kinfo.ro_base - text_end if unmapped_size: gef_print("Non-mapping area: {:#x} - {:#x} (ZERO fill)".format(text_end, kinfo.ro_base)) open(dumped_mem_file, "a").write("\0" * unmapped_size) # dump rodata start = kinfo.ro_base end = start + fixed_ro_base_size gef_print("Dumping .rodata area: {:#x} - {:#x}".format(start, end)) try: gdb.execute("append memory {} {:#x} {:#x}".format(dumped_mem_file, start, end), to_string=True) except gdb.MemoryError: err("Memory read error. Make sure the context is in supervisor mode / Ring-0") return None gef_print("Dumped to {:s}".format(dumped_mem_file)) # apply vmlinux-to-elf cmd = "{!r} {!r} {!r} --base-address={:#x}".format(vmlinux2elf, dumped_mem_file, symboled_vmlinux_file, kinfo.text_base) warn("Execute `{:s}`".format(cmd)) os.system(cmd) # Error if not os.path.exists(symboled_vmlinux_file) or os.path.getsize(symboled_vmlinux_file) == 0: return None # Success return symboled_vmlinux_file @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel def do_invoke(self, args): info("Wait for memory scan") text_base = Kernel.get_kernel_base() if text_base is None: err("Failed to resolve kbase") return symboled_vmlinux_file = self.dump_kernel_elf(args.rescan) if symboled_vmlinux_file is None: err("Failed to create kernel ELF") return # load symbol info("Adding symbol") # Prior to gdb 8.x, add-symbol-file command requires a .text address # gdb 9.x: Usage: add-symbol-file FILE [-readnow | -readnever] [-o OFF] [ADDR] [-s SECT-NAME SECT-ADDR]... # gdb 8.x: Usage: add-symbol-file FILE ADDR [-readnow | -readnever | -s SECT-NAME SECT-ADDR]... # But the created ELF has no .text, only a .kernel # Applying an empty symbol has no effect, so tentatively specify the same address as the .kernel. cmd = "add-symbol-file {!r} {:#x} -s .kernel {:#x}".format(symboled_vmlinux_file, text_base, text_base) warn("Execute `{:s}`".format(cmd)) gdb.execute(cmd) return @register_command class TcmallocDumpCommand(GenericCommand, BufferingOutput): """tcmalloc (google-perftools-2.16) free-list viewer (x64 only).""" _cmdline_ = "tcmalloc-dump" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-c", "--central", action="store_true", help="show central cache instead of thread caches.") parser.add_argument("-f", "--force-heuristic", action="store_true", help="use heuristic detection.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # print freelist of thread cache for all thread", "{0:s} --central # print freelist of central cache", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.modes: # already matched return [] if text == "": # no prefix return [s for s in self.modes if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.modes if s and s.startswith(text.strip())] def initialize(self): """ gef> dt 'tcmalloc::ThreadCache' struct tcmalloc::ThreadCache { /* offset | size */ /* | 0x0008 */ class tcmalloc::ThreadCache * thread_heaps_; /* | 0x0004 */ int thread_heap_count_; /* | 0x0008 */ class tcmalloc::ThreadCache * next_memory_steal_; /* | 0x0008 */ struct std::atomic min_per_thread_cache_size_; /* | 0x0008 */ size_t overall_thread_cache_size_; /* | 0x0008 */ volatile size_t per_thread_cache_size_; /* | 0x0008 */ ssize_t unclaimed_cache_space_; /* 0x0000 | 0x1000 */ class tcmalloc::ThreadCache::FreeList [128] list_; /* 0x1000 | 0x0004 */ int32_t size_; /* 0x1004 | 0x0004 */ int32_t max_size_; /* 0x1008 | 0x0018 */ class tcmalloc::Sampler sampler_; /* 0x1020 | 0x0008 */ class tcmalloc::ThreadCache * next_; /* 0x1028 | 0x0008 */ class tcmalloc::ThreadCache * prev_; } // total: 0x1040 bytes gef> p tcmalloc::Static::sizemap """ kClassSizesMax = 128 self.ThreadCache_offset_next = 0x1020 self.ThreadCache_offset_freelist_array = 0x0 self.ThreadCache_freelist_slot_count = kClassSizesMax """ gef> dt 'tcmalloc::ThreadCache::FreeList' struct tcmalloc::ThreadCache::FreeList { /* offset | size */ /* 0x0000 | 0x0008 */ void * list_; /* 0x0008 | 0x0004 */ uint32_t length_; /* 0x000c | 0x0004 */ uint32_t lowater_; /* 0x0010 | 0x0004 */ uint32_t max_length_; /* 0x0014 | 0x0004 */ uint32_t length_overages_; /* 0x0018 | 0x0004 */ int32_t size_; } // total: 0x20 bytes gef> """ self.sizeof_FreeList = 0x20 self.FreeList_offset_list = 0x0 self.FreeList_offset_length = 0x8 self.FreeList_offset_size = 0x18 """ gef> ptype 'tcmalloc::Static::central_cache_' type = class tcmalloc::CentralFreeList { ... } [128] gef> dt 'tcmalloc::CentralFreeList' struct tcmalloc::CentralFreeList { /* offset | size */ /* | 0x0004 */ const int kMaxNumTransferEntries; /* 0x0000 | 0x0004 */ class SpinLock lock_; /* 0x0008 | 0x0008 */ size_t size_class_; /* 0x0010 | 0x0030 */ struct tcmalloc::Span empty_; /* 0x0040 | 0x0030 */ struct tcmalloc::Span nonempty_; /* 0x0070 | 0x0008 */ size_t num_spans_; /* 0x0078 | 0x0008 */ size_t counter_; /* 0x0080 | 0x0400 */ struct tcmalloc::CentralFreeList::TCEntry [64] tc_slots_; /* 0x0480 | 0x0004 */ int32_t used_slots_; /* 0x0484 | 0x0004 */ int32_t cache_size_; /* 0x0488 | 0x0004 */ int32_t max_cache_size_; } // total: 0x4c0 bytes gef> gef> dt 'tcmalloc::CentralFreeList::TCEntry' struct tcmalloc::CentralFreeList::TCEntry { /* offset | size */ /* 0x0000 | 0x0008 */ void * head; /* 0x0008 | 0x0008 */ void * tail; } // total: 0x10 bytes gef> """ self.CentralCache_array_count = kClassSizesMax kMaxNumTransferEntries = 64 self.CentralCache_freelist_slot_count = kMaxNumTransferEntries self.sizeof_CentralCache = 0x4c0 self.CentralCache_offset_size_class_ = 0x8 self.CentralCache_offset_tc_slots_ = 0x80 self.CentralCache_offset_used_slots_ = 0x480 # for central cache """ gef> dt 'tcmalloc::SizeMap' struct tcmalloc::SizeMap { /* offset | size */ /* | 0x0004 */ const int kMaxSmallSize; /* | 0x0008 */ const size_t kClassArraySize; /* 0x0000 | 0x0879 */ unsigned char [2169] class_array_; /* 0x087c | 0x0200 */ int [128] num_objects_to_move_; /* 0x0a7c | 0x0200 */ int32_t [128] class_to_size_; /* 0x0c80 | 0x0400 */ size_t [128] class_to_pages_; /* 0x1080 | 0x0008 */ size_t min_span_size_in_pages_; /* 0x1088 | 0x0008 */ size_t num_size_classes; } // total: 0x1090 bytes gef> gef> hexdump dword "(long)&'tcmalloc::Static::sizemap_'+0xa7c" 400 0x7ffff7fb03dc: 0x00000000 0x00000008 0x00000010 0x00000020 0x7ffff7fb03ec: 0x00000030 0x00000040 0x00000050 0x00000060 0x7ffff7fb03fc: 0x00000070 0x00000080 0x00000090 0x000000a0 0x7ffff7fb040c: 0x000000b0 0x000000c0 0x000000d0 0x000000e0 0x7ffff7fb041c: 0x000000f0 0x00000100 0x00000120 0x00000140 0x7ffff7fb042c: 0x00000160 0x00000180 0x000001a0 0x000001c0 0x7ffff7fb043c: 0x000001e0 0x00000200 0x00000240 0x00000280 0x7ffff7fb044c: 0x000002c0 0x00000300 0x00000380 0x00000400 0x7ffff7fb045c: 0x00000480 0x00000500 0x00000580 0x00000600 0x7ffff7fb046c: 0x00000700 0x00000800 0x00000900 0x00000a00 0x7ffff7fb047c: 0x00000b00 0x00000c00 0x00000d00 0x00001000 0x7ffff7fb048c: 0x00001200 0x00001400 0x00001800 0x00001a00 0x7ffff7fb049c: 0x00002000 0x00002400 0x00002800 0x00003000 0x7ffff7fb04ac: 0x00003400 0x00004000 0x00005000 0x00006000 0x7ffff7fb04bc: 0x00006800 0x00008000 0x0000a000 0x0000c000 0x7ffff7fb04cc: 0x0000e000 0x00010000 0x00012000 0x00014000 0x7ffff7fb04dc: 0x00016000 0x00018000 0x0001a000 0x0001c000 0x7ffff7fb04ec: 0x0001e000 0x00020000 0x00022000 0x00024000 0x7ffff7fb04fc: 0x00026000 0x00028000 0x0002a000 0x0002c000 0x7ffff7fb050c: 0x0002e000 0x00030000 0x00032000 0x00034000 0x7ffff7fb051c: 0x00036000 0x00038000 0x0003a000 0x0003c000 0x7ffff7fb052c: 0x0003e000 0x00040000 0x00000000 0x00000000 0x7ffff7fb053c: 0x00000000 0x00000000 0x00000000 0x00000000 * gef> """ self.class_to_size_dic = [ 0x00000000, 0x00000008, 0x00000010, 0x00000020, 0x00000030, 0x00000040, 0x00000050, 0x00000060, 0x00000070, 0x00000080, 0x00000090, 0x000000a0, 0x000000b0, 0x000000c0, 0x000000d0, 0x000000e0, 0x000000f0, 0x00000100, 0x00000120, 0x00000140, 0x00000160, 0x00000180, 0x000001a0, 0x000001c0, 0x000001e0, 0x00000200, 0x00000240, 0x00000280, 0x000002c0, 0x00000300, 0x00000380, 0x00000400, 0x00000480, 0x00000500, 0x00000580, 0x00000600, 0x00000700, 0x00000800, 0x00000900, 0x00000a00, 0x00000b00, 0x00000c00, 0x00000d00, 0x00001000, 0x00001200, 0x00001400, 0x00001800, 0x00001a00, 0x00002000, 0x00002400, 0x00002800, 0x00003000, 0x00003400, 0x00004000, 0x00005000, 0x00006000, 0x00006800, 0x00008000, 0x0000a000, 0x0000c000, 0x0000e000, 0x00010000, 0x00012000, 0x00014000, 0x00016000, 0x00018000, 0x0001a000, 0x0001c000, 0x0001e000, 0x00020000, 0x00022000, 0x00024000, 0x00026000, 0x00028000, 0x0002a000, 0x0002c000, 0x0002e000, 0x00030000, 0x00032000, 0x00034000, 0x00036000, 0x00038000, 0x0003a000, 0x0003c000, 0x0003e000, 0x00040000, ] return def get_heap_key(self): # for future use return 0 def get_central_cache_(self): if self.args.force_heuristic: return None try: return AddressUtil.parse_address("&'tcmalloc::Static::central_cache_'") except gdb.error: return None def get_central_cache_heuristic(self): self.quiet_info("Use heuristic search for central_cache_") """ gef> dt 'tcmalloc::Span' struct tcmalloc::Span { /* offset | size */ /* 0x0000 | 0x0008 */ PageID start; /* 0x0008 | 0x0008 */ Length length; /* 0x0010 | 0x0008 */ struct tcmalloc::Span * next; /* 0x0018 | 0x0008 */ struct tcmalloc::Span * prev; /* 0x0020 | 0x0008 */ union {...} ; /* 0x0028 | 0x0004 */ unsigned int refcount : 16; /* 0x002a | 0x0004 */ unsigned int sizeclass : 8; /* 0x002b | 0x0004 */ unsigned int location : 2; /* 0x002b | 0x0004 */ unsigned int sample : 1; /* 0x002b | 0x0001 */ bool has_span_iter : 1; } // total: 0x30 bytes gef> gef> telescope 0x00007ffff7e06800 -n 0x7ffff7e06800|+0x0000|+000: 0x0000000000000000 // lock_ 0x7ffff7e06808|+0x0008|+001: 0x0000000000000000 // size_class_ 0x7ffff7e06810|+0x0010|+002: 0x0000000000000000 // empty_.start 0x7ffff7e06818|+0x0018|+003: 0x0000000000000000 // empty_.length 0x7ffff7e06820|+0x0020|+004: 0x00007ffff7e06810 // empty_.next 0x7ffff7e06828|+0x0028|+005: 0x00007ffff7e06810 // empty_.prev 0x7ffff7e06830|+0x0030|+006: 0x0000000000000000 // empty_.union 0x7ffff7e06838|+0x0038|+007: 0x0000000000000000 // empty_.union 0x7ffff7e06840|+0x0040|+008: 0x0000000000000000 // nonempty_.start 0x7ffff7e06848|+0x0048|+009: 0x0000000000000000 // nonempty_.length 0x7ffff7e06850|+0x0050|+010: 0x00007ffff7e06840 // nonempty_.next 0x7ffff7e06858|+0x0058|+011: 0x00007ffff7e06840 // nonempty_.prev 0x7ffff7e06860|+0x0060|+012: 0x0000000000000000 // nonempty_.union 0x7ffff7e06868|+0x0068|+013: 0x0000000000000000 // nonempty_.union 0x7ffff7e06870|+0x0070|+014: 0x0000000000000000 """ offset_next1 = 0x20 offset_prev1 = 0x28 offset_next2 = 0x50 offset_prev2 = 0x58 for m in ProcessMap.get_process_maps(): if "[heap]" in m.path: continue if m.permission != Permission.READ | Permission.WRITE: continue for current_page in range(m.page_start, m.page_end, get_pagesize()): # fast check x = read_memory(current_page, get_pagesize()) if set(x) == {0}: continue # exact check for current in range(current_page, current_page + get_pagesize(), current_arch.ptrsize): error = False for i in range(self.CentralCache_array_count): base = current + self.sizeof_CentralCache * i try: n1 = read_int_from_memory(base + offset_next1) p1 = read_int_from_memory(base + offset_prev1) n2 = read_int_from_memory(base + offset_next2) p2 = read_int_from_memory(base + offset_prev2) except gdb.MemoryError: error = True break if not is_valid_addr(n1): break if not is_valid_addr(p1): break if not is_valid_addr(n2): break if not is_valid_addr(p2): break if error: break if i > 40: # heuristic threshold return current return None def get_thread_heaps_(self): if self.args.force_heuristic: return None try: return AddressUtil.parse_address("&'tcmalloc::ThreadCache::thread_heaps_'") except gdb.error: return None def get_thread_heap_list_heuristic(self): """thread_heap_ itself cannot be found, so it returns the detected list.""" self.quiet_info("Use heuristic search for thread_heap_") """ gef> tls $tls = 0x7ffff7c5e080 --------------------------------- TLS-0x80 --------------------------------- 0x7ffff7c5e000|+0x0000|+000: 0x00007ffff7bbffc0 0x7ffff7c5e008|+0x0008|+001: 0x00007ffff7bc08c0 0x7ffff7c5e010|+0x0010|+002: 0x0000000000000000 0x7ffff7c5e018|+0x0018|+003: 0x0000000000000000 0x7ffff7c5e020|+0x0020|+004: 0x0000000000000000 0x7ffff7c5e028|+0x0028|+005: 0x0000000000000000 0x7ffff7c5e030|+0x0030|+006: 0x0000000000000000 0x7ffff7c5e038|+0x0038|+007: 0x0000000000000000 0x7ffff7c5e040|+0x0040|+008: 0x0000000000000000 0x7ffff7c5e048|+0x0048|+009: 0x0000000000000000 0x7ffff7c5e050|+0x0050|+010: 0x0000000000000000 0x7ffff7c5e058|+0x0058|+011: 0x0000000000000000 0x7ffff7c5e060|+0x0060|+012: 0x0000000000000000 0x7ffff7c5e068|+0x0068|+013: 0x0000000000000000 0x7ffff7c5e070|+0x0070|+014: 0x0000555555599000 <-- here 0x7ffff7c5e078|+0x0078|+015: 0x0000000000000000 ------------------------------------ TLS ----------------------------------- 0x7ffff7c5e080|+0x0000|+000: 0x00007ffff7c5e080 ... """ # search offset for i in range(1, 8): orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() found = True candidate_thread_heaps = [] candidate_next = [] candidate_prev = [] for thread in gdb.selected_inferior().threads(): thread.switch() # change thread # search thread_heaps tls = current_arch.get_tls() if tls is None: continue val = read_int_from_memory(tls - current_arch.ptrsize * i) if not is_valid_addr(val): found = False break p = read_int_from_memory(val + self.ThreadCache_offset_next) b = read_int_from_memory(val + self.ThreadCache_offset_next + current_arch.ptrsize) candidate_next.append(p) candidate_next.append(b) candidate_thread_heaps.append(val) orig_thread.switch() # revert thread orig_frame.select() if not candidate_thread_heaps: found = False elif set(candidate_next) | set(candidate_prev) != set(candidate_thread_heaps) | {0}: found = False if found: return candidate_thread_heaps return None def dump_thread_heap_freelist_single(self, freelist, idx): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") chunk = read_int_from_memory(freelist + self.FreeList_offset_list) length = read_int_from_memory(freelist + self.FreeList_offset_length) & 0xffff_ffff real_length = 0 error = False if chunk != 0: # freelist exists seen = [] out = [] while chunk != 0: real_length += 1 seen.append(chunk) # corrupted memory check try: new_chunk = read_int_from_memory(chunk) out.append(" -> " + Color.colorify_hex(chunk, freed_address_color)) chunk = new_chunk except gdb.MemoryError: out.append(Color.colorify(" -> {:#x} (corrupted)".format(chunk), corrupted_msg_color)) error = True break # heap key decode chunk ^= self.get_heap_key() # loop check if chunk in seen: out.append(Color.colorify(" -> {:#x} (loop)".format(chunk), corrupted_msg_color)) error = True break # corrupted length check if length != real_length and error is False: out.append(Color.colorify(" (length corrupted; len != {:d})".format(length), corrupted_msg_color)) error = True chunksize = read_int_from_memory(freelist + self.FreeList_offset_size) # print self.out.append("freelist[idx={:d}, size={:s}, len={:d}] @ {!s}".format( idx, Color.colorify_hex(chunksize, chunk_size_color), length, ProcessMap.lookup_address(freelist), )) self.out.extend(out) return def dump_thread_heaps(self): # exact way thread_heap_head = self.get_thread_heaps_() if thread_heap_head: self.out.append(titlify("thread_heaps_ (head) @ {:#x}".format(thread_heap_head))) thread_heap = read_int_from_memory(thread_heap_head) thread_heaps = [] while thread_heap: thread_heaps.append(thread_heap) thread_heap = read_int_from_memory(thread_heap + self.ThreadCache_offset_next) else: # heuristic way thread_heaps = self.get_thread_heap_list_heuristic() if thread_heaps is None: err("Could not find tcmalloc::ThreadCache::thread_heaps_") return heap_key = self.get_heap_key() if heap_key != 0: self.out.append("heap_key: {:#x} (xor chunk->fd)".format(heap_key)) for thread_heap in thread_heaps: self.out.append(titlify('thread cache @ {:#x}'.format(thread_heap))) freelist = thread_heap + self.ThreadCache_offset_freelist_array for i in range(self.ThreadCache_freelist_slot_count): self.dump_thread_heap_freelist_single(freelist, i) freelist += self.sizeof_FreeList return def dump_central_cache_freelist_single(self, freelist, i, j): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") chunk = read_int_from_memory(freelist + self.FreeList_offset_list) if chunk != 0: # freelist exists seen = [] out = [] while chunk != 0: seen.append(chunk) # corrupted memory check try: new_chunk = read_int_from_memory(chunk) out.append(" -> " + Color.colorify_hex(chunk, freed_address_color)) chunk = new_chunk except gdb.MemoryError: out.append(Color.colorify(" -> {:#x} (corrupted)".format(chunk), corrupted_msg_color)) break # heap key decode chunk ^= self.get_heap_key() # loop check if chunk in seen: out.append(Color.colorify(" -> {:#x} (loop)".format(chunk), corrupted_msg_color)) break # print self.out.append("central_cache_[{:d}].tc_slot[{:d}] @ {!s}".format( i, j, ProcessMap.lookup_address(freelist), )) self.out.extend(out) return def dump_central_cache(self): central_cache_ = self.get_central_cache_() if central_cache_ is None: central_cache_ = self.get_central_cache_heuristic() if central_cache_ is None: err("Could not find tcmalloc::Static::central_cache_") return self.out.append(titlify("central_cache_ @ {:#x}".format(central_cache_))) heap_key = self.get_heap_key() if heap_key != 0: self.out.append("heap_key: {:#x} (xor chunk->fd)".format(heap_key)) for i in range(self.CentralCache_array_count): central_cache_i = central_cache_ + i * self.sizeof_CentralCache # ¢ral_cache[i] # check slot count used_slots = read_int32_from_memory(central_cache_i + self.CentralCache_offset_used_slots_) max_slots = self.CentralCache_freelist_slot_count if used_slots == 0: continue # calc class -> size size_class = read_int_from_memory(central_cache_i + self.CentralCache_offset_size_class_) size_byte = self.class_to_size_dic[size_class] # dump self.out.append(titlify( "central_cache_[{:d}] @ {:#x} (used_slots:{:d}/{:d}, size_class:{:#x}, chunk_size:{:#x})".format( i, central_cache_i, used_slots, max_slots, size_class, size_byte, ), )) tc_slots = central_cache_i + self.CentralCache_offset_tc_slots_ # ¢ral_cache[i].tc_slots_ for j in range(min(used_slots, self.CentralCache_freelist_slot_count)): addr = tc_slots + j * 0x10 # ¢ral_cache[i].tc_slots_[j] self.dump_central_cache_freelist_single(addr, i, j) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): self.out = [] self.initialize() if args.central: self.dump_central_cache() else: self.dump_thread_heaps() self.print_output() return @register_command class GoHeapDumpCommand(GenericCommand, BufferingOutput): """go language v1.24.4 mheap dumper (x64 only).""" _cmdline_ = "go-heap-dump" _category_ = "05-c. Heap - Other" # TODO: arena, central, mspan->next parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--mheap", type=AddressUtil.parse_address, help="the address of runtime.mheap_.") parser.add_argument("--mspan", type=AddressUtil.parse_address, help="the address of the target mspan.") parser.add_argument("-d", "--dump", action="store_true", help="with hexdump.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="display also empty slots.") _syntax_ = parser.format_help() class_to_size_dic = [ 0x0, 0x8, 0x10, 0x18, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x100, 0x120, 0x140, 0x160, 0x180, 0x1a0, 0x1c0, 0x1e0, 0x200, 0x240, 0x280, 0x2c0, 0x300, 0x380, 0x400, 0x480, 0x500, 0x580, 0x600, 0x700, 0x800, 0x900, 0xa80, 0xc00, 0xc80, 0xd80, 0x1000, 0x1300, 0x1500, 0x1800, 0x1980, 0x1a80, 0x1b00, 0x2000, 0x2500, 0x2600, 0x2800, 0x2a80, 0x3000, 0x3500, 0x3800, 0x4000, 0x4800, 0x4a80, 0x5000, 0x5500, 0x6000, 0x6a80, 0x7000, 0x8000, ] def get_struct_offset(self, type_name, member_name): tp = GefUtil.cached_lookup_type(type_name) if tp is None: return None if member_name not in tp: return None field = tp[member_name] if not hasattr(field, "bitpos"): return None return field.bitpos // 8 def get_struct_size(self, type_name): tp = GefUtil.cached_lookup_type(type_name) if tp is None: return None return tp.sizeof def initialize(self): if hasattr(self, "initialized") and self.initialized: return True self.PageShift = 13 self.PageSize = 1 << self.PageShift # assume 1.22.2 (Ubuntu 24.04) """ struct runtime.mheap { /* offset | size */ ... /* 0x10148 | 0x0018 */ []*runtime.mspan allspans; ... /* 0x101d8 | 0x0008 */ [1]*[4194304]*runtime.heapArena arenas; ... /* 0x10288 | 0x6600 */ [136]struct { runtime.mcentral runtime.mcentral; runtime.pad [24]uint8 } central; ... } // total: 0x16ab8 bytes """ self.offset_allspans = self.get_struct_offset("runtime.mheap", "allspans") or 0x10148 self.sizeof_mheap = self.get_struct_size("runtime.mheap") or 0x16ab8 """ struct runtime.mspan { /* offset | size */ ... /* 0x0000 | 0x0008 */ runtime.mspan * next; /* 0x0008 | 0x0008 */ runtime.mspan * prev; ... /* 0x0018 | 0x0008 */ uintptr startAddr; /* 0x0020 | 0x0008 */ uintptr npages; ... /* 0x0032 | 0x0002 */ uint16 nelems; ... /* 0x0040 | 0x0008 */ runtime.gcBits * allocBits; ... /* 0x0062 | 0x0001 */ runtime.spanClass spanclass; ... } // total: 0xa0 bytes """ self.offset_next = self.get_struct_offset("runtime.mspan", "next") or 0x0 self.offset_prev = self.get_struct_offset("runtime.mspan", "next") or 0x8 self.offset_startAddr = self.get_struct_offset("runtime.mspan", "startAddr") or 0x18 self.offset_npages = self.get_struct_offset("runtime.mspan", "npages") or 0x20 self.offset_nelems = self.get_struct_offset("runtime.mspan", "nelems") or 0x32 self.offset_allocBits = self.get_struct_offset("runtime.mspan", "allocBits") or 0x40 self.offset_spanclass = self.get_struct_offset("runtime.mspan", "spanClass") or 0x62 self.initialized = True return True def get_mheap_(self): # use symbol try: return AddressUtil.parse_address("&'runtime.mheap_'") except gdb.error: pass # use heuristic search (TODO: Check if it is always correct) elf = Elf.get_elf() if elf is None or not elf.is_valid(): return None bss = elf.get_shdr(".bss") mheap = bss.sh_addr + bss.sh_size - self.sizeof_mheap if is_valid_addr(mheap): return mheap return None def parse_mheap(self, mheap): self.out.append(titlify("runtime.mheap_ @ {:#x}".format(mheap))) current = read_int_from_memory(mheap + self.offset_allspans) mspans = [] while True: try: mspan_addr = read_int_from_memory(current) except gdb.MemoryError: self.out.append("Memory read error") return [] if not mspan_addr: break mspan = self.parse_mspan(mspan_addr) if mspan: mspans.append(mspan) current += current_arch.ptrsize mspans = sorted(mspans, key=lambda m: (m.chunk_size, m.address)) return mspans def parse_mspan(self, mspan): # read member try: start_addr = read_int_from_memory(mspan + self.offset_startAddr) except gdb.MemoryError: self.out.append("Memory read error") return None if not self.args.verbose and start_addr == 0: return None # spanclass = (sizeclass << 1) | (noscan bit) spanclass = read_int8_from_memory(mspan + self.offset_spanclass) >> 1 chunk_size = self.class_to_size_dic[spanclass] if not self.args.verbose and chunk_size == 0: return None next_ = read_int_from_memory(mspan + self.offset_next) prev_ = read_int_from_memory(mspan + self.offset_prev) npages = read_int_from_memory(mspan + self.offset_npages) end_addr = start_addr + npages * self.PageSize aligned_nelems = nelems = read_int_from_memory(mspan + self.offset_nelems) & 0xffff while aligned_nelems % 8: aligned_nelems += 1 allocBits_addr = read_int_from_memory(mspan + self.offset_allocBits) allocBits_data = read_memory(allocBits_addr, aligned_nelems) allocBits_array = [((b >> i) & 1) for b in allocBits_data for i in range(8)] Mspan = collections.namedtuple("Mspan", [ "address", "next", "prev", "start_addr", "end_addr", "npages", "chunk_size", "nelems", "alloc_bits", ]) mspan = Mspan(mspan, next_, prev_, start_addr, end_addr, npages, chunk_size, nelems, allocBits_array[:nelems]) return mspan def dump_mspan_data(self, mspan): chunk_data = read_memory(mspan.start_addr, mspan.end_addr - mspan.start_addr) chunk_hexdump = hexdump(chunk_data, base=mspan.start_addr, color=False, unit=8) lines = chunk_hexdump.splitlines() color_dic = { # (b, idx % 2): color name (0, 0): "bright_yellow", (0, 1): "yellow", (1, 0): "graphite", (1, 1): "bright_black", } # coloring for i in range(len(lines)): line = lines[i] offset1 = i * 0x10 idx1 = offset1 // mspan.chunk_size if idx1 >= mspan.nelems: color1 = "" else: b1 = mspan.alloc_bits[idx1] color1 = color_dic[b1, idx1 % 2] offset2 = i * 0x10 + 8 idx2 = offset2 // mspan.chunk_size if idx2 >= mspan.nelems: color2 = "" else: b2 = mspan.alloc_bits[idx2] color2 = color_dic[b2, idx2 % 2] lines[i] = "{:s}{:s}{:s}{:s}{:s}".format( line[:19], Color.colorify(line[19:37], color1), line[37:38], Color.colorify(line[38:56], color2), line[56:], ) self.out.extend(lines) return def dump_mspans(self, mspans): chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") page_address_color = Config.get_gef_setting("theme.heap_page_address") for mspan in mspans: # meta data chunk_size_str = Color.colorify_hex(mspan.chunk_size, chunk_size_color) range_addr_str = Color.colorify_hex(mspan.start_addr, page_address_color) range_addr_str += "-" range_addr_str += Color.colorify_hex(mspan.end_addr, page_address_color) range_size = mspan.end_addr - mspan.start_addr msg = "mspan @ {!s} [{:s} sz={:#x} chunk_size={:s} next={!s}, prev:{!s}]".format( ProcessMap.lookup_address(mspan.address), range_addr_str, range_size, chunk_size_str, ProcessMap.lookup_address(mspan.next), ProcessMap.lookup_address(mspan.prev), ) self.out.append(msg) if self.args.dump: self.dump_mspan_data(mspan) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): self.out = [] self.initialize() if args.mspan is not None: mspans = [self.parse_mspan(args.mspan)] elif args.mheap: mspans = self.parse_mheap(args.mheap) else: mheap = self.get_mheap_() if mheap is None: err("Could not find runtime.mheap_") return mspans = self.parse_mheap(mheap) mspans = [x for x in mspans if x is not None] self.dump_mspans(mspans) self.print_output() return @register_command class TlsfHeapDumpCommand(GenericCommand, BufferingOutput): """TLSF (Two-Level Segregated Fit) v2.4.6 free-list viewer (x64 only).""" _cmdline_ = "tlsf-heap-dump" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--pool", type=AddressUtil.parse_address, help="the address of memory pool.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="display also empty slots.") _syntax_ = parser.format_help() def __init__(self): super().__init__() self.REAL_FLI = 24 self.MAX_SLI = 32 self.size_dic = {} for i in range(1, 8): self.size_dic[0, i * 4] = (0x10 * i, 0x10 * (i + 1)) for i in range(8): self.size_dic[1, i * 4] = (0x80 + 0x10 * i, 0x80 + 0x10 * (i + 1)) for i in range(16): self.size_dic[2, i * 2] = (0x100 + 0x10 * i, 0x100 + 0x10 * (i + 1)) for fl in range(3, self.REAL_FLI): base = 0x200 * (2 ** (fl - 3)) step = 0x10 * (2 ** (fl - 3)) for i in range(self.MAX_SLI): self.size_dic[fl, i] = (base + step * i, base + step * (i + 1)) return def get_pool(self): try: mp = AddressUtil.parse_address("&mp") except gdb.error: return None if not is_valid_addr(mp): return None pool = read_int_from_memory(mp) if not is_valid_addr(pool): return None if read_int32_from_memory(pool) == 0x2A59FA59: # TLSF_SIGNATURE return pool return None def parse_pool(self, pool): """ typedef struct TLSF_struct { u32_t tlsf_signature; #if TLSF_USE_LOCKS pthread_mutex_t lock; #endif #if TLSF_STATISTIC size_t used_size; size_t max_size; #endif area_info_t *area_head; u32_t fl_bitmap; u32_t sl_bitmap[REAL_FLI]; bhdr_t *matrix[REAL_FLI][MAX_SLI]; } tlsf_t; """ sig = read_int32_from_memory(pool) if sig != 0x2A59FA59: return None area_head = None if area_head is None: # !TLSF_USE_LOCKS && !TLSF_STATISTIC x = read_int_from_memory(pool + current_arch.ptrsize) if is_valid_addr(x): area_head = x offset_area_head = current_arch.ptrsize if area_head is None: # !TLSF_USE_LOCKS && TLSF_STATISTIC x = read_int_from_memory(pool + current_arch.ptrsize * 3) if is_valid_addr(x): area_head = x offset_area_head = current_arch.ptrsize * 3 if area_head is None: # TLSF_USE_LOCKS && !TLSF_STATISTIC x = read_int_from_memory(pool + current_arch.ptrsize * 6) if is_valid_addr(x): area_head = x offset_area_head = current_arch.ptrsize * 6 if area_head is None: # TLSF_USE_LOCKS && TLSF_STATISTIC x = read_int_from_memory(pool + current_arch.ptrsize * 8) if is_valid_addr(x): area_head = x offset_area_head = current_arch.ptrsize * 8 if area_head is None: return None offset_fl_bitmap = offset_area_head + current_arch.ptrsize fl_bitmap = read_int32_from_memory(pool + offset_fl_bitmap) offset_sl_bitmap = offset_fl_bitmap + 4 sl_bitmap = read_memory(pool + offset_sl_bitmap, 4 * self.REAL_FLI) sl_bitmap = slice_unpack(sl_bitmap, 4) offset_matrix = align_to_ptrsize(offset_sl_bitmap + 4 * self.REAL_FLI) matrix = read_memory(pool + offset_matrix, current_arch.ptrsize * self.REAL_FLI * self.MAX_SLI) matrix = slice_unpack(matrix, current_arch.ptrsize) matrix = slicer(matrix, self.MAX_SLI) matrix_addr = [pool + offset_matrix + current_arch.ptrsize * i for i in range(self.REAL_FLI * self.MAX_SLI)] matrix_addr = slicer(matrix_addr, self.MAX_SLI) Pool = collections.namedtuple("Pool", ["addr", "sig", "area_head", "fl_bitmap", "sl_bitmap", "matrix", "matrix_addr"]) return Pool(pool, sig, area_head, fl_bitmap, sl_bitmap, matrix, matrix_addr) def dump_pool(self, pool): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") self.out.append("pool: {:#x}".format(pool.addr)) self.out.append("pool->area_head: {:#x}".format(pool.area_head)) for i, j in itertools.product(range(self.REAL_FLI), range(self.MAX_SLI)): if self.args.verbose or pool.matrix[i][j]: matrix_addr = pool.matrix_addr[i][j] min_size, max_size = self.size_dic.get((i, j), (0, 0)) if min_size == max_size == 0: title = "pool->matrix[{:2d}][{:2d}] @{:#x} (chunk_size=???-???)".format(i, j, matrix_addr) elif min_size + 0x10 == max_size: title = "pool->matrix[{:2d}][{:2d}] @{:#x} (chunk_size={:#x})".format(i, j, matrix_addr, min_size) else: title = "pool->matrix[{:2d}][{:2d}] @{:#x} (chunk_size={:#x}-{:#x})".format(i, j, matrix_addr, min_size, max_size) self.out.append(titlify(title)) current = pool.matrix[i][j] seen = [] while True: if not is_valid_addr(current): if current == 0: msg = " -> {:s}".format(Color.colorify_hex(current, freed_address_color)) else: msg = " -> {:s} (corrupted)".format(Color.colorify_hex(current, corrupted_msg_color)) self.out.append(msg) break if current in seen: msg = " -> {:s} (loop detected)".format(Color.colorify_hex(current, corrupted_msg_color)) self.out.append(msg) break seen.append(current) prev_hdr = read_int_from_memory(current + current_arch.ptrsize * 0) size = read_int_from_memory(current + current_arch.ptrsize * 1) prev_ = read_int_from_memory(current + current_arch.ptrsize * 2) next_ = read_int_from_memory(current + current_arch.ptrsize * 3) msg = " -> {:s} (prev_hdr={:#x}, size={:s}, prev={!s}, next={!s})".format( Color.colorify_hex(current, freed_address_color), prev_hdr, Color.colorify_hex(size & ~0xf, chunk_size_color), ProcessMap.lookup_address(prev_), ProcessMap.lookup_address(next_), ) self.out.append(msg) current = next_ return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): if args.pool: pool_addr = args.pool else: pool_addr = self.get_pool() if pool_addr is None: err("Could not find pool") return pool = self.parse_pool(pool_addr) if pool is None: err("Failed to parse pool") return self.out = [] self.dump_pool(pool) self.print_output() return @register_command class HoardHeapDumpCommand(GenericCommand, BufferingOutput): """Hoard v3.2 (2025/12/31) heap free-list viewer (x64 only).""" _cmdline_ = "hoard-heap-dump" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-b", "--superblock", type=AddressUtil.parse_address, action="append", help="the address of superblock.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def get_super_blocks(self): super_blocks = [] for m in ProcessMap.get_process_maps(): if m.path.startswith(("/", "[")): continue if m.permission != Permission.READ | Permission.WRITE: continue for p in range(m.page_start, m.page_end, get_pagesize()): if not is_valid_addr(p): continue v = read_int_from_memory(p) # vtable check if not is_valid_addr(v): continue # magic check m = read_int_from_memory(p + current_arch.ptrsize) if p ^ m != 0xcafe_d00d: continue super_blocks.append(p) return super_blocks @Cache.cache_until_next def get_all_freelist_head_candidate_from_tls(self): orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() threads = gdb.selected_inferior().threads() if not threads: return direction = TlsCommand.get_direction() head_candidates = [] for thread in threads: try: thread.switch() except gdb.error: continue tls = current_arch.get_tls() for i in range(1, 0x20): head_candidate_addr = tls + current_arch.ptrsize * i * direction head_candidate = read_int_from_memory(head_candidate_addr) if not is_single_link_list(head_candidate): continue head_candidates.append(head_candidate) orig_thread.switch() # revert thread orig_frame.select() return head_candidates def get_freelist_start(self, sb): # pattern1: HoardSuperblockHeaderHelper->_freeList freeList = read_int_from_memory(sb + current_arch.ptrsize * 11) if freeList: return freeList # pattern2: thread variable holds it objectSize = read_int_from_memory(sb + current_arch.ptrsize * 2) totalObjects = read_int32_from_memory(sb + current_arch.ptrsize * 3 + 4) sizeof_super_block_header = 0x70 sb_start = sb + sizeof_super_block_header sb_end = sb_start + objectSize * totalObjects heads = self.get_all_freelist_head_candidate_from_tls() candidates = [h for h in heads if sb_start <= h < sb_end] return candidates def dump_super_block(self, sb): """ struct Hoard::HoardSuperblockHeaderHelper<...> { /* offset | size */ /* 0x0000 | 0x0008 */ int (**)(void) _vptr.HoardSuperblockHeaderHelper; /* 0x0008 | 0x0008 */ const size_t _magicNumber; /* 0x0010 | 0x0008 */ const size_t _objectSize; /* 0x0018 | 0x0001 */ const bool _objectSizeIsPowerOfTwo; /* 0x001c | 0x0004 */ const unsigned int _totalObjects; /* 0x0020 | 0x0001 */ class HL::SpinLockType _theLock; /* 0x0028 | 0x0008 */ class Hoard::SmallHeap * _owner; /* 0x0030 | 0x0008 */ Hoard::HoardSuperblockHeaderHelper<...>::BlockType * _prev; /* 0x0038 | 0x0008 */ Hoard::HoardSuperblockHeaderHelper<...>::BlockType * _next; /* 0x0040 | 0x0004 */ unsigned int _reapableObjects; /* 0x0044 | 0x0004 */ unsigned int _objectsFree; /* 0x0048 | 0x0008 */ const char * _start; /* 0x0050 | 0x0008 */ char * _position; /* 0x0058 | 0x0008 */ class FreeSLList _freeList; // or TLS } // total: 0x60 bytes (+ 0x10 bytes padding) struct FreeSLList::Entry { /* offset | size */ /* 0x0000 | 0x0008 */ class FreeSLList::Entry * next; } // total: 0x8 bytes """ freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") sz = read_int_from_memory(sb + current_arch.ptrsize * 2) self.out.append(titlify("superblock @{:#x} (chunk_size={:#x})".format(sb, sz))) reap_count = read_int32_from_memory(sb + current_arch.ptrsize * 8) free_count = read_int32_from_memory(sb + current_arch.ptrsize * 8 + 4) if reap_count == free_count == 0: self.out.append("Uninitialized") return self.out.append("Before allocating from freelist, you must use up all unused blocks") self.out.append("There are {:s} unused blocks left".format(Color.colorify_hex(reap_count, "bold"))) for current in self.get_freelist_start(sb): self.out.append("freelist @{:#x}:".format(current)) seen = [] while True: if current in seen: self.out.append(Color.colorify(" -> {:#x} (loop) ".format(current), corrupted_msg_color)) break seen.append(current) if current and not is_valid_addr(current): self.out.append(Color.colorify(" -> {:#x} (corrupted) ".format(current), corrupted_msg_color)) break self.out.append(" -> {:s}".format(Color.colorify_hex(current, freed_address_color))) if current == 0: break current = read_int_from_memory(current) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): if args.superblock: super_blocks = args.superblock else: super_blocks = self.get_super_blocks() if super_blocks is None: err("Could not find superblock") return self.out = [] for super_block in super_blocks: self.dump_super_block(super_block) self.print_output() return @register_command class MimallocHeapDumpCommand(GenericCommand, BufferingOutput): """mimalloc heap free-list viewer (x64 only).""" _cmdline_ = "mimalloc-heap-dump" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-m", "--mi-heap-main", type=AddressUtil.parse_address, help="the address of _mi_heap_main (v2.x) / heap_main (v3.x).") group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--v21x", action="store_true", help="for mimalloc v2.1.x.") group.add_argument("--v22x", action="store_true", help="for mimalloc v2.2.x.") group.add_argument("--v30x", action="store_true", help="for mimalloc v3.0.x.") parser.add_argument("-d", "--use-decode", action="store_true", help="use pointer decoding (for debug build).") parser.add_argument("-D", "--dump-chunk", action="store_true", help="dump each chunks.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = [ "In mimalloc, the member offsets of important structures vary depending on the version.", "You should be able to check the version with a command like `strings libmimalloc.so | grep git`.", "If you cannot determine it, please choose an option that can successfully decode it.", "", "For _mi_heap_main (v2.x) or heap_main (v3.x), GEF tries to resolve the address from symbol.", "If symbols are not available, GEF scans the TLS area for automatic detection.", "If, for some reason, detection fails, please specify the address manually.", ] _note_ = "\n".join(_note_) def initialize(self): if self.args.v21x: """ struct mi_heap_s { // v2.1.9 /* offset | size */ /* 0x0000 | 0x0008 */ mi_tld_t * tld; /* 0x0008 | 0x0008 */ mi_block_t * _Atomic thread_delayed_free; /* 0x0010 | 0x0008 */ mi_threadid_t thread_id; /* 0x0018 | 0x0004 */ mi_arena_id_t arena_id; /* 0x0020 | 0x0008 */ uintptr_t cookie; /* 0x0028 | 0x0010 */ uintptr_t [2] keys; /* 0x0038 | 0x0088 */ mi_random_ctx_t random; /* 0x00c0 | 0x0008 */ size_t page_count; /* 0x00c8 | 0x0008 */ size_t page_retired_min; /* 0x00d0 | 0x0008 */ size_t page_retired_max; /* 0x00d8 | 0x0008 */ mi_heap_t * next; /* 0x00e0 | 0x0001 */ _Bool no_reclaim; /* 0x00e1 | 0x0001 */ uint8_t tag; /* 0x00e8 | 0x0410 */ mi_page_t *[130] pages_free_direct; /* 0x04f8 | 0x0708 */ mi_page_queue_t [75] pages; } // total: 0xc00 bytes """ self.offset_next = 0xd8 self.offset_pages_free_direct = 0xe8 self.MI_PAGES_DIRECT = 130 """ struct mi_page_s { /* offset | size */ /* 0x0000 | 0x0004 */ uint32_t slice_count; /* 0x0004 | 0x0004 */ uint32_t slice_offset; /* 0x0008 | 0x0001 */ uint8_t is_committed : 1; /* 0x0008 | 0x0001 */ uint8_t is_zero_init : 1; /* 0x0008 | 0x0001 */ uint8_t is_huge : 1; /* 0x000a | 0x0002 */ uint16_t capacity; /* 0x000c | 0x0002 */ uint16_t reserved; /* 0x000e | 0x0001 */ mi_page_flags_t flags; /* 0x000f | 0x0001 */ uint8_t free_is_zero : 1; /* 0x000f | 0x0001 */ uint8_t retire_expire : 7; /* 0x0010 | 0x0008 */ mi_block_t * free; /* 0x0018 | 0x0008 */ mi_block_t * local_free; /* 0x0020 | 0x0002 */ uint16_t used; /* 0x0022 | 0x0001 */ uint8_t block_size_shift; /* 0x0023 | 0x0001 */ uint8_t heap_tag; /* 0x0028 | 0x0008 */ size_t block_size; /* 0x0030 | 0x0008 */ uint8_t * page_start; /* 0x0038 | 0x0010 */ uintptr_t [2] keys; /* 0x0048 | 0x0008 */ _Atomic mi_thread_free_t xthread_free; /* 0x0050 | 0x0008 */ _Atomic uintptr_t xheap; /* 0x0058 | 0x0008 */ struct mi_page_s * next; /* 0x0060 | 0x0008 */ struct mi_page_s * prev; /* 0x0068 | 0x0008 */ void *[1] padding; } // total: 0x70 bytes """ self.offset_capacity = 0x4 * 2 + 2 # with padding self.offset_free = 0x10 self.offset_local_free = 0x18 self.offset_used = 0x20 self.offset_block_size = 0x28 self.offset_keys0 = 0x38 self.offset_keys1 = 0x40 elif self.args.v22x: """ struct mi_heap_s { /* offset | size */ /* 0x0000 | 0x0008 */ mi_tld_t * tld; /* 0x0008 | 0x0008 */ mi_block_t * _Atomic thread_delayed_free; /* 0x0010 | 0x0008 */ mi_threadid_t thread_id; /* 0x0018 | 0x0004 */ mi_arena_id_t arena_id; /* 0x0020 | 0x0008 */ uintptr_t cookie; /* 0x0028 | 0x0010 */ uintptr_t [2] keys; /* 0x0038 | 0x0088 */ mi_random_ctx_t random; /* 0x00c0 | 0x0008 */ size_t page_count; /* 0x00c8 | 0x0008 */ size_t page_retired_min; /* 0x00d0 | 0x0008 */ size_t page_retired_max; /* 0x00d8 | 0x0008 */ long generic_count; /* 0x00e0 | 0x0008 */ long generic_collect_count; /* 0x00e8 | 0x0008 */ mi_heap_t * next; /* 0x00f0 | 0x0001 */ _Bool no_reclaim; /* 0x00f1 | 0x0001 */ uint8_t tag; /* 0x00f8 | 0x0410 */ mi_page_t *[130] pages_free_direct; /* 0x0508 | 0x0708 */ mi_page_queue_t [75] pages; } // total: 0xc10 bytes """ self.offset_next = 0xe8 self.offset_pages_free_direct = 0xf8 self.MI_PAGES_DIRECT = 130 """ struct mi_page_s { /* offset | size */ /* 0x0000 | 0x0004 */ uint32_t slice_count; /* 0x0004 | 0x0004 */ uint32_t slice_offset; /* 0x0008 | 0x0001 */ uint8_t is_committed : 1; /* 0x0008 | 0x0001 */ uint8_t is_zero_init : 1; /* 0x0008 | 0x0001 */ uint8_t is_huge : 1; /* 0x000a | 0x0002 */ uint16_t capacity; /* 0x000c | 0x0002 */ uint16_t reserved; /* 0x000e | 0x0001 */ mi_page_flags_t flags; /* 0x000f | 0x0001 */ uint8_t free_is_zero : 1; /* 0x000f | 0x0001 */ uint8_t retire_expire : 7; /* 0x0010 | 0x0008 */ mi_block_t * free; /* 0x0018 | 0x0008 */ mi_block_t * local_free; /* 0x0020 | 0x0002 */ uint16_t used; /* 0x0022 | 0x0001 */ uint8_t block_size_shift; /* 0x0023 | 0x0001 */ uint8_t heap_tag; /* 0x0028 | 0x0008 */ size_t block_size; /* 0x0030 | 0x0008 */ uint8_t * page_start; /* 0x0038 | 0x0010 */ uintptr_t [2] keys; /* 0x0048 | 0x0008 */ _Atomic mi_thread_free_t xthread_free; /* 0x0050 | 0x0008 */ _Atomic uintptr_t xheap; /* 0x0058 | 0x0008 */ struct mi_page_s * next; /* 0x0060 | 0x0008 */ struct mi_page_s * prev; /* 0x0068 | 0x0008 */ void *[1] padding; } // total: 0x70 bytes """ self.offset_capacity = 0x4 * 2 + 2 # with padding self.offset_free = 0x10 self.offset_local_free = 0x18 self.offset_used = 0x20 self.offset_block_size = 0x28 self.offset_keys0 = 0x38 self.offset_keys1 = 0x40 elif self.args.v30x: """ struct mi_heap_s { /* offset | size */ /* 0x0000 | 0x0008 */ mi_tld_t * tld; /* 0x0008 | 0x0008 */ mi_arena_t * exclusive_arena; /* 0x0010 | 0x0008 */ uintptr_t cookie; /* 0x0018 | 0x0088 */ mi_random_ctx_t random; /* 0x00a0 | 0x0008 */ size_t page_count; /* 0x00a8 | 0x0008 */ size_t page_retired_min; /* 0x00b0 | 0x0008 */ size_t page_retired_max; /* 0x00b8 | 0x0008 */ size_t generic_count; /* 0x00c0 | 0x0008 */ mi_heap_t * next; /* 0x00c8 | 0x0008 */ long full_page_retain; /* 0x00d0 | 0x0001 */ _Bool allow_page_reclaim; /* 0x00d1 | 0x0001 */ _Bool allow_page_abandon; /* 0x00d2 | 0x0001 */ uint8_t tag; /* 0x00d8 | 0x0410 */ mi_page_t *[130] pages_free_direct; /* 0x04e8 | 0x0708 */ mi_page_queue_t [75] pages; /* 0x0bf0 | 0x0018 */ mi_memid_t memid; } // total: 0xc08 bytes """ self.offset_next = 0xc0 self.offset_pages_free_direct = 0xd8 self.MI_PAGES_DIRECT = 130 """ struct mi_page_s { /* offset | size */ /* 0x0000 | 0x0008 */ _Atomic mi_threadid_t xthread_id; /* 0x0008 | 0x0008 */ mi_block_t * free; /* 0x0010 | 0x0002 */ uint16_t used; /* 0x0012 | 0x0002 */ uint16_t capacity; /* 0x0014 | 0x0002 */ uint16_t reserved; /* 0x0016 | 0x0001 */ uint8_t block_size_shift; /* 0x0017 | 0x0001 */ uint8_t retire_expire; /* 0x0018 | 0x0008 */ mi_block_t * local_free; /* 0x0020 | 0x0008 */ _Atomic mi_thread_free_t xthread_free; /* 0x0028 | 0x0008 */ size_t block_size; /* 0x0030 | 0x0008 */ uint8_t * page_start; /* 0x0038 | 0x0001 */ mi_heaptag_t heap_tag; /* 0x0039 | 0x0001 */ _Bool free_is_zero; /* 0x0040 | 0x0010 */ uintptr_t [2] keys; /* 0x0050 | 0x0008 */ mi_heap_t * heap; /* 0x0058 | 0x0008 */ struct mi_page_s * next; /* 0x0060 | 0x0008 */ struct mi_page_s * prev; /* 0x0068 | 0x0008 */ size_t slice_committed; /* 0x0070 | 0x0018 */ mi_memid_t memid; } // total: 0x88 bytes """ self.offset_capacity = 0x12 self.offset_free = 0x08 self.offset_local_free = 0x18 self.offset_used = 0x10 self.offset_block_size = 0x28 self.offset_keys0 = 0x40 self.offset_keys1 = 0x48 return def get_mi_heap_main(self): try: if self.args.v30x: return AddressUtil.parse_address("&heap_main") else: return AddressUtil.parse_address("&_mi_heap_main") except gdb.error: pass tls = current_arch.get_tls() for i in range(1, 10): offset = current_arch.ptrsize * i if not is_valid_addr(tls - offset): continue mi_heap_main = read_int_from_memory(tls - offset) if not is_valid_addr(mi_heap_main): continue tld_main = read_int_from_memory(mi_heap_main) if not is_valid_addr(tld_main): continue thread_id = read_int_from_memory(tld_main) if not is_valid_addr(thread_id): continue return mi_heap_main return None def dump_list(self, head, current, key0, key1, bs): corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") def ptr_decode(addr, key0, key1): addr = (addr - key0) & 0xffff_ffff_ffff_ffff shift = key0 & 0x3f return ror(addr, shift) ^ key1 seen = [] while True: # loop check if current in seen: self.out.append(Color.colorify(" -> {:#x} (loop) ".format(current), corrupted_msg_color)) break seen.append(current) # check wrong value if current != 0 and not is_valid_addr(current): self.out.append(Color.colorify(" -> {:#x} (corrupted) ".format(current), corrupted_msg_color)) break # ok self.out.append(" -> {:s}".format(Color.colorify_hex(current, freed_address_color))) # check the end of the list if current == 0 or current == head: break # dump if self.args.dump_chunk: data = read_memory(current, bs) out = hexdump(data, show_symbol=False, base=current, unit=8) self.out.append(out) # get next current = read_int_from_memory(current) if self.args.use_decode: current = ptr_decode(current, key0, key1) return def dump_page(self, mi_page): bs = read_int_from_memory(mi_page + self.offset_block_size) cap = read_int16_from_memory(mi_page + self.offset_capacity) used = read_int16_from_memory(mi_page + self.offset_used) key0 = read_int64_from_memory(mi_page + self.offset_keys0) key1 = read_int64_from_memory(mi_page + self.offset_keys1) if self.args.use_decode: self.out.append(titlify( "mi_page_t @{:#x} (block_size={:#x}, capacity={:#x}, used={:#x}, key0={:#x}, key1={:#x})".format( mi_page, bs, cap, used, key0, key1, ), )) else: self.out.append(titlify( "mi_page_t @{:#x} (block_size={:#x}, capacity={:#x}, used={:#x})".format( mi_page, bs, cap, used, ), )) # freelist freelist_addr = mi_page + self.offset_free self.out.append("freelist @{:#x}:".format(freelist_addr)) current = read_int_from_memory(freelist_addr) self.dump_list(mi_page, current, key0, key1, bs) # local freelist local_freelist_addr = mi_page + self.offset_local_free self.out.append("local_freelist @{:#x}:".format(local_freelist_addr)) current = read_int_from_memory(local_freelist_addr) self.dump_list(mi_page, current, key0, key1, bs) return def dump_heap(self, mi_heap): seen = [] for i in range(self.MI_PAGES_DIRECT): mi_page_t = read_int_from_memory(mi_heap + self.offset_pages_free_direct + current_arch.ptrsize * i) if not is_valid_addr(mi_page_t): continue x = read_int_from_memory(mi_page_t) if x == 0: """ 0x7ffff7e72448|+0x00e8|+029: 0x00007ffff7e6be80 <_mi_page_empty> -> 0x0000000000000000 0x7ffff7e72450|+0x00f0|+030: 0x00007ffff7e6be80 <_mi_page_empty> -> 0x0000000000000000 0x7ffff7e72458|+0x00f8|+031: 0x0000040203400088 -> 0x01000eb001000700 <- $rcx 0x7ffff7e72460|+0x0100|+032: 0x00007ffff7e6be80 <_mi_page_empty> -> 0x0000000000000000 0x7ffff7e72468|+0x0108|+033: 0x00007ffff7e6be80 <_mi_page_empty> -> 0x0000000000000000 """ continue if mi_page_t in seen: continue self.dump_page(mi_page_t) seen.append(mi_page_t) return None @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): if int(args.v21x) + int(args.v22x) + int(args.v30x) > 1: err("Version error") return self.initialize() if args.mi_heap_main: mi_heap_main = args.mi_heap_main else: mi_heap_main = self.get_mi_heap_main() if mi_heap_main is None: err("Could not find _mi_heap_main") return self.out = [] self.out.append("mi_heap_main: {:#x}".format(mi_heap_main)) mi_heap = mi_heap_main while is_valid_addr(mi_heap): self.dump_heap(mi_heap) mi_heap = read_int_from_memory(mi_heap + self.offset_next) self.print_output() return @register_command class SnmallocHeapDumpCommand(GenericCommand, BufferingOutput): """snmalloc (as of June 2025) heap free-list viewer (x64 only).""" _cmdline_ = "snmalloc-heap-dump" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--all", action="store_true", help="dump all thread_alloc.") parser.add_argument("-l", "--laden", action="store_true", help="dump laden (large or inactive slabs).") parser.add_argument("-r", "--remote", action="store_true", help="dump remote_alloc (WIP).") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="display also empty freelists.") _syntax_ = parser.format_help() _note_ = [ "This command dumps the following four categories:", "- small_fast_free_lists: Free list per small size class (fast path).", "- alloc_classes: Per size class list of active slabs.", "- laden: The set of all slabs and large allocations from this allocator that are full or almost full.", " - The end of the list may not be dumped correctly.", "- remote_alloc: Message queue for allocations being returned to this allocator.", " - Currently status: WIP.", ] _note_ = "\n".join(_note_) def get_current_thread_alloc(self): # fast path try: thread_alloc = AddressUtil.parse_address("&'snmalloc::ThreadAlloc::alloc'") return read_int_from_memory(thread_alloc) except gdb.error: pass # slow path """ gef> tls ------------------------ TLS-0x80 ----------------------- ... 0x7ffff7f3f758|+0x0058|+011: 0x00007fbff7800000 <- here 0x7ffff7f3f760|+0x0060|+012: 0x0000000000000001 0x7ffff7f3f768|+0x0068|+013: 0x0000000000000000 0x7ffff7f3f770|+0x0070|+014: 0x0000000000000000 0x7ffff7f3f778|+0x0078|+015: 0x0000000000000000 -------------------------- TLS -------------------------- 0x7ffff7f3f780|+0x0000|+000: 0x00007ffff7f3f780 0x7ffff7f3f788|+0x0008|+001: 0x00007ffff7f40120 """ tls = current_arch.get_tls() for i in range(1, 16): addr = tls - (current_arch.ptrsize * i) val = read_int_from_memory(addr) if not is_valid_addr(val): continue if val & 0xf_ffff: continue if not is_single_link_list(val): continue return val return None def get_thread_alloc_list(self, all_thread=False): if all_thread: # travarse all threads orig_thread = gdb.selected_thread() orig_frame = gdb.selected_frame() thread_allocs = [] for thread in gdb.selected_inferior().threads(): thread.switch() # change thread thread_alloc = self.get_current_thread_alloc() if thread_alloc: thread_allocs.append((thread.num, thread_alloc)) orig_thread.switch() # revert thread orig_frame.select() return thread_allocs else: thread_alloc = self.get_current_thread_alloc() if thread_alloc: return [(gdb.selected_thread().num, thread_alloc)] return None def initialize(self): if hasattr(self, "initialized") and self.initialized: return True try: self.NUM_SMALL_SIZECLASSES = AddressUtil.parse_address('snmalloc::NUM_SMALL_SIZECLASSES') except gdb.error: self.NUM_SMALL_SIZECLASSES = 43 # hardcoded value try: self.INTERMEDIATE_BITS = AddressUtil.parse_address('snmalloc::INTERMEDIATE_BITS') except gdb.error: self.INTERMEDIATE_BITS = 2 # hardcoded value try: self.MIN_ALLOC_BITS = AddressUtil.parse_address('snmalloc::MIN_ALLOC_STEP_BITS') except gdb.error: self.MIN_ALLOC_BITS = 4 # hardcoded value """ gef> dt 'snmalloc::Alloc' struct snmalloc::Allocator<...> { /* offset | size */ /* 0x0000 | 0x0158 */ struct snmalloc::FastFreeLists snmalloc::FastFreeLists; // = 43 * 8 bytes /* 0x0158 | 0x0018 */ class snmalloc::Pooled<...> snmalloc::Pooled<...>; /* 0x0170 | 0x1a10 */ struct snmalloc::RemoteDeallocCache<...> remote_dealloc_cache; /* 0x1b80 | 0x0408 */ struct snmalloc::Allocator<...>::SlabMetadataCache [43] alloc_classes; // 43 * 0x18 bytes /* 0x1f88 | 0x0010 */ class snmalloc::SeqSet<...> laden; /* 0x1f98 | 0x0028 */ class snmalloc::LocalEntropy entropy; /* 0x2000 | 0x0100 */ std::conditional_t remote_alloc; /* 0x2100 | 0x0240 */ std::conditional_t backend_state; /* 0x2340 | 0x0018 */ class snmalloc::Ticker<...> ticker; } // total: 0x2400 bytes gef> """ try: self.offset_alloc_classes = AddressUtil.parse_address("&((('snmalloc::Alloc'*)0)->alloc_classes)") except gdb.error: self.offset_alloc_classes = 0x1b80 # hardcoded value try: self.offset_laden = AddressUtil.parse_address("&((('snmalloc::Alloc'*)0)->laden)") except gdb.error: self.offset_laden = 0x1f88 # hardcoded value try: self.offset_remote_alloc = AddressUtil.parse_address("&((('snmalloc::Alloc'*)0)->remote_alloc)") except gdb.error: self.offset_remote_alloc = 0x2000 # hardcoded value self.initialized = True return True @Cache.cache_this_session def class_to_size(self, cl): def from_exp_mant(m_e, MANTISSA_BITS, LOW_BITS): if MANTISSA_BITS > 0: m_e = m_e + 1 MANTISSA_MASK = (1 << MANTISSA_BITS) - 1 m = m_e & MANTISSA_MASK e = m_e >> MANTISSA_BITS b = 0 if e == 0 else 1 shifted_e = e - b extended_m = (m + (b << MANTISSA_BITS)) return extended_m << (shifted_e + LOW_BITS) else: return 1 << (m_e + LOW_BITS) return from_exp_mant(cl, self.INTERMEDIATE_BITS, self.MIN_ALLOC_BITS) def parse_single_link_list(self, head): """Return the single linked list (including the head) and error message.""" corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") # travase next cur = head seen = [] while True: if cur == 0: seen.append(cur) break if not is_valid_addr(cur): seen.append(cur) return seen, Color.colorify("(corrupted)", corrupted_msg_color) if cur in seen: seen.append(cur) return seen, Color.colorify("(loop detected)", corrupted_msg_color) seen.append(cur) cur = read_int_from_memory(cur) return seen, None def parse_double_link_list(self, head): """Return the double linked list (excluding the head) and error message.""" corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") # travarse next cur = head seen = [] while True: if not is_valid_addr(cur): seen.append(cur) return seen, Color.colorify("(corrupted)", corrupted_msg_color) if cur in seen: break seen.append(cur) cur = read_int_from_memory(cur) if cur != seen[0]: return seen, Color.colorify("(loop detected)", corrupted_msg_color) # check prev for i, x in enumerate(seen): p = read_int_from_memory(x + current_arch.ptrsize) if p != seen[i - 1]: return seen, Color.colorify("(corrupted)", corrupted_msg_color) if head in seen: seen = [x for x in seen if x != head] return seen, None def dump_small_fast(self, thread_alloc): """ gef> dt snmalloc::FastFreeLists struct snmalloc::FastFreeLists { /* offset | size */ /* 0x0000 | 0x0158 */ class snmalloc::freelist::Iter<...> [43] small_fast_free_lists; // -> freed chunk } // total: 0x158 bytes gef> """ self.out.append(titlify("FastFreeLists.small_fast_free_lists[{:d}] @ {:#x}".format( self.NUM_SMALL_SIZECLASSES, thread_alloc, ))) freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") # travarse small_fast_free_lists[0-42] printed_flag = False for i in range(self.NUM_SMALL_SIZECLASSES): free_list_i = thread_alloc + current_arch.ptrsize * i head = read_int_from_memory(free_list_i) free_list, error = self.parse_single_link_list(head) # skip if empty if not self.args.verbose: if len(free_list) == 1 and free_list[0] == 0: continue # print self.out.append("small_fast_free_lists[{:d}, size={:s}] @ {!s}:".format( i, Color.colorify_hex(self.class_to_size(i), Config.get_gef_setting("theme.heap_chunk_size")), ProcessMap.lookup_address(free_list_i), )) for i, chunk in enumerate(free_list): chunk_str = Color.colorify_hex(chunk, freed_address_color) # skip if empty if not self.args.verbose: if 4 <= i < len(free_list) - 5: if i == 4: self.out.append(" ...") continue if i < len(free_list) - 1: self.out.append(" -> {:s}".format(chunk_str)) elif error: self.out.append(" -> {:s} {:s}".format(chunk_str, error)) else: self.out.append(" -> {:s} (num: {:#x})".format(chunk_str, len(free_list) - 1)) printed_flag = True if printed_flag is False: self.out.append("Nothing to dump") return def dump_slab_meta(self, slab_meta, cl): """ gef> dt 'snmalloc::FrontendSlabMetadata, \\ snmalloc::NoClientMetaDataProvider>' struct snmalloc::FrontendSlabMetadata<...> { /* offset | size */ /* 0x0000 | 0x0001 */ class snmalloc::FrontendSlabMetadata_Trait snmalloc::FrontendSlabMetadata_Trait; /* 0x0000 | 0x0010 */ class snmalloc::SeqSet<...>::Node node; /* 0x0010 | 0x0018 */ class snmalloc::freelist::Builder<...> free_queue; // -> freed chunk /* 0x0022 | 0x0002 */ uint16_t needed_; /* 0x0024 | 0x0001 */ bool sleeping_; /* 0x0025 | 0x0001 */ bool large_; /* 0x0000 | 0x0001 */ snmalloc::NoClientMetaDataProvider::StorageType client_meta_; } // total: 0x28 bytes gef> gef> dt 'snmalloc::freelist::Builder, \\ snmalloc::capptr::bound<(snmalloc::capptr::dimension::Spatial)0, (snmalloc::capptr::dimension::AddressSpaceControl)0, \\ (snmalloc::capptr::dimension::Wildness)0> >' struct snmalloc::freelist::Builder<...> { /* offset | size */ /* | 0x0008 */ const size_t LENGTH; /* 0x0000 | 0x0008 */ snmalloc::stl::Array head; /* 0x0008 | 0x0008 */ snmalloc::stl::Array end; /* 0x0010 | 0x0001 */ snmalloc::stl::Array length; } // total: 0x18 bytes gef> """ freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") offset_free_queue = 0x10 offset_needed_ = 0x22 offset_sleeping_ = 0x24 offset_large_ = 0x25 free_queue = slab_meta + offset_free_queue head = read_int_from_memory(free_queue) free_list, error = self.parse_single_link_list(head) # skip if empty if not self.args.verbose: if len(free_list) == 1 and free_list[0] == 0: return False if cl is None: cl_msg = "???" else: cl_msg = Color.colorify_hex(self.class_to_size(cl), Config.get_gef_setting("theme.heap_chunk_size")) is_laden = cl is None needed_ = read_int16_from_memory(slab_meta + offset_needed_) sleeping_ = read_int8_from_memory(slab_meta + offset_sleeping_) large_ = read_int8_from_memory(slab_meta + offset_large_) # print self.out.append("free_queue[size={:s}, needed_={:#x}, sleeping_={:#x}, large_={:#x}] @ {!s}:".format( cl_msg, needed_, sleeping_, large_, ProcessMap.lookup_address(free_queue), )) for i, chunk in enumerate(free_list): if is_laden: chunk_str = hex(chunk) else: chunk_str = Color.colorify_hex(chunk, freed_address_color) # skip if empty if not self.args.verbose: if 4 <= i < len(free_list) - 5: if i == 4: self.out.append(" ...") continue if i < len(free_list) - 1: self.out.append(" -> {:s}".format(chunk_str)) elif error: if is_laden: self.out.append(" -> {:s} {:s} (but expected)".format(chunk_str, error)) else: self.out.append(" -> {:s} {:s}".format(chunk_str, error)) else: self.out.append(" -> {:s} (num: {:#x})".format(chunk_str, len(free_list) - 1)) return True def dump_alloc_classes(self, thread_alloc): """ gef> dt 'struct snmalloc::Allocator >\\ ::SlabMetadataCache' struct snmalloc::Allocator<...>::SlabMetadataCache { /* offset | size */ /* 0x0000 | 0x0010 */ class snmalloc::SeqSet<...> available; // -> struct snmalloc::FrontendSlabMetadata<...> /* 0x0010 | 0x0002 */ uint16_t unused; /* 0x0012 | 0x0002 */ uint16_t length; } // total: 0x18 bytes """ self.out.append(titlify("alloc_classes (SlabMetadataCache[{:d}]) @ {:#x}".format( self.NUM_SMALL_SIZECLASSES, thread_alloc + self.offset_alloc_classes, ))) offset_length = 0x12 sizeof_slab_meta = 0x18 # travarse SlabMetadataCache[0-42] printed_flag = False for i in range(self.NUM_SMALL_SIZECLASSES): entry = thread_alloc + self.offset_alloc_classes + (sizeof_slab_meta * i) slab_meta_list, error = self.parse_double_link_list(entry) length = read_int16_from_memory(entry + offset_length) if not self.args.verbose: if slab_meta_list == [] and error is None: if length == 0: continue # unused self.out.append("SlabMetadataCache[{:d}, size={:s}] @ {!s}: {:#x} slab(s)".format( i, Color.colorify_hex(self.class_to_size(i), Config.get_gef_setting("theme.heap_chunk_size")), ProcessMap.lookup_address(entry), length, )) # travarse SlabMetadataCache[i].available for slab_meta in slab_meta_list: self.dump_slab_meta(slab_meta, cl=i) printed_flag = True if printed_flag is False: self.out.append("Nothing to dump") return def dump_laden(self, thread_alloc): self.out.append(titlify("laden (SeqSet) @ {:#x}".format( thread_alloc + self.offset_laden, ))) slab_meta_list, error = self.parse_double_link_list(thread_alloc + self.offset_laden) if not self.args.verbose: if slab_meta_list == [] and error is None: self.out.append("Nothing to dump") return printed_flag = False for slab_meta in slab_meta_list: printed_flag |= self.dump_slab_meta(slab_meta, cl=None) if printed_flag is False: self.out.append("Nothing to dump") return def dump_remote_alloc(self, thread_alloc): """ struct snmalloc::RemoteAllocator { /* offset | size */ /* | 0x0018 */ struct snmalloc::FreeListKey key_global; // static /* 0x0000 | 0x0100 */ struct snmalloc::FreeListMPSCQ list; } // total: 0x100 bytes gef> gef> dt 'snmalloc::FreeListMPSCQ' struct snmalloc::FreeListMPSCQ { /* offset | size */ /* 0x0000 | 0x0008 */ snmalloc::freelist::AtomicQueuePtr back; /* 0x0040 | 0x0008 */ snmalloc::freelist::AtomicQueuePtr front; } // total: 0x100 bytes gef> """ self.out.append(titlify("remote_alloc (RemoteAllocator) @ {:#x}".format( thread_alloc + self.offset_remote_alloc, ))) offset_front = 0x40 remote_alloc = thread_alloc + self.offset_remote_alloc head = read_int_from_memory(remote_alloc + offset_front) obj_list, error = self.parse_single_link_list(head) if obj_list == [0] and error is None: self.out.append("Nothing to dump") return for obj in obj_list: # TODO: WIP self.out.append(" -> {:#x}".format(obj)) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): self.out = [] if self.initialize() is False: return # get thread_alloc self.thread_alloc_list = self.get_thread_alloc_list(args.all) if not self.thread_alloc_list: self.quiet_err("Could not find snmalloc::ThreadAlloc::alloc") return # dump for th_num, thread_alloc in self.thread_alloc_list: self.out.append(titlify("ThreadAlloc @ {:#x} (Thread Id:{:d})".format( thread_alloc, th_num, ), color="bold", msg_color="bold")) self.dump_small_fast(thread_alloc) # FastFreeLists self.dump_alloc_classes(thread_alloc) # SlabMetadataCache if self.args.laden: self.dump_laden(thread_alloc) # SeqSet if self.args.remote: self.dump_remote_alloc(thread_alloc) # RemoteAllocator # print self.print_output() return @register_command class CageCommand(GenericCommand, BufferingOutput): """Display v8 (Chromium and d8) ubercage area.""" _cmdline_ = "cage" _category_ = "05-b. Heap - Chromium/V8" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address for filtering.") parser.add_argument("-f", "--force-heuristic", action="store_true", help="use heuristic detection.") parser.add_argument("-v", "--verbose", action="store_true", help="show with zero page.") parser.add_argument("-vv", "--vverbose", action="store_true", help="show with permission NONE.") parser.add_argument("-vvv", "--vvverbose", action="store_true", help="show all maps (=~ vmmap).") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def get_sym_addr(self, sym, force_heuristic=False): if force_heuristic: return None try: x = AddressUtil.parse_address(f"&{sym}") return read_int_from_memory(x) except Exception: pass return None def get_sym_value(self, sym, force_heuristic=False): if force_heuristic: return None try: return AddressUtil.parse_address(f"{sym}") except Exception: pass return None @Cache.cache_until_next def get_isolate(self, force_heuristic=False): sym = "&'v8::internal::g_current_isolate_'.isolate_data_" addr = self.get_sym_value(sym, force_heuristic) if addr: return addr tls = current_arch.get_tls() for i in range(256 * 4): # heuristic; d8: 256, chromium: 1024 try: x = read_int_from_memory(tls - current_arch.ptrsize * i) except gdb.MemoryError: continue if x == 0: continue if x & 7: continue if not is_valid_addr(x): continue try: y = read_int_from_memory(x) except gdb.MemoryError: continue if y == 0: continue if y & 0xffff_ffff: continue if not is_valid_addr(y): continue return x return None @Cache.cache_until_next def get_cage_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.cage_base_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr addr = self.get_isolate(force_heuristic) if addr: return read_int_from_memory(addr) return None @Cache.cache_until_next def get_table_candidates(self): isolate = self.get_isolate(force_heuristic=True) if not isolate: return None """ gef> dt 'v8::internal::IsolateData' struct v8::internal::IsolateData { /* offset | size */ /* | 0x0008 */ const intptr_t kIsolateRootBias; /* 0x0000 | 0x0008 */ const v8::internal::Address cage_base_; /* 0x0008 | 0x0040 */ v8::internal::StackGuard stack_guard_; /* 0x0048 | 0x0001 */ uint8_t is_marking_flag_; /* 0x0049 | 0x0001 */ uint8_t is_minor_marking_flag_; /* 0x004a | 0x0001 */ uint8_t is_shared_space_isolate_flag_; /* 0x004b | 0x0001 */ uint8_t uses_shared_heap_flag_; /* 0x004c | 0x0001 */ v8::base::Flags<...> execution_mode_; /* 0x004d | 0x0001 */ uint8_t stack_is_iterable_; /* 0x004e | 0x0001 */ uint8_t error_message_param_; /* 0x004f | 0x0001 */ uint8_t [1] tables_alignment_padding_; /* 0x0050 | 0x0008 */ int32_t * regexp_static_result_offsets_vector_; /* 0x0058 | 0x0038 */ v8::internal::Address [7] builtin_tier0_entry_table_; /* 0x0090 | 0x0038 */ v8::internal::Address [7] builtin_tier0_table_; /* 0x00c8 | 0x0018 */ v8::internal::LinearAllocationArea new_allocation_info_; /* 0x00e0 | 0x0018 */ v8::internal::LinearAllocationArea old_allocation_info_; /* 0x00f8 | 0x0028 */ v8::internal::Address [5] fast_c_call_alignment_padding_; /* 0x0120 | 0x0010 */ struct {...} ; /* 0x0130 | 0x0008 */ v8::internal::Address fast_api_call_target_; /* 0x0138 | 0x0008 */ size_t long_task_stats_counter_; /* 0x0140 | 0x00f0 */ v8::internal::ThreadLocalTop thread_local_top_; /* 0x0230 | 0x0018 */ v8::internal::HandleScopeData handle_scope_data_; /* 0x0248 | 0x0020 */ void *[4] embedder_data_; /* 0x0268 | 0x0030 */ v8::internal::ExternalPointerTable external_pointer_table_; /* 0x0298 | 0x0008 */ v8::internal::ExternalPointerTable * shared_external_pointer_table_; /* 0x02a0 | 0x0030 */ v8::internal::CppHeapPointerTable cpp_heap_pointer_table_; /* 0x02d0 | 0x0008 */ const v8::internal::Address trusted_cage_base_; /* 0x02d8 | 0x0030 */ v8::internal::TrustedPointerTable trusted_pointer_table_; /* 0x0308 | 0x0008 */ v8::internal::TrustedPointerTable * shared_trusted_pointer_table_; /* 0x0310 | 0x0008 */ v8::internal::TrustedPointerPublishingScope * trusted_pointer_publishing_scope_; /* 0x0318 | 0x0008 */ const v8::internal::Address code_pointer_table_base_address_; /* 0x0320 | 0x0008 */ v8::internal::Address api_callback_thunk_argument_; /* 0x0328 | 0x0008 */ v8::internal::Address js_dispatch_table_base_; /* 0x0330 | 0x0008 */ v8::internal::Address regexp_exec_vector_argument_; /* 0x0338 | 0x0008 */ v8::internal::Tagged<...> continuation_preserved_embedder_data_; /* 0x0340 | 0x23b8 */ v8::internal::RootsTable roots_table_; /* 0x26f8 | 0x3320 */ v8::internal::ExternalReferenceTable external_reference_table_; /* 0x5a18 | 0x49b8 */ v8::internal::Address [2359] builtin_entry_table_; /* 0xa3d0 | 0x49b8 */ v8::internal::Address [2359] builtin_table_; /* 0xed88 | 0x0008 */ v8::internal::wasm::StackMemory * active_stack_; /* 0xed90 | 0x0008 */ v8::internal::Tagged<...> active_suspender_; /* 0xed98 | 0x0004 */ int32_t date_cache_stamp_; /* 0xed9c | 0x0001 */ uint8_t is_date_cache_used_; /* 0xed9d | 0x0003 */ uint8_t [3] raw_arguments_padding_; /* 0xeda0 | 0x0010 */ v8::internal::IsolateData::RawArgument [2] raw_arguments_; /* 0xedb0 | 0x0000 */ uint8_t [0] trailing_padding_; } // total: 0xedb0 bytes gef> [d8] gef> telescope 0x555555857000 256 -M 0xfff -P r__ -n \ -t 0 cage_base_ \ -t 77 external_pointer_table_.base \ -t 84 cpp_heap_pointer_table_.base \ -t 90 trusted_cage_base_ \ -t 91 trusted_pointer_table_ \ -t 99 code_pointer_table_base_address_ \ -t 101 js_dispatch_table_base_ 0x555555857000|+0x0000|+000: cage_base_ : 0x00001adc00000000 -> 0x0000000000081140 0x5555558570d8|+0x00d8|+027: : 0x00001adc001c0000 -> 0x0000000000081140 0x5555558570f0|+0x00f0|+030: : 0x00001adc00080000 -> 0x0000000000081140 0x555555857268|+0x0268|+077: external_pointer_table_.base : 0x00007fff4c000000 -> 0x0000000000000000 0x5555558572a0|+0x02a0|+084: cpp_heap_pointer_table_.base : 0x00007fff2c000000 -> 0x0000000000000000 0x5555558572d8|+0x02d8|+091: trusted_pointer_table_ : 0x00007fff28000000 -> 0x0000000000000000 0x555555857318|+0x0318|+099: code_pointer_table_base_address_: 0x00007fffa4000000 -> 0x0000000000000000 0x555555857328|+0x0328|+101: js_dispatch_table_base_ : 0x00007fff94000000 -> 0x0000000000000000 gef> [chromium] gef> telescope 0x3ea4004a4000 256 -M 0xfff -P r__ -n \ -t 0 cage_base_ \ -t 77 external_pointer_table_.base \ -t 84 cpp_heap_pointer_table_.base \ -t 90 trusted_cage_base_ \ -t 91 trusted_pointer_table_ \ -t 99 code_pointer_table_base_address_ \ -t 100 js_dispatch_table_base_ 0x3ea4004a4000|+0x0000|+000: cage_base_ : 0x0000321700000000 -> 0x0000000000000000 0x3ea4004a4268|+0x0268|+077: external_pointer_table_.base : 0x0000720dbb154000 -> 0x0000000000000000 0x3ea4004a42a0|+0x02a0|+084: cpp_heap_pointer_table_.base : 0x0000720d9b154000 -> 0x0000000000000000 0x3ea4004a42d8|+0x02d8|+091: trusted_pointer_table_ : 0x0000720d97154000 -> 0x0000000000000000 0x3ea4004a4318|+0x0318|+099: code_pointer_table_base_address_: 0x0000720de3154000 -> 0x0000000000000000 0x3ea4004a4320|+0x0320|+100: js_dispatch_table_base_ : 0x0000720d87154000 -> 0x0000000000000000 gef> """ # In d8 it is aligned to 0x100_0000 bytes. candidates_z_ffffff = [] # In chromium it is aligned to 0x1000 bytes. candidates_z_fff = [] for i in range(256): addr = isolate + current_arch.ptrsize * i try: value = read_int_from_memory(addr) except gdb.MemoryError: return [] if value & 0xfff: continue if not is_valid_addr(value): continue pm = ProcessMap.lookup_address(value) try: if pm.section.permission.value != Permission.READ: continue except Exception: continue if value & 0xff_ffff == 0: candidates_z_ffffff.append(value) candidates_z_fff.append(value) if len(candidates_z_ffffff) >= 6: return candidates_z_ffffff return candidates_z_fff @Cache.cache_until_next def get_external_pointer_table_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.external_pointer_table_.base_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_table_candidates() if candidates and len(candidates) > 1: return candidates[1] return None @Cache.cache_until_next def get_external_pointer_table_size(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.external_pointer_table_.kReservationSize" value = self.get_sym_value(sym, force_heuristic) if value: return value return 0x400_0000 # hard-coded @Cache.cache_until_next def get_shared_external_pointer_table_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.shared_external_pointer_table_.base_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr return None @Cache.cache_until_next def get_shared_external_pointer_table_size(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.shared_external_pointer_table_.kReservationSize" value = self.get_sym_value(sym, force_heuristic) if value: return value return 0x400_0000 # hard-coded @Cache.cache_until_next def get_cpp_heap_pointer_table_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.cpp_heap_pointer_table_.base_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_table_candidates() if candidates and len(candidates) > 2: return candidates[2] return None @Cache.cache_until_next def get_cpp_heap_pointer_table_size(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.cpp_heap_pointer_table_.kReservationSize" value = self.get_sym_value(sym, force_heuristic) if value: return value return 0x400_0000 # hard-coded @Cache.cache_until_next def get_trusted_pointer_table_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.trusted_pointer_table_.base_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_table_candidates() if candidates and len(candidates) > 3: return candidates[3] return None @Cache.cache_until_next def get_trusted_pointer_table_size(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.trusted_pointer_table_.kReservationSize" value = self.get_sym_value(sym, force_heuristic) if value: return value return 0x400_0000 # hard-coded @Cache.cache_until_next def get_shared_trusted_pointer_table_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.shared_trusted_pointer_table_.base_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr return None @Cache.cache_until_next def get_shared_trusted_pointer_table_size(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.shared_trusted_pointer_table_.kReservationSize" value = self.get_sym_value(sym, force_heuristic) if value: return value return 0x400_0000 # hard-coded @Cache.cache_until_next def get_code_pointer_table_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.code_pointer_table_base_address_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_table_candidates() if candidates and len(candidates) > 4: return candidates[4] return None @Cache.cache_until_next def get_code_pointer_table_size(self, force_heuristic=False): return 0x400_0000 # hard-coded @Cache.cache_until_next def get_js_dispatch_table_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.js_dispatch_table_base_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_table_candidates() if candidates and len(candidates) > 5: return candidates[5] return None @Cache.cache_until_next def get_js_dispatch_table_size(self, force_heuristic=False): return 0x400_0000 # hard-coded @Cache.cache_until_next def get_code_range_base(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'->heap_.code_range_.base_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr maps = ProcessMap.get_process_maps() if not maps: return None for i, m in enumerate(maps): if m.permission.value != Permission.READ | Permission.WRITE | Permission.EXECUTE: continue if (m.page_start & 0xffff) != 0: continue if m.size > 0x2000_0000: continue if m.size == 0x2000_0000: return m.page_start if m.size < 0x2000_0000: if i == len(maps) - 1: continue if maps[i + 1].permission.value & Permission.EXECUTE == 0: continue return m.page_start return None @Cache.cache_until_next def get_code_range_size(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'->heap_.code_range_.size_" value = self.get_sym_value(sym, force_heuristic) if value: return value return 0x2000_0000 # hard-coded @Cache.cache_until_next def get_cage_rw_space_candidates(self): maps = ProcessMap.get_process_maps() if not maps: return None candidates = [] for m in maps: if m.permission.value != Permission.READ | Permission.WRITE: continue if not self.is_cage(m): continue candidates.append(m) return candidates @Cache.cache_until_next def get_new_space_start(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.new_allocation_info_.start_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_cage_rw_space_candidates() if candidates and len(candidates) > 1: return candidates[1].page_start return None @Cache.cache_until_next def get_new_space_limit(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.new_allocation_info_.limit_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_cage_rw_space_candidates() if candidates and len(candidates) > 1: return candidates[1].page_end return None @Cache.cache_until_next def get_old_space_start(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.old_allocation_info_.start_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_cage_rw_space_candidates() if candidates and len(candidates) > 0: return candidates[0].page_start return None @Cache.cache_until_next def get_old_space_limit(self, force_heuristic=False): sym = "'v8::internal::g_current_isolate_'.isolate_data_.old_allocation_info_.limit_" addr = self.get_sym_addr(sym, force_heuristic) if addr: return addr candidates = self.get_cage_rw_space_candidates() if candidates and len(candidates) > 0: return candidates[0].page_end return None def dump_entry(self, entry, path): # get color line_color = "" if entry.path.startswith("[stack]"): line_color = Config.get_gef_setting("theme.address_stack") elif entry.path.startswith("[heap]"): line_color = Config.get_gef_setting("theme.address_heap") elif entry.permission.value & Permission.EXECUTE: line_color = Config.get_gef_setting("theme.address_code") elif entry.permission.value & Permission.WRITE: line_color = Config.get_gef_setting("theme.address_writable") elif entry.permission.value & Permission.READ: line_color = Config.get_gef_setting("theme.address_readonly") elif entry.permission.value == Permission.NONE: line_color = Config.get_gef_setting("theme.address_valid_but_none") if entry.permission.value == (Permission.READ | Permission.WRITE | Permission.EXECUTE): line_color += " " + Config.get_gef_setting("theme.address_rwx") # make line lines = [] lines.append(Color.colorify( AddressUtil.format_address(entry.page_start, current_arch.ptrsize, long_fmt=True), line_color, )) lines.append(Color.colorify( AddressUtil.format_address(entry.page_end, current_arch.ptrsize, long_fmt=True), line_color, )) lines.append(Color.colorify( AddressUtil.format_address(entry.size, current_arch.ptrsize, long_fmt=True), line_color, )) lines.append(Color.colorify( AddressUtil.format_address(entry.offset, current_arch.ptrsize, long_fmt=True), line_color, )) lines.append(Color.colorify( str(entry.permission), line_color, )) if entry.path and path: path = entry.path + "," + path elif entry.path: path = entry.path lines.append(Color.colorify(path, line_color)) self.out.append(" ".join(lines)) return def is_external_pointer_table(self, entry): area_start = self.get_external_pointer_table_base(self.args.force_heuristic) area_size = self.get_external_pointer_table_size(self.args.force_heuristic) if area_start is not None and area_size is not None: area_end = area_start + area_size return area_start <= entry.page_start < area_end return False def is_shared_external_pointer_table(self, entry): area_start = self.get_shared_external_pointer_table_base(self.args.force_heuristic) area_size = self.get_shared_external_pointer_table_size(self.args.force_heuristic) if area_start is not None and area_size is not None: area_end = area_start + area_size return area_start <= entry.page_start < area_end return False def is_cpp_heap_pointer_table(self, entry): area_start = self.get_cpp_heap_pointer_table_base(self.args.force_heuristic) area_size = self.get_cpp_heap_pointer_table_size(self.args.force_heuristic) if area_start is not None and area_size is not None: area_end = area_start + area_size return area_start <= entry.page_start < area_end return False def is_trusted_pointer_table(self, entry): area_start = self.get_trusted_pointer_table_base(self.args.force_heuristic) area_size = self.get_trusted_pointer_table_size(self.args.force_heuristic) if area_start is not None and area_size is not None: area_end = area_start + area_size if area_start <= entry.page_start < area_end: # keep trusted space address if entry.permission.value == Permission.READ | Permission.WRITE: v = read_int_from_memory(entry.page_start) self.trusted_space_high = v & 0x0000_ffff_0000_0000 return True return False def is_shared_trusted_pointer_table(self, entry): area_start = self.get_shared_trusted_pointer_table_base(self.args.force_heuristic) area_size = self.get_shared_trusted_pointer_table_size(self.args.force_heuristic) if area_start is not None and area_size is not None: area_end = area_start + area_size return area_start <= entry.page_start < area_end return False def is_code_pointer_table(self, entry): area_start = self.get_code_pointer_table_base(self.args.force_heuristic) area_size = self.get_code_pointer_table_size(self.args.force_heuristic) if area_start is not None and area_size is not None: area_end = area_start + area_size return area_start <= entry.page_start < area_end return False def is_js_dispatch_table_space(self, entry): area_start = self.get_js_dispatch_table_base(self.args.force_heuristic) area_size = self.get_js_dispatch_table_size(self.args.force_heuristic) if area_start is not None and area_size is not None: area_end = area_start + area_size return area_start <= entry.page_start < area_end return False def is_code_range(self, entry): area_start = self.get_code_range_base(self.args.force_heuristic) area_size = self.get_code_range_size(self.args.force_heuristic) if area_start is not None and area_size is not None: area_end = area_start + area_size return area_start <= entry.page_start < area_end return False def is_new_space(self, entry): area_start = self.get_new_space_start(self.args.force_heuristic) area_end = self.get_new_space_limit(self.args.force_heuristic) if area_start is None or area_end is None: return False return entry.page_start <= area_start < area_end <= entry.page_end def is_old_space(self, entry): area_start = self.get_old_space_start(self.args.force_heuristic) area_end = self.get_old_space_limit(self.args.force_heuristic) if area_start is None or area_end is None: return False return entry.page_start <= area_start < area_end <= entry.page_end def is_ro_space(self, entry): if self.is_cage(entry): return entry.permission.value == Permission.READ return False def is_array_buffer(self, entry): if self.is_cage(entry): if entry.permission.value == Permission.READ | Permission.WRITE: if (entry.page_start & 0xffff_ffff) == 0: return True return False def is_cage(self, entry): area_start = self.get_cage_base(self.args.force_heuristic) if area_start is None: return False area_end = area_start + (1 * 1024 * 1024 * 1024 * 1024) # 1TB return area_start <= entry.page_start < area_end def is_trusted_space(self, entry): if not hasattr(self, "trusted_space_high"): return False if self.trusted_space_high == (entry.page_start & 0x0000_ffff_0000_0000): return True return False @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): maps = ProcessMap.get_process_maps() if not maps: err("Could not find any maps") return if args.vvverbose: args.vverbose = True if args.vverbose: args.verbose = True self.out = [] # To find the trusted_space from the trusted_pointer_table, traverse it in reverse order for entry in maps[::-1]: # location filtering if args.location is not None: if args.location < entry.page_start or entry.page_end <= args.location: continue if not args.vverbose: # None permission filtering if entry.permission.value == Permission.NONE: continue if not args.verbose: # zero contents filtering try: if entry.size <= 0x10_0000: d = read_memory(entry.page_start, entry.size) if set(d) == {0}: continue except gdb.error: pass # known already entry filtering if entry.path and not entry.path.startswith("[anon:v8"): if args.vvverbose: self.dump_entry(entry, "") continue # display the cage regions if self.is_external_pointer_table(entry): self.dump_entry(entry, "[v8:external_pointer_table]") elif self.is_shared_external_pointer_table(entry): self.dump_entry(entry, "[v8:shared_external_pointer_table]") elif self.is_cpp_heap_pointer_table(entry): self.dump_entry(entry, "[v8:cpp_heap_pointer_table]") elif self.is_trusted_pointer_table(entry): self.dump_entry(entry, "[v8:trusted_pointer_table]") elif self.is_shared_trusted_pointer_table(entry): self.dump_entry(entry, "[v8:shared_trusted_pointer_table]") elif self.is_code_pointer_table(entry): self.dump_entry(entry, "[v8:code_pointer_table]") elif self.is_js_dispatch_table_space(entry): self.dump_entry(entry, "[v8:js_dispatch_table]") elif self.is_code_range(entry): self.dump_entry(entry, "[v8:code_range]") elif self.is_new_space(entry): self.dump_entry(entry, "[v8:new_space]") elif self.is_old_space(entry): self.dump_entry(entry, "[v8:old_space]") elif self.is_ro_space(entry): self.dump_entry(entry, "[v8:ro_space]") elif self.is_array_buffer(entry): self.dump_entry(entry, "[v8:ArrayBuffer]") elif self.is_cage(entry): self.dump_entry(entry, "[v8:cage]") elif self.is_trusted_space(entry): self.dump_entry(entry, "[v8:trusted_space]") else: if args.vvverbose: self.dump_entry(entry, "") # Order the results in ascending order self.out = self.out[::-1] self.print_output(check_terminal_size=True) return @register_command class V8ListMapsCommand(GenericCommand, BufferingOutput): """List v8 (Chromium and d8) built-in maps.""" _cmdline_ = "v8-list-maps" _category_ = "05-b. Heap - Chromium/V8" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use map cache.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = [ "Simplified built-in maps structure:", "", " +-cage-------------+", "chromium: partition-alloc | +-ro_space-----+ |", "+-d8: glibc-heap-+ | | ... | |", "| ... | | | ... | |", "| *map |------------>| map | |", "| *map |------------>| map | |", "| *map1 |------------>| map1 |<----+", "| *map |------------>| map | | |", "| *map |------------>| map | | |", "| ... | | | ... | | + cage_base", "+----------------+ | | ... | | |", " | +-old_space----+ | |", " | | ... | | |", " | | +0x10: ofs |-----+", " | | ... | |", " | +--------------+ |", " | | ... | |", " | +--------------+ |", " | | ... | |", " | +--------------+ |", " | ... |", " +------------------+", "", "For Chromium: this command needs `--no-sandbox` to bypass `seccomp`.", "Also, since it uses V8 commands internally, `_v8_internal_Print_Object` must be resolvable.", ] _note_ = "\n".join(_note_) @staticmethod def redirect_stdout(output_path): syscall_table = get_syscall_table() # dup ret = ExecSyscall(syscall_table.name_table["dup"].nr, [1]).exec_code() stdout_oldfd = ret["reg"][current_arch.return_register] # open p = PatchCommand.PatchInfo(current_arch.sp, output_path.encode() + b"\0") p.patch(silent=True) flags = 0o100 | 0o1 | 0o1000 # O_CREAT | O_WRONLY | O_TRUNC ret = ExecSyscall(syscall_table.name_table["open"].nr, [current_arch.sp, flags, 0o666]).exec_code() file_fd = ret["reg"][current_arch.return_register] PatchCommand.PatchInfo.revert_to_tag(p.tag, silent=True) def u2i(x): x = struct.pack(" 0xa54b000013000004 <- cage_base + 0x10 0x555555859180|+0x0008|+001: 0x000011b70000140d -> 0xa64b000003000004 0x555555859188|+0x0010|+002: 0x000011b700001435 -> 0xa74b000008000004 0x555555859190|+0x0018|+003: 0x000011b70000145d -> 0xa84b000004000004 0x555555859198|+0x0020|+004: 0x000011b700001485 -> 0xa94b000003000004 0x5555558591a0|+0x0028|+005: 0x000011b7000014ad -> 0xaa4b000003000004 0x5555558591a8|+0x0030|+006: 0x000011b7000014d5 -> 0xab4b000003000004 0x5555558591b0|+0x0038|+007: 0x000011b7000014fd -> 0xac4b000002000004 """ # glibc heap contains a array of addresses of v8 heap objects # find the top of the array cage_mask = cage_base & 0xffff_ffff_0000_0000 while sidx >= 0: v = heap_contents[sidx] if v & 1 == 0: break if cage_mask != (v & 0xffff_ffff_0000_0000): break sidx -= 1 sidx += 1 # find the tail of the array while eidx < len(heap_contents): v = heap_contents[eidx] if v & 1 == 0: break if cage_mask != (v & 0xffff_ffff_0000_0000): break eidx += 1 # dump try: # The v8 command simply invokes V8's own dump functions via an inferior function call. # The output is printed to stdout, but since it is not a GDB command, GDB cannot capture that output directly. # Therefore, GEF temporarily redirect stdout to collect the results. stdout_oldfd = None stdout_oldfd = V8ListMapsCommand.redirect_stdout(self.output_path) if stdout_oldfd is None: raise RuntimeError("Failed to redirect: {:s}".format(self.output_path)) tqdm = GefUtil.get_tqdm() for idx in tqdm(range(sidx, eidx), leave=False): v = heap_contents[idx] gdb.execute("v8 {:#x}".format(v), to_string=True) finally: V8ListMapsCommand.revert_stdout(stdout_oldfd) return def list_maps(self, region, cage_base): self.output_path = os.path.join(GEF_TEMP_DIR, "v8-list-maps-{:#x}.txt".format(cage_base)) # not found a cache file if self.args.rescan or not os.path.exists(self.output_path): self.do_list_maps(region, cage_base) else: info("Use cache") res = open(self.output_path).read() for line in res.splitlines(): line = re.sub(r"^(0x[0-9a-f]+)", lambda x:Color.blueify(x.group(1)), line) self.out.append(line) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): cage_base = V8ListMapsCommand.get_cage_base() if not cage_base: err("Could not find cage base") return old_space_region = self.get_old_space() if not old_space_region: err("Could not find old space") return self.out = [] self.list_maps(old_space_region, cage_base) self.print_output(check_terminal_size=True) return @register_command class V8DumpSpaceCommand(GenericCommand, BufferingOutput): """Dump v8 (Chromium and d8) heap objects in each space.""" _cmdline_ = "v8-dump-space" _category_ = "05-b. Heap - Chromium/V8" parser = argparse.ArgumentParser(prog=_cmdline_) spaces = ["old_space", "new_space", "ro_space", "trusted_space", "all", "o", "n", "r", "t", "a"] parser.add_argument("target_space", metavar="TARGET_SPACE", choices=spaces, nargs="?", default="all", help="the space name to dump.") parser.add_argument("-m", "--max-count", type=AddressUtil.parse_address, default=0, help="max count for each space.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="display also object details for string like objects (slow!).") parser.add_argument("-vv", "--vverbose", action="store_true", help="display also object details for all objects (very slow!).") _syntax_ = parser.format_help() _note_ = [ "It only works with the debug build of d8 or Chromium.", "Since many parts are detected heuristically and testing is insufficient,", "it is highly likely that it will not work depending on the version of v8.", ] _note_ = "\n".join(_note_) def get_target_regions(self): res = gdb.execute("cage --no-pager", to_string=True) if "Not found" in res: return [] regions = [] for line in res.splitlines(): line = Color.remove_color(line) if self.args.target_space in ["all", "a"]: if "_space]" not in line: continue elif self.args.target_space in ["old_space", "o"]: if "[v8:old_space]" not in line: continue elif self.args.target_space in ["new_space", "n"]: if "[v8:new_space]" not in line: continue elif self.args.target_space in ["ro_space", "r"]: if "[v8:ro_space]" not in line: continue elif self.args.target_space in ["trusted_space", "t"]: if "[v8:trusted_space]" not in line: continue start, limit, _, _, perm, path = line.split() start = int(start, 16) limit = int(limit, 16) regions.append([start, limit, perm, path]) return regions def is_map(self, value, cage_base): if value & 1 == 0: return False # not a tagged pointer map_addr = cage_base + (value - 1) # untag if not is_valid_addr(map_addr): return False value2 = read_int32_from_memory(map_addr) if value2 & 1 == 0: return False # not a metamap meta_map_addr = cage_base + (value2 - 1) # untag if not is_valid_addr(meta_map_addr): return False return True instance_type_dic = {} def load_instance_type_dict(self): lines = open(self.instace_type_cache_path).read().splitlines() for line in lines: instance_type, type_name = line.split("=") self.instance_type_dic[int(instance_type)] = type_name return def append_instance_type_dict(self, instance_type, type_name): self.instance_type_dic[instance_type] = type_name with open(self.instace_type_cache_path, "a") as f: f.write("{:d}={:s}\n".format(instance_type, type_name)) return def get_instance_name(self, map_addr): # load from cache file if not self.instance_type_dic: if os.path.exists(self.instace_type_cache_path): self.load_instance_type_dict() # fast path: load from cache instance_type = read_int16_from_memory((map_addr & ~1) + 8) if instance_type in self.instance_type_dic: return self.instance_type_dic[instance_type] # slow path try: stdout_oldfd = None stdout_oldfd = V8ListMapsCommand.redirect_stdout(self.output_path) if stdout_oldfd is None: raise RuntimeError("Failed to redirect: {:s}".format(self.output_path)) gdb.execute("v8 {:#x}".format(map_addr | 1), to_string=True) finally: V8ListMapsCommand.revert_stdout(stdout_oldfd) map_content = open(self.output_path).read() if V8Command.is_chromium(): # 0x06c300000475 )> r = re.search(r" r = re.search(r"\(([A-Z_]+?)\)>$", map_content) if r: type_name = r.group(1) self.append_instance_type_dict(instance_type, type_name) return type_name else: r = re.search(r"- type: (.+)", map_content) # for d8 if r: type_name = r.group(1) self.append_instance_type_dict(instance_type, type_name) return type_name return "???" def get_object_size(self, addr, map_addr, cage_base, area_end): """get header size and variable size""" """ https://github.com/v8/v8/blob/main/src/objects/map.h Map layout: TaggedPointer map - Always a pointer to the MetaMap root Int The first int field Byte [instance_size] ... Int The second int field Short [instance_type] ... ... """ # fixed length pattern (map has instance size) instance_size = read_int8_from_memory(map_addr + 4) if instance_size > 0: return instance_size * 4, None # instance_size == 0 instance_name = self.get_instance_name(map_addr) # Object sizes are manually specified by referencing src/objects.h etc. # TODO: inline property <-> external property if instance_name == "ARRAY_LIST_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 12, length * 4 elif instance_name == "BIG_INT_BASE_TYPE": length = read_int32_from_memory(addr + 4) return 8, length * 4 elif instance_name == "BYTECODE_ARRAY_TYPE": length = read_int32_from_memory(addr + 8) length >>= 1 length = align(length, 4) return 0x28, length elif instance_name == "BYTE_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 length = align(length, 4) return 8, length elif instance_name == "CLOSURE_FEEDBACK_CELL_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "CODE_TYPE": return 0x44, 0 elif instance_name == "COVERAGE_INFO_TYPE": length = read_int16_from_memory(addr + 4) return 8, length * 4 * 4 elif instance_name == "DESCRIPTOR_ARRAY_TYPE": length = read_int16_from_memory(addr + 4) return 0x14, length * 4 * 3 elif instance_name == "DOUBLE_STRING_CACHE_TYPE": length = read_int32_from_memory(addr + 4) return 8, length * 12 elif instance_name == "EMBEDDER_DATA_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) return 8, length * 4 elif instance_name == "EPHEMERON_HASH_TABLE_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "FEEDBACK_METADATA_TYPE": length1 = read_int32_from_memory(addr + 4) if length1 != 0: length1 = (length1 - 1) // 6 + 1 # 5-bit encode length2 = read_int32_from_memory(addr + 8) length = align(length1 * 4 + length2 * 2, 4) return 12, length elif instance_name == "FEEDBACK_VECTOR_TYPE": length = read_int32_from_memory(addr + 4) return 0x1c, length * 4 elif instance_name == "FIXED_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "FIXED_DOUBLE_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 8 elif instance_name == "FREE_SPACE_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 length -= 2 # size of the free space including the header return 8, length * 4 elif instance_name == "GLOBAL_DICTIONARY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "HASH_TABLE_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "INSTRUCTION_STREAM_TYPE": # in code_range header = 0x10 length = read_int32_from_memory(addr + 12) length = align(header + length, 0x40) return header, length - header elif instance_name == "INTERNALIZED_ONE_BYTE_STRING_TYPE": length = read_int32_from_memory(addr + 8) length = align(length, 4) return 12, length elif instance_name == "INTERNALIZED_TWO_BYTE_STRING_TYPE": length = read_int32_from_memory(addr + 8) length = align(length * 2, 4) return 12, length elif instance_name == "NAME_DICTIONARY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "NAME_TO_INDEX_HASH_TABLE_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "NATIVE_CONTEXT_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, (length * 4) + (2 * 4) elif instance_name == "NUMBER_DICTIONARY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "OBJECT_BOILERPLATE_DESCRIPTION_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 0x10, length * 4 elif instance_name == "ORDERED_HASH_MAP_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "ORDERED_HASH_SET_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "ORDERED_NAME_DICTIONARY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "PREPARSE_DATA_TYPE": data_length = read_int32_from_memory(addr + 4) children_length = read_int32_from_memory(addr + 8) padding = align(data_length, 4) length = data_length + padding + children_length * 4 return 12, length elif instance_name == "PROPERTY_ARRAY_TYPE": length_or_hash = read_int32_from_memory(addr + 4) length = length_or_hash & 0x3ff length >>= 1 return 8, length * 4 elif instance_name == "PROTECTED_FIXED_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "PROTECTED_WEAK_FIXED_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "REGISTERED_SYMBOL_TABLE_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "REG_EXP_MATCH_INFO_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 0x14, length * 4 elif instance_name == "SCOPE_INFO_TYPE": # The logic is very complicated, so the v8 command is used instead. try: stdout_oldfd = None stdout_oldfd = V8ListMapsCommand.redirect_stdout(self.output_path) if stdout_oldfd is None: raise RuntimeError("Failed to redirect: {:s}".format(self.output_path)) gdb.execute("v8 {:#x}".format(addr | 1), to_string=True) finally: V8ListMapsCommand.revert_stdout(stdout_oldfd) content = open(self.output_path).read() r = re.search(r"- length: (\d+)", content) if r: return 4, int(r.group(1)) * 4 else: return 4, 5 * 4 elif instance_name == "SCRIPT_CONTEXT_TABLE_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 0x10, length * 4 elif instance_name == "SCRIPT_CONTEXT_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "SEQ_ONE_BYTE_STRING_TYPE": length = read_int32_from_memory(addr + 8) length = align(length, 4) return 12, length elif instance_name == "SEQ_TWO_BYTE_STRING_TYPE": length = read_int32_from_memory(addr + 8) length = align(length * 2, 4) return 12, length elif instance_name == "SHARED_SEQ_ONE_BYTE_STRING_TYPE": pass # TODO elif instance_name == "SHARED_SEQ_TWO_BYTE_STRING_TYPE": pass # TODO elif instance_name == "SIMPLE_NAME_DICTIONARY_TYPE": pass # TODO elif instance_name == "SIMPLE_NUMBER_DICTIONARY_TYPE": pass # TODO elif instance_name == "SLOPPY_ARGUMENTS_ELEMENTS_TYPE": pass # TODO elif instance_name == "SMALL_ORDERED_HASH_MAP_TYPE": pass # TODO elif instance_name == "SMALL_ORDERED_HASH_SET_TYPE": pass # TODO elif instance_name == "SMALL_ORDERED_NAME_DICTIONARY_TYPE": pass # TODO elif instance_name == "STRONG_DESCRIPTOR_ARRAY_TYPE": pass # TODO elif instance_name == "SWISS_NAME_DICTIONARY_TYPE": length = read_int32_from_memory(addr + 8) data_table_len = length * 2 * 4 ctrl_table_len = align(length + 0x10, 4) property_details_table_len = align(length, 4) return 0x10, data_table_len + ctrl_table_len + property_details_table_len elif instance_name == "TRANSITION_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "TRUSTED_BYTE_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 length = align(length, 4) return 8, length elif instance_name == "TRUSTED_FIXED_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "TRUSTED_WEAK_FIXED_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 elif instance_name == "TURBOSHAFT_FLOAT64_SET_TYPE_TYPE": pass # TODO elif instance_name == "TURBOSHAFT_WORD32_SET_TYPE_TYPE": pass # TODO elif instance_name == "TURBOSHAFT_WORD64_SET_TYPE_TYPE": pass # TODO elif instance_name == "WASM_DISPATCH_TABLE_TYPE": # ??? return 0x1c, 0 elif instance_name == "WASM_NULL_TYPE": pass # TODO elif instance_name == "WASM_TYPE_INFO_TYPE": pass # TODO elif instance_name == "WEAK_ARRAY_LIST_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 12, length * 4 elif instance_name == "WEAK_FIXED_ARRAY_TYPE": length = read_int32_from_memory(addr + 4) length >>= 1 return 8, length * 4 instance_type = read_int16_from_memory((map_addr & ~1) + 8) self.warn_add_out("Unknown instance_type: {:#x}".format(instance_type)) return 0, 0 def walk_space(self, start, limit, cage_base): v = read_int32_from_memory(start) if v & 1: addr = start else: addr = start + 0x10 try: from tqdm import tqdm except ImportError: tqdm = None if tqdm: pbar = tqdm(total=limit - start, leave=False) count = 0 while addr < limit: # check max_count if self.args.max_count: if count >= self.args.max_count: return # get compressed pointer try: map_raw = read_int32_from_memory(addr) except gdb.MemoryError: self.warn_add_out("Cannot read memory at {:#x}".format(addr)) return # check if magic number if map_raw == 0xbeadbeef: self.info_add_out("End of objects") return # check if it is a map if not self.is_map(map_raw, cage_base): self.warn_add_out("Could not find map") return map_addr = cage_base + map_raw - 1 # untag # get size, type, name header_size, variable_size = self.get_object_size(addr, map_addr, cage_base, limit) instance_type = read_int16_from_memory(map_addr + 8) instance_name = self.get_instance_name(map_addr + 1) # dump if variable_size is not None: size_str = "{:#x}+{:#x},".format(header_size, variable_size) else: size_str = "{:#x},".format(header_size) line = "{:s}: map:{:#x}, sz={:12s} type={:#05x}(={:s})".format( Color.colorify_hex(addr + 1, 'blue'), map_addr + 1, size_str, instance_type, instance_name, ) self.out.append(line) # dump details if (self.args.verbose and "STRING" in instance_name) or self.args.vverbose: try: stdout_oldfd = None stdout_oldfd = V8ListMapsCommand.redirect_stdout(self.output_path) if stdout_oldfd is None: raise RuntimeError("Failed to redirect: {:s}".format(self.output_path)) gdb.execute("v8 {:#x}".format(addr | 1), to_string=True) finally: V8ListMapsCommand.revert_stdout(stdout_oldfd) content = open(self.output_path).read() if not content: self.warn_add_out("No content; Something is wrong") return self.out.extend(content.splitlines()[:20]) # check if last if instance_name == "FREE_SPACE_TYPE": self.info_add_out("End of objects") return # goto next if header_size == 0: return if variable_size is None: total_size = header_size else: total_size = header_size + variable_size addr += total_size if tqdm: pbar.update(total_size) count += 1 return def walk_spaces(self, target_regions, cage_base): tqdm = GefUtil.get_tqdm() for start, limit, perm, path in tqdm(target_regions, leave=False): self.out.append(titlify("{:#x}-{:#x} {:s} {:s}".format(start, limit, perm, path))) self.walk_space(start, limit, cage_base) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): cage_base = V8ListMapsCommand.get_cage_base() if not cage_base: err("Cannot determine cage base") return info("The cage base: {:#x}".format(cage_base)) self.output_path = os.path.join(GEF_TEMP_DIR, "v8-dump-space-{:#x}.txt".format(cage_base)) self.instace_type_cache_path = os.path.join(GEF_TEMP_DIR, "v8-dump-space-instance-type.txt") target_regions = self.get_target_regions() if not target_regions: err("Cannot determine target space address range") return self.out = [] self.walk_spaces(target_regions, cage_base) self.print_output(check_terminal_size=True) return @register_command class V8Command(GenericCommand): """Print v8 tagged object, or load more commands from internet.""" _cmdline_ = "v8" _category_ = "05-b. Heap - Chromium/V8" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, nargs="?", help="target map address.") group.add_argument("-l", "--load-v8-gdbinit", action="store_true", help="load gdbinit for v8 from internet.") group.add_argument("-L", "--list-command", action="store_true", help="show newly added commands from v8 gdbinit.") _syntax_ = parser.format_help() def get_gdbinit(self): gdbinit_filename = os.path.join(GEF_TEMP_DIR, "gdbinit-v8") if not os.path.exists(gdbinit_filename): # https://chromium.googlesource.com/v8/v8/+/refs/heads/main/tools/gdbinit url = "https://chromium.googlesource.com/v8/v8/+/refs/heads/main/tools/gdbinit?format=TEXT" gdbinit_data = http_get(url) import base64 gdbinit_data = base64.b64decode(gdbinit_data) open(gdbinit_filename, "wb").write(gdbinit_data) info("Download gdbinit from internet") else: info("Reuse gdbinit cached previously") return gdbinit_filename @staticmethod @Cache.cache_this_session def is_chromium(): maps = ProcessMap.get_process_maps() for m in maps: if m.path.startswith("[anon:partition_alloc]"): return True return False @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): if args.load_v8_gdbinit: gdbinit_filename = self.get_gdbinit() try: gdb.execute("source {:s}".format(gdbinit_filename)) info("Successfully loaded") except gdb.error: err("Failed to load") return if args.list_command: gdb.execute("v8 -l", to_string=True) gdbinit_filename = self.get_gdbinit() data = open(gdbinit_filename).read() for line in data.splitlines(): line = line.strip() if line.startswith(("alias ", "define ")): comm = line.split()[1] gef_print(titlify(comm)) gdb.execute("help {:s}".format(comm)) elif line.startswith(("super(", "super (")) and '"' in line: comm = line.split('"')[-2] gef_print(titlify(comm)) gdb.execute("help {:s}".format(comm)) return if args.address: try: # Since this command is used so often, it can be implemented without loading from internet. cmd = "call (void) _v8_internal_Print_Object((void*)({:#x}))".format(args.address) gdb.execute(cmd) # When attached to chromium and run, the newline is not generated, # so it is not displayed immediately due to buffering. As a workaround, run putchar('\n') and fflush(0). if V8Command.is_chromium(): cmd = "call (void) putchar(0x0a)" gdb.execute(cmd) cmd = "call (void) fflush(0)" gdb.execute(cmd) except gdb.error: pass return @register_command class PartitionAllocDumpCommand(GenericCommand, BufferingOutput): """PartitionAlloc free-list viewer for chromium stable.""" _cmdline_ = "partition-alloc-dump" _category_ = "05-b. Heap - Chromium/V8" parser = argparse.ArgumentParser(prog=_cmdline_) modes = ["fast_malloc", "array_buffer", "buffer", "fm", "ab", "b"] parser.add_argument("target_buffer_root", choices=modes, help="the target buffer_root. The last three are abbreviated forms.") parser.add_argument("-f", "--force-heuristic", action="store_true", help="use heuristic roots detection.") parser.add_argument("-r", "--root", type=AddressUtil.parse_address, help="the memory address of target {buffer,array_buffer,fast_malloc}_root_.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="display also empty slots.") parser.add_argument("--debug", action="store_true", help="[FOR DEVELOPER] enable debug print.") _syntax_ = parser.format_help() _example_ = [ "{0:s} array_buffer # walk from array_buffer_root_", "{0:s} ab # same above", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Chromium mainline is too fast to develop. So if parse is failed, you need fix this gef.py.", "", "Simplified partition alloc structure:", "", " +-root-----------------+", " | ... | +---->+-extent------------+ +-->+-extent------------+ +-> ...", " | next_super_page_ | | | next |--+ | next |--+", " | next_partition_page_ | | +-------------------+ +-------------------+", " | ... | |", " | first_extent_ |----+", " | direct_map_list_ |--------->+-direct_map_extent-+ +-->+-direct_map_extent-+ +-> ...", " | ... | | bucket | | | bucket | |", " | | | next_extent |--+ | next_extent |--+", " | | +-------------------+ +-------------------+", " | |", " +-bucket[0](0x20)------+", " | head |--------->+-slot_span---------+ +-->+-slot_span---------+ +-> ...", " | slot_size |<---------| bucket | | | bucket | |", " | ... | +-----| freelist_head | | | freelist_head | |", " +-bucket[1](0x20)------+ | | next_slot_span |--+ | next_slot_span |--+", " | head | | +-------------------+ +-------------------+", " | slot_size | |", " | ... | |", " +----------------------+ +---->+-slot--------------+", " | ... | | next |---+", " | | | (freed) | |", " +----------------------+ +-slot--------------+ |", " | | |", " | (used) | |", " +-slot--------------+<--+", " +---| next |", " | | (freed) |", " | +-slot--------------+", " | | |", " | | (used) |", " +-->+-slot--------------+", " | next |---> NULL", " | (freed) |", " +-slot--------------+", " | |", " | |", " +-------------------+", "", "`extent`, `slot_span` and `slot` are in super_page.", "", " [~v144.x] [v145.x~; PA_CONFIG(MOVE_METADATA_OUT_OF_GIGACAGE)=y]", " +-super_page-(2MB)-----+ +-super_page(for meta)-+ +-super_page(for chunk)+", " 4KB | Guard Page | | Guard Page | | Guard Page |", " +----------------------+ +----------------------+ +----------------------+", " 4KB | extent * 1 | | extend * 1 | | Unused |", " | slot_span * 126 | | slot_span * 126 | | |", " | unused * 1 | | unused * 1 | | |", " +----------------------+ +----------------------+ +----------------------+", " 8KB | Guard Page | | Guard Page | | Guard Page |", " +----------------------+ +----------------------+ +----------------------+", " 16KB | Partition Page #1 | | ... | | Partition Page #1 |", " | slot | | | | slot |", " | slot | | | | slot |", " | ... | | | | ... |", " +----------------------+ | | +----------------------+", " | ... | | | | ... |", " +----------------------+ | | +----------------------|", " 16KB | Partition Page #126 | | | | Partition Page #126 |", " | slot | | | | slot |", " | slot | | | | slot |", " | ... | | | | ... |", " +----------------------+ +----------------------+ +----------------------+", " 12KB | Unused | | Unused | | Unused |", " +----------------------+ +----------------------+ +----------------------+", " 4KB | Guard Page | | Guard Page | | Guard Page |", " +----------------------+ +----------------------+ +----------------------+", "", " * super_page_for_meta - metadata_offset_ == super_page_for_chunk", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.modes: # already matched return [] if text == "": # no prefix return [s for s in self.modes if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.modes if s and s.startswith(text.strip())] @Cache.cache_this_session def get_roots_heuristic(self): """Search for fast_malloc_root, array_buffer_root_ and buffer_root_""" # the pointers to each root are in the RW area. # first, we list the RW area. filepath = Path.get_filepath(append_proc_root_prefix=False) maps = ProcessMap.get_process_maps() if is_64bit(): codebase = ProcessMap.get_section_base_address(filepath) mask = 0x0000_ffff_0000_0000 chromium_rw_maps = [p for p in maps if p.permission.value == Permission.READ | Permission.WRITE] chromium_rw_maps = [p for p in chromium_rw_maps if (p.page_start & mask) == (codebase & mask) and p.path != filepath] elif is_32bit(): mask = 0xff00_0000 heapbase = HeapbaseCommand.heap_base() chromium_rw_maps = [p for p in maps if p.permission.value == Permission.READ | Permission.WRITE] chromium_rw_maps = [p for p in chromium_rw_maps if p.page_start < heapbase and p.path != filepath] # n_gram([1,2,3,4,5], 3) -> [[1, 2, 3], [2, 3, 4], [3, 4, 5]] def n_gram(target, n): for idx in range(len(target) - n + 1): yield target[idx:idx + n] # Check the RW area roots = [] for maps in chromium_rw_maps: # explode to each qword (if 64 bit arch) or dword (if 32 bit arch) data_list = slice_unpack(read_memory(maps.page_start, maps.size), current_arch.ptrsize) addr_list = list(range(maps.page_start, maps.page_end, current_arch.ptrsize)) """ https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/ \ platform/wtf/allocator/partitions.cc partition_alloc::PartitionRoot* Partitions::fast_malloc_root_ = nullptr; partition_alloc::PartitionRoot* Partitions::array_buffer_root_ = nullptr; partition_alloc::PartitionRoot* Partitions::buffer_root_ = nullptr; 0x5a849775d5a8 : 0x0000000000000000 0x5a849775d5b0 : 0x00005a8497761040 0x5a849775d5b8 : 0x00005a849775d600 0x5a849775d5c0 : 0x0000000100000101 ... 0x5a8497759bc0 : 0x0000000000000000 ... 0x5a849775d600 : 0x0000000000010000 ... 0x5a8497761040 : 0x0000000000000000 """ # check consecutive quadruples for addr, data in zip(n_gram(addr_list, 4), n_gram(data_list, 4)): # root pointer address and root address are close (see above example) if data[0] != 0 and (addr[0] & mask) != (data[0] & mask): # fast_malloc_root_ may be zero. but if non-zero, it holds address close to itself continue if (addr[1] & mask) != (data[1] & mask): # array_buffer_root_ is must be non-zero. it holds address close to itself continue if (addr[2] & mask) != (data[2] & mask): # buffer_root_ is must be non-zero. it holds address close to itself continue # they should be aligned if data[0] & 0x7: continue if data[1] & 0x7: continue if data[2] & 0x7: continue # initialized must be bool 0x01 if (data[3] & 0xff) != 0x01: continue # check root size buffer_root_size = data[1] - data[2] if buffer_root_size < 0x300: # 0x300 is heuristic value continue if buffer_root_size > 0x4000: # 0x4000 is heuristic value continue # check root struct. """ The first 64 bytes of root are mostly 0 due to padding considering the cache line. This is same both at 64-bit and 32bit arch. [WTF::Partitions::InitializeOnce()::buffer_allocator] 0x5a849775d600: 0x0000000000010000 0x0000000000000004 0x5a849775d610: 0x0000000000000000 0x00000000ffffffff 0x5a849775d620: 0x0000000400000001 0xfffffe7fec785000 0x5a849775d630: 0x0000000000000000 0x0000000000000000 [WTF::Partitions::InitializeArrayBufferPartition()::array_buffer_allocator] 0x5a8497761040: 0x0000000000000000 0x0000000000000000 0x5a8497761050: 0x0000000000000001 0x00000000ffffffff 0x5a8497761060: 0x0000000000000000 0xffffcefbec787000 0x5a8497761070: 0x0000000000000000 0x0000000000000000 """ if b"\0" * 8 not in read_memory(data[1], 64): continue if b"\0" * 8 not in read_memory(data[2], 64): continue if self.args.debug: info(f"buffer_root_size: {buffer_root_size:#x}") gdb.execute(f"ml x/8xg {data[1]:#x}; x/8xg {data[2]:#x}") # add candidate Root = collections.namedtuple("Root", ["name", "address"]) root_candidate = [ Root("fast_malloc_root_", addr[0]), Root("array_buffer_root_", addr[1]), Root("buffer_root_", addr[2]), ] roots.append(root_candidate) # debug print if len(roots) == 0: err("Could not find any roots, try check code") return [] if len(roots) == 1: for r in roots[0]: info("Found: {:s}: {:#x}".format(r.name, r.address)) return roots[0] err("Candidates for root are found in multiple locates, try check code") for root in roots: for r in root: gef_print(" candidate: {:20s} {:#x}".format(r.name, r.address)) gef_print() return [] def get_roots(self, force_heuristic): def get_root(root_string): # newer version try: root_addr = AddressUtil.parse_address("&'blink::Partitions::{:s}'".format(root_string)) Root = collections.namedtuple("Root", ["name", "address"]) return [Root(root_string, root_addr)] except gdb.error: pass # older version try: root_addr = AddressUtil.parse_address("&'WTF::Partitions::{:s}'".format(root_string)) Root = collections.namedtuple("Root", ["name", "address"]) return [Root(root_string, root_addr)] except gdb.error: pass return [] roots = [] # user specific if self.args.root: Root = collections.namedtuple("Root", ["name", "address"]) roots += [Root(self.args.target_buffer_root + "_root_", self.args.root)] return roots # try from symbols if not force_heuristic: for root_string in ["fast_malloc_root_", "array_buffer_root_", "buffer_root_"]: roots += get_root(root_string) # maybe no symbols, try heuristic if len(roots) == 0: info("Use heuristic search") roots = self.get_roots_heuristic() # retry checking if len(roots) == 0: info("Could not find the symbol") return roots @Cache.cache_until_next def get_sentinel_slot_spans(self): """sentinel_slot_span is default slot_span, so search for it.""" sentinel = [] # new version try: ns = "partition_alloc::internal::SlotSpanMetadata<(partition_alloc::internal::MetadataKind)1>" t = AddressUtil.parse_address("&'{:s}::sentinel_slot_span_'".format(ns)) sentinel.append(t) return sentinel except gdb.error: pass # old version try: t = AddressUtil.parse_address("&'base::internal::SlotSpanMetadata::sentinel_slot_span_'") sentinel.append(t) except gdb.error: pass try: f = AddressUtil.parse_address("&'base::internal::SlotSpanMetadata::sentinel_slot_span_'") sentinel.append(f) except gdb.error: pass return sentinel def read_root(self, addr, name): ptrsize = current_arch.ptrsize root = {} root["name"] = name root["addr"] = current = read_int_from_memory(addr) """ https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/ \ src/partition_alloc/partition_root.h struct base::PartitionRoot { struct alignas(internal::kPartitionCachelineSize) Settings { BucketDistribution bucket_distribution = BucketDistribution::kNeutral; // uint8_t size_t thread_cache_index = internal::kInvalidThreadCacheIndex; bool with_thread_cache = false; bool use_cookie = false; bool brp_enabled_ = false; size_t in_slot_metadata_size = 0; internal::pool_handle pool_handle = internal::pool_handle::kNullPoolHandle; internal::PoolOffsetLookup offset_lookup; internal::ReservationOffsetTable reservation_offset_table; bool eventually_zero_freed_memory = false; internal::SchedulerLoopQuarantineConfig scheduler_loop_quarantine; bool memory_tagging_enabled_ = false; bool use_random_memory_tagging_enabled_ = false; TagViolationReportingMode memory_tagging_reporting_mode_ = TagViolationReportingMode::kUndefined; ThreadIsolationOption thread_isolation; uint32_t extras_size = 0; std::ptrdiff_t metadata_offset_ = 0; bool enable_free_with_size = false; bool enable_strict_free_size_check = true; } settings_; // 0x40 bytes or 0x80 bytes or 0xc0 bytes internal::Lock lock_; // 8 bytes Bucket buckets_[BucketIndexLookup::kNumBuckets] = {}; Bucket sentinel_bucket_{}; bool initialized_ = false; std::atomic total_size_of_committed_pages_{0}; std::atomic max_size_of_committed_pages_{0}; std::atomic total_size_of_super_pages_{0}; std::atomic total_size_of_direct_mapped_pages_{0}; std::atomic total_size_of_allocated_bytes_{0}; std::atomic max_size_of_allocated_bytes_{0}; std::atomic syscall_count_; std::atomic syscall_total_time_ns_; std::atomic total_size_of_brp_quarantined_bytes{0}; std::atomic total_count_of_brp_quarantined_slots_{0}; std::atomic cumulative_size_of_brp_quarantined_bytes_{0}; std::atomic cumulative_count_of_brp_quarantined_slots_{0}; size_t empty_slot_spans_dirty_bytes_ PA_GUARDED_BY(internal::PartitionRootLock(this)) = 0; int max_empty_slot_spans_dirty_bytes_shift_ = 3; uintptr_t next_super_page_ = 0; uintptr_t next_partition_page_ = 0; uintptr_t next_partition_page_end_ = 0; SuperPageExtentEntry* current_extent_ = nullptr; SuperPageExtentEntry* first_extent_ = nullptr; DirectMapExtent* direct_map_list_ PA_GUARDED_BY(internal::PartitionRootLock(this)) = nullptr; SlotSpanMetadata* global_empty_slot_span_ring_[internal::kMaxEmptySlotSpanRingSize] \ PA_GUARDED_BY(internal::PartitionRootLock(this)) = {}; int16_t global_empty_slot_span_ring_index_ PA_GUARDED_BY(internal::PartitionRootLock(this)) = 0; int16_t global_empty_slot_span_ring_size_ PA_GUARDED_BY(internal::PartitionRootLock(this)) = \ internal::kDefaultEmptySlotSpanRingSize; uintptr_t inverted_self_ = 0; internal::Lock thread_cache_construction_lock_; // 8 bytes size_t scheduler_loop_quarantine_branch_capacity_in_bytes_ = 0; internal::SchedulerLoopQuarantineRoot scheduler_loop_quarantine_root_; internal::GlobalSchedulerLoopQuarantineBranch scheduler_loop_quarantine_; internal::GlobalSchedulerLoopQuarantineBranch scheduler_loop_quarantine_for_advanced_memory_safety_checks_; }; """ x = read_int_from_memory(current + 0x40 + 8) # sizeof(struct Settings) + sizeof(lock_) if is_valid_addr(x): # buckets[0]->active_slot_spans_head current += 0x40 # sizeof(struct Settings) is 1 cache line else: x = read_int_from_memory(current + 0x80 + 8) # sizeof(struct Settings) + sizeof(lock_) if is_valid_addr(x): # buckets[0]->active_slot_spans_head current += 0x80 # sizeof(struct Settings) is 2 cache lines else: current += 0xc0 # sizeof(struct Settings) is 3 cache lines def get_metadata_offset(root_addr, current_addr): """ # v145.x~ has PA_CONFIG(MOVE_METADATA_OUT_OF_GIGACAGE) 0x599a3e48af40|+0x0000|+000: settings (=&root) : 0x0000000000010000 0x599a3e48af48|+0x0008|+001: : 0x0000000000000004 0x599a3e48af50|+0x0010|+002: : 0x0000000000000002 0x599a3e48af58|+0x0018|+003: : 0x000009f400000000 0x599a3e48af60|+0x0020|+004: : 0xfffffffc00000000 0x599a3e48af68|+0x0028|+005: : 0x0000599a3e1dc018 0x599a3e48af70|+0x0030|+006: : 0x000009f400000000 0x599a3e48af78|+0x0038|+007: : 0xfffffffc00000000 0x599a3e48af80|+0x0040|+008: : 0x0000000000000000 0x599a3e48af88|+0x0048|+009: : 0x0000000000000000 0x599a3e48af90|+0x0050|+010: : 0xaaaaaaaaaa000000 0x599a3e48af98|+0x0058|+011: : 0x00000000000f0000 0x599a3e48afa0|+0x0060|+012: : 0x0000000000000000 0x599a3e48afa8|+0x0068|+013: : 0x0000000000000000 0x599a3e48afb0|+0x0070|+014: : 0x0000000000000000 0x599a3e48afb8|+0x0078|+015: : 0x0000000000000000 0x599a3e48afc0|+0x0080|+016: : 0x00000000ffffffff 0x599a3e48afc8|+0x0088|+017: : 0x0000000000000004 0x599a3e48afd0|+0x0090|+018: settings.metadata_offset_: 0x00002e37f278b000 <-- here 0x599a3e48afd8|+0x0098|+019: : 0x0000000000000100 0x599a3e48afe0|+0x00a0|+020: : 0x0000000000000000 0x599a3e48afe8|+0x00a8|+021: : 0x0000000000000000 0x599a3e48aff0|+0x00b0|+022: : 0x0000000000000000 0x599a3e48aff8|+0x00b8|+023: : 0x0000000000000000 0x599a3e48b000|+0x00c0|+024: lock_ : 0x0000000000000000 0x599a3e48b008|+0x00c8|+025: buckets[0] : 0x0000382bf298b920 0x599a3e48b010|+0x00d0|+026: buckets[0] : 0x0000000000000000 0x599a3e48b018|+0x00d8|+027: buckets[0] : 0x0000000000000000 0x599a3e48b020|+0x00e0|+028: buckets[0] : 0x0000000400000010 0x599a3e48b028|+0x00e8|+029: buckets[0] : 0x0000004000000000 0x599a3e48b030|+0x00f0|+030: buckets[0] : 0x0000000000000000 """ if current_addr - root_addr != 0xc0: return get_pagesize() data = read_memory(root_addr + 0x80, 0x40) for x in slice_unpack(data, current_arch.ptrsize): if x & 0xfff: continue if x == 0: continue if (root_addr & 0x0000_ffff_ff00_0000) == (x & 0x0000_ffff_ff00_0000): continue if (x & 0xffff_ffff) == 0: continue return x return get_pagesize() root["metadata_offset_"] = get_metadata_offset(root["addr"], current) root["lock_"] = read_int64_from_memory(current) current += 8 # for 32bit, there is 2 patterns because aligned or packed if is_32bit(): if self.align_pad is None: x = read_int_from_memory(current) if x == 0: self.align_pad = True else: self.align_pad = False if self.align_pad: current += ptrsize root["buckets_"] = [] while True: if read_int_from_memory(current) == 1: # search for `bool initialized` break bucket, current = self.read_bucket(current) root["buckets_"].append(bucket) root["sentinel_bucket_"] = root["buckets_"].pop() root["initialized_"] = read_int_from_memory(current) & 0xff current += ptrsize # with pad root["total_size_of_committed_pages_"] = read_int_from_memory(current) current += ptrsize root["max_size_of_committed_pages_"] = read_int_from_memory(current) current += ptrsize root["total_size_of_super_pages_"] = read_int_from_memory(current) current += ptrsize root["total_size_of_direct_mapped_pages_"] = read_int_from_memory(current) current += ptrsize root["total_size_of_allocated_bytes_"] = read_int_from_memory(current) current += ptrsize root["max_size_of_allocated_bytes_"] = read_int_from_memory(current) current += ptrsize root["syscall_count_"] = read_int_from_memory(current) current += ptrsize root["syscall_total_time_ns_"] = read_int_from_memory(current) current += ptrsize root["total_size_of_brp_quarantined_bytes"] = read_int_from_memory(current) current += ptrsize root["total_count_of_brp_quarantined_slots_"] = read_int_from_memory(current) current += ptrsize root["cumulative_size_of_brp_quarantined_bytes_"] = read_int_from_memory(current) current += ptrsize root["cumulative_count_of_brp_quarantined_slots_"] = read_int_from_memory(current) current += ptrsize root["empty_slot_spans_dirty_bytes_"] = read_int32_from_memory(current) current += ptrsize # with pad root["max_empty_slot_spans_dirty_bytes_shift_"] = read_int32_from_memory(current) current += ptrsize # with pad root["next_super_page_"] = read_int_from_memory(current) current += ptrsize root["next_partition_page_"] = read_int_from_memory(current) current += ptrsize root["next_partition_page_end_"] = read_int_from_memory(current) current += ptrsize root["current_extent_"] = read_int_from_memory(current) current += ptrsize root["first_extent_"] = read_int_from_memory(current) current += ptrsize root["direct_map_list_"] = read_int_from_memory(current) current += ptrsize root["global_empty_slot_span_ring_"] = [] inv = root["addr"] ^ AddressUtil.get_vmem_end_mask() while True: if read_int_from_memory(current + ptrsize) == inv: # search for `inverted_self_` break x = read_int_from_memory(current) current += ptrsize root["global_empty_slot_span_ring_"].append(x) root["global_empty_slot_span_ring_index_"] = read_int16_from_memory(current) current += 2 root["global_empty_slot_span_ring_size_"] = read_int16_from_memory(current) current += 2 if is_64bit(): current += 4 # pad root["inverted_self_"] = read_int_from_memory(current) current += ptrsize root["thread_cache_construction_lock_"] = read_int64_from_memory(current) current += 8 root["scheduler_loop_quarantine_branch_capacity_in_bytes_"] = read_int32_from_memory(current) current += ptrsize # with pad root["scheduler_loop_quarantine_root_"] = read_int_from_memory(current) current += ptrsize root["scheduler_loop_quarantine_"] = read_int_from_memory(current) current += ptrsize root["scheduler_loop_quarantine_for_advanced_memory_safety_checks_"] = read_int_from_memory(current) current += ptrsize Root = collections.namedtuple("Root", root.keys()) root = Root(*root.values()) return root, current @Cache.cache_until_next def read_bucket(self, addr): ptrsize = current_arch.ptrsize bucket = {} bucket["addr"] = current = addr """ https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/ \ src/partition_alloc/partition_bucket.h struct base::internal::PartitionBucket { SlotSpanMetadata* active_slot_spans_head; SlotSpanMetadata* empty_slot_spans_head; SlotSpanMetadata* decommitted_slot_spans_head; uint32_t slot_size; uint32_t num_system_pages_per_slot_span : 8; uint32_t num_full_slot_spans : 24; uint64_t slot_size_reciprocal; bool can_store_raw_size; }; """ bucket["active_slot_spans_head"] = read_int_from_memory(current) current += ptrsize bucket["empty_slot_spans_head"] = read_int_from_memory(current) current += ptrsize bucket["decommitted_slot_spans_head"] = read_int_from_memory(current) current += ptrsize bucket["slot_size"] = read_int32_from_memory(current) current += 4 x = read_int32_from_memory(current) bucket["num_system_pages_per_slot_span"] = x & 0xff bucket["num_full_slot_spans"] = (x >> 8) & 0xff_ffff current += 4 # for 32bit, there is 2 patterns because aligned or packed if is_32bit() and self.align_pad: current += 4 bucket["slot_size_reciprocal"] = read_int64_from_memory(current) current += 8 x = read_int32_from_memory(current) bucket["can_store_raw_size"] = x & 0xff current += ptrsize # with pad Bucket = collections.namedtuple("Bucket", bucket.keys()) bucket = Bucket(*bucket.values()) return bucket, current @Cache.cache_until_next def read_extent(self, addr): ptrsize = current_arch.ptrsize extent = {} extent["addr"] = current = addr extent["super_page_base"] = current - 0x1000 """ https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/ \ src/partition_alloc/partition_superpage_extent_entry.h struct PartitionSuperPageExtentEntry { PartitionRootBase* root; PartitionSuperPageExtentEntry* next; uint16_t number_of_consecutive_super_pages; uint16_t number_of_nonempty_slot_spans; }; """ extent["root"] = read_int_from_memory(current) current += ptrsize extent["next"] = read_int_from_memory(current) current += ptrsize extent["number_of_consecutive_super_pages"] = read_int16_from_memory(current) current += 2 extent["number_of_nonempty_slot_spans"] = read_int16_from_memory(current) current += 2 extent["super_page_end"] = extent["super_page_base"] + extent["number_of_consecutive_super_pages"] * 0x20_0000 Extent = collections.namedtuple("Extent", extent.keys()) extent = Extent(*extent.values()) return extent, current @Cache.cache_until_next def read_direct_map(self, addr): ptrsize = current_arch.ptrsize direct_map = {} direct_map["addr"] = current = addr """ https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/ \ src/partition_alloc/partition_direct_map_extent.h struct PartitionDirectMapExtent { PartitionDirectMapExtent* next_extent; PartitionDirectMapExtent* prev_extent; const PartitionBucket* bucket; size_t reservation_size; size_t padding_for_alignment; }; """ direct_map["next_extent"] = read_int_from_memory(current) current += ptrsize direct_map["prev_extent"] = read_int_from_memory(current) current += ptrsize direct_map["bucket"] = read_int_from_memory(current) current += ptrsize direct_map["reservation_size"] = read_int_from_memory(current) current += ptrsize direct_map["padding_for_alignment"] = read_int_from_memory(current) current += ptrsize DirectMap = collections.namedtuple("DirectMap", direct_map.keys()) direct_map = DirectMap(*direct_map.values()) return direct_map, current @Cache.cache_until_next def read_slot_span(self, addr): ptrsize = current_arch.ptrsize slot_span = {} slot_span["addr"] = current = addr slot_span["super_page_addr"] = AddressUtil.align_address( (slot_span["addr"] & get_pagesize_mask_high()) - self.root.metadata_offset_, ) slot_span["partition_page_index"] = (slot_span["addr"] & get_pagesize_mask_low()) // 0x20 super_page_addr_offset = slot_span["partition_page_index"] * get_pagesize() * 4 slot_span["partition_page_start"] = slot_span["super_page_addr"] + super_page_addr_offset """ https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/ \ src/partition_alloc/partition_page.h struct SlotSpanMetadata { FreelistEntry* freelist_head = nullptr; SlotSpanMetadata* next_slot_span = nullptr; PartitionBucket* const bucket = nullptr; uint32_t num_allocated_slots : kMaxSlotsPerSlotSpanBits; // 15 bits uint32_t num_unprovisioned_slots : kMaxSlotsPerSlotSpanBits; // 15 bits uint32_t marked_full : 1; const uint32_t can_store_raw_size_ : 1; uint16_t freelist_is_sorted_ : 1; uint16_t in_empty_cache_ : 1; uint16_t empty_cache_index_ : internal::base::bits::BitWidth(kMaxEmptySlotSpanRingSize - 1); // 7 or 10 bits }; """ slot_span["freelist_head"] = read_int_from_memory(current) current += ptrsize slot_span["next_slot_span"] = read_int_from_memory(current) current += ptrsize slot_span["bucket"] = read_int_from_memory(current) current += ptrsize x = read_int32_from_memory(current) current += 4 slot_span["num_allocated_slots"] = (x >> 0) & 0x7fff slot_span["num_unprovisioned_slots"] = (x >> 15) & 0x7fff slot_span["marked_full"] = (x >> 30) & 1 slot_span["can_store_raw_size_"] = (x >> 31) & 1 x = read_int16_from_memory(current) current += 2 slot_span["freelist_is_sorted_"] = (x >> 0) & 1 slot_span["in_empty_cache_"] = (x >> 1) & 1 slot_span["empty_cache_index_"] = (x >> 2) & 0x3ff SlotSpan = collections.namedtuple("SlotSpan", slot_span.keys()) slot_span = SlotSpan(*slot_span.values()) return slot_span, current def C(self, address): # coloring function for heap address management_color = Config.get_gef_setting("theme.heap_management_address") # in extent current = self.root.current_extent_ while current: extent, _ = self.read_extent(current) if extent.super_page_base <= address < extent.super_page_end: return Color.colorify_hex(address, management_color) current = extent.next # not in extent, but valid if is_valid_addr(address): return str(ProcessMap.lookup_address(address)) # not valid return "{:#x}".format(address) def P(self, address): # coloring function for heap page address page_address_color = Config.get_gef_setting("theme.heap_page_address") return Color.colorify_hex(address, page_address_color) def dump_root(self, root): self.out.append(titlify("*{} @ {:#x}".format(root.name, root.addr))) self.out.append("struct Settings settings_: ...") self.out.append("std::ptrdiff_t settings.metadata_offset_: {:#x}".format( root.metadata_offset_, )) self.out.append("::partition_alloc::Lock lock_: {:#x}".format( root.lock_, )) self.out.append("Bucket buckets_[{:3d}]:".format(len(root.buckets_))) for idx, bucket in enumerate(root.buckets_): self.dump_bucket(bucket, root, idx) if self.args.verbose: self.out.append("Bucket sentinel_bucket_:") self.dump_bucket(root.sentinel_bucket_, root) else: self.out.append("Bucket sentinel_bucket_: ...") self.out.append("bool initialized_: {:#x}".format( root.initialized_, )) self.out.append("std::atomic total_size_of_committed_pages_: {:#x}".format( root.total_size_of_committed_pages_, )) self.out.append("std::atomic max_size_of_committed_pages_: {:#x}".format( root.max_size_of_committed_pages_, )) self.out.append("std::atomic total_size_of_super_pages_: {:#x}".format( root.total_size_of_super_pages_, )) self.out.append("std::atomic total_size_of_direct_mapped_pages_: {:#x}".format( root.total_size_of_direct_mapped_pages_, )) self.out.append("std::atomic total_size_of_allocated_bytes_: {:#x}".format( root.total_size_of_allocated_bytes_, )) self.out.append("std::atomic max_size_of_allocated_bytes_: {:#x}".format( root.max_size_of_allocated_bytes_, )) self.out.append("std::atomic syscall_count_: {:#x}".format( root.syscall_count_, )) self.out.append("std::atomic syscall_total_time_ns_: {:#x}".format( root.syscall_total_time_ns_, )) self.out.append("std::atomic total_size_of_brp_quarantined_bytes: {:#x}".format( root.total_size_of_brp_quarantined_bytes, )) self.out.append("std::atomic total_count_of_brp_quarantined_slots_: {:#x}".format( root.total_count_of_brp_quarantined_slots_, )) self.out.append("std::atomic cumulative_size_of_brp_quarantined_bytes_: {:#x}".format( root.cumulative_size_of_brp_quarantined_bytes_, )) self.out.append("std::atomic cumulative_count_of_brp_quarantined_slots_: {:#x}".format( root.cumulative_count_of_brp_quarantined_slots_, )) self.out.append("size_t empty_slot_spans_dirty_bytes_: {:#x}".format( root.empty_slot_spans_dirty_bytes_, )) self.out.append("int max_empty_slot_spans_dirty_bytes_shift_: {:#x}".format( root.max_empty_slot_spans_dirty_bytes_shift_, )) self.out.append("uintptr_t next_super_page_: {:s}".format( self.P(root.next_super_page_), )) self.out.append("uintptr_t next_partition_page_: {:s}".format( self.P(root.next_partition_page_), )) self.out.append("uintptr_t next_partition_page_end_: {:s}".format( self.P(root.next_partition_page_end_), )) self.out.append("SuperPageExtentEntry* current_extent_: {:s}".format( self.C(root.current_extent_), )) self.dump_extent_list(root.current_extent_) self.out.append("SuperPageExtentEntry* first_extent_: {:s}".format( self.C(root.first_extent_), )) self.dump_extent_list(root.first_extent_) self.out.append("DirectMapExtent* direct_map_list_: {:s}".format( self.C(root.direct_map_list_), )) self.dump_direct_map_list(root.direct_map_list_, root) ring_len = len(root.global_empty_slot_span_ring_) if self.args.verbose: self.out.append("SlotSpanMetadata* global_empty_slot_span_ring_[{:4d}]:".format( ring_len, )) for i in range(len(root.global_empty_slot_span_ring_)): colored_slot_span = self.C(root.global_empty_slot_span_ring_[i]) self.out.append(" global_empty_slot_span_ring_[{:4d}]: {:s}".format( i, colored_slot_span, )) else: self.out.append("SlotSpanMetadata* global_empty_slot_span_ring_[{:4d}]: ...".format( ring_len, )) self.out.append("int16_t global_empty_slot_span_ring_index_: {:#x}".format( root.global_empty_slot_span_ring_index_, )) self.out.append("int16_t global_empty_slot_span_ring_size_: {:#x}".format( root.global_empty_slot_span_ring_size_, )) inv_inv = root.inverted_self_ ^ AddressUtil.get_vmem_end_mask() self.out.append("uintptr_t inverted_self_: {:#x} (=~{!s})".format( root.inverted_self_, ProcessMap.lookup_address(inv_inv), )) self.out.append("internal::Lock thread_cache_construction_lock_: {:#x}".format( root.thread_cache_construction_lock_, )) self.out.append("size_t scheduler_loop_quarantine_branch_capacity_in_bytes_: {:#x}".format( root.scheduler_loop_quarantine_branch_capacity_in_bytes_, )) self.out.append("internal::SchedulerLoopQuarantineRoot scheduler_loop_quarantine_root_: {:#x}".format( root.scheduler_loop_quarantine_root_, )) self.out.append("internal::GlobalSchedulerLoopQuarantineBranch scheduler_loop_quarantine_: {:#x}".format( root.scheduler_loop_quarantine_, )) self.out.append("internal::GlobalSchedulerLoopQuarantineBranch " "scheduler_loop_quarantine_for_advanced_memory_safety_checks_: {:#x}".format( root.scheduler_loop_quarantine_for_advanced_memory_safety_checks_, )) return def dump_extent_list(self, head): try: current = head while current: extent, _ = self.read_extent(current) self.out.append(" -> extent @{:s}".format( self.C(extent.addr), )) self.out.append(" root:{!s} ".format( ProcessMap.lookup_address(extent.root), )) super_page_info = "{:s} - {:s}".format( self.P(extent.super_page_base), self.P(extent.super_page_end), ) page_info = "(total 0x200000(2MB) * {:d} pages)".format( extent.number_of_consecutive_super_pages, ) self.out.append(" super_page:{:s} {:s}".format( super_page_info, page_info, )) self.out.append(" non_empty_slot_spans:{:d} ".format( extent.number_of_nonempty_slot_spans, )) self.out.append(" next:{:s}".format( self.C(extent.next), )) current = extent.next except Exception: self.err_add_out("Corrupted?") return def dump_direct_map_list(self, head, root): try: current = head while current: direct_map, _ = self.read_direct_map(current) self.out.append(" -> direct_map @{:s}: ".format( self.C(direct_map.addr), )) self.out.append(" next_extent:{:s} ".format( self.C(direct_map.next_extent), )) self.out.append(" prev_extent:{:s} ".format( self.C(direct_map.prev_extent), )) self.out.append(" bucket:{:s} ".format( self.C(direct_map.bucket), )) self.out.append(" reservation_size:{:#x}".format( direct_map.reservation_size, )) self.out.append(" padding_for_alignment:{:#x}".format( direct_map.padding_for_alignment, )) bucket, _ = self.read_bucket(direct_map.bucket) self.dump_bucket(bucket, root) current = direct_map.next_extent except Exception: self.err_add_out("Corrupted?") return def dump_bucket(self, bucket, root, idx=None): sentinel1 = self.get_sentinel_slot_spans() # from symbol sentinel2 = [root.sentinel_bucket_.active_slot_spans_head] # from heuristic search sentinel_or_0 = list(set(sentinel1 + sentinel2 + [0x0])) # uniq if not self.args.verbose: if bucket.active_slot_spans_head in sentinel_or_0: return # skip printing chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") label_active_color = Config.get_gef_setting("theme.heap_label_active") label_inactive_color = Config.get_gef_setting("theme.heap_label_inactive") slot_size = Color.colorify("{:#7x}".format(bucket.slot_size), chunk_size_color) if idx is not None: self.out.append(" buckets_[{:3d}](slot_size:{:s}) @{!s}".format( idx, slot_size, ProcessMap.lookup_address(bucket.addr), )) else: self.out.append(" bucket(slot_size:{:s}) @{!s}".format( slot_size, ProcessMap.lookup_address(bucket.addr), )) self.out.append(" num_system_pages_per_slot_span:{:#x} ".format( bucket.num_system_pages_per_slot_span, )) self.out.append(" num_full_slot_spans:{:#x} ".format(bucket.num_full_slot_spans)) self.out.append(" slot_size_reciprocal:{:#x}".format(bucket.slot_size_reciprocal)) self.out.append(" can_store_raw_size:{:#x}".format(bucket.can_store_raw_size)) if self.args.verbose: target_list = ["active_slot_spans_head", "empty_slot_spans_head", "decommitted_slot_spans_head"] else: target_list = ["active_slot_spans_head"] for key in target_list: head = getattr(bucket, key) # sentinel can be ignored, so skip if not self.args.verbose and head in sentinel_or_0: continue if head in sentinel_or_0: # print sentinel (verbose) colored_key = Color.colorify(key, label_inactive_color) self.out.append(" {:s}:{:s} (=sentinel_pages)".format(colored_key, self.C(head))) else: # default colored_key = Color.colorify(key, label_active_color) self.out.append(" {:s}:{:s}".format(colored_key, self.C(head))) self.dump_slot_span(head, bucket) return def dump_slot_span(self, head, bucket): current = head while current: try: slot_span, _ = self.read_slot_span(current) except Exception: self.err_add_out("Corrupted?") break self.out.append(" -> slot_span @{:s} (#{:3d} of super_page @{:s})".format( self.C(slot_span.addr), slot_span.partition_page_index, self.P(slot_span.super_page_addr), )) self.out.append(" next_slot_span:{:s} ".format( self.C(slot_span.next_slot_span), )) self.out.append(" slot_span_area:{:s}-{:s} ".format( self.P(slot_span.partition_page_start), self.P(slot_span.partition_page_start + bucket.num_system_pages_per_slot_span * get_pagesize()), )) self.out.append(" num_allocated_slots:{:#x}".format( slot_span.num_allocated_slots), ) self.dump_freelist(slot_span.freelist_head, bucket, slot_span) current = slot_span.next_slot_span return def dump_freelist(self, head, bucket, slot_span): corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") self.out.append(" freelist_head:{:s} ".format(self.C(head))) slot_size = bucket.slot_size page_start = slot_span.partition_page_start page_end = page_start + bucket.num_system_pages_per_slot_span * get_pagesize() text = "" cnt = 0 chunk = head seen = [] while chunk: if cnt % 6 == 0: if cnt > 0: text += "\n" text += " " * 23 if chunk in seen: text += Color.colorify("-> {:#x} (loop) ".format(chunk), corrupted_msg_color) break if chunk < page_start or page_end <= chunk: text += Color.colorify("-> {:#x} (corrupted: out of range {:#x}-{:#x}) ".format( chunk, page_start, page_end, ), corrupted_msg_color) break if (chunk - page_start) % slot_size != 0: text += Color.colorify("-> {:#x} (corrupted: not aligned) ".format(chunk), corrupted_msg_color) break try: next_chunk = byteswap(read_int_from_memory(chunk)) if is_64bit() and next_chunk: next_chunk |= chunk & 0xffff_ffff_0000_0000 except gdb.MemoryError: text += Color.colorify("-> {:#x} (corrupted: invalid address) ".format(chunk), corrupted_msg_color) break text += "-> " + Color.colorify_hex(chunk, freed_address_color) + " " cnt += 1 seen.append(chunk) chunk = next_chunk if cnt > 0: text += "(num: {:#x})".format(cnt) if text: self.out.append(text) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): if is_32bit(): self.align_pad = None if self.args.target_buffer_root == "fm": self.args.target_buffer_root = "fast_malloc" elif self.args.target_buffer_root == "b": self.args.target_buffer_root = "buffer" elif self.args.target_buffer_root == "ab": self.args.target_buffer_root = "array_buffer" self.out = [] for r in self.get_roots(args.force_heuristic): ok = False if args.target_buffer_root == "fast_malloc": if r.name == "fast_malloc_root_": ok = True if args.target_buffer_root == "array_buffer": if r.name == "array_buffer_root_": ok = True if args.target_buffer_root == "buffer": if r.name == "buffer_root_": ok = True if not ok: continue try: root, _ = self.read_root(r.address, r.name) except Exception: mem_value = read_int_from_memory(r.address) err("Parse error {:s}: @ {:#x} -> {:#x}".format(r.name, r.address, mem_value)) exc_type, exc_value, exc_traceback = sys.exc_info() gef_print(exc_value) continue self.root = root # for coloring self.dump_root(root) self.print_output() return @register_command class ScallocHeapDumpCommand(GenericCommand, BufferingOutput): """scalloc heap free-list viewer (x64 only).""" _cmdline_ = "scalloc-heap-dump" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--object_space", type=AddressUtil.parse_address, help="use specific address for object_space.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def class_to_objects(self, cl): # number of objects in each span class_to_objects_list = [ 0x0, 0x7f8, 0x3fc, 0x2a8, 0x1fe, 0x198, 0x154, 0x123, 0xff, 0xe2, 0xcc, 0xb9, 0xaa, 0x9c, 0x91, 0x88, 0x7f, 0x40, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, 0x10, 0x8, 0x4, 0x2, 0x1, ] assert cl < len(class_to_objects_list) return class_to_objects_list[cl] def class_to_size(self, cl): class_to_size_list = [ 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, ] assert cl < len(class_to_size_list) return class_to_size_list[cl] def read_arena(self, addr, arena_name): if addr is None: return None """ class Arena { const char* name_; uintptr_t start_; uintptr_t end_; uintptr_t len_; UNUSED uint8_t pad[64 - ((sizeof(name_) + sizeof(start_) + sizeof(end_) + sizeof(len_)) % 64)]; std::atomic current_; UNUSED uint8_t pad2_[64 - ((sizeof(current_)) % 64)]; } """ dic = {} dic["addr"] = addr dic["name"] = arena_name dic["name_"] = read_cstring_from_memory(read_int_from_memory(addr)) dic["start"] = read_int_from_memory(addr + current_arch.ptrsize * 1) dic["end"] = read_int_from_memory(addr + current_arch.ptrsize * 2) dic["len"] = read_int_from_memory(addr + current_arch.ptrsize * 3) dic["current"] = read_int_from_memory(addr + 0x40) Arena = collections.namedtuple("Arena", dic.keys()) arena = Arena(*dic.values()) return arena def get_object_space_heuristic(self): maps = ProcessMap.get_process_maps() for m in maps: if m.path != "": continue if m.size > 0x100_0000: # heuristic continue for addr in range(m.page_start, m.page_end, current_arch.ptrsize): v = read_int_from_memory(addr) if v == 0 or not is_valid_addr(v): continue if read_cstring_from_memory(v) != "object": continue start = read_int_from_memory(addr + current_arch.ptrsize) if not is_valid_addr(start): continue end = read_int_from_memory(addr + current_arch.ptrsize * 2) if not is_valid_addr(end - 1): continue len_ = read_int_from_memory(addr + current_arch.ptrsize * 3) if end - start != len_: continue pad1 = read_int_from_memory(addr + current_arch.ptrsize * 4) if pad1 != 0: continue pad2 = read_int_from_memory(addr + current_arch.ptrsize * 5) if pad2 != 0: continue pad3 = read_int_from_memory(addr + current_arch.ptrsize * 6) if pad3 != 0: continue pad4 = read_int_from_memory(addr + current_arch.ptrsize * 7) if pad4 != 0: continue current = read_int_from_memory(addr + current_arch.ptrsize * 8) if not is_valid_addr(current): continue return addr return None def get_object_space(self): object_space = None # use specific address if self.args.object_space: object_space = self.args.object_space ## use symbol if object_space is None: try: object_space = AddressUtil.parse_address("&_ZN7scalloc12object_spaceE") except gdb.error: object_space = None # heuristic search if object_space is None: object_space = self.get_object_space_heuristic() object_space = self.read_arena(object_space, "object_space") return object_space def decode_top(self, raw): kValueBits = 48 kValueMask= (1 << kValueBits) - 1 kExtendMask = 0xffff_ffff_ffff_ffff return (raw & kValueMask) | (((raw >> (kValueBits - 1)) & 0x1) * kExtendMask) def read_span(self, addr): """ class Span { DoubleListNode { DoubleListNode* next_; DoubleListNode* prev_; } span_link_; AtomicCoreID owner_; std::atomic epoch_; int32_t size_class_; UNUSED char padding_[8]; IncrementalFreeList { void* list_; // Incremental free list. intptr_t bump_pointer_; int32_t len_; // Number of free objects. int32_t increment_; // Size of an object. } local_free_list_; RemoteFreeList { AtomicTaggedValue top_; int8_t pad_[64 - (sizeof(top_) % 64)]; } remote_free_list_; } """ dic = {} dic["addr"] = addr dic["next"] = read_int_from_memory(addr + current_arch.ptrsize * 0) dic["prev"] = read_int_from_memory(addr + current_arch.ptrsize * 1) dic["owner"] = read_int_from_memory(addr + current_arch.ptrsize * 2) dic["epoch"] = read_int32_from_memory(addr + current_arch.ptrsize * 3) dic["size_class"] = read_int32_from_memory(addr + current_arch.ptrsize * 3 + 4) dic["object_num"] = self.class_to_objects(dic["size_class"]) # number of objects in each span dic["object_size"] = self.class_to_size(dic["size_class"]) dic["freelist"] = read_int_from_memory(addr + current_arch.ptrsize * 5) dic["freelist_addr"] = addr + current_arch.ptrsize * 5 dic["bump_pointer"] = read_int_from_memory(addr + current_arch.ptrsize * 6) # top pointer in glibc dic["freelist_len"] = read_int32_from_memory(addr + current_arch.ptrsize * 7) dic["freelist_increment"] = read_int32_from_memory(addr + current_arch.ptrsize * 7 + 4) # = object_size dic["top"] = read_int_from_memory(addr + current_arch.ptrsize * 8) # encoded remote freelist dic["top_addr"] = addr + current_arch.ptrsize * 8 dic["top_decoded"] = self.decode_top(dic["top"]) Span = collections.namedtuple("Span", dic.keys()) span = Span(*dic.values()) return span def dump_freelist(self, head): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") cur = head cnt = 0 while True: if cur == 0 and cnt > 0: self.out.append(" -> {:s} (num: {:#x})".format(Color.colorify_hex(cur, freed_address_color), cnt)) else: self.out.append(" -> {:s}".format(Color.colorify_hex(cur, freed_address_color))) if cur == 0: break cur = read_int_from_memory(cur) cnt += 1 return cnt def dump_spans(self, arena): self.out.append(titlify("Arena ({:s}) @{:#x}".format(arena.name, arena.addr))) current = arena.start while current < arena.current: span = self.read_span(current) if span.object_size != 0: self.out.append(titlify("Span @{:#x} (size: {:#x}, capacity: {:#x}, available: {:#x})".format( span.addr, span.object_size, span.object_num, span.freelist_len, ))) self.out.append("freelist @ {!s}:".format(ProcessMap.lookup_address(span.freelist_addr))) cnt = self.dump_freelist(span.freelist) self.out.append("bump_pointer: {!s} (num: {:#x})".format( ProcessMap.lookup_address(span.bump_pointer), span.freelist_len - cnt, )) self.out.append("remote freelist @ {!s}:".format(ProcessMap.lookup_address(span.top_addr))) self.dump_freelist(span.top_decoded) current += 0x20_0000 # kVirtualSpanSize return span @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_64",)) def do_invoke(self, args): object_space = self.get_object_space() if object_space is None: err("Could not find object_space") return self.out = [] self.dump_spans(object_space) self.print_output() return @register_command class MuslHeapDumpCommand(GenericCommand, BufferingOutput): """musl v1.2.5 (src/malloc/mallocng) heap reusable chunks viewer (x64/x86 only).""" # See https://h-noson.hatenablog.jp/entry/2021/05/03/161933#-177pts-mooosl _cmdline_ = "musl-heap-dump" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) modes = ["ctx", "unused"] parser.add_argument("command", choices=modes, nargs="?", default="unused", help="dump mode (default: %(default)s).") parser.add_argument("-i", "--active-idx", type=int, help="the active index of dump target.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="also dump an empty active index.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.modes: # already matched return [] if text == "": # no prefix return [s for s in self.modes if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.modes if s and s.startswith(text.strip())] def get_malloc_context_heuristic(self): try: # search for malloc malloc = AddressUtil.parse_address("malloc") self.info_add_out("malloc: {:#x}".format(malloc)) # search for __libc_malloc_impl """ [pattern 1] 0x7ffff7d7dde0 : jmp 0x7ffff7d8ece2 <__libc_malloc_impl> [pattern 2] 0x7ffff7f71650 : endbr64 0x7ffff7f71654 : jmp 0x7ffff7f72ff0 [pattern 3] 0xf7f78bf0 : jmp 0xf7f8bc3c """ res = gdb.execute("x/10i {:#x}".format(malloc), to_string=True) for line in res.splitlines(): m = re.search(r"jmp\s*(0x\w+)", line) if not m: continue __libc_malloc_impl = int(m.group(1), 16) break self.info_add_out("__libc_malloc_impl: {:#x}".format(__libc_malloc_impl)) # search for __malloc_alloc_meta """ [pattern 1] 0x7ffff7d8ed0a <__libc_malloc_impl+40>: call 0x7ffff7d88dc1 <__errno_location> 0x7ffff7d8ed34 <__libc_malloc_impl+82>: call 0x7ffff7da0c3b 0x7ffff7d8ed48 <__libc_malloc_impl+102>: call 0x7ffff7d8e36b 0x7ffff7d8ed4d <__libc_malloc_impl+107>: call 0x7ffff7d8e382 0x7ffff7d8ed52 <__libc_malloc_impl+112>: call 0x7ffff7d8e45a <__malloc_alloc_meta> [pattern 2] 0x7ffff7f7303b: call 0x7ffff7f8a640 0x7ffff7f73074: call 0x7ffff7f72290 [pattern 3] 0xf7f8bc40: call 0xf7f7cf84 0xf7f8bc67: call 0xf7f84e85 <__errno_location> 0xf7f8bc88: call 0xf7f9cd53 0xf7f8bc9b: call 0xf7f8b3aa 0xf7f8bca0: call 0xf7f8b3d2 0xf7f8bca5: call 0xf7f8b439 """ __malloc_alloc_meta_candidate = [] res = gdb.execute("x/100i {:#x}".format(__libc_malloc_impl), to_string=True) for line in res.splitlines(): m = re.search(r"call\s*(0x\w+)", line) if not m: continue addr = int(m.group(1), 16) __malloc_alloc_meta_candidate.append(addr) # search for __malloc_context """ [pattern 1] 0x7ffff7d8e45a <__malloc_alloc_meta>: push r12 0x7ffff7d8e45c <__malloc_alloc_meta+2>: push rbp 0x7ffff7d8e45d <__malloc_alloc_meta+3>: push rbx 0x7ffff7d8e45e <__malloc_alloc_meta+4>: sub rsp,0x10 0x7ffff7d8e462 <__malloc_alloc_meta+8>: cmp DWORD PTR [rip+0x26d67f],0x0 # 0x7ffff7ffbae8 <__malloc_context+8> [pattern 2] 0x7ffff7f72290: endbr64 0x7ffff7f72294: push r12 0x7ffff7f72296: push rbp 0x7ffff7f72297: push rbx 0x7ffff7f72298: sub rsp,0x10 0x7ffff7f7229c: mov rax,QWORD PTR fs:0x28 0x7ffff7f722a5: mov QWORD PTR [rsp+0x8],rax 0x7ffff7f722aa: xor eax,eax 0x7ffff7f722ac: mov eax,DWORD PTR [rip+0x89816] # 0x7ffff7ffbac8 0x7ffff7f722b2: test eax,eax [pattern 3] 0xf7f8b439: push ebp 0xf7f8b43a: push edi 0xf7f8b43b: push esi 0xf7f8b43c: push ebx 0xf7f8b43d: call 0xf7f7cf84 0xf7f8b442: add ebx,0x6fbbe # libc_bss_base 0xf7f8b448: sub esp,0x1c 0xf7f8b44b: cmp DWORD PTR [ebx+0x708],0x0 """ for cand in __malloc_alloc_meta_candidate: self.info_add_out("alloc_meta (candidate): {:#x}".format(cand)) res = gdb.execute("x/10i {:#x}".format(cand), to_string=True) for line in res.splitlines(): if is_x86_64(): m = re.search(r"DWORD PTR \[rip\+0x\w+\].*#\s*(0x\w+)", line) if not m: continue __malloc_context_init_done = int(m.group(1), 16) else: m = re.search(r"DWORD PTR \[e[abcd]x\+(0x\w+)\]", line) if not m: continue __malloc_context_init_done_offset = int(m.group(1), 16) maps = ProcessMap.get_process_maps() rw_maps = [p for p in maps if p.permission.value == Permission.READ | Permission.WRITE] rw_maps = [p for p in rw_maps if "libc.so" in p.path] libc_bss_base = rw_maps[0].page_start __malloc_context_init_done = libc_bss_base + __malloc_context_init_done_offset # check value = read_int32_from_memory(__malloc_context_init_done) if value not in [0, 1]: # init_done is 1 or 0 continue # found self.info_add_out("__malloc_context.init_done: {:#x}".format(__malloc_context_init_done)) __malloc_context = __malloc_context_init_done - current_arch.ptrsize x = read_int_from_memory(__malloc_context) if x == get_pagesize(): __malloc_context -= current_arch.ptrsize self.info_add_out("__malloc_context: {:#x}".format(__malloc_context)) return __malloc_context return None except Exception: err("Could not find &__malloc_context") return None def get_malloc_context(self): try: return AddressUtil.parse_address("&__malloc_context") except gdb.error: self.info_add_out("Could not find the symbol, GEF will use heuristic search") return self.get_malloc_context_heuristic() def class_to_size(self, cl): class_to_size_list = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 18, 20, 25, 31, 36, 42, 50, 63, 72, 84, 102, 127, 146, 170, 204, 255, 292, 340, 409, 511, 584, 682, 818, 1023, 1169, 1364, 1637, 2047, 2340, 2730, 3276, 4095, 4680, 5460, 6552, 8191, ] assert cl < len(class_to_size_list) return class_to_size_list[cl] * 0x10 def read_ctx(self): ptrsize = current_arch.ptrsize ctx = {} ctx["addr"] = current = self.get_malloc_context() if current is None: return None """ struct malloc_context { uint64_t secret; #ifndef PAGESIZE size_t pagesize; #endif int init_done; unsigned mmap_counter; struct meta *free_meta_head; struct meta *avail_meta; size_t avail_meta_count; size_t avail_meta_area_count; size_t meta_alloc_shift; struct meta_area *meta_area_head; struct meta_area *meta_area_tail; unsigned char *avail_meta_areas; struct meta *active[48]; size_t usage_by_class[48]; uint8_t unmap_seq[32]; uint8_t bounces[32]; uint8_t seq; uintptr_t brk; }; """ ctx["secret"] = read_int64_from_memory(current) current += 8 x = read_int_from_memory(current) if x == get_pagesize(): ctx["pagesize"] = x current += ptrsize else: ctx["pagesize"] = None ctx["init_done"] = read_int32_from_memory(current) current += 4 ctx["mmap_counter"] = read_int32_from_memory(current) current += 4 ctx["free_meta_head"] = read_int_from_memory(current) current += ptrsize ctx["avail_meta"] = read_int_from_memory(current) current += ptrsize ctx["avail_meta_count"] = read_int_from_memory(current) current += ptrsize ctx["avail_meta_area_count"] = read_int_from_memory(current) current += ptrsize ctx["alloc_shift"] = read_int_from_memory(current) current += ptrsize ctx["meta_area_head"] = read_int_from_memory(current) current += ptrsize ctx["meta_area_tail"] = read_int_from_memory(current) current += ptrsize ctx["avail_meta_areas"] = read_int_from_memory(current) current += ptrsize ctx["active"] = [] for _ in range(48): ctx["active"].append(read_int_from_memory(current)) current += ptrsize ctx["usage_by_class"] = [] for _ in range(48): ctx["usage_by_class"].append(read_int_from_memory(current)) current += ptrsize ctx["unmap_seq"] = read_memory(current, 32) current += 32 ctx["bounces"] = read_memory(current, 32) current += 32 ctx["seq"] = ord(read_memory(current, 1)) current += ptrsize # with padding ctx["brk"] = read_int_from_memory(current) current += ptrsize Ctx = collections.namedtuple("Ctx", ctx.keys()) return Ctx(*ctx.values()) def dump_ctx(self, ctx): self.out.append(titlify("__malloc_context: {:#x}".format(ctx.addr))) self.out.append(" uint64_t secret: {:#x}".format(ctx.secret)) if ctx.pagesize: self.out.append(" size_t pagesize: {:#x}".format(ctx.pagesize)) self.out.append(" int init_done: {:#x}".format(ctx.init_done)) self.out.append(" unsigned int mmap_counter: {:#x}".format(ctx.mmap_counter)) self.out.append(" struct meta* free_meta_head: {!s}".format(ProcessMap.lookup_address(ctx.free_meta_head))) self.out.append(" struct meta* avail_meta: {!s}".format(ProcessMap.lookup_address(ctx.avail_meta))) self.out.append(" size_t avail_meta_count: {:#x}".format(ctx.avail_meta_count)) self.out.append(" size_t avail_meta_area_count: {:#x}".format(ctx.avail_meta_area_count)) self.out.append(" size_t alloc_shift: {:#x}".format(ctx.alloc_shift)) self.out.append(" struct meta_area* meta_area_head: {!s}".format(ProcessMap.lookup_address(ctx.meta_area_head))) self.out.append(" struct meta_area* meta_area_tail: {!s}".format(ProcessMap.lookup_address(ctx.meta_area_tail))) self.out.append(" unsigned char* avail_meta_areas: {!s}".format(ProcessMap.lookup_address(ctx.avail_meta_areas))) self.out.append(" struct meta* active[48]:") for i in range(48): self.out.append(" active[{:2d}] (for chunk_size={:#7x}): {:#x}".format(i, self.class_to_size(i), ctx.active[i])) self.out.append(" size_t usage_by_class[48]:") for i in range(48): self.out.append(" usage_by_class[{:2d}]: {:#x}".format(i, ctx.usage_by_class[i])) self.out.append(" uint8_t unmap_seq[32]: {}".format(" ".join(["{:02x}".format(x) for x in ctx.unmap_seq]))) self.out.append(" uint8_t bounces[32]: {}".format(" ".join(["{:02x}".format(x) for x in ctx.bounces]))) self.out.append(" uint8_t seq: {:#x}".format(ctx.seq)) self.out.append(" uintptr_t brk: {!s}".format(ProcessMap.lookup_address(ctx.brk))) return def read_meta(self, addr): ptrsize = current_arch.ptrsize meta = {} meta["addr"] = current = addr """ struct meta { struct meta *prev; struct meta *next; struct group *mem; volatile int avail_mask; volatile int freed_mask; uintptr_t last_idx:5; uintptr_t freeable:1; uintptr_t sizeclass:6; uintptr_t maplen:8*sizeof(uintptr_t)-12; }; """ meta["prev"] = read_int_from_memory(current) current += ptrsize meta["next"] = read_int_from_memory(current) current += ptrsize meta["mem"] = read_int_from_memory(current) current += ptrsize meta["avail_mask"] = read_int32_from_memory(current) current += 4 meta["freed_mask"] = read_int32_from_memory(current) current += 4 x = read_int_from_memory(current) meta["last_idx"] = x & 0b11111 meta["freeable"] = (x >> 5) & 0b1 meta["sizeclass"] = (x >> 6) & 0b111111 meta["maplen"] = (x >> 12) current += ptrsize Meta = collections.namedtuple("Meta", meta.keys()) return Meta(*meta.values()) def make_state(self, meta): avail_mask = meta.avail_mask freed_mask = meta.freed_mask text = "" for _ in range(meta.last_idx + 1): if avail_mask & 1: text = "A" + text elif freed_mask & 1: text = "F" + text else: text = "U" + text avail_mask >>= 1 freed_mask >>= 1 return text def read_group(self, meta, offset): ptrsize = current_arch.ptrsize group = {} group["addr"] = current = meta.mem + offset group["data"] = read_memory(group["addr"], self.class_to_size(meta.sizeclass)) """ from source code: struct group { struct meta *meta; unsigned char active_idx:5; char pad[UNIT - sizeof(struct meta *) - 1]; // UNIT = 16 unsigned char storage[]; }; however, the actual usage is as follows. (x64) struct group { struct meta *meta; unsigned int slot_offset32; unsigned char is_slot_offset32; unsigned char slot_index:5; unsigned char reserved:3; unsigned short slot_offset16; }; """ group["meta"] = read_int_from_memory(current) current += ptrsize x = read_int32_from_memory(current) current += 4 if is_x86_64() else 8 y = read_int32_from_memory(current) group["reserved"] = (x >> 13) & 0b111 group["slot_idx"] = (y >> 8) & 0b11111 if y & 0xff: group["slot_offset"] = x else: group["slot_offset"] = (y >> 16) & 0xffff current += ptrsize Group = collections.namedtuple("Group", group.keys()) return Group(*group.values()) def dump_chunk(self, group, state): chunk_used_color = Config.get_gef_setting("theme.heap_chunk_used") chunk_freed_color = Config.get_gef_setting("theme.heap_chunk_freed") subinfo = "state:{:5s} meta:{:<#14x} reserved:{:#x}".format(state, group.meta, group.reserved) if state == "Used": subinfo += " slot_idx:{:<#3x} slot_offset:{:#x}".format(group.slot_idx, group.slot_offset) data = slicer(group.data, current_arch.ptrsize * 2) addr = group.addr group_line_threshold = 8 # create dump text unpack = u32 if current_arch.ptrsize == 4 else u64 width = current_arch.ptrsize * 2 + 2 done = False for blk, blks in itertools.groupby(data): repeat_count = len(list(blks)) d1, d2 = unpack(blk[:current_arch.ptrsize]), unpack(blk[current_arch.ptrsize:]) dascii = "".join([chr(x) if 0x20 <= x < 0x7f else "." for x in blk]) fmt = "{:#x}: {:#0{:d}x} {:#0{:d}x} | {:s} | {:s}" if repeat_count < group_line_threshold: for _ in range(repeat_count): dump = fmt.format(addr, d1, width, d2, width, dascii, subinfo) if state == "Used": self.out.append(Color.colorify(dump, chunk_used_color)) else: self.out.append(Color.colorify(dump, chunk_freed_color)) addr += current_arch.ptrsize * 2 if subinfo: subinfo = "" else: dump = fmt.format(addr, d1, width, d2, width, dascii, subinfo) dump += "* {:#d} lines, {:#x} bytes".format(repeat_count - 1, (repeat_count - 1) * current_arch.ptrsize * 2) if state == "Used": self.out.append(Color.colorify(dump, chunk_used_color)) else: self.out.append(Color.colorify(dump, chunk_freed_color)) addr += current_arch.ptrsize * 2 * repeat_count if subinfo: subinfo = "" if done: break # print dump = dump.rstrip() return def dump_meta(self, ctx): self.out.append("Legend for `Unused chunks list`: A:Avail F:Freed U:Used") self.out.append(" 1. Search most right 'A' and return it") self.out.append(" 2. Search most right 'F' and return it") self.out.append(" 3. If nothing is found, create new meta") # iterate __malloc_context.active for idx in range(48): if self.args.active_idx is not None and idx != self.args.active_idx: continue current = ctx.active[idx] if current == 0: continue self.out.append(titlify("active[{:2d}] (chunk_size={:#x})".format(idx, self.class_to_size(idx)))) management_color = Config.get_gef_setting("theme.heap_management_address") # iterate list of meta seen = [] while current not in seen: meta = self.read_meta(current) self.out.append("meta @ {:s}".format(Color.colorify_hex(meta.addr, management_color))) text = " " colored_prev = Color.colorify_hex(meta.prev, management_color) colored_next = Color.colorify_hex(meta.next, management_color) text += "prev:{:s} next:{:s} ".format(colored_prev, colored_next) colored_mem = Color.colorify_hex(meta.mem, management_color) text += "meta:{:s} ".format(colored_mem) text += "avail_mask:{:#x} freed_mask:{:#x} ".format(meta.avail_mask, meta.freed_mask) text += "last_idx:{:#x} freeable:{:#x} ".format(meta.last_idx, meta.freeable) text += "sizeclass:{:#x} maplen:{:#x}".format(meta.sizeclass, meta.maplen) self.out.append(text) state = self.make_state(meta) self.out.append(" Unused chunks list: {}".format(repr(state))) # dump chunks if state != "F" or self.args.verbose: dic = {"A": "Avail", "F": "Freed", "U": "Used"} for i in range(meta.last_idx + 1): offset = self.class_to_size(idx) * i group = self.read_group(meta, offset) self.dump_chunk(group, dic[state[-i - 1]]) self.out.append("") seen.append(current) current = meta.next return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): self.out = [] ctx = self.read_ctx() if ctx is None: return if args.command == "ctx": self.dump_ctx(ctx) elif args.command == "unused": self.dump_meta(ctx) self.print_output() return class uClibcNgHeap: """Manage uClibc heap-specific settings.""" class uClibcChunk: """uClibc chunk class.""" def __init__(self, addr, from_base=False): self.ptrsize = current_arch.ptrsize if from_base: self.chunk_base_address = addr self.address = addr + 2 * self.ptrsize else: self.chunk_base_address = AddressUtil.align_address(addr - 2 * self.ptrsize) self.address = addr self.size_addr = AddressUtil.align_address(self.address - self.ptrsize) return def get_chunk_size(self): return read_int_from_memory(self.size_addr) & (~0x03) @property def size(self): return self.get_chunk_size() # if freed functions def get_fwd_ptr(self, sll): try: # Not a single-linked-list (sll) or no Safe-Linking support yet if not sll: return read_int_from_memory(self.address) # Unmask ("reveal") the Safe-Linking pointer else: return read_int_from_memory(self.address) ^ (self.address >> 12) except gdb.MemoryError: return None @property def fwd(self): return self.get_fwd_ptr(False) fd = fwd # for compat def get_bkw_ptr(self): return read_int_from_memory(self.address + self.ptrsize) @property def bck(self): return self.get_bkw_ptr() bk = bck # for compat # endif freed functions def has_p_bit(self): return read_int_from_memory(self.size_addr) & 0x01 def has_m_bit(self): return read_int_from_memory(self.size_addr) & 0x02 def flags_as_string(self): flags = [] if self.has_p_bit(): flags.append(Color.colorify("PREV_INUSE", Config.get_gef_setting("theme.heap_chunk_flag_prev_inuse"))) if self.has_m_bit(): flags.append(Color.colorify("IS_MMAPPED", Config.get_gef_setting("theme.heap_chunk_flag_is_mmapped"))) return "|".join(flags) def to_str(self, is_fastbin=False): chunk_c = Color.colorify("Chunk", Config.get_gef_setting("theme.heap_chunk_label")) size_c = Color.colorify_hex(self.get_chunk_size(), Config.get_gef_setting("theme.heap_chunk_size")) base_c = Color.colorify_hex(self.chunk_base_address, Config.get_gef_setting("theme.heap_chunk_address_freed")) addr_c = Color.colorify_hex(self.address, Config.get_gef_setting("theme.heap_chunk_address_freed")) flags = self.flags_as_string() if is_fastbin: decoded_fd = ProcessMap.lookup_address(self.get_fwd_ptr(sll=True)) fd = self.get_fwd_ptr(sll=False) msg = "{:s}(base={:s}, addr={:s}, size={:s}, flags={:s}, fd={:#x}(={!s})".format( chunk_c, base_c, addr_c, size_c, flags, fd, decoded_fd, ) else: fd = ProcessMap.lookup_address(self.fd) bk = ProcessMap.lookup_address(self.bk) msg = "{:s}(base={:s}, addr={:s}, size={:s}, flags={:s}, fd={!s}, bk={!s})".format( chunk_c, base_c, addr_c, size_c, flags, fd, bk, ) return msg @register_command class UclibcNgHeapDumpCommand(GenericCommand, BufferingOutput): """uclibc-ng (libc/stdlib/malloc-standard) heap reusable chunks viewer (x64/x86 only).""" _cmdline_ = "uclibc-ng-heap-dump" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--malloc_state", type=AddressUtil.parse_address, help="use specific address for malloc_context.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="also dump an empty active index.") _syntax_ = parser.format_help() _note_ = [ "The main structural differences between uclibc-ng (malloc-standard) and glibc are:", "- No tcache. There are fastbins, an unsorted bin, small bins, and large bins.", "- No thread arena. Therefore, chunks do not have the NON_MAIN_ARENA flag.", "The structure of malloc-standard has remained largely unchanged from version 1.0 to the latest.", "As a result, it should be usable with any version.", "Since the final version of uclibc (not uclibc-ng) uses the same structure,", "this command should also be usable with uclibc.", ] _note_ = "\n".join(_note_) fast_size_table = [ # 64bit 32bit ["none", 0x10], ["none", 0x18], [0x20, 0x20], ["none", 0x28], [0x30, 0x30], ["none", 0x38], [0x40, 0x40], ["none", 0x48], [0x50, "none"], ["none", "none"], ["none", None], ] size_table = [ # 64bit 32bit ["none", "none"], ["any", "any"], ["none", (0x10, 0x18)], ["none", (0x18, 0x20)], [(0x20, 0x30), (0x20, 0x28)], ["none", (0x28, 0x30)], [(0x30, 0x40), (0x30, 0x38)], ["none", (0x38, 0x40)], [(0x40, 0x50), (0x40, 0x48)], ["none", (0x48, 0x50)], [(0x50, 0x60), (0x50, 0x58)], ["none", (0x58, 0x60)], [(0x60, 0x70), (0x60, 0x68)], ["none", (0x68, 0x70)], [(0x70, 0x80), (0x70, 0x78)], ["none", (0x78, 0x80)], [(0x80, 0x90), (0x80, 0x88)], ["none", (0x88, 0x90)], [(0x90, 0xa0), (0x90, 0x98)], ["none", (0x98, 0xa0)], [(0xa0, 0xb0), (0xa0, 0xa8)], ["none", (0xa8, 0xb0)], [(0xb0, 0xc0), (0xb0, 0xb8)], ["none", (0xb8, 0xc0)], [(0xc0, 0xd0), (0xc0, 0xc8)], ["none", (0xc8, 0xd0)], [(0xd0, 0xe0), (0xd0, 0xd8)], ["none", (0xd8, 0xe0)], [(0xe0, 0xf0), (0xe0, 0xe8)], ["none", (0xe8, 0xf0)], [(0xf0, 0x100), (0xf0, 0xf8)], ["none", (0xf8, 0x100)], [(0x100, 0x140), (0x100, 0x140)], [(0x140, 0x180), (0x140, 0x180)], [(0x180, 0x1c0), (0x180, 0x1c0)], [(0x1c0, 0x200), (0x1c0, 0x200)], [(0x200, 0x280), (0x200, 0x280)], [(0x280, 0x300), (0x280, 0x300)], [(0x300, 0x380), (0x300, 0x380)], [(0x380, 0x400), (0x380, 0x400)], [(0x400, 0x500), (0x400, 0x500)], [(0x500, 0x600), (0x500, 0x600)], [(0x600, 0x700), (0x600, 0x700)], [(0x700, 0x800), (0x700, 0x800)], [(0x800, 0xa00), (0x800, 0xa00)], [(0xa00, 0xc00), (0xa00, 0xc00)], [(0xc00, 0xe00), (0xc00, 0xe00)], [(0xe00, 0x1000), (0xe00, 0x1000)], [(0x1000, 0x1400), (0x1000, 0x1400)], [(0x1400, 0x1800), (0x1400, 0x1800)], [(0x1800, 0x1c00), (0x1800, 0x1c00)], [(0x1c00, 0x2000), (0x1c00, 0x2000)], [(0x2000, 0x2800), (0x2000, 0x2800)], [(0x2800, 0x3000), (0x2800, 0x3000)], [(0x3000, 0x3800), (0x3000, 0x3800)], [(0x3800, 0x4000), (0x3800, 0x4000)], [(0x4000, 0x5000), (0x4000, 0x5000)], [(0x5000, 0x6000), (0x5000, 0x6000)], [(0x6000, 0x7000), (0x6000, 0x7000)], [(0x7000, 0x8000), (0x7000, 0x8000)], [(0x8000, 0xa000), (0x8000, 0xa000)], [(0xa000, 0xc000), (0xa000, 0xc000)], [(0xc000, 0xe000), (0xc000, 0xe000)], [(0xe000, 0x10000), (0xe000, 0x10000)], [(0x10000, 0x14000), (0x10000, 0x14000)], [(0x14000, 0x18000), (0x14000, 0x18000)], [(0x18000, 0x1c000), (0x18000, 0x1c000)], [(0x1c000, 0x20000), (0x1c000, 0x20000)], [(0x20000, 0x28000), (0x20000, 0x28000)], [(0x28000, 0x30000), (0x28000, 0x30000)], [(0x30000, 0x38000), (0x30000, 0x38000)], [(0x38000, 0x40000), (0x38000, 0x40000)], [(0x40000, 0x50000), (0x40000, 0x50000)], [(0x50000, 0x60000), (0x50000, 0x60000)], [(0x60000, 0x70000), (0x60000, 0x70000)], [(0x70000, 0x80000), (0x70000, 0x80000)], [(0x80000, 0xa0000), (0x80000, 0xa0000)], [(0xa0000, 0xc0000), (0xa0000, 0xc0000)], [(0xc0000, 0xe0000), (0xc0000, 0xe0000)], [(0xe0000, 0x100000), (0xe0000, 0x100000)], [(0x100000, 0x140000), (0x100000, 0x140000)], [(0x140000, 0x180000), (0x140000, 0x180000)], [(0x180000, 0x1c0000), (0x180000, 0x1c0000)], [(0x1c0000, 0x200000), (0x1c0000, 0x200000)], [(0x200000, 0x280000), (0x200000, 0x280000)], [(0x280000, 0x300000), (0x280000, 0x300000)], [(0x300000, 0x380000), (0x300000, 0x380000)], [(0x380000, 0x400000), (0x380000, 0x400000)], [(0x400000, 0x500000), (0x400000, 0x500000)], [(0x500000, 0x600000), (0x500000, 0x600000)], [(0x600000, 0x700000), (0x600000, 0x700000)], [(0x700000, 0x800000), (0x700000, 0x800000)], [(0x800000, 0xa00000), (0x800000, 0xa00000)], [(0xa00000, 0xc00000), (0xa00000, 0xc00000)], [(0xc00000, 0xe00000), (0xc00000, 0xe00000)], [(0xe00000, 0x0), (0xe00000, 0x0)], ] def get_malloc_state(self): # fast path try: return AddressUtil.parse_address("&__malloc_state") except gdb.error: pass # slow path # Do not use AddressUtil.parse_address("malloc"). # For libc without symbols, the malloc@plt of the main binary will be resolved. # You can definitely get the address of malloc by finding the libc path and looking for the GOT of libc itself. libc = ProcessMap.process_lookup_path("libuClibc-") if libc is None: return None ret = gdb.execute("got --no-pager --quiet --file {!r} ".format(libc.path), to_string=True) if not ret: return None elem = Color.remove_color(ret).splitlines()[0].split() if elem[-1].endswith(">"): malloc = int(elem[-2], 16) else: malloc = int(elem[-1], 16) # heuristic search from assembly lines = gdb.execute("x/40i {:#x}".format(malloc), to_string=True) if is_x86_64(): for line in lines.splitlines(): m = re.search(r"\[rip\+0x\w+\].*#\s*(0x\w+)", line) if m: malloc_state = int(m.group(1), 16) if is_valid_addr(malloc_state): max_fast = read_int_from_memory(malloc_state) if max_fast != 0 and (max_fast & ~0x3) <= 0xb0: return malloc_state elif is_x86_32(): base = None regname = None for line in lines.splitlines(): if base is None: m = re.search(r"^\s*(0x\w+).+:\s+add\s+(\S+),\s*(0x\w+)", line) if m: base = int(m.group(1), 16) + int(m.group(3), 16) regname = m.group(2) continue else: m = re.search(r"DWORD PTR \[(\S+)\+(0x\S+)\]", line) if m and m.group(1) == regname: malloc_state = base + int(m.group(2), 16) if is_valid_addr(malloc_state): max_fast = read_int_from_memory(malloc_state) if max_fast != 0 and (max_fast & ~0x3) <= 0xb0: return malloc_state # TODO # - Other architecture # - static build + stripped return None def read_malloc_state(self, specified_malloc_state_ptr): """ struct malloc_state { /* The maximum chunk size to be eligible for fastbin */ size_t max_fast; /* low 2 bits used as flags */ /* Fastbins */ mfastbinptr fastbins[NFASTBINS]; /* Base of the topmost chunk -- not otherwise kept in a bin */ mchunkptr top; /* The remainder from the most recent split of a small request */ mchunkptr last_remainder; /* Normal bins packed as described above */ mchunkptr bins[NBINS * 2]; /* Bitmap of bins. Trailing zero map handles cases of largest binned size */ unsigned int binmap[BINMAPSIZE+1]; /* Tunable parameters */ unsigned long trim_threshold; size_t top_pad; size_t mmap_threshold; /* Memory map support */ int n_mmaps; int n_mmaps_max; int max_n_mmaps; /* Cache malloc_getpagesize */ unsigned int pagesize; /* Track properties of MORECORE */ unsigned int morecore_properties; /* Statistics */ size_t mmapped_mem; size_t sbrked_mem; size_t max_sbrked_mem; size_t max_mmapped_mem; size_t max_total_mem; }; """ malloc_state = {} if specified_malloc_state_ptr is None: malloc_state["address"] = current = self.get_malloc_state() if current is None: return None else: malloc_state["address"] = current = specified_malloc_state_ptr malloc_state["max_fast"] = max_fast = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["max_fast_flags"] = [] if max_fast & 1: malloc_state["max_fast_flags"] += ["ANYCHUNKS_BIT"] if max_fast & 2: malloc_state["max_fast_flags"] += ["FASTCHUNKS_BIT"] if is_64bit(): self.NFASTBINS = 11 else: self.NFASTBINS = 10 malloc_state["fastbins"] = [] for i in range(self.NFASTBINS): n = read_int_from_memory(current) size = self.fast_size_table[i][is_32bit()] malloc_state["fastbins"].append((current, n, size)) current += current_arch.ptrsize malloc_state["top"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["last_remainder"] = read_int_from_memory(current) current += current_arch.ptrsize self.NBINS = 96 self.NSMALLBINS = 32 self.NLARGEBINS = self.NBINS - self.NSMALLBINS malloc_state["smallbins"] = [] for i in range(self.NSMALLBINS): n = read_int_from_memory(current) p = read_int_from_memory(current + current_arch.ptrsize) size = self.size_table[i][is_32bit()] malloc_state["smallbins"].append((current, n, p, size)) current += current_arch.ptrsize * 2 malloc_state["largebins"] = [] for i in range(self.NLARGEBINS): n = read_int_from_memory(current) p = read_int_from_memory(current + current_arch.ptrsize) size = self.size_table[i + self.NSMALLBINS][is_32bit()] malloc_state["largebins"].append((current, n, p, size)) current += current_arch.ptrsize * 2 self.BINMAPSIZE = 3 malloc_state["binmap"] = [] for _ in range(self.BINMAPSIZE + 1): x = read_int32_from_memory(current) malloc_state["binmap"].append(x) current += 4 malloc_state["trim_threshold"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["top_pad"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["mmap_threshold"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["n_mmaps"] = read_int32_from_memory(current) current += 4 malloc_state["n_mmaps_max"] = read_int32_from_memory(current) current += 4 malloc_state["max_n_mmaps"] = read_int32_from_memory(current) current += 4 malloc_state["pagesize"] = read_int32_from_memory(current) current += 4 malloc_state["morecore_properties"] = morecore_properties = read_int32_from_memory(current) current += 4 malloc_state["morecore_properties_flags"] = [] if morecore_properties & 1: malloc_state["morecore_properties_flags"] += ["MORECORE_CONTIGUOUS_BIT"] if is_64bit(): current += 4 # pad malloc_state["mmaped_mem"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["sbrked_mem"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["max_sbrked_mem"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["max_mmaped_mem"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_state["max_total_mem"] = read_int_from_memory(current) current += current_arch.ptrsize try: top_sz = read_int_from_memory(malloc_state["top"] + current_arch.ptrsize) & ~0b11 heap_end = malloc_state["top"] + top_sz malloc_state["heap_base"] = heap_end - malloc_state["sbrked_mem"] except gdb.MemoryError: malloc_state["heap_base"] = 0 MallocState = collections.namedtuple("MallocState", malloc_state.keys()) return MallocState(*malloc_state.values()) def dump_malloc_state(self, malloc_state): chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") self.verbose_add_out("malloc_state: {!s}".format(ProcessMap.lookup_address(malloc_state.address))) max_fast_flags = "|".join(malloc_state.max_fast_flags) self.verbose_add_out("max_fast: {:#x} ({:s})".format(malloc_state.max_fast, max_fast_flags)) self.out.append(titlify("Fast Bins")) for i in range(self.NFASTBINS): addr, n, size = malloc_state.fastbins[i] if n != 0 or self.args.verbose: if isinstance(size, int): colored_size = Color.colorify("{:#4x}".format(size), chunk_size_color) else: colored_size = Color.colorify(size, chunk_size_color) self.out.append("fastbins[idx={:d}, size={:s}, @{!s}]: fd={!s}".format( i, colored_size, ProcessMap.lookup_address(addr), ProcessMap.lookup_address(n), )) if n != 0: seen = [] while is_valid_addr(n) and n not in seen: seen.append(n) chunk = uClibcNgHeap.uClibcChunk(n, from_base=True) self.out.append(" -> {}".format(chunk.to_str(is_fastbin=True))) n = chunk.get_fwd_ptr(True) self.verbose_add_out("top: {!s}".format(ProcessMap.lookup_address(malloc_state.top))) self.verbose_add_out("last_remainder: {!s}".format(ProcessMap.lookup_address(malloc_state.last_remainder))) self.out.append(titlify("Unsorted Bin / Small Bins")) for i in range(len(malloc_state.smallbins)): addr, n, p, size = malloc_state.smallbins[i] if (n and addr - current_arch.ptrsize * 2 != n) or self.args.verbose: if isinstance(size, tuple): colored_size = Color.colorify("{:#x}-{:#x}".format(*size), chunk_size_color) else: colored_size = Color.colorify(size, chunk_size_color) self.out.append("{:s}[idx={:d}, size={:s}, @{!s}]: fd={!s}, bk={!s}".format( ["small_bins", "unsorted_bin"][i == 1], i, colored_size, ProcessMap.lookup_address(addr), ProcessMap.lookup_address(n), ProcessMap.lookup_address(p), )) if n and addr - current_arch.ptrsize * 2 != n: seen = [addr - current_arch.ptrsize * 2] while is_valid_addr(n) and n not in seen: seen.append(n) chunk = uClibcNgHeap.uClibcChunk(n, from_base=True) self.out.append(" -> {}".format(chunk.to_str())) n = chunk.fwd self.out.append(titlify("Large Bins")) for i in range(len(malloc_state.largebins)): addr, n, p, size = malloc_state.largebins[i] if addr - current_arch.ptrsize * 2 != n or self.args.verbose: if isinstance(size, tuple): colored_size = Color.colorify("{:#x}-{:#x}".format(*size), chunk_size_color) else: colored_size = Color.colorify(size, chunk_size_color) self.out.append("large_bins[idx={:d}, size={:s}, @{!s}]: fd={!s}, bk={!s}".format( self.NSMALLBINS + i, colored_size, ProcessMap.lookup_address(addr), ProcessMap.lookup_address(n), ProcessMap.lookup_address(p), )) if addr - current_arch.ptrsize * 2 != n: seen = [addr - current_arch.ptrsize * 2] while is_valid_addr(n) and n not in seen: seen.append(n) chunk = uClibcNgHeap.uClibcChunk(n, from_base=True) self.out.append(" -> {}".format(chunk.to_str())) n = chunk.fwd for i in range(self.BINMAPSIZE + 1): self.verbose_add_out("binmap[{:d}]: {:#x}".format(i, malloc_state.binmap[i])) self.verbose_add_out("trim_threshold: {:#x}".format(malloc_state.trim_threshold)) self.verbose_add_out("top_pad: {:#x}".format(malloc_state.top_pad)) self.verbose_add_out("mmap_threshold: {:#x}".format(malloc_state.mmap_threshold)) self.verbose_add_out("n_mmaps: {:#x}".format(malloc_state.n_mmaps)) self.verbose_add_out("n_mmaps_max: {:#x}".format(malloc_state.n_mmaps_max)) self.verbose_add_out("max_n_mmaps: {:#x}".format(malloc_state.max_n_mmaps)) self.verbose_add_out("pagesize: {:#x}".format(malloc_state.pagesize)) mp_flags = "|".join(malloc_state.morecore_properties_flags) self.verbose_add_out("morecore_properties: {:#x} ({:s})".format(malloc_state.morecore_properties, mp_flags)) self.verbose_add_out("mmaped_mem: {:#x}".format(malloc_state.mmaped_mem)) self.verbose_add_out("sbrked_mem: {:#x}".format(malloc_state.sbrked_mem)) self.verbose_add_out("max_sbrked_mem: {:#x}".format(malloc_state.max_sbrked_mem)) self.verbose_add_out("max_mmaped_mem: {:#x}".format(malloc_state.max_mmaped_mem)) self.verbose_add_out("max_total_mem: {:#x}".format(malloc_state.max_total_mem)) self.verbose_add_out("(heap_base): {:#x}".format(malloc_state.heap_base)) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): self.out = [] malloc_state = self.read_malloc_state(args.malloc_state) if malloc_state is None: err("Could not find malloc_state") return self.dump_malloc_state(malloc_state) self.print_output() return @register_command class UclibcNgVisualHeapCommand(UclibcNgHeapDumpCommand, BufferingOutput): """Visualize chunks on a heap for uClibc-ng.""" _cmdline_ = "uclibc-ng-visual-heap" _category_ = "05-c. Heap - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", nargs="?", type=AddressUtil.parse_address, help="the address interpreted as the beginning of a contiguous chunk. (default: [heap] of vmmap)") parser.add_argument("--malloc_state", type=AddressUtil.parse_address, help="use specific address for malloc_context.") parser.add_argument("-c", dest="max_count", type=AddressUtil.parse_address, help="Maximum count to parse. It is used when there is a very large amount of chunks.") parser.add_argument("-f", "--full", action="store_true", help="display the same line without omitting.") parser.add_argument("-d", "--dark-color", action="store_true", help="use the dark color if chunk is allocated.") parser.add_argument("-s", "--safe-linking-decode", action="store_true", help="decode safe-linking encoded pointer if tcache or fastbins.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() normal_colors = [ Color.redify, Color.greenify, Color.blueify, Color.yellowify, ] dark_colors = [ lambda x: Color.colorify(x, "bright_black"), lambda x: Color.colorify(x, "graphite"), ] def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def init_bins_info(self, malloc_state): self.bins_info = { "fastbins": {}, "small_bins": {}, "large_bins": {}, } # fastbins for i in range(self.NFASTBINS): addr, n, size = malloc_state.fastbins[i] seen = [] while n and n not in seen: seen.append(n) try: chunk = uClibcNgHeap.uClibcChunk(n, from_base=True) n = chunk.get_fwd_ptr(True) except gdb.MemoryError: break self.bins_info["fastbins"][i] = seen # smallbins / unsortedbin for i in range(len(malloc_state.smallbins)): addr, n, p, size = malloc_state.smallbins[i] seen = [] while n and addr - current_arch.ptrsize * 2 != n and n not in seen: seen.append(n) try: chunk = uClibcNgHeap.uClibcChunk(n, from_base=True) n = chunk.fwd except gdb.MemoryError: break self.bins_info["small_bins"][i] = seen # largebins for i in range(len(malloc_state.largebins)): addr, n, p, size = malloc_state.largebins[i] seen = [] while n and addr - current_arch.ptrsize * 2 != n and n not in seen: seen.append(n) try: chunk = uClibcNgHeap.uClibcChunk(n, from_base=True) n = chunk.fwd except gdb.MemoryError: break self.bins_info["large_bins"][i] = seen # make table # dict[address] = ["bins info1", "bins info2", ...] self.bins_dict_for_address = {} for fastbin_idx, fastbin_list in self.bins_info["fastbins"].items(): for address in fastbin_list: pos = ",".join([str(i + 1) for i, x in enumerate(fastbin_list) if x == address]) sz = self.fast_size_table[fastbin_idx][is_32bit()] m = "fastbins[idx={:d},sz={:#x}][{:s}/{:d}]".format(fastbin_idx, sz, pos, len(fastbin_list)) self.bins_dict_for_address[address] = self.bins_dict_for_address.get(address, []) + [m] for smallbin_idx, smallbin_list in self.bins_info["small_bins"].items(): for address in smallbin_list: pos = ",".join([str(i + 1) for i, x in enumerate(smallbin_list) if x == address]) if smallbin_idx == 0: m = "unsortedbins[{:s}/{:d}]".format(pos, len(smallbin_list)) else: size = self.size_table[smallbin_idx][is_32bit()] if isinstance(size, tuple): sz = "{:#x}-{:#x}".format(size[0], size[1]) else: sz = size m = "smallbins[idx={:d},sz={:s}][{:s}/{:d}]".format(smallbin_idx, sz, pos, len(smallbin_list)) self.bins_dict_for_address[address] = self.bins_dict_for_address.get(address, []) + [m] for largebin_idx, largebin_list in self.bins_info["large_bins"].items(): for address in largebin_list: pos = ",".join([str(i + 1) for i, x in enumerate(largebin_list) if x == address]) size = self.size_table[self.NSMALLBINS + largebin_idx][is_32bit()] if isinstance(size, tuple): sz = "{:#x}-{:#x}".format(size[0], size[1]) else: sz = size m = "largebins[idx={:d},sz={:s}][{:s}/{:d}]".format( self.NSMALLBINS + largebin_idx, sz, pos, len(largebin_list), ) self.bins_dict_for_address[address] = self.bins_dict_for_address.get(address, []) + [m] return def get_bins_info(self, malloc_state, address): info = self.bins_dict_for_address.get(address, []) if address == malloc_state.top: info.append("top") return info def generate_visual_chunk(self, malloc_state, chunk, idx): unpack = u32 if current_arch.ptrsize == 4 else u64 data = slicer(chunk.data, current_arch.ptrsize * 2) group_line_threshold = 8 addr = chunk.chunk_base_address width = current_arch.ptrsize * 2 + 2 exceed_top = False has_bins_info = False out_tmp = [] # Group rows to display rows with the same value together. prev_bins_info = "" for blk, blks in itertools.groupby(data): repeat_count = len(list(blks)) d1, d2 = unpack(blk[:current_arch.ptrsize]), unpack(blk[current_arch.ptrsize:]) dascii = "".join([chr(x) if 0x20 <= x < 0x7f else "." for x in blk]) if self.args.full or repeat_count < group_line_threshold: # non-collapsed line for _ in range(repeat_count): bins_info = self.get_bins_info(malloc_state, addr) if bins_info: bins_info = " <- {:s}".format(", ".join(bins_info)) has_bins_info = True else: bins_info = "" if self.args.safe_linking_decode: if chunk.address == addr and "fastbins" in prev_bins_info: d1 = chunk.get_fwd_ptr(True) offset1 = addr - chunk.chunk_base_address offset2 = addr - malloc_state.heap_base out_tmp.append("{:#x}|{:+#08x}|{:+#08x}: {:#0{:d}x} {:#0{:d}x} | {:s} | {:s}".format( addr, offset1, offset2, d1, width, d2, width, dascii, bins_info, ).rstrip()) addr += current_arch.ptrsize * 2 prev_bins_info = bins_info if addr > malloc_state.top + current_arch.ptrsize * 4: exceed_top = True break else: # collapsed line bins_info = self.get_bins_info(malloc_state, addr) if bins_info: bins_info = " <- {:s}".format(", ".join(bins_info)) has_bins_info = True else: bins_info = "" offset1 = addr - chunk.chunk_base_address offset2 = addr - malloc_state.heap_base out_tmp.append("{:#x}|{:+#08x}|{:+#08x}: {:#0{:d}x} {:#0{:d}x} | {:s} | {:s}".format( addr, offset1, offset2, d1, width, d2, width, dascii, bins_info, ).rstrip()) addr += current_arch.ptrsize * 2 * repeat_count out_tmp.append("* {:#d} lines, {:#x} bytes".format( repeat_count - 1, (repeat_count - 1) * current_arch.ptrsize * 2, )) prev_bins_info = bins_info if exceed_top: break # coloring if self.args.dark_color and not has_bins_info: color_func = self.dark_colors[idx % len(self.dark_colors)] else: color_func = self.normal_colors[idx % len(self.normal_colors)] self.out.append("\n".join(map(color_func, out_tmp))) # corrupted case if exceed_top: self.out.append(Color.boldify("...")) return def generate_visual_heap(self, malloc_state, dump_start, max_count): sect = ProcessMap.process_lookup_address(dump_start) if sect: end = sect.page_end else: # If qemu-user 8.1 or higher, the process_lookup_address to obtain the section list uses # info proc mappings internally. # This is fast, but does not return an accurate list in some cases. # For example, sparc64 may not include the heap area. # So it detects the end of the page from malloc_state.top. end = malloc_state.top + uClibcNgHeap.uClibcChunk(malloc_state.top, from_base=True).size try: from tqdm import tqdm except ImportError: tqdm = None if tqdm: pbar = tqdm(total=end - dump_start, leave=False) addr = dump_start i = 0 while addr < end: chunk = uClibcNgHeap.uClibcChunk(addr + current_arch.ptrsize * 2) # corrupt check if chunk.size == 0: msg = "{} Corrupted (chunk.size == 0)".format(Color.colorify("[!]", "bold red")) self.out.append(msg) chunk.data = read_memory(addr, max(malloc_state.top - addr + 0x10, 0)) self.generate_visual_chunk(malloc_state, chunk, i) break elif addr != malloc_state.top and addr + chunk.size > malloc_state.top: msg = "{} Corrupted (addr + chunk.size > malloc_state.top)".format(Color.colorify("[!]", "bold red")) self.out.append(msg) chunk.data = read_memory(addr, max(malloc_state.top - addr + 0x10, 0)) self.generate_visual_chunk(malloc_state, chunk, i) break elif addr + chunk.size > end: msg = "{} Corrupted (addr + chunk.size > sect.page_end)".format(Color.colorify("[!]", "bold red")) self.out.append(msg) chunk.data = read_memory(addr, max(malloc_state.top - addr + 0x10, 0)) self.generate_visual_chunk(malloc_state, chunk, i) break # maybe not corrupted try: chunk.data = read_memory(addr, chunk.size) except gdb.MemoryError: break self.generate_visual_chunk(malloc_state, chunk, i) addr += chunk.size i += 1 if tqdm: pbar.update(chunk.size) if max_count and max_count <= i: break if tqdm: pbar.close() return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): malloc_state = self.read_malloc_state(args.malloc_state) if malloc_state is None: err("Could not find malloc_state") return if malloc_state.heap_base is None or not is_valid_addr(malloc_state.heap_base): err("Could not find the heap base") return self.init_bins_info(malloc_state) if args.location is None: dump_start = malloc_state.heap_base else: dump_start = args.location self.out = [] Cache.reset_gef_caches(all=True) self.generate_visual_heap(malloc_state, dump_start, args.max_count) self.print_output() return @register_command class XStringCommand(GenericCommand, BufferingOutput): """Dump string like x/s command, but with hex-string style.""" _cmdline_ = "xs" _category_ = "03-b. Memory - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("count", metavar="COUNT", nargs="?", help="repeat count for displaying.") parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="dump target address.") parser.add_argument("-l", "--max-length", type=AddressUtil.parse_address, help="maximum number of characters to display. 0 means unlimited.") parser.add_argument("-H", "--hex", action="store_true", help="show in hex style.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet mode.") _syntax_ = parser.format_help() def dump_string(self, address, count, max_length, tohex, quiet): for _ in range(count): if not is_valid_addr(address): err("Memory read error at {:#x}".format(address)) break # read string current = address size = get_pagesize() - (address & get_pagesize_mask_low()) s = b"" while True: # check accessibility if not is_valid_addr(current): break # read string s += read_memory(current, size) pos = s.find(b"\0") if pos != -1: s = s[:pos] break # not found 0x0, read more current += size size = get_pagesize() # cut off if max_length and len(s) >= max_length: cs = s[:max_length] + b"..." else: cs = s if tohex: cs = cs.hex() else: cs = repr(cs) if quiet: self.out.append("{:s}".format(cs)) else: self.out.append("{!s}: {:s} ({:#x} bytes)".format( ProcessMap.lookup_address(address), cs, len(s), )) # go to next address if pos == -1: address += 1 else: address += pos + 1 return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.count is None: count = 1 else: count = args.count if args.count.startswith("/"): count = count[1:] if args.count.endswith("s"): count = count[:-1] try: count = int(count) except ValueError: err("Failed to parse: {}".format(args.count)) return if args.max_length is not None: max_length = args.max_length else: max_length = Config.get_gef_setting("context.nb_max_string_length") self.out = [] self.dump_string(args.address, count, max_length, args.hex, args.quiet) self.print_output(check_terminal_size=True) return @register_command class XColoredCommand(GenericCommand, BufferingOutput): """Dump address like x/x command, but with coloring at some intervals.""" _cmdline_ = "xc" _category_ = "03-b. Memory - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("format", metavar="FMT", nargs="?", default="", help="dump format.") parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="dump address.") parser.add_argument("-i", "--interval", type=AddressUtil.parse_address, help="the line of interval for coloring.") parser.add_argument("-c", "--color-num", type=AddressUtil.parse_address, default=4, help="the number of colors used (1-5).") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet mode.") _syntax_ = parser.format_help() colors = [ Color.greenify, Color.redify, Color.blueify, Color.yellowify, Color.cyanify, ] @parse_args @only_if_gdb_running def do_invoke(self, args): if args.color_num < 1 or len(self.colors) < args.color_num: err("Invalid --color-num") return try: ret = gdb.execute("x{:s} {:#x}".format(args.format, args.address), to_string=True) ret = ret.strip() except gdb.error as e: err(e) return self.out = [] for i, line in enumerate(ret.splitlines()): if args.interval and args.interval > 0: color_func = self.colors[:args.color_num][(i // args.interval) % args.color_num] else: color_func = self.colors[:args.color_num][0] self.out.append(color_func(line)) self.print_output(check_terminal_size=True) return @register_command class XphysAddrCommand(GenericCommand): """Dump physical memory taking into account ROM mapping.""" _cmdline_ = "xp" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("format", metavar="/FMT", help="specified output format.") parser.add_argument("location", metavar="ADDRESS", type=AddressUtil.parse_address, help="dump target address.") _syntax_ = parser.format_help() _example_ = [ "{0:s} /16xg 0x11223344", ] _example_ = "\n".join(_example_).format(_cmdline_) @staticmethod def print_fmt_i(target, data, count): kwargs = {} kwargs["code"] = data.hex() if is_x86_32(): kwargs["arch"] = "X86" kwargs["mode"] = "32" elif is_x86_64(): kwargs["arch"] = "X86" kwargs["mode"] = "64" elif is_arm32(): kwargs["arch"] = "ARM" if target & 1: kwargs["mode"] = "THUMB" else: kwargs["mode"] = "ARM" elif is_arm64(): kwargs["arch"] = "ARM64" kwargs["mode"] = "ARM" out = [] try: for insn in Disasm.capstone_disassemble(target, count, **kwargs): msg = " {:s}".format(insn.colored_text(12, highlight=False)) out.append(msg) except gdb.error: pass out = "\n".join(out) return out @staticmethod def parse_type_unit_count(fmt): m = re.search(r"/(\d*)([xibhwg]*)", fmt) if not m: return None dump_type = "x" dump_unit = current_arch.ptrsize dump_count = 1 if m.group(1): dump_count = int(m.group(1)) for c in m.group(2): if c in ["x", "i"]: dump_type = c elif c in ["b", "h", "w", "g"]: dump_unit = {"b": 1, "h": 2, "w": 4, "g": 8}[c] else: err("Unsupported format: {}".format(c)) return None return dump_type, dump_unit, dump_count @staticmethod def fix_size_and_target(dump_type, dump_unit, dump_count, target): if dump_type == "x": dump_size = dump_count * dump_unit return dump_size, target if dump_type == "i": if is_x86(): # The length is unknown, so it is read in 10-byte chunks. dump_size = dump_count * 10 return dump_size, target if target & 1: # fix thumb2 if is_arm32(): target -= 1 else: err("Unsupported odd address: {}".format(target)) return None # ARM opcode is at most 4byte dump_size = dump_count * 4 return dump_size, target return None @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64", "RISCV32", "RISCV64")) def do_invoke(self, args): # arg parse ret = XphysAddrCommand.parse_type_unit_count(args.format) if ret is None: self.usage() return dump_type, dump_unit, dump_count = ret # fix for size and target (when thumb2) ret = XphysAddrCommand.fix_size_and_target(dump_type, dump_unit, dump_count, args.location) if ret is None: return dump_size, target = ret # read data = read_physmem(target, dump_size) if data is None: err("Memory read error") return # print if dump_type == "x": out = hexdump(data, show_symbol=False, base=args.location, unit=dump_unit) elif dump_type == "i": out = XphysAddrCommand.print_fmt_i(args.location, data, dump_count) gef_print(out) return @register_command class XSecureMemAddrCommand(GenericCommand): """Dump secure memory via qemu-system memory map.""" _cmdline_ = "xsm" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--phys", action="store_true", help="treat ADDRESS as a physical address.") group.add_argument("--off", action="store_true", help="treat ADDRESS as an offset of secure memory top.") group.add_argument("--virt", action="store_true", help="treat ADDRESS as a virtual address.") parser.add_argument("format", metavar="/FMT", help="specified output format.") parser.add_argument("location", metavar="ADDRESS", type=AddressUtil.parse_address, help="dump target address.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") _syntax_ = parser.format_help() _example_ = [ "{0:s} /16xw --phys 0xe11e3d0 # absolute (physical/non-ASLR) address of secure memory", "{0:s} /16xw --off 0x11e3d0 # the offset from secure memory area", "{0:s} /16xw --virt 0x783ae3d0 # secure memory ASLR is supported", ] _example_ = "\n".join(_example_).format(_cmdline_) @staticmethod def v2p_secure(vaddr, verbose=False): # vaddr -> addr1 or None maps = PageMap.get_page_maps(FORCE_PREFIX_S=True, verbose=verbose) if maps is None: return None for vstart, vend, pstart, _pend in maps: if vstart <= vaddr < vend: offset = vaddr - vstart paddr = pstart + offset if verbose: info("v2p: {:#x} -> {:#x}".format(vaddr, paddr)) return paddr return None @staticmethod def p2v_secure(paddr, verbose=False): # paddr -> [addr1, addr2, ...] or [] maps = PageMap.get_page_maps(FORCE_PREFIX_S=True, verbose=verbose) if maps is None: return [] result = [] for vstart, _vend, pstart, pend in maps: if pstart <= paddr < pend: offset = paddr - pstart vaddr = vstart + offset if verbose: info("p2v: {:#x} -> {:#x}".format(paddr, vaddr)) result.append(vaddr) return result @staticmethod def read_secure_memory(sm, offset, dump_size, verbose=False): qemu_system_pid = Pid.get_pid() if qemu_system_pid is None: err("Could not find qemu-system pid") return None if dump_size > sm.size: dump_size = sm.size if verbose: info("Target offset: {:#x}".format(offset)) info("Read address: {:#x}, size:{:#x}".format(sm.page_start + offset, dump_size)) with open("/proc/{:d}/mem".format(qemu_system_pid), "rb") as fd: try: fd.seek(sm.page_start + offset, 0) data = fd.read(dump_size) except Exception: return None if verbose: info("Read size result: {:#x}".format(len(data))) return data @staticmethod def get_sm_offset(sm, args): if args.phys: if sm.sm_base <= args.location < sm.sm_base + sm.sm_size: return args.location - sm.sm_base err("Phys {:#x} is not default secure memory ({:#x}-{:#x})".format( args.location, sm.sm_base, sm.sm_base + sm.sm_size, )) return None elif args.off: if 0 <= args.location < sm.size: return args.location err("Offset {:#x} is not default secure memory ({:#x}-{:#x})".format( args.location, sm.sm_base, sm.sm_base + sm.sm_size, )) return None elif args.virt: target_phys = XSecureMemAddrCommand.v2p_secure(args.location, args.verbose) if target_phys is None: err("Could not find physical address") return None if sm.sm_base <= target_phys < sm.sm_base + sm.sm_size: return target_phys - sm.sm_base err("Virt {:#x} is not default secure memory ({:#x}-{:#x})".format( args.location, sm.sm_base, sm.sm_base + sm.sm_size, )) return None return None def redirect_to_xp(self, dump_count, dump_type, dump_unit): if self.args.off: return if self.args.phys: phys_addr = self.args.location info("Redirect to xp command") elif self.args.virt: maps = PageMap.get_page_maps_arm64_optee_secure_memory() for m in maps: if m[2] == 0: continue if m[0] <= self.args.location < m[1]: phys_base = m[2] offset = self.args.location - m[0] phys_addr = phys_base + offset info("Redirect to xp command (virt:{:#x} -> phys:{:#x})".format( self.args.location, phys_addr, )) break else: return gdb.execute("xp/{:d}{:s}{:s} {:#x}".format( dump_count, dump_type, {1: "b", 2: "h", 4: "w", 8: "g"}[dump_unit], phys_addr, )) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32", "ARM64")) def do_invoke(self, args): # arg parse ret = XphysAddrCommand.parse_type_unit_count(args.format) if ret is None: self.usage() return dump_type, dump_unit, dump_count = ret # get offset sm = QemuMonitor.get_secure_memory_map(args.verbose) if sm is None: err("Could not find secure memory maps") return target_offset = XSecureMemAddrCommand.get_sm_offset(sm, args) if target_offset is None: self.redirect_to_xp(dump_count, dump_type, dump_unit) return # fix for size and offset (when thumb2) ret = XphysAddrCommand.fix_size_and_target(dump_type, dump_unit, dump_count, target_offset) if ret is None: return dump_size, target_offset = ret # read data = XSecureMemAddrCommand.read_secure_memory(sm, target_offset, dump_size, args.verbose) if data is None: err("Memory read error") return # print if dump_type == "x": out = hexdump(data, show_symbol=False, base=args.location, unit=dump_unit) elif dump_type == "i": out = XphysAddrCommand.print_fmt_i(args.location, data, dump_count) gef_print(out) return class TemporaryDummyBreakpoint(gdb.Breakpoint): """Create a breakpoint to avoid gdb cache problem.""" # The wsm command directly modifies /proc//mem of qemu-system. # However, even when the memory modification succeeds, the change may not be reflected in code behavior. # The cause is unknown; one possible explanation is qemu's internal caching. # Setting a breakpoint appears to bypass this cache, so a temporary breakpoint is used as a workaround. def __init__(self): super().__init__("*{:#x}".format(0x0), type=gdb.BP_BREAKPOINT, internal=True, temporary=True) return def stop(self): EventHandler.__gef_check_disabled_bp__ = True self.enabled = False return False @register_command class WSecureMemAddrCommand(GenericCommand): """Write secure memory via qemu-system memory map.""" _cmdline_ = "wsm" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) modes = ["byte", "short", "dword", "qword", "string", "hex"] parser.add_argument("mode", choices=modes, help="the mode that represents the value of the argument.") group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--phys", action="store_true", help="treat ADDRESS as a physical address.") group.add_argument("--off", action="store_true", help="treat ADDRESS as an offset of secure memory top.") group.add_argument("--virt", action="store_true", help="treat ADDRESS as a virtual address.") parser.add_argument("value", metavar="VALUE", help="write value.") parser.add_argument("location", metavar="ADDRESS", type=AddressUtil.parse_address, help="write target address.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") _syntax_ = parser.format_help() _example_ = [ "{0:s} dword 0x41414141 --phys 0xe11e3d0 # absolute (physical/non-ASLR) address of secure memory", '{0:s} string "AA\\\\x41\\\\x41" --off 0x11e3d0 # the offset of secure memory', '{0:s} hex "4141 4141" --off 0x11e3d0 # hex string is supported (invalid character is ignored)', "{0:s} byte 0x41 --virt 0x783ae3d0 # secure memory ASLR is supported", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete="use_user_complete") return def complete(self, text, word): # noqa if text.strip() in self.modes: # already matched return [] if text == "": # no prefix return [s for s in self.modes if ((word is None) or (s and word in s))] # finally, look for possible values for given prefix return [s for s in self.modes if s and s.startswith(text.strip())] @staticmethod def write_secure_memory(sm, offset, data, verbose=False): qemu_system_pid = Pid.get_pid() if qemu_system_pid is None: return None write_size = len(data) if write_size > sm.size: write_size = sm.size data = data[:write_size] if verbose: info("Target offset: {:#x}".format(offset)) info("Write address: {:#x}, size:{:#x}".format(sm.page_start + offset, write_size)) with open("/proc/{:d}/mem".format(qemu_system_pid), "r+b") as fd: try: fd.seek(sm.page_start + offset, 0) ret = fd.write(data) except Exception: return None if verbose: info("Written size result: {:#x}".format(ret)) # avoid qemu-system caches TemporaryDummyBreakpoint() # By default, "context code" uses Disasm.gdb_disassemble. # However, due to gdb's cache, secure memory changes may not appear in disassembly. # Therefore, if capstone is available, change it to disassemble by capstone. if Config.get_gef_setting("context_code.use_capstone") is False: Config.set_gef_setting("context_code.use_capstone", True) return ret def redirect_to_write_physmem(self, data): if self.args.off: return if self.args.phys: phys_addr = self.args.location info("Redirect to write_physmem") elif self.args.virt: maps = PageMap.get_page_maps_arm64_optee_secure_memory() for m in maps: if m[2] == 0: continue if m[0] <= self.args.location < m[1]: phys_base = m[2] offset = self.args.location - m[0] phys_addr = phys_base + offset info("Redirect to write_physmem (virt:{:#x} -> phys:{:#x})".format( self.args.location, phys_addr, )) break else: return try: write_physmem(phys_addr, data) except Exception: err("Failed to write adata") return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32", "ARM64")) def do_invoke(self, args): try: if args.mode == "byte": data = p8(int(args.value, 0)) elif args.mode == "short": data = p16(int(args.value, 0)) elif args.mode == "dword": data = p32(int(args.value, 0)) elif args.mode == "qword": data = p64(int(args.value, 0)) elif args.mode == "string": try: data = codecs.escape_decode(args.value)[0] except binascii.Error: err('Could not decode "\\xXX" encoded string') return elif args.mode == "hex": data = "" for c in args.value.lower(): if c in "0123456789abcdef": data += c data = bytes.fromhex(data) except Exception: self.usage() return # initialize sm = QemuMonitor.get_secure_memory_map(args.verbose) if sm is None: err("Could not find secure memory maps") return target_offset = XSecureMemAddrCommand.get_sm_offset(sm, args) if target_offset is None: self.redirect_to_write_physmem(data) return # write ret = WSecureMemAddrCommand.write_secure_memory(sm, target_offset, data, args.verbose) if ret is None: err("Memory write error") return @register_command class BreakSecureMemAddrCommand(GenericCommand): """Set a breakpoint in virtual memory by specifying the physical memory of the secure world.""" _cmdline_ = "bsm" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="PHYS_ADDRESS", type=AddressUtil.parse_address, help="the target physical address to set a breakpoint.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0xe1008d8", ] _example_ = "\n".join(_example_).format(_cmdline_) def aarch64_get_page_maps_el3(self): res = PageMap.get_page_maps_by_pagewalk("pagewalk 3 --quiet --no-pager --no-merge --disable-color") res = sorted(set(res.splitlines())) res = list(filter(lambda line: line.endswith("]"), res)) res = list(filter(lambda line: "[+]" not in line, res)) maps = [] for line in res: vrange, prange, *_ = line.split() vstart, vend = [int(x, 16) for x in vrange.split("-")] pstart, pend = [int(x, 16) for x in prange.split("-")] maps.append((vstart, vend, pstart, pend)) if maps == []: warn("Make sure you are in EL1 (=kernel mode)") warn("Make sure qemu 3.x or higher") return None return maps def aarch64_switch_el(self, target_el): cpsr = get_register("$cpsr") & 0xffff_ffff current_el = int((cpsr >> 2) & 0b11) if target_el == current_el: info("Current EL{:d} == Target EL{:d}".format(current_el, target_el)) return 0 # change EL try: saved_cpsr = cpsr cpsr = cpsr & ~(0b11 << 2) # clear EL cpsr |= target_el << 2 # set desired EL gdb.parse_and_eval("$cpsr = {:#x}".format(cpsr)) info("Moving to EL{:d}".format(target_el)) except gdb.error: err("Maybe unsupported to change to EL{:d}".format(target_el)) return 0 return saved_cpsr def aarch64_revert_el(self, saved_cpsr): if saved_cpsr == 0: return gdb.parse_and_eval("$cpsr = {:#x}".format(saved_cpsr)) saved_el = (saved_cpsr >> 2) & 0b11 info("Moving back to EL{:d}".format(saved_el)) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32", "ARM64")) def do_invoke(self, args): if args.verbose: info("Phys address: {:#x}".format(args.location)) if is_arm64(): maps = self.aarch64_get_page_maps_el3() if maps: virt_addrs = PageMap.p2v_from_map(args.location, maps) # change to EL3 and set bp saved_cpsr = self.aarch64_switch_el(target_el=3) for virt_addr in virt_addrs: gdb.execute("break *{:#x}".format(virt_addr)) self.aarch64_revert_el(saved_cpsr) # found any, fast return if virt_addrs: return virt_addrs = XSecureMemAddrCommand.p2v_secure(args.location, args.verbose) if virt_addrs == []: warn("Could not find virtual address") return for virt_addr in virt_addrs: gdb.execute("break *{:#x}".format(virt_addr)) return class OpteeThreadEnterUserModeBreakpoint(gdb.Breakpoint): """Create a breakpoint to thread_enter_user_mode.""" def __init__(self, vaddr, ta_offset, verbose): super().__init__("*{:#x}".format(vaddr), type=gdb.BP_BREAKPOINT, internal=True) self.ta_offset = ta_offset self.verbose = verbose return @staticmethod def get_ta_loaded_address(verbose=False): Cache.reset_gef_caches() if is_arm32(): res = PageMap.get_page_maps_by_pagewalk("pagewalk -S --quiet --no-pager --disable-color") if verbose: gef_print(res) res = sorted(set(res.splitlines())) res = list(filter(lambda line: "PL0/R-X" in line, res)) elif is_arm64(): res = PageMap.get_page_maps_by_pagewalk("pagewalk 1 --quiet --no-pager --disable-color") if verbose: gef_print(res) res = sorted(set(res.splitlines())) res = list(filter(lambda line: "EL0/R-X" in line, res)) maps = [] for line in res: vrange, prange, *_ = line.split() vstart, vend = [int(x, 16) for x in vrange.split("-")] pstart, pend = [int(x, 16) for x in prange.split("-")] maps.append((vstart, vend, pstart, pend)) if len(maps) == 2: return maps[1] else: return None def stop(self): ta_address = self.get_ta_loaded_address(self.verbose) if ta_address is None: info("Could not find TA address, so continue (this is 1st stop?)") return False ta_vstart, ta_vend, _, _ = ta_address info("TA address: {:#x}".format(ta_vstart)) ta_vsize = ta_vend - ta_vstart if self.ta_offset >= ta_vsize: err("TA offset {:#x} is greater than the size of TA R-X area ({:#x})".format(self.ta_offset, ta_vsize)) self.enabled = False return False gdb.execute("tbreak *{:#x}".format(ta_vstart + self.ta_offset)) return False @register_command class OpteeBreakTaAddrCommand(GenericCommand): """Set a breakpoint to OPTEE-TA.""" _cmdline_ = "optee-break-ta" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("ta_offset", metavar="TA_OFFSET", nargs="?", type=AddressUtil.parse_address, help="The breakpoint target offset of OPTEE-TA.") group.add_argument("-f", "--ta-file", help="parse the TA file (or ELF file) and stop at the entry point.") parser.add_argument("-v", "--verbose", action="store_true", help="show memory map if stopped at __thread_enter_user_mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x2784", "{0:s} -f /path/to/deadbeef-dead-dead-dead-deaddeadbeef.ta", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "It is not straightforward to set a breakpoint on a Trusted Application (TA) while you are still", "in the normal world, because at that moment the TA has not yet been loaded into the secure world.", "", "The TA is loaded only when the TEE OS routine thread_enter_user_mode stops for the second time.", " - At the 1st stop, only ldelf (the user-space loader that actually loads the TA) is executed, so the TA is still absent.", " - At the 2nd stop, ldelf has finished and the TA is finally present in memory.", "", "Now, thread_enter_user_mode calls __thread_enter_user_mode.", "This __thread_enter_user_mode in TEE OS is written directly in assembly.", "Because of this, it is immune to compiler optimizations. By searching memory for the fixed byte sequence", "of this assembly routine, we can reliably locate its offset and set your breakpoint there.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(complete=gdb.COMPLETE_FILENAME) return def get_secure_memory_maps(self): maps = PageMap.get_page_maps_by_pagewalk("pagewalk --optee --quiet --no-pager --disable-color").splitlines() if not maps: err("Could not find memory maps") return None for m in maps: s = m.split(None, 3) if len(s) != 4: continue virt_range, phys_range, size, hint = s if "TEE-OS .text" not in hint: continue virt_start = int(virt_range.split("-")[0], 16) phys_start = int(phys_range.split("-")[0], 16) size = int(size, 16) break else: err("Could not find memory maps") return None return virt_start, phys_start, size def search_thread_enter_user_mode(self): ret = self.get_secure_memory_maps() if ret is None: return virt_start, phys_start, size = ret data = read_physmem(phys_start, size) if not data: err("Memory read error") return None if is_arm32(): # https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/kernel/thread_a32.S """ FUNC __thread_enter_user_mode , : push {r4-r12,lr} cps #CPSR_MODE_SYS mov r4, sp ... gef> xp/3xi 0x0E101158 0xe101158 f05f2de9 push {r4, r5, r6, r7, r8, sb, sl, fp, ip, lr} 0xe10115c 1f0002f1 cps #0x1f 0xe101160 0d40a0e1 mov r4, sp gef> """ byte_seq = b"\xf0\x5f\x2d\xe9" + b"\x1f\x00\x02\xf1" + b"\x0d\x40\xa0\xe1" else: # https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/kernel/thread_a64.S """ FUNC __thread_enter_user_mode , : sub sp, sp, #THREAD_USER_MODE_REC_SIZE // size may change in future store_xregs sp, THREAD_USER_MODE_REC_CTX_REGS_PTR, 0, 2 // macro store_xregs sp, THREAD_USER_MODE_REC_X19, 19, 30 // macro mov x19, sp msr spsel, #1 ... gef> xp/11i 0xE1030C0 0xe1030c0 ff0302d1 sub sp, sp, #0x80 0xe1030c4 e00700a9 stp x0, x1, [sp] <--- here 0xe1030c8 e20b00f9 str x2, [sp, #0x10] 0xe1030cc f35302a9 stp x19, x20, [sp, #0x20] 0xe1030d0 f55b03a9 stp x21, x22, [sp, #0x30] 0xe1030d4 f76304a9 stp x23, x24, [sp, #0x40] 0xe1030d8 f96b05a9 stp x25, x26, [sp, #0x50] 0xe1030dc fb7306a9 stp x27, x28, [sp, #0x60] 0xe1030e0 fd7b07a9 stp x29, x30, [sp, #0x70] 0xe1030e4 f3030091 mov x19, sp <--- here 0xe1030e8 bf4100d5 msr spsel, #1 <--- here gef> """ byte_seq = b"\xf3\x03\x00\x91" + b"\xbf\x41\x00\xd5" x = data.split(byte_seq) if len(x) == 1: err("Could not find __thread_enter_user_mode") return None if len(x) > 2: err("Found multiple candidates") return None if is_arm32(): __thread_enter_user_mode = virt_start + len(x[0]) else: r = x[0].rfind(b"\xe0\x07\x00\xa9") if r == -1 or r < 4: err("Could not find __thread_enter_user_mode") return None __thread_enter_user_mode = virt_start + (r - 4) return __thread_enter_user_mode @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32", "ARM64")) def do_invoke(self, args): thread_enter_user_mode_virt = self.search_thread_enter_user_mode() if thread_enter_user_mode_virt is None: return if args.ta_offset is not None: ta_offset = args.ta_offset else: if not os.path.exists(self.args.ta_file): err("Could not find TA") return contents = open(self.args.ta_file, "rb").read() if not contents.startswith((b"HSTO", b"\x7fELF")): err("Invalid TA/ELF") return elf_header_off = contents.find(b"\x7fELF") if elf_header_off == -1: err("Could not find ELF header") return elf = Elf(self.args.ta_file, elf_header_off) if elf is None: err("Invalid ELF") return ta_offset = elf.e_entry if ta_offset is None: return info("__thread_enter_user_mode @ OPTEE-OS: {:#x}".format(thread_enter_user_mode_virt)) info("Breakpoint target offset of TA: {:#x}".format(ta_offset)) OpteeThreadEnterUserModeBreakpoint(thread_enter_user_mode_virt, ta_offset, args.verbose) info("Temporarily breakpoint at {:#x}".format(thread_enter_user_mode_virt)) return @register_command class OpteeSmcServiceDumpCommand(GenericCommand, BufferingOutput): """Dump the OPTEE SMC (EL3) service (specifically, the arm-trusted-firmware implementation).""" _cmdline_ = "optee-smc-service-dump" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") _syntax_ = parser.format_help() def find_service(self, sm, data): """search services from *.secure-ram.""" def is_valid_secure_addr(addr): return sm.sm_base <= addr < sm.sm_base + sm.sm_size def read_cstring_from_secure_memory(addr): if not is_valid_secure_addr(addr): return None offset = addr - sm.sm_base s = "" i = 0 while offset + i < len(data): c = data[offset + i] if 0x20 <= c < 0x7f: s += chr(c) i += 1 continue if c == 0x00: return s break return None """ typedef struct rt_svc_desc { uint8_t start_oen; uint8_t end_oen; uint8_t call_type; const char *name; rt_svc_init_t init; rt_svc_handle_t handle; } rt_svc_desc_t; """ candidate_services = [] data_list = slice_unpack(data, current_arch.ptrsize) for i in range(len(data_list) - 3): # https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/lib/smccc.h # start_oen, end_oen v = data_list[i] start_oen = v & 0xff end_oen = (v >> 8) & 0xff if start_oen > 64 or end_oen > 64: continue if start_oen > end_oen: continue # call_type call_type = (v >> 16) & 0xff if call_type > 1: continue # padding if (v >> 24) != 0: continue # name name_ptr = data_list[i + 1] if not is_valid_secure_addr(name_ptr): continue name = read_cstring_from_secure_memory(name_ptr) if not name: continue # init init_ptr = data_list[i + 2] if init_ptr != 0 and not is_valid_secure_addr(init_ptr): continue # handle handle_ptr = data_list[i + 3] if not is_valid_secure_addr(handle_ptr): continue s = {} s["address"] = sm.sm_base + i * current_arch.ptrsize s["start_oen"] = start_oen s["end_oen"] = end_oen s["call_type"] = call_type s["name"] = name_ptr s["name_string"] = name s["init"] = init_ptr s["handle"] = handle_ptr Service = collections.namedtuple("Service", s.keys()) candidate_services.append(Service(*s.values())) # filter false positive valid_services = [] for s in candidate_services: if s.name_string == "opteed_fast": valid_services.append(s) valid_min_addr = s.address valid_max_addr = s.address break else: return [] while True: for s in candidate_services: if valid_min_addr - current_arch.ptrsize * 4 == s.address: valid_min_addr = s.address valid_services.append(s) break if valid_max_addr + current_arch.ptrsize * 4 == s.address: valid_max_addr = s.address valid_services.append(s) break else: break return sorted(valid_services, key=lambda x: x.address) def dump_service(self, services): legend = ["Address", "start_oen", "end_oen", "call_type", "init", "handle", "name"] fmt = "{:10s} {:9s} {:7s} {:9s} {:10s} {:10s} {:s}" self.out.append(GefUtil.make_legend(fmt.format(*legend))) for s in services: self.out.append("{:#010x} {:<#9x} {:<#7x} {:<#9x} {:#010x} {:#010x} {:s}".format( s.address, s.start_oen, s.end_oen, s.call_type, s.init, s.handle, s.name_string, )) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM64",)) def do_invoke(self, args): sm = QemuMonitor.get_secure_memory_map(args.verbose) if sm is None: err("Could not find secure memory maps") return None self.out = [] data = XSecureMemAddrCommand.read_secure_memory(sm, 0x0, sm.size, args.verbose) services = self.find_service(sm, data) self.dump_service(services) self.print_output(check_terminal_size=True) return @register_command class OpteeTaDumpCommand(GenericCommand, BufferingOutput): """The base command to dump OPTEE Trusted Application.""" _cmdline_ = "optee-ta-dump" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("memory") subparsers.add_parser("dir") _syntax_ = parser.format_help() uuid_hint = { "023f8f1a-292a-432b-8fc4-de8471358067": "avb", "02a42f43-d8b7-4a57-aa4d-87bd9b5587cb": "crypto_perf", "057f4b66-bdab-11eb-96cf-33d6e41cc849": "acipher-rs", "0864c8ec-bdab-11eb-8926-c7fa47a8c92d": "aes-rs", "0a5a06b2-bdab-11eb-add0-77f29de31296": "authentication-rs", "0bef16a2-bdab-11eb-94be-6f9815f37c21": "bigint-rs", "0e6bf4fe-bdab-11eb-9bc5-3f4ecb50aee7": "diffie_hellman-rs", "10de87e2-bdab-11eb-b73c-63fec73e597c": "digest-rs", "12345678-5b69-11e4-9dbb-101f74f00099": "sdp_basic", "133af0ca-bdab-11eb-9130-43bf7873bf67": "hello_world-rs", "1585d412-bdab-11eb-ba91-3b085fd2601f": "hotp-rs", "17556a46-bdab-11eb-b325-d38c9a9af725": "message_passing_interface-rs", "197c710c-bdab-11eb-8f3f-17a5f698d23b": "random-rs", "1b5f5b74-e9cf-4e62-8c3e-7e41da6d76f6": "mnist-rs (train)", "1cd6d392-bdab-11eb-9082-abc902ac5cd4": "secure_storage-rs", "1ed47816-bdab-11eb-9ebd-3ffe0648da93": "serde-rs", "21b1a1da-bdab-11eb-b614-275a7098826f": "time-rs", "25497083-a58a-4fc5-8a72-1ad7b69b8562": "large", "255fc838-de89-42d3-9a8e-d044c50fa57c": "supp_plugin-rs (ta)", "2a287631-de1b-4fdd-a55c-b9312e40769a": "optee_example_plugins", "3616069b-504d-4044-9497-feb84a073a14": "bti_test", "380231ac-fb99-47ad-a689-9e017eb6e78a": "supp_plugin", "3a2f8978-5dc0-11e8-9c2d-fa7ae01bbebc": "inter_ta-rs (system)", "3b996a7d-2c2b-4a49-a896-e1fb5766d2f4": "socket (?)", "484d4143-2d53-4841-3120-4a6f636b6542": "optee_example_hotp", "4d573443-6a56-4272-ac6f-2425af9ef9bb": "gatekeeper", "528938ce-fc59-11e8-8eb2-f2801f1b9fd1": "miss", "59db8536-e5e6-11eb-8e9b-a316ce7a6568": "tcp_client-rs", "5b9e0e40-2636-11e1-ad9e-0002a5d5c51b": "os_test", "5c206987-16a3-59cc-ab0f-64b9cfc9e758": "subkey1", "5ce0c432-0ab0-40e5-a056-782ca0e6aba2": "concurrent_large", "5dbac793-f574-4871-8ad3-04331ec17f24": "optee_example_aes", "60276949-7ff3-4920-9bce-840c9dcf3098": "tmesg", "614789f2-39c0-4ebf-b235-92b32ac107ed": "sha_perf", "68373894-5bb3-403c-9eec-3114a1f5d3fc": "teep-agent-ta", "69547de6-f47e-11eb-994e-f34e88d5c2b4": "tls_server-rs", "6e256cba-fc4d-4941-ad09-2ca1860342dd": "secstor_ta_mgmt", "7011a688-ddde-4053-a5a9-7b3c4ddf13b8": "device.pta", "731e279e-aafb-4575-a771-38caa6f0cca6": "storage2", "80a4c275-0a47-4905-8285-1486a9771a08": "remoteproc", "873bcd08-c2c3-11e6-a937-d0bf9c45c61c": "socket", "87c2d78e-eb7b-11eb-8d25-df4d5338f285": "udp_socket-rs", "8aaaf200-2450-11e4-abe2-0002a5d5c51b": "optee_example_hello_world", "8d82573a-926d-4754-9353-32dc29997f74": "hello-teep-ta", "a3859d33-b540-4a69-8d29-696dde9115cc": "property-rs", "a4c04d50-f180-11e8-8eb2-f2801f1b9fd1": "sims_keepalive", "a720ccbb-51da-417d-b82e-e5445d474a7a": "subkey2", "a734eed9-d6a1-4244-aa50-7c99719e7b7b": "optee_example_acipher", "a8cfe406-d4f5-4a2e-9f8d-a25dc754c099": "stm32_pwr.pta (?)", "b3091a65-9751-4784-abf7-0298a7cc35ba": "os_test_lib_dl", "b689f2a7-8adf-477a-9f99-32e90c0ad0a2": "storage", "b6c53aba-9669-4668-a7f2-205629d00f86": "optee_example_random", "bc50d971-d4c9-42c4-82cb-343fb7f37896": "optee-ftpm", "bcac6292-5b9d-4b20-a2e5-b389d5e8ae2f": "build_with_optee_utee_sys-rs", "c3f6e2c0-3548-11e1-b86c-0800200c9a66": "create_fail_test", "c7e478c2-89b3-46eb-ac19-571e66c3830d": "signature_verification-rs", "c9d73f40-ba45-4315-92c4-cf1255958729": "client_pool-rs", "cb3e5ba0-adf1-11e0-998b-0002a5d5c51b": "crypt", "d17f73a0-36ef-11e1-984a-0002a5d5c51b": "rpc_test", "d96a5b40-c3e5-21e3-8794-1002a5d5c61b": "invoke_tests.pta", "d96a5b40-e2c7-b1af-8794-1002a5d5c61b": "stats", "dba51a17-0563-11e7-93b1-6fa7b0071a51": "keymaster", "e13010e0-2ae1-11e5-896a-0002a5d5c51b": "concurrent", "e55291e1-521c-4dca-aa24-51e34ab32ad9": "secure_db_abstraction-rs", "e626662e-c0e2-485c-b8c8-09fbce6edf3d": "aes_perf", "e6a33ed4-562b-463a-bb7e-ff5e15a493c8": "sims", "ebb6f4b5-7e33-4ad2-9802-e64f2a7cc20c": "basicAlgUse", "ec55bfe2-d9c7-11eb-8b0e-f3f8fad927f7": "tls_client-rs", "ec59c1fc-b9e0-4c3c-8756-0a3cc48f0088": "error_handling-rs", "ee90d523-90ad-46a0-859d-8eea0b150086": "tpm_log_test", "ef620757-fa2b-4f19-a1c4-6e51cfe4c0f9": "supp_plugin-rs (plugin)", "f04a0fe7-1f5d-4b9b-abf7-619b85b4ce8c": "trusted_keys", "f07bfc66-958c-4a15-99c0-260e4e7375dd": "tee-supplicant plugin", "f157cda0-550c-11e5-a6fa-0002a5d5c51b": "storage_benchmark", "f4e750bb-1437-4fbf-8785-8d3580c34994": "optee_example_secure_storage", "fd02c9da-306c-48c7-a49c-bbd827ae86ee": "pkcs11", "fa9ea860-ef3b-4d59-8457-5564a60c0379": "inter_ta-rs", "ff09aa8a-fbb9-4734-ae8c-d7cd1a3f6744": "mnist-rs (inference)", "ffd2bded-ab7d-4988-95ee-e4962fff7154": "os_test_lib", } def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return @parse_args def do_invoke(self, args): self.usage() return @register_command class OpteeTaDumpMemoryCommand(OpteeTaDumpCommand): """Dump the OPTEE-Trusted-App list from OPTEE kernel memory.""" _cmdline_ = "optee-ta-dump memory" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-o", "--for-old-version", action="store_true", help="for OP-TEE OS before v3.12.0.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _note_ = [ "Walk the global TEE context list (`tee_ctxes`) and print `struct tee_ta_ctx` currently linked to it.", "- A context is added to this list the first time its TA is successfully loaded.", " (that is: after `ldelf` has relocated the ELF and handed the entry point back to the OP-TEE core)", "- A TA that has never been loaded will therefore not appear here.", "- For normal user TAs the entry is removed automatically when the last session is closed and the context is freed,", " so terminated TAs usually vanish from the list.", "- `TA_FLAG_SINGLE_INSTANCE`, `TA_FLAG_INSTANCE_KEEP_ALIVE`, early-TAs and pseudo-TAs stay linked once they", " have been created because the core keeps them resident.", "- ref_count shows the number of sessions currently open for that TA (the live open-session reference counter),", " not a cumulative load count.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(prefix=False) return def find_list_head(self, data, virt_start): def is_valid_rw_addr(addr): return virt_start <= addr < virt_start + len(data) def read_int_from_memory(addr): if not is_valid_rw_addr(addr): return None index = (addr - virt_start) // current_arch.ptrsize return data_list[index] def is_tailq_head(addr, head_next, head_prev, offset): current = head_next prev = addr while True: current_next = read_int_from_memory(current + offset) if current_next is None: return False current_prev = read_int_from_memory(current + offset + current_arch.ptrsize) if current_prev is None: return False if prev != current_prev: return False if current_next == 0: return current + offset == head_prev prev = current + offset current = current_next return False """ struct tee_ta_ctx_head tee_ctxes = TAILQ_HEAD_INITIALIZER(tee_ctxes); struct list { // TAILQ_ENTRY struct list *tqe_next; struct list **tqe_prev; } [OP-TEE OS v3.12.0~] struct tee_ta_ctx { uint32_t flags; /* TA_FLAGS from TA header */ TAILQ_ENTRY(tee_ta_ctx) link; struct ts_ctx { struct TEE_UUID { uint32_t timeLow; uint16_t timeMid; uint16_t timeHiAndVersion; uint8_t clockSeqAndNode[8]; } uuid; const struct ts_ops *ops; } ts_ctx; uint32_t panicked; /* True if TA has panicked, written from asm */ uint32_t panic_code; /* Code supplied for panic */ uint32_t ref_count; /* Reference counter for multi session TA */ bool busy; /* Context is busy and cannot be entered */ bool is_initializing; /* Context initialization is not completed */ bool is_releasing; /* Context is about to be released */ struct condvar { unsigned int spin_lock; struct mutex *m; } busy_cv; /* CV used when context is busy */ }; [OP-TEE OS ~v3.11.0] struct tee_ta_ctx { struct TEE_UUID { uint32_t timeLow; uint16_t timeMid; uint16_t timeHiAndVersion; uint8_t clockSeqAndNode[8]; } uuid; const struct tee_ta_ops *ops; uint32_t flags; /* TA_FLAGS from TA header */ TAILQ_ENTRY(tee_ta_ctx) link; uint32_t panicked; /* True if TA has panicked, written from asm */ uint32_t panic_code; /* Code supplied for panic */ uint32_t ref_count; /* Reference counter for multi session TA */ bool busy; /* Context is busy and cannot be entered */ bool initializing; /* Context is initializing */ struct condvar { unsigned int spin_lock; struct mutex *m; } busy_cv; /* CV used when context is busy */ }; """ if not self.args.for_old_version: offsetof_link = current_arch.ptrsize else: offsetof_link = 16 + current_arch.ptrsize * 2 candidate_head = [] data_list = slice_unpack(data, current_arch.ptrsize) for i in range(len(data_list) - 1): # check if head addr = virt_start + current_arch.ptrsize * i next_value = data_list[i] prev_value = data_list[i + 1] if not is_tailq_head(addr, next_value, prev_value, offsetof_link): continue j = (next_value - virt_start) // current_arch.ptrsize if not self.args.for_old_version: # check flags if is_valid_rw_addr(data_list[j]): continue # check uuid if is_64bit(): if is_valid_rw_addr(data_list[j + 3]) or \ is_valid_rw_addr(data_list[j + 4]): continue else: if is_valid_rw_addr(data_list[j + 3]) or \ is_valid_rw_addr(data_list[j + 4]) or \ is_valid_rw_addr(data_list[j + 5]) or \ is_valid_rw_addr(data_list[j + 6]): continue # check ops if is_64bit(): if not is_valid_rw_addr(data_list[j + 5]): continue else: if not is_valid_rw_addr(data_list[j + 7]): continue else: # check uuid if is_64bit(): if is_valid_rw_addr(data_list[j + 0]) or \ is_valid_rw_addr(data_list[j + 1]): continue else: if is_valid_rw_addr(data_list[j + 0]) or \ is_valid_rw_addr(data_list[j + 1]) or \ is_valid_rw_addr(data_list[j + 2]) or \ is_valid_rw_addr(data_list[j + 3]): continue # check ops if is_64bit(): if not is_valid_rw_addr(data_list[j + 2]): continue else: if not is_valid_rw_addr(data_list[j + 4]): continue # check flags if is_64bit(): if is_valid_rw_addr(data_list[j + 3]): continue else: if is_valid_rw_addr(data_list[j + 5]): continue candidate_head.append(addr) if len(candidate_head) == 0: err("Could not find &tee_ctxes") elif len(candidate_head) > 1: warn("Found multiple canddiate for &tee_ctxes") return candidate_head def get_flags_str(self, flags_value): flags_dic = { 0x0000_1000: "TA_FLAG_DEVICE_ENUM_TEE_STORAGE_PRIVATE", 0x0000_0800: "TA_FLAG_DONT_CLOSE_HANDLE_ON_CORRUPT_OBJECT", 0x0000_0400: "TA_FLAG_DEVICE_ENUM_SUPP", 0x0000_0200: "TA_FLAG_DEVICE_ENUM", 0x0000_0100: "TA_FLAG_CONCURRENT", 0x0000_0080: "TA_FLAG_CACHE_MAINTENANCE", 0x0000_0040: "TA_FLAG_REMAP_SUPPORT", 0x0000_0020: "TA_FLAG_SECURE_DATA_PATH", 0x0000_0010: "TA_FLAG_INSTANCE_KEEP_ALIVE", 0x0000_0008: "TA_FLAG_MULTI_SESSION", 0x0000_0004: "TA_FLAG_SINGLE_INSTANCE", 0x0000_0002: "TA_FLAG_EXEC_DDR", 0x0000_0001: "TA_FLAG_USER_MODE", } flags = [] for k, v in flags_dic.items(): if flags_value & k: flags.append(v) flags_str = " | ".join(flags) if flags_str == "": flags_str = "none" return flags_str.replace("TA_FLAG_", "") def dump_service(self, data, virt_start, heads): import uuid def is_valid_rw_addr(addr): return virt_start <= addr < virt_start + len(data) def read_int_from_memory(addr): if not is_valid_rw_addr(addr): return None index = (addr - virt_start) // current_arch.ptrsize return data_list[index] if not self.args.for_old_version: offsetof_flags = 0 offsetof_link = offsetof_flags + current_arch.ptrsize offsetof_uuid = offsetof_link + current_arch.ptrsize * 2 offsetof_ops = offsetof_uuid + 16 offsetof_ref_count = offsetof_ops + 4 * 2 else: offsetof_uuid = 0 offsetof_ops = offsetof_uuid + 16 offsetof_flags = offsetof_ops + current_arch.ptrsize offsetof_link = offsetof_flags + current_arch.ptrsize offsetof_ref_count = offsetof_link + current_arch.ptrsize * 2 + 4 * 2 data_list = slice_unpack(data, current_arch.ptrsize) for head in heads: self.out.append(titlify("&tee_ctxes: {:#x}".format(head))) taa_ctx_list = [] current = read_int_from_memory(head) while current: d = {} # addr d["addr"] = current # uuid d["raw_uuid"] = data[current + offsetof_uuid - virt_start:][:16] d["uuid_str"] = str(uuid.UUID(bytes_le=d["raw_uuid"])) d["hint"] = self.uuid_hint.get(d["uuid_str"], "???") # flags d["flags"] = read_int_from_memory(current + offsetof_flags) d["flags_string"] = "(" + self.get_flags_str(d["flags"]) + ")" # ops d["ops"] = read_int_from_memory(current + offsetof_ops) # ref_count d["ref_count"] = read_int_from_memory(current + offsetof_ref_count) & 0xffff_ffff # append Ctx = collections.namedtuple("Ctx", d.keys()) taa_ctx_list.append(Ctx(*d.values())) # goto next current = read_int_from_memory(current + offsetof_link) width = max([len(taa.hint) for taa in taa_ctx_list] + [0]) fmt = "{:10s} {:36s} {:{:d}s} {:10s} {:10s} {:s}" legend = ["tee_ta_ctx", "uuid", "hint", width, "ops", "ref_count", "flags"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for ctx in taa_ctx_list: # dump self.out.append("{:#010x} {:36s} {:{:d}s} {:#010x} {:#010x} {:#010x} {:s}".format( ctx.addr, ctx.uuid_str, ctx.hint, width, ctx.ops, ctx.ref_count, ctx.flags, ctx.flags_string, )) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32", "ARM64")) def do_invoke(self, args): maps = PageMap.get_page_maps_by_pagewalk("pagewalk --optee --quiet --no-pager --disable-color").splitlines() if not maps: err("Could not find memory maps") return for m in maps: s = m.split(None, 3) if len(s) != 4: continue virt_range, phys_range, size, hint = s if "TEE-OS .data / stack" not in hint: continue virt_start = int(virt_range.split("-")[0], 16) phys_start = int(phys_range.split("-")[0], 16) size = int(size, 16) break else: err("Could not find memory maps") return self.out = [] data = read_physmem(phys_start, size) list_heads = self.find_list_head(data, virt_start) if not list_heads: if not args.for_old_version: info("Trying with --for-old-version...") gdb.execute("optee-ta-dump memory --for-old-version") return self.dump_service(data, virt_start, list_heads) self.print_output(check_terminal_size=True) return @register_command class OpteeTaDumpDirectoryCommand(OpteeTaDumpCommand): """Dump the OPTEE-Trusted-App list from host directory.""" _cmdline_ = "optee-ta-dump dir" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("host_dir", metavar="HOST_DIR", help="The host directory where you extracted the guest's /lib/optee_armtz/.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_FILENAME) return def get_ta_info_single(self, file_path, first_offset): import uuid IMG_TYPE = {0: "LegacyTA", 1: "BootstrapTA", 2: "EncryptedTA", 3: "Subkey"} ALGOS = { 0x7000_4830: "TEE_ALG_RSASSA_PKCS1_V1_5_SHA256", 0x7041_4930: "TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256", 0x4000_0810: "TEE_ALG_AES_GCM", } d = {} with open(file_path, "rb") as f: f.seek(first_offset) # struct shdr magic = u32(f.read(4)) if magic != 0x4F545348: return None img_type = u32(f.read(4)) img_size = u32(f.read(4)) algo = u32(f.read(4)) hash_size = u16(f.read(2)) sig_size = u16(f.read(2)) d.update({ "magic": magic, "img_type": (img_type, IMG_TYPE.get(img_type, "Unknown")), "img_size": (img_size, GefUtil.get_size_str(img_size, enable_color=False)), "algo": (algo, ALGOS.get(algo, f"unknown-{algo:#x}")), "hash_size": hash_size, "sig_size": sig_size, }) f.seek(hash_size + sig_size, 1) # sub header if img_type == 0: pass elif img_type == 1: # struct shdr_bootstrap_ta raw_uuid = f.read(16) ta_ver = u32(f.read(4)) d["bootstrap_uuid"] = str(uuid.UUID(bytes=raw_uuid)) d["bootstrap_version"] = ta_ver elif img_type == 2: # struct shdr_encrypted_ta enc_algo = u32(f.read(4)) flags = u32(f.read(4)) iv_sz = u16(f.read(2)) tag_sz = u16(f.read(2)) iv = f.read(iv_sz) tag = f.read(tag_sz) d.update({ "enc_algo": (enc_algo, ALGOS.get(enc_algo, f"unknown-{enc_algo:#x}")), "enc_flags": flags, "iv_len": iv_sz, "tag_len": tag_sz, "iv_hex": iv.hex(), "tag_hex": tag.hex(), }) elif img_type == 3: # struct shdr_subkey raw_uuid = f.read(16) name_sz = u32(f.read(4)) subk_version = u32(f.read(4)) max_depth = u32(f.read(4)) sk_algo = u32(f.read(4)) attr_cnt = u32(f.read(4)) f.seek(img_size - len(raw_uuid) - 4 * 5, 1) name = f.read(name_sz).decode(errors="ignore") if name_sz else "" d.update({ "subkey_uuid": str(uuid.UUID(bytes=raw_uuid)), "subkey_name_size": name_sz, "subkey_version": subk_version, "subkey_max_depth": max_depth, "subkey_algo": (sk_algo, ALGOS.get(sk_algo, f"unknown-0x{sk_algo:x}")), "subkey_attr_count": attr_cnt, "next_name": name.rstrip("\0"), }) processed_size = f.tell() TAInfo = collections.namedtuple("TAInfo", d.keys()) return TAInfo(*d.values()), processed_size def get_ta_info(self, file_path): filesize = os.path.getsize(file_path) processed_size = 0 ta_list = [] while processed_size < filesize: ret = self.get_ta_info_single(file_path, processed_size) if ret is None: break ta_info, processed_size = ret ta_list.append(ta_info) return ta_list def dump_directory(self): fmt = "{:39s} {:11s} {:8s} {:s}" legend = ["filename", "TA type", "size", "hint"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for filepath in GefUtil.walk(self.args.host_dir): filename = os.path.basename(filepath) r = re.match( r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.ta", filename, ) if not r: continue # uuid -> hint uuid = filename[:-3] hint = self.uuid_hint.get(uuid, "???") # other info ta_list = self.get_ta_info(filepath) if not ta_list: self.out.append("{:39s} {:11s} {:8s} {:s}".format(filename, "???", "???", hint)) continue # dump self.out.append("{:39s} {:11s} {:8s} {:s}".format( filename, ta_list[0].img_type[1], ta_list[0].img_size[1], hint, )) if self.args.verbose: for ta_info in ta_list: for k, v in ta_info._asdict().items(): if isinstance(v, tuple): self.out.append(" {:20s}: {:#x} ({:s})".format(k, v[0], v[1])) elif isinstance(v, int): self.out.append(" {:20s}: {:#x}".format(k, v)) else: self.out.append(" {:20s}: {:s}".format(k, v)) self.out.append("") return @parse_args def do_invoke(self, args): if not os.path.isdir(args.host_dir): err("Could not find directory") return self.out = [] self.dump_directory() self.print_output(check_terminal_size=True) return @register_command class OpteeShmListCommand(GenericCommand, BufferingOutput): """List dynamic shared-memory buffers currently registered in OP-TEE (for OP-TEE v4.3.0~).""" _cmdline_ = "optee-shm-list" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def find_reg_shm_list(self, data, virt_start): def is_valid_rw_addr(addr): return virt_start <= addr < virt_start + len(data) def read_int_from_memory(addr): if not is_valid_rw_addr(addr): return None index = (addr - virt_start) // current_arch.ptrsize return data_list[index] def is_slist_head(addr, head_next, offset): current = head_next seen = [current] while True: current_next = read_int_from_memory(current + offset) if current_next is None: return False if current_next == 0: return True if current_next in seen: return False seen.append(current) current = current_next return False """ struct list { // SLIST_ENTRY struct list *next; } [OP-TEE OS v4.3.0~] struct mobj_reg_shm { struct mobj { const struct mobj_ops *ops; size_t size; size_t phys_granule; struct refcount { unsigned int val; } refc; } mobj; SLIST_ENTRY(mobj_reg_shm) next; uint64_t cookie; tee_mm_entry_t *mm; paddr_t page_offset; struct refcount mapcount; bool guarded; bool releasing; bool release_frees; paddr_t pages[]; }; """ offsetof_ops = 0 offsetof_size = offsetof_ops + current_arch.ptrsize offsetof_refc = offsetof_size + current_arch.ptrsize * 2 offsetof_next = offsetof_refc + current_arch.ptrsize offsetof_cookie = offsetof_next + 8 # with pad offsetof_mm = offsetof_cookie + 8 offsetof_page_offset = offsetof_mm + current_arch.ptrsize offsetof_pages = offsetof_page_offset + current_arch.ptrsize + 4 + 4 # 4, 4 = map_count, bool*3 candidate_head = [] data_list = slice_unpack(data, current_arch.ptrsize) for i in range(len(data_list) - 1): # check if head head_addr = virt_start + current_arch.ptrsize * i next_value = data_list[i] if not is_slist_head(head_addr, next_value, offsetof_next): continue current = next_value seen = [] entries = [] found = True while current: if not is_valid_rw_addr(current): found = False break if current in seen: found = False break ops = read_int_from_memory(current + offsetof_ops) size = read_int_from_memory(current + offsetof_size) refc = read_int_from_memory(current + offsetof_refc) next_ = read_int_from_memory(current + offsetof_next) cookie = read_int_from_memory(current + offsetof_cookie) mm = read_int_from_memory(current + offsetof_mm) page_offset = read_int_from_memory(current + offsetof_page_offset) seen.append(current) # check ops if is_valid_rw_addr(ops): # r-x found = False break # check size if is_valid_rw_addr(size): found = False break # check size + page_offset if (size + page_offset) % get_pagesize(): found = False break # check refc if is_valid_rw_addr(refc): found = False break if refc == 0 or refc >= 0x100: found = False break # check cookie if is_64bit(): if cookie & 0xffff_0000_0000_0000 != 0xffff_0000_0000_0000: found = False break # check mm if mm and not is_valid_rw_addr(mm): # mm == 0 is ok found = False break # check pages pages = [] for j in range((size + page_offset) // get_pagesize()): p = read_int_from_memory(current + offsetof_pages + current_arch.ptrsize * j) if p & get_pagesize_mask_low(): found = False break pages.append(p) if not found: break # already parsed if len(seen) == 1: # first element for _, ents in candidate_head: if current in [e[0] for e in ents]: found = False break if not found: break # add entry entries.append([current, ops, size, refc, cookie, mm, page_offset, pages]) # goto next current = next_ if found: candidate_head.append([head_addr, entries]) if len(candidate_head) == 0: err("Could not find &tee_ctxes") elif len(candidate_head) > 1: warn("Found multiple canddiate for ®_shm_list") return candidate_head def dump_list(self, list_heads): for head, entries in list_heads: self.out.append(titlify("®_shm_list: {:#x}".format(head))) fmt = "{:12s} {:10s} {:10s} {:10s} {:18s} {:10s} {:11s} {:s}" legend = ["mobj_reg_shm", "ops", "size", "refc", "cookie", "mm", "page_offset", "pages (phys)"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) for addr, ops, size, refc, cookie, mm, page_offset, pages in entries: pages_str = [] if pages: start = end = pages[0] for addr in pages[1:]: if addr == end + get_pagesize(): end = addr else: pages_str.append("{:#x}-{:#x}".format(start, end + get_pagesize())) start = end = addr pages_str.append("{:#x}-{:#x}".format(start, end + get_pagesize())) self.out.append( "{:#010x} {:#010x} {:#010x} {:#010x} {:#018x} {:#010x} {:#010x} {:s}".format( addr, ops, size, refc, cookie, mm, page_offset, ",".join(pages_str), )) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32", "ARM64")) def do_invoke(self, args): maps = PageMap.get_page_maps_by_pagewalk("pagewalk --optee --quiet --no-pager --disable-color").splitlines() if not maps: err("Could not find memory maps") return for m in maps: s = m.split(None, 3) if len(s) != 4: continue virt_range, phys_range, size, hint = s if "TEE-OS .data / stack" not in hint: continue virt_start = int(virt_range.split("-")[0], 16) phys_start = int(phys_range.split("-")[0], 16) size = int(size, 16) break else: err("Could not find memory maps") return data = read_physmem(phys_start, size) parsed_list_heads = self.find_reg_shm_list(data, virt_start) if not parsed_list_heads: err("Could not find reg_shm_list") return self.out = [] self.dump_list(parsed_list_heads) self.print_output(check_terminal_size=True) return @register_command class OpteeBgetDumpCommand(GenericCommand, BufferingOutput): """Dump bget allocator of OPTEE-Trusted-App.""" _cmdline_ = "optee-bget-dump" _category_ = "06-j. Qemu-system/KGDB Cooperation - TrustZone" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") parser.add_argument("-m", "--malloc_ctx", metavar="OFFSET_malloc_ctx", type=AddressUtil.parse_address, help="The offset of `malloc_ctx` at OPTEE-TA.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x2a408", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Simplified heap structure:", "", "+-malloc_ctx-------------------+ +-freed chunk------------+", "| bufsize prevfree |<--+ +-->| bufsize prevfree |= 0 (if upper chunk is used) +--> ...", "| bufsize bsize | | | | bufsize bsize |= the size of this chunk |", "| struct bfhead *flink |-----+ | struct bfhead *flink |------------------------------+", "| struct bfhead *blink | +-----| struct bfhead *blink |", "| (bufsize totalloc) | | |", "| (long numget) | | |", "| (long numrel) | | |", "| (long numpblk) | +-used chunk-------------+", "| (long numpget) | | bufsize prevfree |= the size of upper chunk (if upper chunk is freed)", "| (long numprel) | | bufsize bsize |= the size of this chunk (negative number)", "| (long numdget) | | uchar user_data[bsize] |", "| (long numdrel) | | |", "| (func_ptr compfcn) | | |", "| (func_ptr acqfcn) | +------------------------+", "| (func_ptr relfcn) |", "| (bufsize exp_incr) |", "| (bufsize pool_len) |", "| struct malloc_pool* pool |", "| size_t pool_len |", "| (struct malloc_stats mstats) |", "+------------------------------+", ] _note_ = "\n".join(_note_) def is_readable_virt_memory(self, addr): if is_arm32(): res = PageMap.get_page_maps_by_pagewalk("pagewalk -S --quiet --no-pager --disable-color") res = sorted(set(res.splitlines())) res = list(filter(lambda line: "PL0/RW-" in line, res)) elif is_arm64(): res = PageMap.get_page_maps_by_pagewalk("pagewalk 1 --quiet --no-pager --disable-color") res = sorted(set(res.splitlines())) res = list(filter(lambda line: "EL0/RW-" in line, res)) for line in res: vrange, prange, *_ = line.split() vstart, vend = [int(x, 16) for x in vrange.split("-")] pstart, pend = [int(x, 16) for x in prange.split("-")] if vstart <= addr < vend: return True return False def get_ta_rw_address(self, ta_loaded_rx_end): if is_arm32(): res = PageMap.get_page_maps_by_pagewalk("pagewalk -S --quiet --no-pager --disable-color") res = sorted(set(res.splitlines())) elif is_arm64(): res = PageMap.get_page_maps_by_pagewalk("pagewalk 1 --quiet --no-pager --disable-color") res = sorted(set(res.splitlines())) for line in res: if not re.search("[PE]L1/RW", line): continue vrange, prange, *_ = line.split() vstart, vend = [int(x, 16) for x in vrange.split("-")] pstart, pend = [int(x, 16) for x in prange.split("-")] if vstart == ta_loaded_rx_end: return (vstart, vend, pstart, pend) return None def get_malloc_ctx(self, ta_rw_address_map): vstart = ta_rw_address_map[0] vend = ta_rw_address_map[1] data = read_memory(vstart, vend - vstart) data = slice_unpack(data, current_arch.ptrsize) candidate = [] for i in range(len(data) - 3): if data[i] != 0 or data[i + 1] != 0: # should be 0 continue if not is_valid_addr(data[i + 2]) or not is_valid_addr(data[i + 3]): # should be flink, blink continue addr = vstart + current_arch.ptrsize * i flink_blink = read_int_from_memory(data[i + 2] + current_arch.ptrsize * 3) blink_flink = read_int_from_memory(data[i + 3] + current_arch.ptrsize * 2) if flink_blink != addr or blink_flink != addr: continue link_list_count = 1 flink_cur = data[i + 2] blink_cur = data[i + 3] flink_seen = [] blink_seen = [] while True: if flink_cur in flink_seen: break if blink_cur in blink_seen: break flink_seen.append(flink_cur) blink_seen.append(blink_cur) try: flink_cur = read_int_from_memory(flink_cur + current_arch.ptrsize * 2) blink_cur = read_int_from_memory(blink_cur + current_arch.ptrsize * 3) except gdb.MemoryError: link_list_count = -1 break link_list_count += 1 candidate.append((link_list_count, addr)) if len(candidate) == 0: return None return sorted(candidate, reverse=True)[0][1] # maybe the longest flink is malloc_ctx def parse_flink(self, head): current = head flinks = [] seen = [current] while True: try: prevfree = read_int_from_memory(current + current_arch.ptrsize * 0) bsize = read_int_from_memory(current + current_arch.ptrsize * 1) flink = read_int_from_memory(current + current_arch.ptrsize * 2) blink = read_int_from_memory(current + current_arch.ptrsize * 3) next_prevfree = read_int_from_memory(current + bsize) next_bsize = read_int_from_memory(current + bsize + current_arch.ptrsize) except gdb.MemoryError: flinks.append("memory corrupted") break if flink % 8 or blink % 8 or bsize % 8 or next_prevfree % 8 or next_bsize % 8: flinks.append("unaligned corrupted") break chunk = { "addr": current, "prevfree": prevfree, "bsize": bsize, "flink": flink, "blink": blink, "next_prevfree": next_prevfree, "next_bsize": next_bsize, } Chunk = collections.namedtuple("Chunk", chunk.keys()) flinks.append(Chunk(*chunk.values())) if flink == head: break if flink in seen[1:]: flinks.append("loop detected") break seen.append(current) current = flink return flinks def parse_blink(self, head): current = head blinks = [] seen = [current] while True: try: prevfree = read_int_from_memory(current + current_arch.ptrsize * 0) bsize = read_int_from_memory(current + current_arch.ptrsize * 1) flink = read_int_from_memory(current + current_arch.ptrsize * 2) blink = read_int_from_memory(current + current_arch.ptrsize * 3) next_prevfree = read_int_from_memory(current + bsize) next_bsize = read_int_from_memory(current + bsize + current_arch.ptrsize) except gdb.MemoryError: blinks.append("memory corrupted") break if flink % 8 or blink % 8 or bsize % 8 or next_prevfree % 8 or next_bsize % 8: blinks.append("unaligned corrupted") break chunk = { "addr": current, "prevfree": prevfree, "bsize": bsize, "flink": flink, "blink": blink, "next_prevfree": next_prevfree, "next_bsize": next_bsize, } Chunk = collections.namedtuple("Chunk", chunk.keys()) blinks.append(Chunk(*chunk.values())) if blink == head: break if blink in seen[1:]: blinks.append("loop detected") break seen.append(current) current = blink return blinks def parse_malloc_ctx(self, malloc_ctx_addr): malloc_ctx = {} malloc_ctx["addr"] = current = malloc_ctx_addr malloc_ctx["prevfree"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_ctx["bsize"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_ctx["flink"] = read_int_from_memory(current) malloc_ctx["flink_list"] = self.parse_flink(malloc_ctx["flink"]) current += current_arch.ptrsize malloc_ctx["blink"] = read_int_from_memory(current) malloc_ctx["blink_list"] = self.parse_blink(malloc_ctx["blink"]) current += current_arch.ptrsize # search for pool for _ in range(14): pool_candidate = read_int_from_memory(current) current += current_arch.ptrsize if self.is_readable_virt_memory(pool_candidate): malloc_ctx["pool"] = pool_candidate break else: err("Could not find malloc_ctx->pool") return None malloc_ctx["pool_len"] = read_int_from_memory(current) current += current_arch.ptrsize malloc_ctx["pool_list"] = [] for i in range(malloc_ctx["pool_len"]): buf = read_int_from_memory(malloc_ctx["pool"] + (i * 2) * current_arch.ptrsize) size = read_int_from_memory(malloc_ctx["pool"] + (i * 2 + 1) * current_arch.ptrsize) pool = {"buf": buf, "len": size} Pool = collections.namedtuple("Pool", pool.keys()) malloc_ctx["pool_list"].append(Pool(*pool.values())) MallocCtx = collections.namedtuple("MallocCtx", malloc_ctx.keys()) return MallocCtx(*malloc_ctx.values()) def dump_malloc_ctx(self, malloc_ctx): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") self.out.append(titlify("malloc_ctx @ {:#x}".format(malloc_ctx.addr))) self.out.append("prevfree: {:#x}".format(malloc_ctx.prevfree)) self.out.append("bsize: {:#x}".format(malloc_ctx.bsize)) self.out.append("flink: {:#x}".format(malloc_ctx.flink)) for chunk in malloc_ctx.flink_list: if isinstance(chunk, str): self.out.append(" -> {:s}".format(Color.colorify(chunk, corrupted_msg_color))) else: fmt = " -> {:s}: prevfree:{:#x} bsize:{:s} flink:{:#010x} blink:{:#010x}" fmt += " next_prevfree:{:#010x} next_bsize:{:#010x} (={:#010x})" self.out.append(fmt.format( Color.colorify("{:#010x}".format(chunk.addr), freed_address_color), chunk.prevfree, Color.colorify("{:#010x}".format(chunk.bsize), chunk_size_color), chunk.flink, chunk.blink, chunk.next_prevfree, chunk.next_bsize, (-chunk.next_bsize) & 0xffff_ffff, )) self.out.append("blink: {:#x}".format(malloc_ctx.blink)) for chunk in malloc_ctx.blink_list: if isinstance(chunk, str): self.out.append(" -> {:s}".format(Color.colorify(chunk, corrupted_msg_color))) else: fmt = " -> {:s}: prevfree:{:#x} bsize:{:s} flink:{:#010x} blink:{:#010x}" fmt += " next_prevfree:{:#010x} next_bsize:{:#010x} (={:#010x})" self.out.append(fmt.format( Color.colorify("{:#010x}".format(chunk.addr), freed_address_color), chunk.prevfree, Color.colorify("{:#010x}".format(chunk.bsize), chunk_size_color), chunk.flink, chunk.blink, chunk.next_prevfree, chunk.next_bsize, (-chunk.next_bsize) & 0xffff_ffff, )) self.out.append("pool: {:#x}".format(malloc_ctx.pool)) self.out.append("pool_len: {:#x}".format(malloc_ctx.pool_len)) for i in range(malloc_ctx.pool_len): pool = malloc_ctx.pool_list[i] self.out.append(" pool[{:d}] buf:{:#x} size:{:#x}".format(i, pool.buf, pool.len)) return def dump_chunk_list(self, malloc_ctx): freed_address_color = Config.get_gef_setting("theme.heap_chunk_address_freed") used_address_color = Config.get_gef_setting("theme.heap_chunk_address_used") corrupted_msg_color = Config.get_gef_setting("theme.heap_corrupted_msg") chunk_size_color = Config.get_gef_setting("theme.heap_chunk_size") chunk_used_color = Config.get_gef_setting("theme.heap_chunk_used") chunk_freed_color = Config.get_gef_setting("theme.heap_chunk_freed") for i in range(malloc_ctx.pool_len): pool = malloc_ctx.pool_list[i] pool_start = pool.buf pool_end = pool.buf + pool.len self.out.append(titlify("pool[{:d}] @ {:#x} - {:#x}".format(i, pool_start, pool_end))) chunk = pool_start seen = [] while chunk < pool_end: if chunk in seen: self.out.append(Color.colorify("loop detected", corrupted_msg_color)) break seen.append(chunk) try: prevfree = read_int_from_memory(chunk + current_arch.ptrsize * 0) bsize = read_int_from_memory(chunk + current_arch.ptrsize * 1) flink = read_int_from_memory(chunk + current_arch.ptrsize * 2) blink = read_int_from_memory(chunk + current_arch.ptrsize * 3) except gdb.MemoryError: self.out.append(Color.colorify("unaligned orrupted", corrupted_msg_color)) break bsize_inv = (-bsize) & 0xffff_ffff if bsize_inv < 0x8000_0000: # used self.out.append("{:s} {:s}: prevfree:{:#010x} bsize:{:#010x} ({:s})".format( Color.colorify("used", chunk_used_color), Color.colorify("{:#010x}".format(chunk), used_address_color), prevfree, bsize, Color.colorify("{:#010x}".format(bsize_inv), chunk_size_color), )) chunk += bsize_inv else: # freed self.out.append( "{:s} {:s}: prevfree:{:#010x} bsize:{:s} flink:{:#010x} blink:{:#010x}".format( Color.colorify("free", chunk_freed_color), Color.colorify("{:#010x}".format(chunk), freed_address_color), prevfree, Color.colorify("{:#010x}".format(bsize), chunk_size_color), flink, blink, ), ) chunk += bsize if chunk % 8: self.out.append(Color.colorify("unaligned orrupted", corrupted_msg_color)) break return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32", "ARM64")) def do_invoke(self, args): self.out = [] ta_address_map = OpteeThreadEnterUserModeBreakpoint.get_ta_loaded_address() if ta_address_map is None: err("Could not find TA address") return ta_address = ta_address_map[0] self.verbose_info("TA loaded address (RX): {:#x} - {:#x}".format(ta_address_map[0], ta_address_map[1])) if args.malloc_ctx is None: ta_rw_address_map = self.get_ta_rw_address(ta_address_map[1]) if ta_rw_address_map is None: err("Could not find TA rw address") return self.verbose_info("TA loaded address (RW): {:#x} - {:#x}".format(ta_rw_address_map[0], ta_rw_address_map[1])) malloc_ctx_addr = self.get_malloc_ctx(ta_rw_address_map) if malloc_ctx_addr is None: err("Could not find malloc_ctx") return else: self.verbose_info("The offset of malloc_ctx: {:#x}".format(args.malloc_ctx)) malloc_ctx_addr = ta_address + args.malloc_ctx self.verbose_info("malloc_ctx: {:#x}".format(malloc_ctx_addr)) malloc_ctx = self.parse_malloc_ctx(malloc_ctx_addr) if malloc_ctx is None: err("Failed to parse") return self.dump_malloc_ctx(malloc_ctx) self.dump_chunk_list(malloc_ctx) self.print_output(check_terminal_size=True) return @register_command class CpuidCommand(GenericCommand, BufferingOutput): """Get cpuid result.""" _cmdline_ = "cpuid" _category_ = "04-a. Register - View" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s}", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Disable `-enable-kvm` option for qemu-system.", ] _note_ = "\n".join(_note_) def execute_cpuid(self, num, subnum=0): codes = [b"\x0f\xa2"] # cpuid if is_x86_64(): regs = {"$rax": num, "$rcx": subnum} else: regs = {"$eax": num, "$ecx": subnum} ret = ExecAsm(codes, regs=regs).exec_code() if is_x86_64(): eax = ret["reg"]["$rax"] & 0xffff_ffff ebx = ret["reg"]["$rbx"] & 0xffff_ffff ecx = ret["reg"]["$rcx"] & 0xffff_ffff edx = ret["reg"]["$rdx"] & 0xffff_ffff else: eax = ret["reg"]["$eax"] ebx = ret["reg"]["$ebx"] ecx = ret["reg"]["$ecx"] edx = ret["reg"]["$edx"] return eax, ebx, ecx, edx def make_out(self, id, subid, eax, ebx, ecx, edx): if eax == ebx == ecx == edx == 0: return def o(msg): self.out.append(msg) return if subid is None: o(titlify("cpuid (eax={:#x})".format(id))) else: o(titlify("cpuid (eax={:#x}, ecx={:#x})".format(id, subid))) o(Color.colorify("eax={:#x}, ebx={:#x}, ecx={:#x}, edx={:#x}".format(eax, ebx, ecx, edx), "bold yellow")) def c(reg, shift, mask, msg): val = (reg >> shift) & mask msg = " " + msg + " (={:#x})".format(val) if val: return Color.boldify(msg) else: return msg if id == 0: vid = String.bytes2str(p32(ebx) + p32(edx) + p32(ecx)) o("eax: Maximum Input Value for Basic CPUID Information") o("ebx+edx+ecx: Vendor ID (={!r})".format(vid)) elif id == 1: o("eax: Version Information") o(c(eax, 0, 0xf, "EAX 3- 0: Stepping ID")) o(c(eax, 4, 0xf, "EAX 7- 4: Model Number")) o(c(eax, 8, 0xf, "EAX 11- 8: Family Code")) o(c(eax, 12, 0b11, "EAX 13-12: Processor Type")) o(c(eax, 14, 0b11, "EAX 15-14: Reserved")) o(c(eax, 16, 0xf, "EAX 19-16: Extended Model")) o(c(eax, 20, 0xff, "EAX 27-20: Extended Family")) o(c(eax, 28, 0xf, "EAX 31-28: Reserved")) o("ebx: Additional Information") o(c(ebx, 0, 0xff, "EBX 7- 0: Brand Index")) o(c(ebx, 8, 0xff, "EBX 15- 8: CLFLUSH line size")) o(c(ebx, 16, 0xff, "EBX 23-16: The number of logical processors")) o(c(ebx, 24, 0xff, "EBX 31-24: Initial APIC ID")) o("edx,ecx: Feature Information") o(c(edx, 0, 1, "EDX 0: FPU (Floating Point Unit on-chip)")) o(c(edx, 1, 1, "EDX 1: VME (Virtual 8086 Mode Enhancements)")) o(c(edx, 2, 1, "EDX 2: DE (Debugging Extensions)")) o(c(edx, 3, 1, "EDX 3: PSE (Page Size Extension)")) o(c(edx, 4, 1, "EDX 4: TSC (Time Stamp Counter)")) o(c(edx, 5, 1, "EDX 5: MSR (Model Specific Registers RDMSR and WRMSR instructions)")) o(c(edx, 6, 1, "EDX 6: PAE (Physical Address Extension)")) o(c(edx, 7, 1, "EDX 7: MCE (Machine Check Exception)")) o(c(edx, 8, 1, "EDX 8: CX8 (CMPXCHG8B instruction)")) o(c(edx, 9, 1, "EDX 9: APIC (APIC on-chip)")) o(c(edx, 10, 1, "EDX 10: Reserved")) o(c(edx, 11, 1, "EDX 11: SEP (SYSENTER and SYSEXIT instructions)")) o(c(edx, 12, 1, "EDX 12: MTRR (Memory Type Range Registers)")) o(c(edx, 13, 1, "EDX 13: PGE (Page Global Bit)")) o(c(edx, 14, 1, "EDX 14: MCA (Machine Check Architecture)")) o(c(edx, 15, 1, "EDX 15: CMOV (Conditional Move instructions)")) o(c(edx, 16, 1, "EDX 16: PAT (Page Attribute Table)")) o(c(edx, 17, 1, "EDX 17: PSE-36 (36-Bit Page Size Extension)")) o(c(edx, 18, 1, "EDX 18: PSN (Processor Serial Number)")) o(c(edx, 19, 1, "EDX 19: CLFSH (CLFLUSH instruction)")) o(c(edx, 20, 1, "EDX 20: Reserved")) o(c(edx, 21, 1, "EDX 21: DS (Debug Store)")) o(c(edx, 22, 1, "EDX 22: ACPI (Thermal Monitor and Software Controlled Clock Facilities)")) o(c(edx, 23, 1, "EDX 23: MMX (Intel MMX technology)")) o(c(edx, 24, 1, "EDX 24: FXSR (FXSAVE and FXRSTOR instructions)")) o(c(edx, 25, 1, "EDX 25: SSE (Streaming SIMD Extension)")) o(c(edx, 26, 1, "EDX 26: SSE2 (STreaming SIMD Extension 2)")) o(c(edx, 27, 1, "EDX 27: SS (Self Snoop)")) o(c(edx, 28, 1, "EDX 28: HTT (Max APIC IDs reserved field is Valid)")) o(c(edx, 29, 1, "EDX 29: TM (Thermal Monitor)")) o(c(edx, 30, 1, "EDX 30: Reserved")) o(c(edx, 31, 1, "EDX 31: PBE (Pending Break Enable)")) o(c(ecx, 0, 1, "ECX 0: SSE3 (Streaming SIMD Extensions 3)")) o(c(ecx, 1, 1, "ECX 1: PCLMULQDQ (PCLMULQDQ instruction)")) o(c(ecx, 2, 1, "ECX 2: DTES64 (64-bit DS Area)")) o(c(ecx, 3, 1, "ECX 3: MONITOR (MONITOR/MWAIT instruction)")) o(c(ecx, 4, 1, "ECX 4: DS-CPL (CPL Qualified Debug Store)")) o(c(ecx, 5, 1, "ECX 5: VMX (Intel VT (Virtual Machine eXtensions))")) o(c(ecx, 6, 1, "ECX 6: SMX (Safer Mode eXtensions)")) o(c(ecx, 7, 1, "ECX 7: EIST (Enhanced Intel SpeedStep Technology)")) o(c(ecx, 8, 1, "ECX 8: TM2 (Thermal Monitor 2)")) o(c(ecx, 9, 1, "ECX 9: SSSE3 (Supplemental Streaming SIMD Extensions 3)")) o(c(ecx, 10, 1, "ECX 10: CNXT-ID (L1 Context ID)")) o(c(ecx, 11, 1, "ECX 11: SDBG (IA32_DEBUG_INTERFACE MSR for silicon debug)")) o(c(ecx, 12, 1, "ECX 12: FMA (FMA extensions using YMM state)")) o(c(ecx, 13, 1, "ECX 13: CMPXCHG16B (CMPXCHG16B instruction)")) o(c(ecx, 14, 1, "ECX 14: xTPR (xTPR update control)")) o(c(ecx, 15, 1, "ECX 15: PDCM (Perfmon and Debug Capability MSR)")) o(c(ecx, 16, 1, "ECX 16: Reserved")) o(c(ecx, 17, 1, "ECX 17: PCID (Process-Context IDentifiers)")) o(c(ecx, 18, 1, "ECX 18: DCA (Direct Cache Access)")) o(c(ecx, 19, 1, "ECX 19: SSE4_1 (Streaming SIMD Extensions 4.1)")) o(c(ecx, 20, 1, "ECX 20: SSE4_2 (Streaming SIMD Extensions 4.2)")) o(c(ecx, 21, 1, "ECX 21: x2APIC")) o(c(ecx, 22, 1, "ECX 22: MOVBE (MOVBE instruction)")) o(c(ecx, 23, 1, "ECX 23: POPCNT (POPulation CouNt instruction)")) o(c(ecx, 24, 1, "ECX 24: TSC-Deadline")) o(c(ecx, 25, 1, "ECX 25: AESNI (AESNI Instruction)")) o(c(ecx, 26, 1, "ECX 26: XSAVE (XSAVE instruction)")) o(c(ecx, 27, 1, "ECX 27: OSXSAVE (OSXSAVE instruction)")) o(c(ecx, 28, 1, "ECX 28: AVX (Intel Advanced Vector eXtensions)")) o(c(ecx, 29, 1, "ECX 29: F16C (16-bit Floating-point Conversion instructions)")) o(c(ecx, 30, 1, "ECX 30: RDRAND (RDRAND instruction)")) o(c(ecx, 31, 1, "ECX 31: RAZ (Reserved for use by hypervisor to indicate guest status)")) elif id == 2: o("Cache and TLB Information") elif id == 3: o("eax,ebx: Reserved") o("edx+ecx: Processor Serial Number") elif id == 4: o("Information of cache configuration descriptor") elif id == 5: o("Information of MONITOR/MWAIT") elif id == 6: o("Information of power management") o(c(eax, 0, 1, "EAX 0: Digital temperature sensor")) o(c(eax, 1, 1, "EAX 1: Intel Turbo Boost Technology")) o(c(eax, 2, 1, "EAX 2: ARAT (Always Running APIC Timer)")) o(c(eax, 3, 1, "EAX 3: Reserved")) o(c(eax, 4, 1, "EAX 4: Power limit notification controls")) o(c(eax, 5, 1, "EAX 5: Clock modulation duty cycle extensions")) o(c(eax, 6, 1, "EAX 6: Package thermal management")) o(c(eax, 7, 1, "EAX 7: Hardware-managed P-state base support (HWP)")) o(c(eax, 8, 1, "EAX 8: HWP notification interrupt enable MSR")) o(c(eax, 9, 1, "EAX 9: HWP activity window MSR")) o(c(eax, 10, 1, "EAX 10: HWP energy/performance preference MSR")) o(c(eax, 11, 1, "EAX 11: HWP package level request MSR")) o(c(eax, 12, 1, "EAX 12: Reserved")) o(c(eax, 13, 1, "EAX 13: HDC (Hardware Duty Cycle programming)")) o(c(eax, 14, 1, "EAX 14: Intel Turbo Boost Max Technology 3.0")) o(c(eax, 15, 1, "EAX 15: HWP Capabilities, Highest Performance change")) o(c(eax, 16, 1, "EAX 16: HWP PECI override")) o(c(eax, 17, 1, "EAX 17: Flexible HWP")) o(c(eax, 18, 1, "EAX 18: Fast access mode for IA32_HWP_REQUEST MSR")) o(c(eax, 19, 1, "EAX 19: Hardware feedback MSRs")) o(c(eax, 20, 1, "EAX 20: Ignoring Idle Logical Processor HWP request")) o(c(eax, 21, 1, "EAX 21: Reserved")) o(c(eax, 22, 1, "EAX 22: Reserved")) o(c(eax, 23, 1, "EAX 23: Enhanced hardware feedback MSRs")) o(c(eax, 24, 0x7f, "EAX 30-24: Reserved")) o(c(eax, 31, 1, "EAX 31: IP payloads are LIP")) o(c(ebx, 0, 0xf, "EBX 3- 0: Number of interrupted thresholds of digital temperature sensor")) o(c(ebx, 4, 0xfff_ffff, "EBX 31- 4: Reserved")) o(c(ecx, 0, 1, "ECX 0: Hardware Coordination Feedback Capability (APERF and MPERF)")) o(c(ecx, 1, 1, "ECX 1: Reserved")) o(c(ecx, 2, 1, "ECX 2: Reserved")) o(c(ecx, 3, 1, "ECX 3: Performance-energy bias preference")) o(c(ecx, 4, 0xfff_ffff, "ECX 31- 4: Reserved")) o(c(edx, 0, 1, "EDX 0: Performance feature report")) o(c(edx, 1, 1, "EDX 1: Energy efficiency capacity report")) o(c(edx, 2, 0x3f, "EDX 7- 2: Reserved")) o(c(edx, 8, 0xf, "EDX 11- 8: The size of the hardware feedback interface structure")) o(c(edx, 12, 0xf, "EDX 15-12: Reserved")) o(c(edx, 16, 0xffff, "EDX 31-16: Index of rows for the hardware feedback interface structure")) elif id == 7 and subid == 0: o("eax: Maximum Input Value for Extended CPUID Information") o("ebx,edx,edx: Extended Feature Information") o(c(ebx, 0, 1, "EBX 0: FSGSBASE (FSGSBASE instructions)")) o(c(ebx, 1, 1, "EBX 1: TSC_ADJUST (IA32_TSC_ADJUST MSR supported)")) o(c(ebx, 2, 1, "EBX 2: SGX (Software Guard Extensions)")) o(c(ebx, 3, 1, "EBX 3: BMI1 (Bit Manipulation Instructions)")) o(c(ebx, 4, 1, "EBX 4: HLE (Hardware Lock Elision)")) o(c(ebx, 5, 1, "EBX 5: AVX2 (Advanced Vector Extensions 2.0)")) o(c(ebx, 6, 1, "EBX 6: FDP_EXCPTN_ONLY (x87 FPU Data Pointer updated only on x87 Exceptions)")) o(c(ebx, 7, 1, "EBX 7: SMEP (Supervisor Mode Execution Protection)")) o(c(ebx, 8, 1, "EBX 8: BMI2 (Bit Manipulation Instructions 2)")) o(c(ebx, 9, 1, "EBX 9: ERMS (Enhanced REP MOVSB/STOSB)")) o(c(ebx, 10, 1, "EBX 10: INVPCID (INVPCID instruction)")) o(c(ebx, 11, 1, "EBX 11: RTM (Restricted Transactional Memory)")) o(c(ebx, 12, 1, "EBX 12: PQM (Platform QoS Monitoring)")) o(c(ebx, 13, 1, "EBX 13: x87 FPU CS and DS deprecated")) o(c(ebx, 14, 1, "EBX 14: MPX (Memory Protection eXtensions)")) o(c(ebx, 15, 1, "EBX 15: PQE (Platform QoS Enforcement)")) o(c(ebx, 16, 1, "EBX 16: AVX512F (AVX512 Foundation)")) o(c(ebx, 17, 1, "EBX 17: AVX512DQ (AVX512 Double/Quadword instructions)")) o(c(ebx, 18, 1, "EBX 18: RDSEED (RDSEED instruction)")) o(c(ebx, 19, 1, "EBX 19: ADX (Multi-Precision Add-Carry instruction eXtensions)")) o(c(ebx, 20, 1, "EBX 20: SMAP (Supervisor Mode Access Prevention)")) o(c(ebx, 21, 1, "EBX 21: AVX512IFMA (AVX512 Integer FMA instructions)")) o(c(ebx, 22, 1, "EBX 22: (Intel) PCOMMIT (Persistent Commit instruction)")) o(c(ebx, 22, 1, "EBX 22: (AMD) RDPID (RDPID instruction and TSC_AUX MSR iupport)")) o(c(ebx, 23, 1, "EBX 23: CLFLUSHOPT (CLFLUSHOPT instruction)")) o(c(ebx, 24, 1, "EBX 24: CLWB (Cache Line Write-Back instruction)")) o(c(ebx, 25, 1, "EBX 25: PT (Intel Processor Trace)")) o(c(ebx, 26, 1, "EBX 26: AVX512PF (AVX512 Prefetch instructions)")) o(c(ebx, 27, 1, "EBX 27: AVX512ER (AVX512 Exponent/Reciprocal instructions)")) o(c(ebx, 28, 1, "EBX 28: AVX512CD (AVX512 Conflict Detection instructions)")) o(c(ebx, 29, 1, "EBX 29: SHA (SHA-1/SHA-256 instructions)")) o(c(ebx, 30, 1, "EBX 30: AVX512BW (AVX512 Byte/Word instructions)")) o(c(ebx, 31, 1, "EBX 31: AVX512VL (AVX512 Vector Length Extensions)")) o(c(ecx, 0, 1, "ECX 0: PREFETCHWT1 (PREFETCHWT1 instruction)")) o(c(ecx, 1, 1, "ECX 1: AVX512VBMI (AVX512 Vector Byte Manipulation Instructions)")) o(c(ecx, 2, 1, "ECX 2: UMIP (User Mode Instruction Prevention)")) o(c(ecx, 3, 1, "ECX 3: PKU (Protection Keys for User-mode pages)")) o(c(ecx, 4, 1, "ECX 4: OSPKE (OS has Enabled Protection Keys)")) o(c(ecx, 5, 1, "ECX 5: WAITPKG (Wait and Pause Enhancements)")) o(c(ecx, 6, 1, "ECX 6: AVX512VBMI2 (AVX512 Vector Byte Manipulation Instructions 2)")) o(c(ecx, 7, 1, "ECX 7: CET_SS (CET shadow stack)")) o(c(ecx, 8, 1, "ECX 8: GFNI (Galois Field NI / Galois Field Affine Transformation)")) o(c(ecx, 9, 1, "ECX 9: VAES (VEX-encoded AES-NI)")) o(c(ecx, 10, 1, "ECX 10: VPCL (VEX-encoded PCLMUL)")) o(c(ecx, 11, 1, "ECX 11: AVX512VNNI (AVX512 Vector Neural Network Instructions)")) o(c(ecx, 12, 1, "ECX 12: AVX512BITALG (AVX512 Bitwise Algorithms)")) o(c(ecx, 13, 1, "ECX 13: TME_EN (Total Memory Encryption)")) o(c(ecx, 14, 1, "ECX 14: AVX512 VPOPCNTDQ")) o(c(ecx, 15, 1, "ECX 15: Reserved")) o(c(ecx, 16, 1, "ECX 16: LA57 (5-Level paging)")) o(c(ecx, 17, 0x1f, "ECX 21-17: MAWAU (MPX Address-Width Adjust for CPL=3)")) o(c(ecx, 22, 1, "ECX 22: RDPID (Read Processor ID)")) o(c(ecx, 23, 1, "ECX 23: KL (Key Locker)")) o(c(ecx, 24, 1, "ECX 24: Reserved")) o(c(ecx, 25, 1, "ECX 25: CLDEMOTE (Cache Line Demote)")) o(c(ecx, 26, 1, "ECX 26: Reserved")) o(c(ecx, 27, 1, "ECX 27: MOVDIRI (32-bit Direct Stores)")) o(c(ecx, 28, 1, "ECX 28: MOVDIRI64B (64-bit Direct Stores)")) o(c(ecx, 29, 1, "ECX 29: ENQCMD (ENQueue Stores)")) o(c(ecx, 30, 1, "ECX 30: SGX_LC (SGX Launch Configuration)")) o(c(ecx, 31, 1, "ECX 31: PKS (Protection Keys for Supervisor-mode pages)")) o(c(edx, 0, 1, "EDX 0: Reserved")) o(c(edx, 1, 1, "EDX 1: Reserved")) o(c(edx, 2, 1, "EDX 2: AVX512_4VNNIW")) o(c(edx, 3, 1, "EDX 3: AVX512_4FMAPS")) o(c(edx, 4, 1, "EDX 4: Fast Short REP MOV")) o(c(edx, 5, 1, "EDX 5: UINTR (User Interrupts)")) o(c(edx, 6, 1, "EDX 6: Reserved")) o(c(edx, 7, 1, "EDX 7: Reserved")) o(c(edx, 8, 1, "EDX 8: AVX512_VP2INTERSECT")) o(c(edx, 9, 1, "EDX 9: Reserved")) o(c(edx, 10, 1, "EDX 10: MD_CLEAR")) o(c(edx, 11, 1, "EDX 11: Reserved")) o(c(edx, 12, 1, "EDX 12: Reserved")) o(c(edx, 13, 1, "EDX 13: TSX force abort MSR")) o(c(edx, 14, 1, "EDX 14: SERIALIZE")) o(c(edx, 15, 1, "EDX 15: Hybrid")) o(c(edx, 16, 1, "EDX 16: TSX suspend load address tracking")) o(c(edx, 17, 1, "EDX 17: Reserved")) o(c(edx, 18, 1, "EDX 18: PCONFIG")) o(c(edx, 19, 1, "EDX 19: Reserved")) o(c(edx, 20, 1, "EDX 20: CET_IBT (CET Indirect Branch Tracking)")) o(c(edx, 21, 1, "EDX 21: Reserved")) o(c(edx, 22, 1, "EDX 22: AMX-BF16 (Tile computation on bfloat16)")) o(c(edx, 23, 1, "EDX 23: AVX512FP16")) o(c(edx, 24, 1, "EDX 24: AMX-TILE (Tile architecture)")) o(c(edx, 25, 1, "EDX 25: AMX-INT8 (Tile computation on 8-bit integers)")) o(c(edx, 26, 1, "EDX 26: IBRS/IBPB (Indirect Branch Restricted Speculation/Predictor Barrier)")) o(c(edx, 27, 1, "EDX 27: STIBP (Single Thread Indirect Branch Predictors)")) o(c(edx, 28, 1, "EDX 28: L1D_FLUSH (L1 Data Cache Flush)")) o(c(edx, 29, 1, "EDX 29: IA32_ARCH_CAPABILITIES MSR")) o(c(edx, 30, 1, "EDX 30: IA32_CORE_CAPABILITIES MSR")) o(c(edx, 31, 1, "EDX 31: SSBD (Speculative Store Bypass Disable)")) elif id == 7 and subid != 0: o("ebx,edx,edx: Extended Feature Information") elif id == 8: o("Reserved") elif id == 9: o("eax: PLATFORM_DCA_CAP MSR") o("ebx,ecx,edx: Reserved") elif id == 10: o("DCA parameters") o(c(eax, 0, 0xff, "EAX 7- 0: Revision")) o(c(eax, 8, 0xff, "EAX 15- 8: Number of PeMo counters per logical processor")) o(c(eax, 16, 0xff, "EAX 23-16: Bit width of PeMo counter")) o(c(eax, 24, 0xff, "EAX 31-24: EBX bit vector length")) o(c(ebx, 0, 1, "EBX 0: Core cycles event unavailable")) o(c(ebx, 1, 1, "EBX 1: Instructions retired event unavailable")) o(c(ebx, 2, 1, "EBX 2: Reference cycles event unavailable")) o(c(ebx, 3, 1, "EBX 3: Last level cache references event unavailable")) o(c(ebx, 4, 1, "EBX 4: Last level cache misses event unavailable")) o(c(ebx, 5, 1, "EBX 5: Branch instructions retired event unavailable")) o(c(ebx, 6, 1, "EBX 6: Branch mispredicts retired event unavailable")) o(c(ebx, 7, 0x1ff_ffff, "EBX 31- 7: Reserved")) o(c(ecx, 0, 0xffff_ffff, "ECX 31- 0: Reserved")) o(c(edx, 0, 0x1f, "EDX 4- 0: Number of fixed function PeMo counters")) o(c(edx, 5, 0xff, "EDX 12- 5: Bit width of fixed function PeMo counter")) o(c(edx, 13, 1, "EDX 13: Reserved")) o(c(edx, 14, 1, "EDX 14: Reserved")) o(c(edx, 15, 1, "EDX 15: AnyThread deprecation")) o(c(edx, 16, 0xffff, "EDX 31-16: Reserved")) elif id == 11: o("Information of topology enumeration") elif id == 12: o("Reserved") elif id == 13: if subid == 0: m = "main" elif subid == 1: m = "sub" else: m = "XCR0.{:d}".format(subid) o("Information of extended state enumeration ({:s})".format(m)) elif id in [15, 16]: o("Intel Resource Director Technology (Intel RDT), Cache, Memory Bandwidth Allocation Enumeration") elif id == 18: o("Information of Intel SGX") elif id == 20: o("Information of Intel Processor Trace Enumeration") o(c(ebx, 0, 1, "EBX 0: CR3 filtering")) o(c(ebx, 1, 1, "EBX 1: Configurable PSB, Cycle-Accurate Mode")) o(c(ebx, 2, 1, "EBX 2: Filtering preserved across warm reset")) o(c(ebx, 3, 1, "EBX 3: MTC timing packet, suppression of COFI-based packets")) o(c(ebx, 4, 1, "EBX 4: PTWRITE")) o(c(ebx, 5, 1, "EBX 5: Power Event Trace")) o(c(ebx, 6, 1, "EBX 6: PSB and PMI preservation MSRs")) o(c(ebx, 7, 0x1ff_ffff, "EBX 31- 7: Reserved")) o(c(ecx, 0, 1, "ECX 0: ToPA output scheme")) o(c(ecx, 1, 1, "ECX 1: ToPA tables hold multiple output entries")) o(c(ecx, 2, 1, "ECX 2: Single-range output scheme")) o(c(ecx, 3, 1, "ECX 3: Trace Transport output support")) o(c(ecx, 4, 0x7ff_ffff, "ECX 30- 4: Reserved")) o(c(ecx, 31, 1, "ECX 31: IP payloads are LIP")) elif id == 21: o("TSC and Nominal Core Crystal Clock") elif id == 22: o("Information of CPU frequency") o(c(eax, 0, 0xffff, "EAX 15- 0: Processor Base Frequency (MHz)")) o(c(eax, 16, 0xffff, "EAX 31-16: Reserved")) o(c(ebx, 0, 0xffff, "EBX 15- 0: Maximum Frequency (MHz)")) o(c(ebx, 16, 0xffff, "EBX 31-16: Reserved")) o(c(ecx, 0, 0xffff, "ECX 15- 0: Bus (Reference) Frequency (MHz)")) o(c(ecx, 16, 0xffff, "ECX 31-16: Reserved")) o(c(edx, 0, 0xffff_ffff, "EDX 31- 0: Reserved")) elif id == 23: o("Information of System-On-Chip Vendor Attribute Enumeration") elif id == 24: o("Information of Deterministic Address Translation Parameters") elif id == 26: o("Information of Hybrid Information Enumeration") elif id == 31: o("Information of V2 Extended Topology Enumeration") elif id == 0x4000_0000: vid = String.bytes2str(p32(ebx) + p32(ecx) + p32(edx)) o("eax: Maximum Input Value for Hypervisor Function CPUID Information") o("ebx+ecx+edx: Hypervisor Brand String (={!r})".format(vid)) elif id == 0x4000_0001: o("Hypervisor") o(c(eax, 0, 1, "EAX 0: Clocksource")) o(c(eax, 1, 1, "EAX 1: NOP IO Delay")) o(c(eax, 2, 1, "EAX 2: MMU Op")) o(c(eax, 3, 1, "EAX 3: Clocksource 2")) o(c(eax, 4, 1, "EAX 4: Async PF")) o(c(eax, 5, 1, "EAX 5: Steal Time")) o(c(eax, 6, 1, "EAX 6: PV EOI")) o(c(eax, 7, 1, "EAX 7: PV UNHALT")) o(c(eax, 8, 1, "EAX 8: Reserved")) o(c(eax, 9, 1, "EAX 9: PV TLB flush")) o(c(eax, 10, 1, "EAX 10: PV async PF VMEXIT")) o(c(eax, 11, 1, "EAX 11: PV send IPI")) o(c(eax, 12, 1, "EAX 12: PV poll control")) o(c(eax, 13, 1, "EAX 13: PV sched yield")) o(c(eax, 14, 1, "EAX 14: Async PF INT")) o(c(eax, 15, 1, "EAX 15: MSI extended destination ID")) o(c(eax, 16, 1, "EAX 16: Hypercall map GPA range")) o(c(eax, 17, 1, "EAX 17: Hypercall map GPA range")) o(c(eax, 18, 1, "EAX 18: Migration control")) o(c(eax, 19, 0x3f, "EAX 24-19: Reserved")) o(c(eax, 25, 1, "EAX 25: Clocksource Stable")) o(c(eax, 26, 0x3f, "EAX 31-26: Reserved")) o(c(edx, 0, 1, "EDX 0: vCPUs realtime, never preempted")) o(c(edx, 1, 0x7fff_ffff, "EDX 31- 1: Reserved")) elif id == 0x4000_0003: o("Hypervisor") o(c(eax, 0, 1, "EAX 0: VP_RUNTIME")) o(c(eax, 1, 1, "EAX 1: TIME_REF_COUNT")) o(c(eax, 2, 1, "EAX 2: Basic SynIC MSRs")) o(c(eax, 3, 1, "EAX 3: Synthetic Timer")) o(c(eax, 4, 1, "EAX 4: APIC access")) o(c(eax, 5, 1, "EAX 5: Hypercall MSRs")) o(c(eax, 6, 1, "EAX 6: VP Index MSR")) o(c(eax, 7, 1, "EAX 7: System Reset MSR")) o(c(eax, 8, 1, "EAX 8: Access stats MSRs")) o(c(eax, 9, 1, "EAX 9: Reference TSC")) o(c(eax, 10, 1, "EAX 10: Guest Idle MSR")) o(c(eax, 11, 1, "EAX 11: Timer Frequency MSRs")) o(c(eax, 12, 1, "EAX 12: Debug MSRs")) o(c(eax, 13, 1, "EAX 13: Reenlightenment controls")) o(c(eax, 14, 0x3_ffff, "EAX 31-14: Reserved")) o(c(ebx, 0, 1, "EBX 0: CreatePartitions")) o(c(ebx, 1, 1, "EBX 1: AccessPartitionId")) o(c(ebx, 2, 1, "EBX 2: AccessMemoryPool")) o(c(ebx, 3, 1, "EBX 3: AdjustMemoryBuffers")) o(c(ebx, 4, 1, "EBX 4: PostMessages")) o(c(ebx, 5, 1, "EBX 5: SignalEvents")) o(c(ebx, 6, 1, "EBX 6: CreatePort")) o(c(ebx, 7, 1, "EBX 7: ConnectPort")) o(c(ebx, 8, 1, "EBX 8: AccessStats")) o(c(ebx, 9, 1, "EBX 9: Reserved")) o(c(ebx, 10, 1, "EBX 10: Reserved")) o(c(ebx, 11, 1, "EBX 11: Debugging")) o(c(ebx, 12, 1, "EBX 12: CpuManagement")) o(c(ebx, 13, 1, "EBX 13: ConfigureProfiler")) o(c(ebx, 14, 1, "EBX 14: EnableExpandedStackwalking")) o(c(ebx, 15, 1, "EBX 15: Reserved")) o(c(ebx, 16, 1, "EBX 16: AccessVSM")) o(c(ebx, 17, 1, "EBX 17: AccessVpRegisters")) o(c(ebx, 18, 1, "EBX 18: Reserved")) o(c(ebx, 19, 1, "EBX 19: Reserved")) o(c(ebx, 20, 1, "EBX 20: EnableExtendedHypercalls")) o(c(ebx, 21, 1, "EBX 21: StartVirtualProcessor")) o(c(ebx, 22, 0x3ff, "EBX 31-22: Reserved")) o(c(edx, 0, 1, "EDX 0: MWAIT instruction support (deprecated)")) o(c(edx, 1, 1, "EDX 1: Guest debugging support")) o(c(edx, 2, 1, "EDX 2: Performance Monitor support")) o(c(edx, 3, 1, "EDX 3: Physical CPU dynamic partitioning event support")) o(c(edx, 4, 1, "EDX 4: Hypercall input params via XMM registers")) o(c(edx, 5, 1, "EDX 5: Virtual guest idle state support")) o(c(edx, 6, 1, "EDX 6: Hypervisor sleep state support")) o(c(edx, 7, 1, "EDX 7: NUMA distance query support")) o(c(edx, 8, 1, "EDX 8: Timer frequency details available")) o(c(edx, 9, 1, "EDX 9: Synthetic machine check injection support")) o(c(edx, 10, 1, "EDX 10: Guest crash MSR support")) o(c(edx, 11, 1, "EDX 11: Debug MSR support")) o(c(edx, 12, 1, "EDX 12: NPIEP support")) o(c(edx, 13, 1, "EDX 13: Hypervisor disable support")) o(c(edx, 14, 1, "EDX 14: Extended GVA ranges for flush virtual address list available")) o(c(edx, 15, 1, "EDX 15: Hypercall output via XMM registers")) o(c(edx, 16, 1, "EDX 16: Virtual guest idle state")) o(c(edx, 17, 1, "EDX 17: Soft interrupt polling mode available")) o(c(edx, 18, 1, "EDX 18: Hypercall MSR lock available")) o(c(edx, 19, 1, "EDX 19: Direct synthetic timers support")) o(c(edx, 20, 1, "EDX 20: PAT register available for VSM")) o(c(edx, 21, 1, "EDX 21: BNDCFGS register available for VSM")) o(c(edx, 22, 1, "EDX 22: Reserved")) o(c(edx, 23, 1, "EDX 23: Synthetic time unhalted timer")) o(c(edx, 24, 1, "EDX 24: Reserved")) o(c(edx, 25, 1, "EDX 25: Reserved")) o(c(edx, 26, 1, "EDX 26: Intel Last Branch Record (LBR) feature")) o(c(edx, 27, 1, "EDX 31-27: Reserved")) elif id == 0x4000_0004: o("Hypervisor implementation recommendations") o(c(eax, 0, 1, "EAX 0: Hypercall for address space switches")) o(c(eax, 1, 1, "EAX 1: Hypercall for local TLB flushes")) o(c(eax, 2, 1, "EAX 2: Hypercall for remote TLB flushes")) o(c(eax, 3, 1, "EAX 3: MSRs for accessing APIC registers")) o(c(eax, 4, 1, "EAX 4: Hypervisor MSR for system RESET")) o(c(eax, 5, 1, "EAX 5: Relaxed timing")) o(c(eax, 6, 1, "EAX 6: DMA remapping")) o(c(eax, 7, 1, "EAX 7: Interrupt remapping")) o(c(eax, 8, 1, "EAX 8: x2APIC MSRs")) o(c(eax, 9, 1, "EAX 9: Deprecating AutoEOI")) o(c(eax, 10, 1, "EAX 10: Hypercall for SyntheticClusterIpi")) o(c(eax, 11, 1, "EAX 11: Interface ExProcessorMasks")) o(c(eax, 12, 1, "EAX 12: Nested Hyper-V partition")) o(c(eax, 13, 1, "EAX 13: INT for MBEC system calls")) o(c(eax, 14, 1, "EAX 14: Enlightenment VMCS interface")) o(c(eax, 15, 1, "EAX 15: Synced timeline")) o(c(eax, 16, 1, "EAX 16: Reserved")) o(c(eax, 17, 1, "EAX 17: Direct local flush entire")) o(c(eax, 18, 1, "EAX 18: No architectural core sharing")) o(c(eax, 19, 0x1fff, "EAX 31-19: Reserved")) elif id == 0x4000_0006: o("Hypervisor hardware features enable") o(c(eax, 0, 1, "EAX 0: APIC overlay assist")) o(c(eax, 1, 1, "EAX 1: MSR bitmaps")) o(c(eax, 2, 1, "EAX 2: Architectural performance counters")) o(c(eax, 3, 1, "EAX 3: Second-level address translation")) o(c(eax, 4, 1, "EAX 4: DMA remapping")) o(c(eax, 5, 1, "EAX 5: Interrupt remapping")) o(c(eax, 6, 1, "EAX 6: Memory patrol scrubber")) o(c(eax, 7, 1, "EAX 7: DMA protection")) o(c(eax, 8, 1, "EAX 8: HPET")) o(c(eax, 9, 1, "EAX 9: Volatile synthetic timers")) o(c(eax, 10, 0x3f_ffff, "EAX 31-10: Reserved")) elif id == 0x4000_0007: o("Hypervisor CPU management features") o(c(eax, 0, 1, "EAX 0: Start logical processor")) o(c(eax, 1, 1, "EAX 1: Create root virtual processor")) o(c(eax, 2, 1, "EAX 2: Performance counter sync")) o(c(eax, 3, 0x1fff_ffff, "EAX 31- 3: Reserved")) o(c(ebx, 0, 1, "EBX 0: Processor power management")) o(c(ebx, 1, 1, "EBX 1: MWAIT idle states")) o(c(ebx, 2, 1, "EBX 2: Logical processor idling")) o(c(ebx, 3, 0x1fff_ffff, "EBX 31- 3: Reserved")) o(c(ecx, 0, 1, "ECX 0: Remap guest uncached")) o(c(ecx, 1, 0x7fff_ffff, "ECX 31- 1: Reserved")) elif id == 0x4000_0008: o("Hypervisor shared virtual memory (SVM) features") o(c(eax, 0, 1, "EAX 0: SVM (Shared Virtual Memory)")) o(c(eax, 1, 0x7fff_ffff, "EAX 31- 1: Reserved")) elif id == 0x4000_0009: o("Nested hypervisor feature identification") o(c(eax, 0, 1, "EAX 0: Reserved")) o(c(eax, 1, 1, "EAX 1: Reserved")) o(c(eax, 2, 1, "EAX 2: Synthetic Timer")) o(c(eax, 3, 1, "EAX 3: Reserved")) o(c(eax, 4, 1, "EAX 4: Interrupt control registers")) o(c(eax, 5, 1, "EAX 5: Hypercall MSRs")) o(c(eax, 6, 1, "EAX 6: VP index MSR")) o(c(eax, 7, 0x1f, "EAX 11- 7: Reserved")) o(c(eax, 12, 1, "EAX 12: Reenlightenment controls")) o(c(eax, 13, 0x7_ffff, "EAX 31-13: Reserved")) o(c(edx, 0, 0xf, "EDX 3- 0: Reserved")) o(c(eax, 4, 1, "EDX 4: Hypercall input params via XMM registers")) o(c(edx, 5, 0x3ff, "EDX 14- 5: Reserved")) o(c(edx, 15, 1, "EDX 15: Hypercall output via XMM registers")) o(c(edx, 16, 1, "EDX 16: Reserved")) o(c(edx, 17, 1, "EDX 17: Soft interrupt polling mode available")) o(c(edx, 18, 0x3fff, "EDX 31-18: Reserved")) elif id == 0x4000_000a: o("Nested hypervisor feature identification") o(c(eax, 0, 0x1_ffff, "EAX 16- 0: Reserved")) o(c(eax, 17, 1, "EAX 17: Direct virtual flush hypercalls")) o(c(eax, 18, 1, "EAX 18: Flush GPA space and list hypercalls")) o(c(eax, 19, 1, "EAX 19: Enlightened MSR bitmaps")) o(c(eax, 20, 1, "EAX 20: Combining virtualization exceptions in page fault exception class")) o(c(eax, 21, 0x7ff, "EAX 31-21: Reserved")) elif id == 0x4000_0010: o("Hypervisor timing information") o("eax: (Virtual) TSC frequency in kHz") o("ebx: (Virtual) Bus (local apic timer) frequency in kHz") o("ecx,edx: Reserved") elif id == 0x8000_0000: o("eax: Maximum Input Value for Extended Function CPUID Information") o("ebx,ecx,edx: Reserved") elif id == 0x8000_0001: o("eax,ebx: Extended Processor Signature") o("edx,ecx: Extended Processor Feature") o(c(edx, 0, 1, "EDX 0: FPU (Floating Point Unit on-chip)")) o(c(edx, 1, 1, "EDX 1: VME (Virtual 8086 Mode Enhancements)")) o(c(edx, 2, 1, "EDX 2: DE (Debugging Extensions)")) o(c(edx, 3, 1, "EDX 3: PSE (Page Size Extension)")) o(c(edx, 4, 1, "EDX 4: TSC (Time Stamp Counter)")) o(c(edx, 5, 1, "EDX 5: MSR (Model Specific Registers RDMSR and WRMSR instructions)")) o(c(edx, 6, 1, "EDX 6: PAE (Physical Address Extension)")) o(c(edx, 7, 1, "EDX 7: MCE (Machine Check Exception)")) o(c(edx, 8, 1, "EDX 8: CX8 (CMPXCHG8B instruction)")) o(c(edx, 9, 1, "EDX 9: APIC (APIC on-chip)")) o(c(edx, 10, 1, "EDX 10: Reserved")) o(c(edx, 11, 1, "EDX 11: (Intel) SEP (SYSENTER and SYSEXIT instructions)")) o(c(edx, 11, 1, "EDX 11: (AMD) SYSCALL (SYSCALL and SYSRET instructions)")) o(c(edx, 12, 1, "EDX 12: MTRR (Memory Type Range Registers)")) o(c(edx, 13, 1, "EDX 13: PGE (Page Global Bit)")) o(c(edx, 14, 1, "EDX 14: MCA (Machine Check Architecture)")) o(c(edx, 15, 1, "EDX 15: CMOV (Conditional MOVe instructions)")) o(c(edx, 16, 1, "EDX 16: PAT (Page Attribute Table)")) o(c(edx, 17, 1, "EDX 17: PSE-36 (36-Bit Page Size Extension)")) o(c(edx, 18, 1, "EDX 18: Reserved")) o(c(edx, 19, 1, "EDX 19: MP (MultiProcessing capable)")) o(c(edx, 20, 1, "EDX 20: (Intel) XD (No-execute page protection)")) o(c(edx, 20, 1, "EDX 20: (AMD) NX (No-execute page protection)")) o(c(edx, 21, 1, "EDX 21: Reserved")) o(c(edx, 22, 1, "EDX 22: MMX+ (MMX instruction extensions)")) o(c(edx, 23, 1, "EDX 23: MMX (Intel MMX technology)")) o(c(edx, 24, 1, "EDX 24: FXSR (FXSAVE and FXRSTOR instructions)")) o(c(edx, 25, 1, "EDX 25: FFXSR (Fast FXSAVE/FXRSTOR)")) o(c(edx, 26, 1, "EDX 26: P1GB (1GB Page support)")) o(c(edx, 27, 1, "EDX 27: RDTSCP (RDTSCP instruction)")) o(c(edx, 28, 1, "EDX 28: Reserved")) o(c(edx, 29, 1, "EDX 29: LM (Long Mode (EM64T))")) o(c(edx, 30, 1, "EDX 30: 3DNow!+ (3DNow! extended)")) o(c(edx, 31, 1, "EDX 31: 3DNow! (3DNow! instructions)")) o(c(ecx, 0, 1, "ECX 0: LAHF (LAHF/SAHF supported in 64-bit mode)")) o(c(ecx, 1, 1, "ECX 1: CMPL (Core Multi-Processing Legacy mode)")) o(c(ecx, 2, 1, "ECX 2: SVM (Secure Virtual Machine)")) o(c(ecx, 3, 1, "ECX 3: EAS (Extended APIC Space)")) o(c(ecx, 4, 1, "ECX 4: AMC8 (AltMovCr8; LOCK MOV CR0 means MOV CR8)")) o(c(ecx, 5, 1, "ECX 5: ABM (Advanced Bit Manipulation; LZCNT instruction)")) o(c(ecx, 6, 1, "ECX 6: SSE4A (SSE4A instructions)")) o(c(ecx, 7, 1, "ECX 7: MASSE (Mis-Aligned SSE Support)")) o(c(ecx, 8, 1, "ECX 8: PREFETCH (3DNow! PREFETCH/PREFETCHHW instructions)")) o(c(ecx, 9, 1, "ECX 9: OSVW (OS-Visible Workaround)")) o(c(ecx, 10, 1, "ECX 10: IBS (Instruction-Based Sampling)")) o(c(ecx, 11, 1, "ECX 11: XOP (eXtended OPeration)")) o(c(ecx, 12, 1, "ECX 12: SKINIT (SKINIT/STGI instructions)")) o(c(ecx, 13, 1, "ECX 13: WDT (WatchDog Timer)")) o(c(ecx, 14, 1, "ECX 14: Reserved")) o(c(ecx, 15, 1, "ECX 15: LWP (Light Weight Profiling)")) o(c(ecx, 16, 1, "ECX 16: FMA4 (4-operands FMA instructions)")) o(c(ecx, 17, 1, "ECX 17: TCE (Translation Cache Extension)")) o(c(ecx, 18, 1, "ECX 18: Reserved")) o(c(ecx, 19, 1, "ECX 19: MSR (Node ID MSR")) o(c(ecx, 20, 1, "ECX 20: Reserved")) o(c(ecx, 21, 1, "ECX 21: TBM (Trailing Bit Manipulation instructions)")) o(c(ecx, 22, 1, "ECX 22: TOPOEXT (TOPology EXTensions)")) o(c(ecx, 23, 1, "ECX 23: PERFCTR_CORE (CORE PERFormance CounTeR extensions)")) o(c(ecx, 24, 1, "ECX 24: PERFCTR_NB (NB PERFormance CounTeR extensions")) o(c(ecx, 25, 1, "ECX 25: Streaming performance monitor architecture")) o(c(ecx, 26, 1, "ECX 26: DBX (Data breakpoint eXtensions)")) o(c(ecx, 27, 1, "ECX 27: PERFTSC (PERFormance Time Stamp Counter)")) o(c(ecx, 28, 1, "ECX 28: PERFCTR_L2 (L2 PERFormance CounTeR extensions)")) o(c(ecx, 29, 1, "ECX 29: MONITORX/MWAITX instructions")) o(c(ecx, 30, 1, "ECX 30: Address mask extension for instruction breakpoint")) o(c(ecx, 31, 1, "ECX 31: Reserved")) elif id in [0x8000_0002, 0x8000_0003, 0x8000_0004]: vid = String.bytes2str(p32(eax) + p32(ebx) + p32(ecx) + p32(edx)) o("eax+ebx+ecx+edx: Processor Brand String (={!r})".format(vid)) elif id == 0x8000_0005: o("L1 Cache Information") o("eax: 4/2 MB L1 TLB configuration descriptor") o("ebx: 4 KB L1 TLB configuration descriptor") o("ecx: data L1 cache configuration descriptor") o("edx: code L1 cache configuration descriptor") elif id == 0x8000_0006: o("L2/L3 Cache Information") o("eax: 4/2 MB L2 TLB configuration descriptor") o("ebx: 4 KB L2 TLB configuration descriptor") o("ecx: unified L2 cache configuration descriptor") o("edx: unified L3 cache configuration descriptor") elif id == 0x8000_0007: o("ebx: RAS Capabilities") o(c(ebx, 0, 1, "EBX 0: MCA overflow recovery")) o(c(ebx, 1, 1, "EBX 1: Software uncorrectable error containment and recovery")) o(c(ebx, 2, 1, "EBX 2: HWA (HardWare Assert)")) o(c(ebx, 3, 1, "EBX 3: Scalable MCA")) o(c(ebx, 4, 1, "EBX 4: PFEH (Platform First Error Handling)")) o(c(ebx, 5, 0x7ff_ffff, "EBX 31- 5: Reserved")) o("edx: Advanced Power Management information") o(c(edx, 0, 1, "EDX 0: TS (Temperature Sensor)")) o(c(edx, 1, 1, "EDX 1: FID (Frequency ID control)")) o(c(edx, 2, 1, "EDX 2: VID (Voltage ID control)")) o(c(edx, 3, 1, "EDX 3: TTP (Thermal Trip)")) o(c(edx, 4, 1, "EDX 4: TM (Thermal Monitoring)")) o(c(edx, 5, 1, "EDX 5: STC (Software Thermal Control)")) o(c(edx, 6, 1, "EDX 6: MUL (100MHz Multiplier steps)")) o(c(edx, 7, 1, "EDX 7: HWPS (HardWare P-State control)")) o(c(edx, 8, 1, "EDX 8: ITSC (Invariant TSC)")) o(c(edx, 9, 1, "EDX 9: Core performance boost")) o(c(edx, 10, 1, "EDX 10: Read-only effective frequency interface")) o(c(edx, 11, 1, "EDX 11: Processor feedback interface")) o(c(edx, 12, 1, "EDX 12: Core power reporting")) o(c(edx, 13, 1, "EDX 13: Connected standby")) o(c(edx, 14, 1, "EDX 14: RAPL (Running Average Power Limit)")) o(c(edx, 15, 0x1_ffff, "EAX 31-15: Reserved")) elif id == 0x8000_0008: o("eax: Extended Address Length Information") o(c(eax, 0, 0xff, "EAX 7- 0: Physical address length")) o(c(eax, 8, 0xff, "EAX 15- 8: Linear address length")) o(c(eax, 16, 0xff, "EAX 23-16: Guest physical address length")) o(c(eax, 24, 0xff, "EAX 31-24: Reserved")) o("ebx: Extended Feature Extensions ID") o(c(ebx, 0, 1, "EBX 0: CLZERO (CLZERO instruction)")) o(c(ebx, 1, 1, "EBX 1: IRPerf (Instructions Retired count support)")) o(c(ebx, 2, 1, "EBX 2: XSAVE always saves/restores error pointers")) o(c(ebx, 3, 1, "EBX 3: INVLPGB and TLBSYNC instruction")) o(c(ebx, 4, 1, "EBX 4: RDPRU (RDPRU instruction)")) o(c(ebx, 5, 1, "EBX 5: Reserved")) o(c(ebx, 6, 1, "EBX 6: MBE (Memory Bandwidth Enforcement)")) o(c(ebx, 7, 1, "EBX 7: Reserved")) o(c(ebx, 8, 1, "EBX 8: MCOMMIT (MCOMMIT instruction)")) o(c(ebx, 9, 1, "EBX 9: WBNOINVD (Write Back and do NOt INValiDate cache)")) o(c(ebx, 10, 1, "EBX 10: LBR extensions")) o(c(ebx, 11, 1, "EBX 11: Reserved")) o(c(ebx, 12, 1, "EBX 12: IBPB (Indirect Branch Prediction Barrier)")) o(c(ebx, 13, 1, "EBX 13: WBINVD (Write Back and INValiDate cache)")) o(c(ebx, 14, 1, "EBX 14: IBRS (Indirect Branch Restricted Speculation)")) o(c(ebx, 15, 1, "EBX 15: STIBP (Single Thread Indirect Branch Predictor)")) o(c(ebx, 16, 1, "EBX 16: Reserved")) o(c(ebx, 17, 1, "EBX 17: STIBP always on")) o(c(ebx, 18, 1, "EBX 18: IBRS preferred over software solution")) o(c(ebx, 19, 1, "EBX 19: IBRS provides Same Mode Protection")) o(c(ebx, 20, 1, "EBX 20: EFER.LMLSE is unsupported")) o(c(ebx, 21, 1, "EBX 21: INVLPGB for guest nested translations")) o(c(ebx, 22, 1, "EBX 22: Reserved")) o(c(ebx, 23, 1, "EBX 23: PPIN (Protected Processor Inventory Number)")) o(c(ebx, 24, 1, "EBX 24: SSBD (Speculative Store Bypass Disable)")) o(c(ebx, 25, 1, "EBX 25: VIRT_SPEC_CTL")) o(c(ebx, 26, 1, "EBX 26: SSBD no longer needed")) o(c(ebx, 27, 1, "EBX 27: CPPC (Collaborative Processor Performance Control)")) o(c(ebx, 28, 1, "EBX 28: PSFD (Predictive Store Forward Disable)")) o(c(ebx, 29, 1, "EBX 29: Reserved")) o(c(ebx, 30, 1, "EBX 30: Reserved")) o(c(ebx, 31, 1, "EBX 31: Reserved")) o("ecx: Extended Core Information") o(c(ecx, 0, 0xff, "ECX 7- 0: Number of cores per (number of dies-1)")) o(c(ecx, 8, 0xf, "ECX 11- 8: Reserved")) o(c(ecx, 12, 0xf, "ECX 15-12: Number of LSBs in APIC ID that indicate core ID")) o(c(ecx, 16, 0xffff, "ECX 31-16: Reserved")) elif id == 0x8000_000a: o("SVM Revision and Feature Identification") o(c(edx, 0, 1, "EDX 0: Nested paging")) o(c(edx, 1, 1, "EDX 1: LBR virtualization")) o(c(edx, 2, 1, "EDX 2: SVM lock")) o(c(edx, 3, 1, "EDX 3: NRIP save")) o(c(edx, 4, 1, "EDX 4: MSR-based TSC rate control")) o(c(edx, 5, 1, "EDX 5: VMCB clean bits")) o(c(edx, 6, 1, "EDX 6: Flush by ASID")) o(c(edx, 7, 1, "EDX 7: Decode assists")) o(c(edx, 8, 1, "EDX 8: Reserved")) o(c(edx, 9, 1, "EDX 9: Reserved")) o(c(edx, 10, 1, "EDX 10: Pause intercept filter")) o(c(edx, 11, 1, "EDX 11: Encrypted micro-code patch")) o(c(edx, 12, 1, "EDX 12: PAUSE filter threshold")) o(c(edx, 13, 1, "EDX 13: AMD virtual interrupt controller")) o(c(edx, 14, 1, "EDX 14: Reserved")) o(c(edx, 15, 1, "EDX 15: Virtualized VMLOAD/VMSAVE")) o(c(edx, 16, 1, "EDX 16: Virtualized GIF")) o(c(edx, 17, 1, "EDX 17: GMET (Guest Mode Execution Trap")) o(c(edx, 18, 1, "EDX 18: Reserved")) o(c(edx, 19, 1, "EDX 19: SVM supervisor shadow stack restrictions")) o(c(edx, 20, 1, "EDX 20: SPEC_CTRL virtualization")) o(c(edx, 21, 1, "EDX 21: Reserved")) o(c(edx, 22, 1, "EDX 22: Reserved")) o(c(edx, 23, 1, "EDX 23: Host MCE override")) o(c(edx, 24, 1, "EDX 24: INVLPGB/TLBSYNC hypervisor enable")) o(c(edx, 25, 0x7f, "EDX 31-25: Reserved")) elif id == 0x8000_0019: o("TLB Configuration Descriptors") elif id == 0x8000_001a: o("Performance Optimization Identifiers") o(c(eax, 0, 1, "EAX 0: FP128 (128-bit SSE full-width pipelines)")) o(c(eax, 1, 1, "EAX 1: MOVU (Efficient MOVU SSE instructions)")) o(c(eax, 2, 1, "EAX 2: FP256 (256-bit AVX full-width pipelines)")) o(c(eax, 3, 0x1fff_ffff, "EAX 31- 3: Reserved")) elif id == 0x8000_001b: o("Instruction Based Sampling Identifiers") o(c(eax, 0, 1, "EAX 0: IBSFFV (IBS Feature Flags Valid)")) o(c(eax, 1, 1, "EAX 1: FetchSam (IBS Fetch Sampling)")) o(c(eax, 2, 1, "EAX 2: OpSam (IBS Execution Sampling)")) o(c(eax, 3, 1, "EAX 3: RdWrOpCnt (Read/write of Op Counter)")) o(c(eax, 4, 1, "EAX 4: OpCnt (Op Counting mode)")) o(c(eax, 5, 1, "EAX 5: BrnTrgt (Branch Target address reporting)")) o(c(eax, 6, 1, "EAX 6: OpCntExt (IBS op cur/max count extended by 7 bits)")) o(c(eax, 7, 1, "EAX 7: RipInvalidChk (IBS RIP invalid indication)")) o(c(eax, 8, 1, "EAX 8: OpBrnFuse (IBS fused Branch micro-op indication)")) o(c(eax, 9, 1, "EAX 9: IbsFetchCtlExtd (IBS Fetch Control Extended MSR)")) o(c(eax, 10, 1, "EAX 10: IbsOpData4 (IBS Op Data 4 MSR)")) o(c(eax, 11, 0x1f_ffff, "EAX 31-11: Reserved")) elif id == 0x8fff_ffff: vid = String.bytes2str(p32(eax) + p32(ebx) + p32(ecx) + p32(edx)) o("eax+ebx+ecx+edx: Easter egg (={!r})".format(vid)) elif id == 0xc000_0000: o("eax: Maximum Input Value for Extended Function CPUID Information") o("ebx,ecx,edx: Reserved") elif id == 0xc000_0001: o("Centaur features") o(c(edx, 0, 1, "EDX 0: AIS (Alternate Instruction Set available)")) o(c(edx, 1, 1, "EDX 1: AIS_EN (Alternate Instruction Set ENabled)")) o(c(edx, 2, 1, "EDX 2: RNG (Random Number Generator available)")) o(c(edx, 3, 1, "EDX 3: RNG_EN (Random Number Generator ENabled)")) o(c(edx, 4, 1, "EDX 4: LH (LongHaul MSR 0000_110Ah)")) o(c(edx, 5, 1, "EDX 5: FEMMS")) o(c(edx, 6, 1, "EDX 6: ACE (Advanced Cryptography Engine available)")) o(c(edx, 7, 1, "EDX 7: ACE_EN (Advanced Cryptography Engine Enabled)")) o(c(edx, 8, 1, "EDX 8: ACE2 (Montgomery Multiplier and Hash Engine available)")) o(c(edx, 9, 1, "EDX 9: ACE2_EN (Montgomery Multiplier and Hash Engine Enabled)")) o(c(edx, 10, 1, "EDX 10: PHE (Padlock Hash Engine available)")) o(c(edx, 11, 1, "EDX 11: PHE_EN (Padlock Hash Engine ENabled)")) o(c(edx, 12, 1, "EDX 12: PMM (Padlock Montgomery Multiplier available)")) o(c(edx, 13, 1, "EDX 13: PMM_EN (Padlock Montgomery Multiplier ENabled)")) o(c(edx, 14, 0x3_ffff, "EAX 31-14: Reserved")) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64")) @only_if_kvm_disabled def do_invoke(self, args): self.out = [] # Basic Information eax, _, _, _ = self.execute_cpuid(0) valid_max_cpuid = min(eax, 0x20) for id in range(valid_max_cpuid + 1): if id == 4: for subid in range(3): eax, ebx, ecx, edx = self.execute_cpuid(id, subid) self.make_out(id, subid, eax, ebx, ecx, edx) elif id == 7: eax, _, _, _ = self.execute_cpuid(id, 0) for subid in range(eax + 1): eax, ebx, ecx, edx = self.execute_cpuid(id, subid) self.make_out(id, subid, eax, ebx, ecx, edx) elif id == 13: for subid in range(63): eax, ebx, ecx, edx = self.execute_cpuid(id, subid) self.make_out(id, subid, eax, ebx, ecx, edx) elif id in [16, 18, 19, 20, 23, 24, 26, 31]: eax, ebx, ecx, edx = self.execute_cpuid(id, 0) self.make_out(id, 0, eax, ebx, ecx, edx) else: eax, ebx, ecx, edx = self.execute_cpuid(id) self.make_out(id, None, eax, ebx, ecx, edx) # Hypervisor Information valid_max_cpuid, _, _, _ = self.execute_cpuid(0x4000_0000) for id in range(0x4000_0000, valid_max_cpuid + 1): eax, ebx, ecx, edx = self.execute_cpuid(id) self.make_out(id, None, eax, ebx, ecx, edx) # Extended Information valid_max_cpuid, _, _, _ = self.execute_cpuid(0x8000_0000) for id in range(0x8000_0000, valid_max_cpuid + 1): eax, ebx, ecx, edx = self.execute_cpuid(id) self.make_out(id, None, eax, ebx, ecx, edx) for id in [0x8fff_ffff]: eax, ebx, ecx, edx = self.execute_cpuid(id) self.make_out(id, None, eax, ebx, ecx, edx) # Transmeta Specific Information valid_max_cpuid, _, _, _ = self.execute_cpuid(0x8086_0000) for id in range(0x8086_0000, valid_max_cpuid + 1): eax, ebx, ecx, edx = self.execute_cpuid(id) self.make_out(id, None, eax, ebx, ecx, edx) # Centaur(VIA) Specific Information valid_max_cpuid, _, _, _ = self.execute_cpuid(0xc000_0000) for id in range(0xc000_0000, valid_max_cpuid + 1): eax, ebx, ecx, edx = self.execute_cpuid(id) self.make_out(id, None, eax, ebx, ecx, edx) self.print_output() return @register_command class MsrCommand(GenericCommand): """Read or write MSR value.""" _cmdline_ = "msr" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("msr_target", metavar="MSR_NAME|MSR_CONST", nargs="?", help="the MSR name or constant to know the value.") parser.add_argument("msr_value", metavar="MSR_VALUE", nargs="?", type=AddressUtil.parse_address, help="the MSR value to update.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # show frequently used MSRs", "{0:s} 0xc0000080 # read msr", "{0:s} MSR_EFER # another valid format", "{0:s} 0xc0000080 0xd01 # write msr", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Disable`-enable-kvm` option for qemu-system.", ] _note_ = "\n".join(_note_) msr_table = [ # frequently used x86-64 MSRs ["MSR_EFER", 0xc000_0080, "Extended feature register"], ["MSR_STAR", 0xc000_0081, "Legacy mode SYSCALL target"], ["MSR_LSTAR", 0xc000_0082, "Long mode SYSCALL target"], ["MSR_CSTAR", 0xc000_0083, "Compat mode SYSCALL target"], ["MSR_SYSCALL_MASK", 0xc000_0084, "EFLAGS mask for syscall"], ["MSR_FS_BASE", 0xc000_0100, "64bit FS base"], ["MSR_GS_BASE", 0xc000_0101, "64bit GS base"], ["MSR_KERNEL_GS_BASE", 0xc000_0102, "SwapGS GS shadow"], ["MSR_TSC_AUX", 0xc000_0103, "Auxiliary TSC"], # x86-32 and x86-64 ["MSR_IA32_SYSENTER_CS", 0x0000_0174, "Sysenter CS"], ["MSR_IA32_SYSENTER_ESP", 0x0000_0175, "Sysenter ESP"], ["MSR_IA32_SYSENTER_EIP", 0x0000_0176, "Sysenter EIP"], ["MSR_IA32_U_CET", 0x0000_06a0, "User mode CET"], ["MSR_IA32_S_CET", 0x0000_06a2, "Kernel mode CET"], ["MSR_IA32_PL0_SSP", 0x0000_06a4, "Ring-0 shadow stack pointer"], ["MSR_IA32_PL1_SSP", 0x0000_06a5, "Ring-1 shadow stack pointer"], ["MSR_IA32_PL2_SSP", 0x0000_06a6, "Ring-2 shadow stack pointer"], ["MSR_IA32_PL3_SSP", 0x0000_06a7, "Ring-3 shadow stack pointer"], ["MSR_IA32_INT_SSP_TAB", 0x0000_06a8, "Exception shadow stack table"], ] def lookup_name2const(self, target_name): for name, const, _desc in self.msr_table: if name == target_name: return const try: return int(target_name, 0) except ValueError: return None def lookup_const2name(self, target_const): for name, const, _desc in self.msr_table: if const == target_const: return name return "Unknown" def print_const_table(self): gef_print(titlify("MSR table (frequently used only)")) fmt = "{:30s} {:10s} {:30s} {:s}" legend = ["Name", "Const", "Description", "Value"] gef_print(GefUtil.make_legend(fmt.format(*legend))) for name, const, desc in self.msr_table: value = MsrCommand.read_msr(const) if value is None: gef_print("{:30s} {:#010x} {:30s} {!s}".format( name, const, desc, None, )) continue sym = "" if is_valid_addr(value): sym = Symbol.get_symbol_string(value) gef_print("{:30s} {:#010x} {:30s} {:s}{:s}".format( name, const, desc, AddressUtil.format_address(value), sym, )) info("See more info: https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/msr-index.h") return @staticmethod def read_msr(const): codes = [b"\x0f\x32"] # rdmsr if is_x86_64(): regs = {"$rcx": const} else: regs = {"$ecx": const} ret = ExecAsm(codes, regs=regs).exec_code() if ret is None: return None if is_x86_64(): edx = ret["reg"]["$rdx"] & 0xffff_ffff eax = ret["reg"]["$rax"] & 0xffff_ffff else: edx = ret["reg"]["$edx"] eax = ret["reg"]["$eax"] return ((edx << 32) | eax) & 0xffff_ffff_ffff_ffff @staticmethod def write_msr(const, value): codes = [b"\x0f\x30"] # wrmsr if is_x86_64(): regs = {"$rcx": const, "$rdx": value >> 32, "$rax": value & 0xffff_ffff} else: regs = {"$ecx": const, "$edx": value >> 32, "$eax": value & 0xffff_ffff} ret = ExecAsm(codes, regs=regs).exec_code() return bool(ret) @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64")) @only_if_in_kernel @only_if_kvm_disabled def do_invoke(self, args): # list if args.msr_target is None and args.msr_value is None: self.print_const_table() return # search for const table const = self.lookup_name2const(args.msr_target) if const is None: self.usage() return if args.msr_value is None: # exec rdmsr value = MsrCommand.read_msr(const) if value is None: err("Failed to read") return name = self.lookup_const2name(const) if args.quiet: gef_print("{:s}".format(AddressUtil.format_address(value))) else: gef_print("{:s} ({:#x}): {:s}".format(name, const, AddressUtil.format_address(value))) else: # exec wrmsr ret = MsrCommand.write_msr(const, args.msr_value) if ret: info("Success to write") else: err("Failed to write") return @register_command class CetCommand(GenericCommand): """Display Intel CET settings.""" _cmdline_ = "cet" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() U_S_COMMON_BITS = { "SH_STK_EN": 0, # Shadow Stack enable "WR_SHSTK_EN": 1, # WRSS{D,Q}W enable "ENDBR_EN": 2, # IBT enable "LEG_IW_EN": 3, # Legacy indirect-write compat "NO_TRACK_EN": 4, # No-track prefix enable "SUPPRESS_DIS": 5, # Suppress disable # 6..9 reserved "SUPPRESS": 10, # IBT suppression state "TRACKER": 11, # TRACKER state bit-field (0:IDLE, 1:WAIT_FOR_ENDBRANCH) # 12..63 EB_LEG_BITMAP_BASE (bitmap base, <<12) } TRACKER_STATES = {0: "IDLE", 1: "WAIT_FOR_ENDBRANCH"} def decode_cet_bits(self, val): if val is None: return None d = {} for name, bit in self.U_S_COMMON_BITS.items(): d[name] = (val >> bit) & 1 tracker = (val >> 11) & 0x1 d["TRACKER"] = "{:d} ({:s})".format(d["TRACKER"], self.TRACKER_STATES[tracker]) d["EB_LEG_BITMAP_BASE"] = AddressUtil.format_address(val & ~0xfff) return d def print_cet_bits(self, title, d): gef_print(titlify(title)) for k, v in d.items(): if k in ["TRACKER", "EB_LEG_BITMAP_BASE"]: gef_print("{:20s} : {:s}".format(k, v)) else: gef_print("{:20s} : {:x}".format(k, v)) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64")) @only_if_in_kernel @only_if_kvm_disabled def do_invoke(self, args): cr4 = get_register("cr4", use_monitor=True) gef_print(titlify("CET summary")) if cr4 is not None: cet_master = (cr4 >> 23) & 1 gef_print("CR4 = {:#x} (CR4.CET = {:#x})".format(cr4, cet_master)) else: gef_print("CR4 = N/A") IA32_U_CET = MsrCommand.read_msr(0x6a0) ud = self.decode_cet_bits(IA32_U_CET) if ud is not None: self.print_cet_bits("IA32_U_CET ({:#x})".format(IA32_U_CET), ud) else: gef_print("IA_32_U_CET = N/A") IA32_S_CET = MsrCommand.read_msr(0x6a2) sd = self.decode_cet_bits(IA32_S_CET) if sd is not None: self.print_cet_bits("IA32_S_CET ({:#x})".format(IA32_S_CET), ud) else: gef_print("IA_32_U_CET = N/A") IA32_PL0_SSP = MsrCommand.read_msr(0x6a4) IA32_PL1_SSP = MsrCommand.read_msr(0x6a5) IA32_PL2_SSP = MsrCommand.read_msr(0x6a6) IA32_PL3_SSP = MsrCommand.read_msr(0x6a7) IA32_INT_SSP_TAB = MsrCommand.read_msr(0x6a8) gef_print(titlify("SSP MSRs")) gef_print("PL0_SSP = {:s}".format(AddressUtil.format_address(IA32_PL0_SSP))) gef_print("PL1_SSP = {:s}".format(AddressUtil.format_address(IA32_PL1_SSP))) gef_print("PL2_SSP = {:s}".format(AddressUtil.format_address(IA32_PL2_SSP))) gef_print("PL3_SSP = {:s}".format(AddressUtil.format_address(IA32_PL3_SSP))) gef_print("IA32_INTERRUPT_SSP_TABLE_ADDR = {:s}".format(AddressUtil.format_address(IA32_INT_SSP_TAB))) return @register_command class MteTagsCommand(GenericCommand): """Display the MTE tag for the specified address (ARM64 only).""" _cmdline_ = "mte-tags" _category_ = "02-f. Process Information - Security" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="the start address to display the MTE tag.") parser.add_argument("count", metavar="COUNT", nargs="?", type=AddressUtil.parse_address, help="repeat count for MTE tag displaying (every 16 bytes).") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("rr",)) @only_if_specific_arch(arch=("ARM64",)) def do_invoke(self, args): auxv = Auxv.get_auxiliary_values() HWCAP2_MTE = 1 << 18 if auxv and "AT_HWCAP2" in auxv and (auxv["AT_HWCAP2"] & HWCAP2_MTE) == 0: err("MTE is unsupported") return codes = [b"\x00\x00\x60\xD9"] # ldg x0, [x0] count = args.count or 1 for i in range(count): address = args.address + 16 * i if not is_valid_addr(address): break ret = ExecAsm(codes, regs={"$x0": address}).exec_code() tag = (ret["reg"]["$x0"] >> 56) & 0xff gef_print("{!s}: {:#04x} ({:#018x})".format(ProcessMap.lookup_address(address), tag, tag << 56)) return @register_command class PacKeysCommand(GenericCommand): """Pretty-print PAC keys from qemu registers (ARM64 only).""" _cmdline_ = "pac-keys" _category_ = "04-a. Register - View" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM64",)) def do_invoke(self, args): for keyname in ["APIA", "APIB", "APDA", "APDB", "APGA"]: try: lo = get_register("{:s}KEYLO_EL1".format(keyname)) hi = get_register("{:s}KEYHI_EL1".format(keyname)) bs = " ".join(slicer(p64(lo).hex() + p64(hi).hex(), 2)) gef_print("{:s}KEY: {:#018x} {:#018x} ({:s})".format(keyname, hi, lo, bs)) except Exception: err("Failed to get the value of PAC keys") break return @register_command class VBARCommand(GenericCommand, BufferingOutput): """Pretty-print ARM/ARM64 vector table.""" _cmdline_ = "vbar" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-a", "--address", type=AddressUtil.parse_address, help="the vector address.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="display all instructions (for ARM64).") _syntax_ = parser.format_help() A32_VECTOR_NAMES = [ (0x00, 0x04, "Reset"), (0x04, 0x04, "Undefined Instruction"), (0x08, 0x04, "Supervisor Call"), (0x0c, 0x04, "Prefetch Abort"), (0x10, 0x04, "Data Abort"), (0x14, 0x04, "Reserved"), (0x18, 0x04, "IRQ Interrupt"), (0x1c, 0x04, "FIQ Interrupt"), ] A64_VECTOR_NAMES = [ (0x000, 0x80, "Current EL (SP0) - Synchronous"), (0x080, 0x80, "Current EL (SP0) - IRQ/vIRQ"), (0x100, 0x80, "Current EL (SP0) - FIQ/vFIQ"), (0x180, 0x80, "Current EL (SP0) - SError/vSError"), (0x200, 0x80, "Current EL (SPx) - Synchronous"), (0x280, 0x80, "Current EL (SPx) - IRQ/vIRQ"), (0x300, 0x80, "Current EL (SPx) - FIQ/vFIQ"), (0x380, 0x80, "Current EL (SPx) - SError/vSError"), (0x400, 0x80, "Lower EL (AArch64) - Synchronous"), (0x480, 0x80, "Lower EL (AArch64) - IRQ/vIRQ"), (0x500, 0x80, "Lower EL (AArch64) - FIQ/vFIQ"), (0x580, 0x80, "Lower EL (AArch64) - SError/vSError"), (0x600, 0x80, "Lower EL (AArch32) - Synchronous"), (0x680, 0x80, "Lower EL (AArch32) - IRQ/vIRQ"), (0x700, 0x80, "Lower EL (AArch32) - FIQ/vFIQ"), (0x780, 0x80, "Lower EL (AArch32) - SError/vSError"), ] def get_vbar_arm32(self): if self.args.address is not None: vbars = [("User specified", self.args.address)] return vbars vbars = [] # VBAR sctlr = get_register("$SCTLR") or get_register("$SCTLR_EL1") if (sctlr >> 13) & 1: vbars.append(("$VBAR ($SCTLR.V==1)", 0xffff_0000)) # default else: vbar = get_register("$VBAR") or get_register("$VBAR_EL1") vbars.append(("$VBAR ($SCTLR.V==0)", vbar)) # VBAR_S sctlr_s = get_register("$SCTLR_S") or get_register("$SCTLR_EL1_S") if sctlr_s is not None: if (sctlr_s >> 13) & 1: vbars.append(("$VBAR_S ($SCTLR_S.V==1)", 0xffff_0000)) # default else: vbar = get_register("$VBAR_S") or get_register("$VBAR_EL1_S") vbars.append(("$VBAR_S ($SCTLR_S.V==0)", vbar)) return vbars def dump_vbar_arm32(self): vbars = self.get_vbar_arm32() max_width = max(len(x[2]) for x in self.A32_VECTOR_NAMES) for regname, vbar in vbars: self.out.append(titlify(regname)) # address check if "$VBAR_S" in regname and not is_in_secure(): vbar_phys = XSecureMemAddrCommand.v2p_secure(vbar) if vbar_phys is None: self.err_add_out("Invalid VBAR address: {:#x}".format(vbar)) continue else: if not is_valid_addr(vbar): if vbar is None: self.err_add_out("Invalid VBAR address: None") else: self.err_add_out("Invalid VBAR address: {:#x}".format(vbar)) continue # read each entry for ofs, _sz, s in self.A32_VECTOR_NAMES: s = Color.colorify(s.ljust(max_width), "bold") if "$VBAR_S" in regname and not is_in_secure(): try: code = read_physmem(vbar_phys + ofs, 4) except gdb.MemoryError: self.out.append("[{:+#05x}] {:s}: {:s}".format(ofs, s, "Memory access error")) continue try: insn_str = gdb.execute("pdisas {:#x} code={:s} -l 1".format(vbar + ofs, code.hex()), to_string=True) insn_str = insn_str.replace(" ", "") except gdb.error: self.out.append("[{:+#05x}] {:s}: {:s}".format(ofs, s, "Capstone not found")) continue else: try: insn = get_insn(vbar + ofs) except gdb.MemoryError: self.out.append("[{:+#05x}] {:s}: {:s}".format(ofs, s, "Memory access error")) continue insn_str = insn.colored_text(4) self.out.append("[{:+#05x}] {:s}: {:s}".format(ofs, s, insn_str.strip())) return def get_vbar_arm64(self): if self.args.address is not None: vbars = [("User specified", self.args.address)] return vbars vbars = [] # VBAR vbar = get_register("$VBAR") or get_register("$VBAR_EL1") vbars.append(("$VBAR", vbar)) # VBAR_EL2 vbar = get_register("$VBAR_EL2") vbars.append(("$VBAR_EL2", vbar)) # VBAR_EL3 vbar = get_register("$VBAR_EL3") vbars.append(("$VBAR_EL3", vbar)) return vbars def dump_vbar_arm64(self): vbars = self.get_vbar_arm64() max_width = max(len(x[2]) for x in self.A64_VECTOR_NAMES) def get_EL(): CPSR = get_register("$cpsr") & 0xffff_ffff return (CPSR >> 2) & 0b11 base_EL = get_EL() for regname, vbar in vbars: self.out.append(titlify(regname)) # switch EL if regname == "$VBAR" and base_EL != 1: gdb.execute("switch-el 1", to_string=True) elif regname == "$VBAR_EL2" and base_EL != 2: gdb.execute("switch-el 2", to_string=True) elif regname == "$VBAR_EL3" and base_EL != 3: gdb.execute("switch-el 3", to_string=True) else: gdb.execute("switch-el {:d}".format(base_EL), to_string=True) # address check if not is_valid_addr(vbar): if vbar is None: self.err_add_out("Invalid VBAR address: None") else: self.err_add_out("Invalid VBAR address: {:#x}".format(vbar)) continue # read each entry for ofs, sz, s in self.A64_VECTOR_NAMES: if self.args.verbose: # full pos = 0 while pos < sz: insn = get_insn(vbar + ofs + pos) insn_str = insn.colored_text(4) if pos == 0: s = Color.colorify(s.ljust(max_width), "bold") self.out.append("[{:+#06x}] {:s}: {:s}".format(ofs, s, insn_str)) else: s = " " * max_width self.out.append("{:8s} {:s}: {:s}".format("", s, insn_str)) pos += insn.size else: # compact insn = get_insn(vbar + ofs) insn_str = insn.colored_text(4) s = Color.colorify(s.ljust(max_width), "bold") self.out.append("[{:+#06x}] {:s}: {:s}".format(ofs, s, insn_str)) # revert gdb.execute("switch-el {:d}".format(base_EL), to_string=True) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32", "ARM64")) def do_invoke(self, args): self.out = [] if is_arm32(): self.dump_vbar_arm32() elif is_arm64(): self.dump_vbar_arm64() self.print_output(check_terminal_size=True) return class BitInfo: """Printing various bit information of the register.""" def __init__(self, name, register_bit=None, bit_info=(), desc=None): self.name = name if register_bit is None: self.register_bit = current_arch.ptrsize * 8 else: self.register_bit = register_bit self.description = desc # bit_info: [[bits, short_name, short_description, long_description], ...] self.bit_info = bit_info return @staticmethod def bits_split(x, bits): # split by 4bits. e.g., 0bXXYYYY -> 0b00XX_YYYY out = "" for i in range(bits): if x & (1 << i): out = "1" + out else: out = "0" + out if i % 4 == 3: out = "_" + out return "0b" + out[1:] def print_value(self, regval, split=False): regname = Color.colorify(self.name, "bold red") value_str = Color.colorify_hex(regval, "bold yellow") if split: bit_split_str = BitInfo.bits_split(regval, self.register_bit) value_str += Color.colorify(" (={:s})".format(bit_split_str), "bold yellow") self.out.append("{:s} = {:s}".format(regname, value_str)) return def print_description(self): if self.description: self.out.append(Color.boldify(self.description)) return def print_bitinfo(self, regval): # preprocess max_width_bits = 2 # default max_width_sym = 0 max_width_val = 0 bit_range_strs = [] bit_values = [] for bits, sym, *_ in self.bit_info: if isinstance(bits, range): bits = list(bits) # search for max width for bit_ragne_string if isinstance(bits, int): b = "{:d}".format(bits) bit_range_strs.append(b) else: # e.g., [11, 12, 0, 1, 2, 10] -> [0, 1, 2, 10, 11, 12] bits = sorted(bits) # e.g., [0, 1, 2, 10, 11, 12] -> [[0, 1, 2], [10, 11, 12]] gen = itertools.groupby(bits, key=lambda n, c=itertools.count(): n - next(c)) # noqa: B008 gr_bits = [list(g) for _, g in gen] tmp = [] for gb in gr_bits: if len(gb) == 1: tmp.append("{:d}".format(gb[0])) else: tmp.append("{:d}-{:d}".format(gb[-1], gb[0])) bit_str = ",".join(tmp[::-1]) bit_range_strs.append(bit_str) max_width_bits = max(max_width_bits, len(bit_str)) # search for max width for sym if sym: max_width_sym = max(max_width_sym, len(sym)) # search for max width for val if isinstance(bits, int): val = (regval & (1 << bits)) >> bits bit_values.append(val) else: val = 0 for i, x in enumerate(bits): val |= (regval & (1 << x)) >> (x - i) bit_values.append(val) max_width_val = max(max_width_val, len("{:#x}".format(val))) # here, preprocess is finieshed. # - max_width_bits # - max_width_sym # - max_width_val # - bit_range_strs # e.g., ["0", "4-1", "8-7,5"] # - bit_values # e.g., [0b1, 0b1111, 0b1101] # actual perform for i, (_, sym, *desc) in enumerate(self.bit_info): b = bit_range_strs[i] val = bit_values[i] msg = "bit{:>{:d}s}: ".format(b, max_width_bits) if val: msg += Color.boldify("{:>#{:d}x} ".format(val, max_width_val)) else: msg += "{:>#{:d}x} ".format(val, max_width_val) if sym is not None: msg += "{:{:d}s} ".format(sym, max_width_sym) if len(desc) == 1: # short description only if desc[0]: msg += desc[0] elif len(desc) >= 2: # short description and long description if desc[0]: msg += desc[0] + "; " msg += "; ".join(desc[1:]) self.out.append(msg) return def make_out(self, regval, split=False): self.out = [] self.print_value(regval, split) self.print_description() self.print_bitinfo(regval) return self.out def print(self, regval, split=False): self.make_out(regval, split) if self.out: gef_print("\n".join(self.out)) return @register_command class QemuRegistersCommand(GenericCommand, BufferingOutput): """Get registers via qemu-monitor and show the detail of x64/x86 system registers.""" _cmdline_ = "qreg" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-v", "--verbose", action="store_true", help="also display detailed bit information.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def qregisters_x86_x64(self): red = lambda x: Color.colorify(x, "bold red") yellow = lambda x: Color.colorify(x, "bold yellow") # CR0 self.out.append(titlify("CR0 (Control Register 0)")) desc = "It contains system control flags that control operating mode and states of the processor" bit_info = [ [31, "PG", "Paging", "If 1, enable paging and use CR3 register, else disable paging"], [30, "CD", "Cache disable", "If 1, disable the memory cache globally"], [29, "NW", "Not-write through", "If 1, disable write-through caching globally"], [18, "AM", "Alignment mask", "If 1, alignment check enabled when EFLAGS.AC==1 and Ring-3"], [16, "WP", "Write protect", "If 1, the CPU can't write to read-only pages when Ring-0"], [5, "NE", "Numeric error", "If 1, enable internal x87 FPU error reporting, else enable PC style x87 error detection"], [4, "ET", "Extension type", "x64: always 1. i386: if 1, x87 DX math coprosessor instructions is supported"], [3, "TS", "Task switched", "If 1, allow the saving x87 task context upon a task switch only after x87 instruction used"], [2, "EM", "Emulation", "If 1, no x87 FPU present, else x87 FPU present"], [1, "MP", "Monitor co-processor", "If 1, WAIT/FWAIT instructions generate #NM exception when CR0.TS"], [0, "PE", "Protected mode enable", "If 1, system is in protected mode, else system is in real mode"], ] cr0 = get_register("cr0", use_monitor=True) self.out.extend(BitInfo("CR0", 32, bit_info, desc).make_out(cr0)) # CR1 self.out.append(titlify("CR1 (Control Register 1)")) self.out.append("Reserved") # CR2 self.out.append(titlify("CR2 (Control Register 2)")) desc = "When page fault, the address attempted to access is stored (PFLA: Page Fault Linear Address)" cr2 = get_register("cr2", use_monitor=True) self.out.extend(BitInfo("CR2", desc=desc).make_out(cr2)) # CR3 self.out.append(titlify("CR3 (Control Register 3)")) desc = "It contains the physical address of the base of the paging-structure hierarchy and two flags" bit_info = [ [62, "LAM_U48", "User LAM48 enable", "If 1 and CR3.LAM_U57 is 0, enables LAM48 (masking of linear-address bits 62:48) for user pointers"], [61, "LAM_U57", "User LAM57 enable", "If 1, enables LAM57 (masking of linear-address bits 62:57) for user pointers; overrides CR3.LAM_U48"], [range(12, 32), None, None, "Base of page directory base, typically it points to PML4T if 4-level paging"], [range(0, 12), None, None, "Process context identifier when CR4.PCIDE=1"], [4, "PCD", "Page-level Cache Disable", "If 1, disable Page-Directory itself caching when CR4.PCIDE=0"], [3, "PWT", "Page-level Write-Through", "If 1, enable write through Page-Directory itself caching when CR4.PCIDE=0"], ] cr3 = get_register("cr3", use_monitor=True) self.out.extend(BitInfo("CR3", bit_info=bit_info, desc=desc).make_out(cr3)) # CR4 self.out.append(titlify("CR4 (Control Register 4)")) desc = "It contains flags that architectural extensions, indicate OS or executive support" bit_info = [ [28, "LAM_SUP", "Supervisor LAM enable", "If 1, enables LAM for supervisor pointers (kernel addresses)"], [27, "LASS", "Linear-address-space Separation", "If 1, enables LASS (linear-address-space separation)"], [25, "UINTR", "Enable user-mode inter-processor interrupts", "If 1, enable User-Interrupt Delivery"], [24, "PKS", "Enable protection keys for supervisor-mode pages", "If 1, enable PKS"], [23, "CET", "Control-flow Enforcement Technology", "If 1, enable CET"], [22, "PKE", "Protection Key Enable", "If 1, enable PKE"], [21, "SMAP", "Supervisor Mode Access Protection Enable", "If 1, access of data in a higher ring generates a fault"], [20, "SMEP", "Supervisor Mode Execution Protection Enable", "If 1, execution of code in a higher ring generates a fault"], [19, "KL", "Key-Locker Enable", "If 1, enable LOADIWKEY"], [18, "OSXSAVE", "Enable XSAVE and Processor Extended States", "If 1, enable XSAVE/XSAVEC/XSAVEOPT/XSAVES/XRSTOR/XRSTORS/XSETBV/XGETBV"], [17, "PCIDE", "PCID Enable", "If 1, enable process-context identifiers (PCIDs)"], [16, "FSGSBASE", "FSGSBASE Enable", "If 1, enable RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE"], [14, "SMXE", "Safer Mode Extensions Enable", "If 1, enable Trusted Execution Technology (TXT)"], [13, "VMXE", "Virtual Machine Extensions Enable", "If 1, enable Intel VT-x x86 virtualization"], [12, "LA57", "57bit linear addresses", "If 1, enable 5-Level Paging"], [11, "UMIP", "User-Mode Instruction Prevention", "If 1, SGDT/SIDT/SLDT/SMSW/STR instructions can only be executed in ring0"], [10, "OSXMMEXCPT", "OS support for Unmasked SIMD FP Exceptions", "If 1, enable unmasked SSE exceptions"], [9, "OSFXSR", "OS support for FXSAVE/FXRSTOR", "If 1, enable SSE instructions and fast FPU save & restore"], [8, "PCE", "Performance-Monitoring Counter enable", "If 1, RDPMC instruction can be executed at any privilege level"], [7, "PGE", "Page Global Enabled", "If 1, address translations (PDE or PTE records) may be shared between address spaces"], [6, "MCE", "Machine Check Exception", "If 1, enable machine check interrupts to occur"], [5, "PAE", "Physical Address Extension", "If 1, changes page table layout to translate 32bit virtaddr into 36bit physaddr"], [4, "PSE", "Page Size Extension", "If 1, page size is 4MB, else 4KB, this bit is ignored when PAE or x86-64 long mode"], [3, "DE", "Debugging Extensions", "If 1, enable debug register based breaks on I/O space access"], [2, "TSD", "Time Stamp Disable", "If 1, RDTSC instruction can only be executed in ring0"], [1, "PVI", "Protected-mode Virtual Interrupts", "If 1, enable support for the virtual interrupt flag (VIF) in protected mode"], [0, "VME", "Virtual 8086 Mode Extensions", "If 1, enable support for the virtual interrupt flag (VIF) in virtual-8086 mode"], ] cr4 = get_register("cr4", use_monitor=True) self.out.extend(BitInfo("CR4", bit_info=bit_info, desc=desc).make_out(cr4)) # CR8 self.out.append(titlify("CR8 (Control Register 8)")) desc = "Contain task priority level" bit_info = [ [range(0, 4), "TPL", "Task Priority Class"], ] cr8 = get_register("cr8", use_monitor=True) if cr8 is not None: # only access x86 64-bit mode self.out.extend(BitInfo("CR8", bit_info=bit_info, desc=desc).make_out(cr8)) # XCR0 # QEMU's monitor does not currently support displaying XCR0. Therefore, this code will not be executed. self.out.append(titlify("XCR0 (Extended Control Register 0)")) desc = "Contain task priority level" bit_info = [ [19, "APX", "Intel APX", "Enables Intel APX; EGPR (R16-R31) state is managed via XSAVE"], [18, "AMX_TILEDATA", "Intel AMX tile data", "Enables XSAVE-managed AMX tile data state (requires XCR0[18:17]=0b11 for AMX instructions)"], [17, "AMX_TILECFG", "Intel AMX tile config", "Enables XSAVE-managed AMX tile config state (requires XCR0[18:17]=0b11 for AMX instructions)"], [9, "PKRU", "Protection Keys", "Enables XSAVE-managed PKRU state"], [7, "HI16_ZMM", "AVX-512 ZMM16-31", "Enables XSAVE-managed upper ZMM registers ZMM16-ZMM31"], [6, "ZMM_HI256", "AVX-512 upper halves", "Enables XSAVE-managed upper 256 bits of ZMM0-ZMM15"], [5, "OPMASK", "AVX-512 opmask", "Enables XSAVE-managed opmask registers k0-k7"], [4, "BNDCSR", "MPX bounds config/status", "Enables XSAVE-managed BNDCFGU and BNDSTATUS (MPX)"], [3, "BNDREG", "MPX bounds registers", "Enables XSAVE-managed BND0-BND3 registers (MPX)"], [2, "AVX", "AVX YMM state", "Enables XSAVE-managed YMM state (requires SSE enabled)"], [1, "SSE", "SSE XMM/MXCSR state", "Enables XSAVE-managed XMM registers and MXCSR"], [0, "X87", "x87 FPU/MMX state", "x87 FPU/MMX state (architecturally required)"], ] xcr0 = get_register("xcr0", use_monitor=True) if xcr0 is not None: self.out.extend(BitInfo("XCR0", bit_info=bit_info, desc=desc).make_out(xcr0)) # DR0-DR3 self.out.append(titlify("DR0-DR3 (Debug Address Register 0-3)")) desc = "Contain linear addresses of up to 4 HW breakpoints. If paging is enabled, they are translated to physical addresses" dr0 = get_register("dr0", use_monitor=True) dr1 = get_register("dr1", use_monitor=True) dr2 = get_register("dr2", use_monitor=True) dr3 = get_register("dr3", use_monitor=True) self.out.extend(BitInfo("DR0").make_out(dr0)) self.out.extend(BitInfo("DR1").make_out(dr1)) self.out.extend(BitInfo("DR2").make_out(dr2)) self.out.extend(BitInfo("DR3", desc=desc).make_out(dr3)) # DR4-DR5 self.out.append(titlify("DR4-DR5 (Debug Register 4-5)")) self.out.append("Reserved") # DR6 self.out.append(titlify("DR6 (Debug Status Register 6)")) desc = "It permits the debugger to determine which debug conditions have occurred" bit_info = [ [16, "RTM", "restricted transactional memory", "If 0, the debug exception or breakpoint exception occurred inside an RTM region"], [15, "BT", "task switch", "If 1, the debug instruction resulted from a task switch where TSS.T of target task was set"], [14, "BS", "single step", "If 1, the debug exception was triggered by the single-step execution mode (enabled with EFLAGS.TF)"], [13, "BD", "debug register access detected", "If 1, the next instruction accesses one of the debug registers"], [3, "B3", "breakpoint condition detected", "If 1, breakpoint condition was met when a debug exception for DR3"], [2, "B2", "breakpoint condition detected", "If 1, breakpoint condition was met when a debug exception for DR2"], [1, "B1", "breakpoint condition detected", "If 1, breakpoint condition was met when a debug exception for DR1"], [0, "B0", "breakpoint condition detected", "If 1, breakpoint condition was met when a debug exception for DR0"], ] dr6 = get_register("dr6", use_monitor=True) self.out.extend(BitInfo("DR6", 32, bit_info, desc).make_out(dr6)) # DR7 self.out.append(titlify("DR7 (Debug Control Register 7)")) desc = "A local breakpoint bit deactivates on hardware task switches, while a global does not" bit_info = [ [[30, 31], "LEN3", "Size of DR3 breakpoint"], [[28, 29], "R/W3", "Breakpoint conditions for DR3"], [[26, 27], "LEN2", "Size of DR2 breakpoint"], [[24, 25], "R/W2", "Breakpoint conditions for DR2"], [[22, 23], "LEN1", "Size of DR1 breakpoint"], [[20, 21], "R/W1", "Breakpoint conditions for DR1"], [[18, 19], "LEN0", "Size of DR0 breakpoint"], [[16, 17], "R/W0", "Breakpoint conditions for DR0"], [13, "GD", "General Detect enable"], [11, "RTM", "Restricted Transactional Memory"], [9, "GE", "Global Exact breakpoint"], [8, "LE", "Local Exact breakpoint"], [7, "G3", "Global DR3 breakpoint"], [6, "L3", "Local DR3 breakpoint"], [5, "G2", "Global DR2 breakpoint"], [4, "L2", "Local DR2 breakpoint"], [3, "G1", "Global DR1 breakpoint"], [2, "L1", "Local DR1 breakpoint"], [1, "G0", "Global DR0 breakpoint"], [0, "L0", "Local DR0 breakpoint"], ] dr7 = get_register("dr7", use_monitor=True) self.out.extend(BitInfo("DR7", bit_info=bit_info, desc=desc).make_out(dr7)) # EFER self.out.append(titlify("EFER (Extended Feature Enable Register; MSR_EFER:0xc0000080)")) efer = get_register("efer", use_monitor=True) bit_info = [ [21, "AIBRSE", "Automatic IBRS Enable"], [20, "UAIE", "Upper Address Ignore Enable"], [18, "INTWB", "Interruptible WBINVD/WBNOINVD Enable"], [17, "MCOMMIT", "MCOMMIT instruction Enable"], [15, "TCE", "Translation Cache Extension"], [14, "FFXSR", "Fast FXSAVE/FXRSTOR"], [13, "LMSLE", "Long Mode Segment Limit Enable"], [12, "SVME", "Secure Virtual Machine Enable"], [11, "NXE", "No-Execute Enable"], [10, "LMA", "Long Mode Active"], [8, "LME", "Long Mode Enable"], [4, "L2D", "L2 Cache Disable", "only AMD K6"], [3, "GEWBED", "Global EWBE# Disable", "only AMD K6"], [2, "SEWBED", "Speculative EWBE# Disable", "only AMD K6"], [1, "DPE", "Data Prefetch Enable", "only AMD K6"], [0, "SCE", "System Call Extensions"], ] self.out.extend(BitInfo("EFER", bit_info=bit_info).make_out(efer)) # TR res = gdb.execute("monitor info registers", to_string=True) self.out.append(titlify("TR (Task Register)")) tr = re.search(r"TR\s*=\s*(\S+) (\S+) (\S+) (\S+)", res) trseg, base, limit, attr = [int(tr.group(i), 16) for i in range(1, 5)] self.out.append("{:s} = {:s}".format(red("TR"), yellow("{:#x}".format(trseg)))) self.out.append("seg: {:s}: segment selector for TSS (Task State Segment)".format( Color.boldify("{:#x} (rpl:{:d},ti:{:d},index:{:d})".format( trseg, trseg & 0b11, (trseg >> 2) & 1, trseg >> 3), ), )) self.out.append(" base : {:s}: starting address of TSS".format( Color.colorify_hex(base, "bold"), )) self.out.append(" limit: {:s}: segment limit or fixed value(={:s})".format( Color.colorify_hex(limit, "bold"), "=__KERNEL_TSS_LIMIT x64:0x206f/x86:0x206b", )) self.out.append(" attr : {:s}: attribute".format(Color.colorify_hex(attr, "bold"))) # GDTR self.out.append(titlify("GDTR (Global Descriptor Table Register)")) gdtr = re.search(r"GDT\s*=\s*(\S+) (\S+)", res) base, limit = [int(gdtr.group(i), 16) for i in range(1, 3)] self.out.append("{:s} = {:s}:{:s}".format( red("GDTR"), yellow("{:#x}".format(base)), yellow("{:#x}".format(limit)), )) self.out.append("base : {:s}: starting address of GDT (Global Descriptor Table)".format( Color.colorify_hex(base, "bold"), )) self.out.append("limit: {:s}: (size of GDT) - 1".format( Color.colorify_hex(limit, "bold"), )) ret = gdb.execute("gdtinfo -q -n --only-gdt", to_string=True) self.out.append(ret.rstrip()) # IDTR self.out.append(titlify("IDTR (Interrupt Descriptor Table Register)")) idtr = re.search(r"IDT\s*=\s*(\S+) (\S+)", res) base, limit = [int(idtr.group(i), 16) for i in range(1, 3)] self.out.append("{:s} = {:s}:{:s}".format( red("IDTR"), yellow("{:#x}".format(base)), yellow("{:#x}".format(limit)), )) self.out.append("base : {:s}: starting address of IDT (Interrupt Descriptor Table)".format( Color.colorify_hex(base, "bold"), )) self.out.append("limit: {:s}: (size of IDT) - 1".format( Color.colorify_hex(limit, "bold"), )) ret = gdb.execute("idtinfo -q -n", to_string=True) self.out.append(ret.rstrip()) # LDTR self.out.append(titlify("LDTR (Local Descriptor Table Register)")) ldtr = re.search(r"LDT\s*=\s*(\S+) (\S+) (\S+) (\S+)", res) seg, base, limit, attr = [int(ldtr.group(i), 16) for i in range(1, 5)] self.out.append("{:s} = {:s}".format(red("LDTR"), yellow("{:#x}".format(seg)))) self.out.append("seg: {:s}: segment selector for LDT (Local Descriptor Table)".format( Color.boldify("{:#x} (rpl:{:d},ti:{:d},index:{:d})".format( seg, seg & 0b11, (seg >> 2) & 1, seg >> 3), ), )) self.out.append(" base : {:s}: starting address of LDT".format( Color.colorify_hex(base, "bold"), )) self.out.append(" limit: {:s}: segment limit".format( Color.colorify_hex(limit, "bold"), )) self.out.append(" attr : {:s}: attribute".format( Color.colorify_hex(attr, "bold"), )) ret = gdb.execute("gdtinfo -q -n --only-ldt", to_string=True) self.out.append(ret.rstrip()) return def qregisters(self): res = gdb.execute("monitor info registers", to_string=True).strip() self.out.append(titlify("info registers")) for line in res.splitlines(): self.out.append(line) if is_x86(): if not self.args.verbose: self.info_add_out("use `-v` for print Additional info") else: self.info_add_out("Additional info") self.qregisters_x86_x64() return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) def do_invoke(self, args): self.out = [] self.qregisters() self.print_output() return class PageMap: """A collection of utility functions that are related to memory map from page tables.""" @staticmethod @Cache.cache_until_next def get_page_maps_by_pagewalk(command): if is_kgdb(): info("Start `pagewalk`") res = gdb.execute(command, to_string=True) if 'Exception raised' in res: gef_print(res) return res @staticmethod @Cache.cache_until_next def get_page_maps_arm64_optee_secure_memory(verbose=False): # heuristic search of qemu-system memory sm = QemuMonitor.get_secure_memory_map(verbose) if sm is None: err("Could not find secure memory maps") return None data = XSecureMemAddrCommand.read_secure_memory(sm, 0x0, sm.size, verbose) data_list = slice_unpack(data, 8) """ enum teecore_memtypes { MEM_AREA_TEE_RAM = 1, MEM_AREA_TEE_RAM_RX, MEM_AREA_TEE_RAM_RO, MEM_AREA_TEE_RAM_RW, MEM_AREA_INIT_RAM_RO, MEM_AREA_INIT_RAM_RX, MEM_AREA_NEX_RAM_RO, MEM_AREA_NEX_RAM_RW, MEM_AREA_NEX_DYN_VASPACE, MEM_AREA_TEE_DYN_VASPACE, MEM_AREA_TEE_COHERENT, MEM_AREA_TEE_ASAN, MEM_AREA_IDENTITY_MAP_RX, MEM_AREA_NSEC_SHM, MEM_AREA_NEX_NSEC_SHM, MEM_AREA_RAM_NSEC, MEM_AREA_RAM_SEC, MEM_AREA_ROM_SEC, MEM_AREA_IO_NSEC, MEM_AREA_IO_SEC, MEM_AREA_EXT_DT, MEM_AREA_MANIFEST_DT, MEM_AREA_TRANSFER_LIST, MEM_AREA_RES_VASPACE, MEM_AREA_SHM_VASPACE, MEM_AREA_TS_VASPACE, MEM_AREA_PAGER_VASPACE, MEM_AREA_SDP_MEM, MEM_AREA_DDR_OVERALL, MEM_AREA_SEC_RAM_OVERALL, MEM_AREA_MAXTYPE }; struct tee_mmap_region { unsigned int type; /* enum teecore_memtypes */ unsigned int region_size; paddr_t pa; vaddr_t va; size_t size; uint32_t attr; /* TEE_MATTR_* above */ }; """ maps = [] old_i = -1 for i in range(len(data_list) - 4): type_ = data_list[i] & 0xffff_ffff region_size = (data_list[i] >> 32) & 0xffff_ffff if type_ == 0 or 30 < type_: # enum teecore_memtypes continue if region_size & 0xfff or region_size < 0x1000 or 0xffff_f000 < region_size: continue pa, va, size, attr = data_list[i + 1:i + 5] if pa & 0xfff or 0xffff_f000 < pa: continue if va & 0xfff or 0xffff_f000 < va: continue if size & 0xfff or size < 0x1000 or 0xffff_f000 < size: continue if len(maps) > 0 and old_i + 5 != i: # Judging continuity continue maps.append([va, va + size, pa, pa + size]) old_i = i return maps @staticmethod def get_page_maps(FORCE_PREFIX_S, verbose=False): if is_arm64(): if FORCE_PREFIX_S is True: return PageMap.get_page_maps_arm64_optee_secure_memory(verbose) # already parsed else: res = PageMap.get_page_maps_by_pagewalk("pagewalk 1 --quiet --no-pager --no-merge --disable-color") else: if FORCE_PREFIX_S is None: res = PageMap.get_page_maps_by_pagewalk("pagewalk --quiet --no-pager --no-merge --disable-color") elif FORCE_PREFIX_S is True: res = PageMap.get_page_maps_by_pagewalk("pagewalk -S --quiet --no-pager --no-merge --disable-color") elif FORCE_PREFIX_S is False: res = PageMap.get_page_maps_by_pagewalk("pagewalk -s --quiet --no-pager --no-merge --disable-color") res = sorted(set(res.splitlines())) res = list(filter(lambda line: line.endswith("]"), res)) res = list(filter(lambda line: "[+]" not in line, res)) maps = [] for line in res: vrange, prange, *_ = line.split() vstart, vend = [int(x, 16) for x in vrange.split("-")] pstart, pend = [int(x, 16) for x in prange.split("-")] maps.append((vstart, vend, pstart, pend)) if maps == []: if is_x86(): warn("Make sure you are in ring0 (=kernel mode)") elif is_arm32(): warn("Make sure you are in supervisor mode (=kernel mode)") warn("Make sure qemu 3.x or higher") elif is_arm64(): warn("Make sure you are in EL1 (=kernel mode)") warn("Make sure qemu 3.x or higher") return None return maps @staticmethod def v2p_from_map(address, maps): for vstart, vend, pstart, _pend in maps: if vstart <= address < vend: offset = address - vstart paddr = pstart + offset return paddr return None @staticmethod def p2v_from_map(address, maps): # return list vaddrs = [] for vstart, _vend, pstart, pend in maps: if pstart <= address < pend: offset = address - pstart vaddr = vstart + offset vaddrs.append(vaddr) return vaddrs @register_command class Virt2PhysCommand(GenericCommand): """Translate from virtual address to physical address.""" _cmdline_ = "v2p" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group() group.add_argument("-S", dest="force_secure", action="store_true", help="ARMv7: use TTBRn_ELm_S to parse start. ARMv8: heuristic search the memory of qemu-system.") group.add_argument("-s", dest="force_normal", action="store_true", help="ARMv7/v8: use TTBRn_ELm to parse start.") parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="the address of data to translate.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0xffffffff855041e0", ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): FORCE_PREFIX_S = None if is_arm32() or is_arm64(): if args.force_normal: FORCE_PREFIX_S = False elif args.force_secure: FORCE_PREFIX_S = True # do not use cache maps = PageMap.get_page_maps(FORCE_PREFIX_S) if maps is None: return paddr = PageMap.v2p_from_map(args.address, maps) if paddr is not None: gef_print("Virt: {:#x} -> Phys: {:#x}".format(args.address, paddr)) return @register_command class Phys2VirtCommand(GenericCommand): """Translate from physical address to virtual address.""" _cmdline_ = "p2v" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group() group.add_argument("-S", dest="force_secure", action="store_true", help="ARMv7: use TTBRn_ELm_S to parse start. ARMv8: heuristic search the memory of qemu-system.") group.add_argument("-s", dest="force_normal", action="store_true", help="ARMv7/v8: use TTBRn_ELm to parse start.") parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="the address of data to translate.") parser.add_argument("-v", "--verbose", action="store_true", help="verbose output (for arm64 secure memory).") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x55041e0", ] _example_ = "\n".join(_example_).format(_cmdline_) @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) def do_invoke(self, args): FORCE_PREFIX_S = None if is_arm32() or is_arm64(): if args.force_normal: FORCE_PREFIX_S = False elif args.force_secure: FORCE_PREFIX_S = True # do not use cache maps = PageMap.get_page_maps(FORCE_PREFIX_S, args.verbose) if maps is None: return vaddrs = PageMap.p2v_from_map(args.address, maps) if args.verbose: loop_max = len(vaddrs) else: loop_max = min(len(vaddrs), 10) if loop_max == 0: gef_print("Not mapped as virt") else: for i in range(loop_max): gef_print("Phys: {:#x} -> Virt: {:#x}".format(args.address, vaddrs[i])) gef_print("Total {:d} results are found".format(len(vaddrs))) return @register_command class PagewalkCommand(GenericCommand, BufferingOutput): """The base command to dump page tables.""" _cmdline_ = "pagewalk" _category_ = "06-a. Qemu-system/KGDB Cooperation - Memory Map" _aliases_ = ["pw", "ptdump", "pt"] parser = argparse.ArgumentParser(prog=_cmdline_) subparsers = parser.add_subparsers(title="command") subparsers.add_parser("x64") subparsers.add_parser("x86") subparsers.add_parser("arm") subparsers.add_parser("arm64") subparsers.add_parser("riscv") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) super().__init__(prefix=prefix) return def read_physmem_cache(self, paddr, size): key = "{:#x}_{:d}".format(paddr, size) if key in self.cache: return self.cache[key] out = read_physmem(paddr, size) self.cache[key] = out return out # merge pages that points same phys page def merge1(self, mappings): # for example, there are 16 pages, # virt: 0xffffffff11107000 -> phys: 0xabcd000 # virt: 0xffffffff11117000 -> phys: 0xabcd000 # virt: 0xffffffff11127000 -> phys: 0xabcd000 # ... # virt: 0xffffffff111d7000 -> phys: 0xabcd000 # virt: 0xffffffff111e7000 -> phys: 0xabcd000 # virt: 0xffffffff111f7000 -> phys: 0xabcd000 # they will be merged by "*". type is changed from int to string. # virt: "0xffffffff111*7000" -> phys: 0xabcd000 # group entries that refer to the same phys page tmp = {} for entry in mappings: # [virt_addr, phys_addr, page_size, page_count, flags] va, other = entry[0], tuple(entry[1:]) if other not in tmp: tmp[other] = [] tmp[other].append(va) # internal merge function def recursive_merge(d): if d == {}: return [""] out = [] if len(d) == 16: tmp = list(d.values()) if tmp.count(tmp[0]) == 16: for vv in recursive_merge(tmp[0]): out.append("*" + vv) return out for k, v in d.items(): for vv in recursive_merge(v): out.append(k + vv) return out # merge if possible merged_mappings = [] for other, va_array in tmp.items(): # usually go through this path if len(va_array) < 16: for va in va_array: merged_mappings.append(["{:016x}".format(va)] + list(other)) continue # fast path for x64 if len(va_array) == 0x10000: va_sorted = sorted([x >> 16 for x in va_array]) if va_sorted[0] + 0xffff == va_sorted[-1]: new_va_str = "{:016x}".format(va_array[0]) new_va_str = new_va_str[:8] + "****" + new_va_str[12:] merged_mappings.append([new_va_str] + list(other)) continue # slow path queue = ["{:016x}".format(x) for x in va_array] # extract dic = {} for q in queue: for i in range(16): dst = dic src = dic for j in range(i + 1): src = src.get(q[j], {}) if j > 0: dst = dst.get(q[j - 1], {}) dst[q[i]] = src # merge for d in recursive_merge(dic): merged_mappings.append([d] + list(other)) # done return sorted(merged_mappings) # merge consecutive pages def merge2(self, mappings): merged_mappings = [] prev = None for now in mappings: # [virt_addr_string, phys_addr, page_size, page_count, flags] # specific case if isinstance(now[0], str) and "*" in now[0]: if prev: merged_mappings += [prev] merged_mappings += [now] prev = None continue # first loop case if prev is None: prev = now continue now_va = int(now[0], 16) if isinstance(now[0], str) else now[0] prev_va = int(prev[0], 16) if isinstance(prev[0], str) else prev[0] now_pa = int(now[1], 16) if isinstance(now[1], str) else now[1] prev_pa = int(prev[1], 16) if isinstance(prev[1], str) else prev[1] now_size = now[2] prev_size = prev[2] #now_cnt = now[3] # unused prev_cnt = prev[3] now_flags = now[4] prev_flags = prev[4] # check consecutiveness if self.args.simple: if prev_va + prev_size == now_va: # va consecutiveness if prev_flags == now_flags: # flags equivalence # ok, they are consecutive (at least virt_addr) prev[2] += now[2] # For simple mode, page_size is ignored. # so we use entry[2] as total_size instead of page_size. continue else: if prev_va + prev_size * prev_cnt == now_va: # va consecutiveness if prev_pa + prev_size * prev_cnt == now_pa: # pa consecutiveness if prev_size == now_size: # page_size equivalence if prev_flags == now_flags: # flags equivalence # ok, they are consecutive prev[3] += 1 # prev_page_cnt update continue merged_mappings += [prev] prev = now if prev: merged_mappings += [prev] return merged_mappings def vrange_filter(self, mappings): filtered_mappings = [] for mapping in mappings: va, _, size, cnt = mapping[:4] if isinstance(va, str) and "*" in va: start = int(va.replace("*", "0"), 16) end = int(va.replace("*", "f"), 16) for addr in self.vrange: if start <= addr < end + size * cnt: filtered_mappings.append(mapping) break else: if isinstance(va, str): va = int(va, 16) for addr in self.vrange: if va <= addr < va + size * cnt: filtered_mappings.append(mapping) break return sorted(filtered_mappings) def prange_filter(self, mappings): filtered_mappings = [] for mapping in mappings: _, pa, size, cnt = mapping[:4] if isinstance(pa, str): pa = int(pa, 16) for addr in self.args.prange: if pa <= addr < pa + size * cnt: filtered_mappings.append(mapping) break return sorted(filtered_mappings) def format_entry(self, entry): va, pa, size, cnt, flags = entry if isinstance(va, str) and "*" in va: vend = "{:016x}".format(int(va.replace("*", "0"), 16) + size * cnt) for pos in [x.span() for x in re.finditer(r"\*", va)]: vend = vend[:pos[0]] + "*" + vend[pos[1]:] pend = pa + size * cnt if self.args.simple: text = "0x{:16s}-0x{:16s} {:37s} {:<#12x} {:<11s} {:<6s} [{:s}]".format( va, vend, "-", size, "-", "-", flags, ) else: text = "0x{:16s}-0x{:16s} {:#018x}-{:#018x} {:<#12x} {:<#11x} {:<6d} [{:s}]".format( va, vend, pa, pend, size * cnt, size, cnt, flags, ) else: if isinstance(va, str): va = int(va, 16) vend = va + size * cnt pend = pa + size * cnt if self.args.simple: text = "{:#018x}-{:#018x} {:37s} {:<#12x} {:<11s} {:<6s} [{:s}]".format( va, vend, "-", size, "-", "-", flags, ) else: text = "{:#018x}-{:#018x} {:#018x}-{:#018x} {:<#12x} {:<#11x} {:<6d} [{:s}]".format( va, vend, pa, pend, size * cnt, size, cnt, flags, ) return text def merging(self): self.mappings = sorted(self.mappings) # merging if self.args.no_merge: pass else: if is_x86_64(): self.mappings = self.merge1(self.mappings) self.quiet_info_add_out("PT Entry (merged similar pages that refer the same physpage): {:d}".format( len(self.mappings), )) self.mappings = self.merge2(self.mappings) self.quiet_info_add_out("PT Entry (merged consecutive pages): {:d}".format( len(self.mappings), )) return def add_color(self, lines): for i in range(len(lines)): line = lines[i].split(None, 5) if len(line) < 6: continue if is_x86() or is_riscv32() or is_riscv64(): if re.search(r"^\[R-- ", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_readonly")) elif re.search(r"^\[..X ", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_code")) elif re.search(r"^\[RW- ", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_writable")) if re.search(r"^\[RWX ", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_rwx")) elif is_arm32(): if re.search(r"PL/R--", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_readonly")) elif re.search(r"PL1/..X", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_code")) elif re.search(r"PL1/RW-", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_writable")) if re.search(r"PL1/RWX", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_rwx")) elif is_arm64(): if re.search(r"EL[1-3]/R--", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_readonly")) elif re.search(r"EL[1-3]/..X", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_code")) elif re.search(r"EL[1-3]/RW-", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_writable")) if re.search(r"EL[1-3]/RWX", line[5]): lines[i] = Color.colorify(lines[i], Config.get_gef_setting("theme.address_rwx")) return lines def make_out(self, mappings): if mappings is None or len(mappings) == 0: self.warn_add_out("No virtual mappings found") return filtered_mappings = mappings.copy() # filter by virtual address range if self.vrange != []: filtered_mappings = self.vrange_filter(filtered_mappings) self.quiet_info_add_out("PT Entry (filtered by virtual address range): {:d}".format( len(filtered_mappings), )) # filter by physical address range if self.args.prange != []: filtered_mappings = self.prange_filter(filtered_mappings) self.quiet_info_add_out("PT Entry (filtered by physical address range): {:d}".format( len(filtered_mappings), )) # create output lines = [] for entry_info in filtered_mappings: line = self.format_entry(entry_info) lines.append(line) # filter by keyword if self.args.filter != []: filtered_lines = [] for line in lines: for re_pattern in self.args.filter: if re_pattern.search(line): filtered_lines.append(line) break lines = filtered_lines self.quiet_info_add_out("PT Entry (filtered by keyword): {:d}".format(len(lines))) # sort by phys if self.args.sort_by_phys: lines = sorted(lines, key=lambda x: x.split()[1]) # check how many result if lines == []: self.warn_add_out("Nothing to display") return # add legend self.out.append(titlify("Memory map")) fmt = "{:37s} {:37s} {:12s} {:11s} {:6s} {:s}" legend = ["Virtual address start-end", "Physical address start-end", "Total size", "Page size", "Count", "Flags"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) # coloring if not self.args.disable_color: lines = self.add_color(lines) # add out self.out.extend(lines) return def is_not_trace_target(self, va_start, va_end): if self.args.trace == []: return False for tr in self.args.trace: if va_start <= tr and tr < va_end: return False return True def is_not_filter_target(self, line): if self.args.filter == []: return False for re_pattern in self.args.filter: if re_pattern.search(line): return False return True # Need not @parse_args because argparse can't stop interpreting options for pagewalk sub-command. @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "x86_16", "ARM32", "ARM64", "RISCV32", "RISCV64")) def do_invoke(self, argv): if is_x86_32() or is_x86_16(): gdb.execute("pagewalk x86 {}".format(" ".join(argv))) elif is_x86_64(): gdb.execute("pagewalk x64 {}".format(" ".join(argv))) elif is_arm32(): gdb.execute("pagewalk arm {}".format(" ".join(argv))) elif is_arm64(): gdb.execute("pagewalk arm64 {}".format(" ".join(argv))) elif is_riscv64() or is_riscv32(): gdb.execute("pagewalk riscv {}".format(" ".join(argv))) return @register_command class PagewalkRiscvCommand(PagewalkCommand): """Dump pagetable for riscv64/32.""" _cmdline_ = "pagewalk riscv" _category_ = "06-a. Qemu-system/KGDB Cooperation - Memory Map" _aliases_ = ["pagewalk riscv32", "pagewalk riscv64"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-L", "--print-each-level", action="store_true", help="show all level pagetables.") parser.add_argument("-N", "--no-merge", action="store_true", help="do not merge similar/consecutive address.") parser.add_argument("-P", "--sort-by-phys", action="store_true", help="sort by physical address.") parser.add_argument("-Q", "--simple", action="store_true", help="merge with ignoring physical address consecutivness.") parser.add_argument("-f", "--filter", metavar="REGEX", action="append", type=re.compile, default=[], help="filter by REGEX pattern.") parser.add_argument("-v", "--vrange", metavar="VADDR", action="append", type=AddressUtil.parse_address, default=[], help="filter by map included specified virtual address.") parser.add_argument("-p", "--prange", metavar="PADDR", action="append", type=AddressUtil.parse_address, default=[], help="filter by map included specified physical address.") parser.add_argument("-t", "--trace", metavar="VADDR", action="append", type=AddressUtil.parse_address, default=[], help="show all level pagetables only associated specified address.") parser.add_argument("-D", "--disable-color", action="store_true", help="disable RWX colored output") parser.add_argument("-c", "--use-cache", action="store_true", help="use previous result.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) self.mappings = None return def format_flags(self, flag_info): flag_info_key = tuple(flag_info) x = self.flags_strings_cache.get(flag_info_key, None) if x is not None: return x flags = [] perm = "" perm += ["-", "R"]["R" in flag_info] perm += ["-", "W"]["W" in flag_info] perm += ["-", "X"]["X" in flag_info] if perm in ["R--", "RW-", "--X", "R-X", "RWX"]: flags.append(perm) else: flags.append("???") if "U" in flag_info: if self.sstatus_sum: flags.append("USER+KERN") else: flags.append("USER") else: flags.append("KERN") if not self.args.simple: if "A" in flag_info: flags.append("ACCESSED") if "D" in flag_info: flags.append("DIRTY") if "G" in flag_info: flags.append("GLOBAL") flag_string = " ".join(flags) self.flags_strings_cache[flag_info_key] = flag_string return flag_string def pagewalk_L5(self): self.quiet_add_out(titlify("Level 5 Entry")) L5E = [] PTE = [] COUNT = 0 bit_shift = sum([ self.bits["L4_BITS"], self.bits["L3_BITS"], self.bits["L2_BITS"], self.bits["L1_BITS"], self.bits["OFFSET"], ]) for va_base, table_base, parent_flags in self.TABLES: entries = self.read_physmem_cache(table_base, 2 ** self.bits["L5_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) for i, entry in enumerate(entries): # valid flag if (entry & 1) == 0: continue # calc virtual address sign_ext = 0xfe00_0000_0000_0000 if ((i >> (self.bits["L5_BITS"] - 1)) & 1) else 0 new_va = va_base + (sign_ext | (i << bit_shift)) new_va_end = new_va + (1 << bit_shift) # calc ppn ppn = (entry >> 10) & 0xfff_ffff_ffff # 44 bit # calc flags flags = parent_flags.copy() if ((entry >> 1) & 1) == 1: flags.append("R") if ((entry >> 2) & 1) == 1: flags.append("W") if ((entry >> 3) & 1) == 1: flags.append("X") if ((entry >> 4) & 1) == 1: flags.append("U") if ((entry >> 5) & 1) == 1: flags.append("G") if ((entry >> 6) & 1) == 1: flags.append("A") if ((entry >> 7) & 1) == 1: flags.append("D") if ((entry >> 1) & 0b111) == 0: # calc next table next_level_entry = ppn * get_pagesize() L5E.append([new_va, next_level_entry, flags]) entry_type = "TABLE" else: # make entry virt_addr = new_va phys_addr = ppn * get_pagesize() page_size = 256 * 1024 * 1024 * 1024 * 1024 page_count = 1 PTE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags(flags)]) entry_type = "256TB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] fmt = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}" line = fmt.format(addr, entry, new_va, new_va_end, entry_type, " ".join(flags)) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("L5 Entry (256TB): {:d}".format(len(L5E))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(L5E))) self.TABLES = L5E self.PTE += PTE return def pagewalk_L4(self): self.quiet_add_out(titlify("Level 4 Entry")) L4E = [] PTE = [] COUNT = 0 bit_shift = sum([ self.bits["L3_BITS"], self.bits["L2_BITS"], self.bits["L1_BITS"], self.bits["OFFSET"], ]) for va_base, table_base, parent_flags in self.TABLES: entries = self.read_physmem_cache(table_base, 2 ** self.bits["L4_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) for i, entry in enumerate(entries): # valid flag if (entry & 1) == 0: continue # calc virtual address if "L5_BITS" in self.bits: new_va = va_base + (i << bit_shift) new_va_end = new_va + (1 << bit_shift) else: sign_ext = 0xffff_0000_0000_0000 if ((i >> (self.bits["L4_BITS"] - 1)) & 1) else 0 new_va = va_base + (sign_ext | (i << bit_shift)) new_va_end = new_va + (1 << bit_shift) # calc ppn ppn = (entry >> 10) & 0xfff_ffff_ffff # 44 bit # calc flags flags = parent_flags.copy() if ((entry >> 1) & 1) == 1: flags.append("R") if ((entry >> 2) & 1) == 1: flags.append("W") if ((entry >> 3) & 1) == 1: flags.append("X") if ((entry >> 4) & 1) == 1: flags.append("U") if ((entry >> 5) & 1) == 1: flags.append("G") if ((entry >> 6) & 1) == 1: flags.append("A") if ((entry >> 7) & 1) == 1: flags.append("D") if ((entry >> 1) & 0b111) == 0: # calc next table next_level_entry = ppn * get_pagesize() L4E.append([new_va, next_level_entry, flags]) entry_type = "TABLE" else: # make entry virt_addr = new_va phys_addr = ppn * get_pagesize() page_size = 512 * 1024 * 1024 * 1024 page_count = 1 PTE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags(flags)]) entry_type = "512GB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] fmt = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}" line = fmt.format(addr, entry, new_va, new_va_end, entry_type, " ".join(flags)) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("L4 Entry (512GB): {:d}".format(len(L4E))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(L4E))) self.TABLES = L4E self.PTE += PTE return def pagewalk_L3(self): self.quiet_add_out(titlify("Level 3 Entry")) L3E = [] PTE = [] COUNT = 0 bit_shift = sum([ self.bits["L2_BITS"], self.bits["L1_BITS"], self.bits["OFFSET"], ]) for va_base, table_base, parent_flags in self.TABLES: entries = self.read_physmem_cache(table_base, 2 ** self.bits["L3_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) for i, entry in enumerate(entries): # valid flag if (entry & 1) == 0: continue # calc virtual address if "L4_BITS" in self.bits: new_va = va_base + (i << bit_shift) new_va_end = new_va + (1 << bit_shift) else: sign_ext = 0xffff_ff80_0000_0000 if ((i >> (self.bits["L3_BITS"] - 1)) & 1) else 0 new_va = va_base + (sign_ext | (i << bit_shift)) new_va_end = new_va + (1 << bit_shift) # calc ppn ppn = (entry >> 10) & 0xfff_ffff_ffff # 44 bit # calc flags flags = parent_flags.copy() if ((entry >> 1) & 1) == 1: flags.append("R") if ((entry >> 2) & 1) == 1: flags.append("W") if ((entry >> 3) & 1) == 1: flags.append("X") if ((entry >> 4) & 1) == 1: flags.append("U") if ((entry >> 5) & 1) == 1: flags.append("G") if ((entry >> 6) & 1) == 1: flags.append("A") if ((entry >> 7) & 1) == 1: flags.append("D") if ((entry >> 1) & 0b111) == 0: # calc next table next_level_entry = ppn * get_pagesize() L3E.append([new_va, next_level_entry, flags]) entry_type = "TABLE" else: # make entry virt_addr = new_va phys_addr = ppn * get_pagesize() page_size = 1 * 1024 * 1024 * 1024 page_count = 1 PTE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags(flags)]) entry_type = "1GB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] fmt = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}" line = fmt.format(addr, entry, new_va, new_va_end, entry_type, " ".join(flags)) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("L3 Entry (1GB): {:d}".format(len(L3E))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(L3E))) self.TABLES = L3E self.PTE += PTE return def pagewalk_L2(self): self.quiet_add_out(titlify("Level 2 Entry")) L2E = [] PTE = [] COUNT = 0 bit_shift = sum([ self.bits["L1_BITS"], self.bits["OFFSET"], ]) for va_base, table_base, parent_flags in self.TABLES: entries = self.read_physmem_cache(table_base, 2 ** self.bits["L2_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) for i, entry in enumerate(entries): # valid flag if (entry & 1) == 0: continue # calc virtual address new_va = va_base + (i << bit_shift) new_va_end = new_va + (1 << bit_shift) # calc ppn if is_riscv64(): ppn = (entry >> 10) & 0xfff_ffff_ffff # 44 bit else: ppn = (entry >> 10) & 0x3f_ffff # 22 bit # calc flags flags = parent_flags.copy() if ((entry >> 1) & 1) == 1: flags.append("R") if ((entry >> 2) & 1) == 1: flags.append("W") if ((entry >> 3) & 1) == 1: flags.append("X") if ((entry >> 4) & 1) == 1: flags.append("U") if ((entry >> 5) & 1) == 1: flags.append("G") if ((entry >> 6) & 1) == 1: flags.append("A") if ((entry >> 7) & 1) == 1: flags.append("D") if ((entry >> 1) & 0b111) == 0: # calc next table next_level_entry = ppn * get_pagesize() L2E.append([new_va, next_level_entry, flags]) entry_type = "TABLE" else: # make entry virt_addr = new_va phys_addr = ppn * get_pagesize() page_size = 2 * 1024 * 1024 page_count = 1 PTE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags(flags)]) entry_type = "2MB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] fmt = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}" line = fmt.format(addr, entry, new_va, new_va_end, entry_type, " ".join(flags)) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("L2 Entry (2MB): {:d}".format(len(L2E))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(L2E))) self.TABLES = L2E self.PTE += PTE return def pagewalk_L1(self): self.quiet_add_out(titlify("Level 1 Entry")) PTE = [] COUNT = 0 bit_shift = self.bits["OFFSET"] for va_base, table_base, parent_flags in self.TABLES: entries = self.read_physmem_cache(table_base, 2 ** self.bits["L1_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) for i, entry in enumerate(entries): # valid flag if (entry & 1) == 0: continue # calc virtual address new_va = va_base + (i << bit_shift) new_va_end = new_va + (1 << bit_shift) # calc ppn if is_riscv64(): ppn = (entry >> 10) & 0xfff_ffff_ffff # 44 bit else: ppn = (entry >> 10) & 0x3f_ffff # 22 bit # calc flags flags = parent_flags.copy() if ((entry >> 1) & 1) == 1: flags.append("R") if ((entry >> 2) & 1) == 1: flags.append("W") if ((entry >> 3) & 1) == 1: flags.append("X") if ((entry >> 4) & 1) == 1: flags.append("U") if ((entry >> 5) & 1) == 1: flags.append("G") if ((entry >> 6) & 1) == 1: flags.append("A") if ((entry >> 7) & 1) == 1: flags.append("D") # make entry virt_addr = new_va phys_addr = ppn * get_pagesize() page_size = 4 * 1024 page_count = 1 PTE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags(flags)]) entry_type = "4KB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("L1 Entry (4KB): {:d}".format(len(PTE))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(PTE))) self.PTE += PTE self.quiet_add_out(titlify("Total")) self.quiet_info_add_out("PT Entry (Total): {:d}".format(len(self.PTE))) self.mappings = self.PTE return def pagewalk(self): satp = get_register("satp") if satp is None: self.err_add_out("Failed to read $satp") return self.quiet_info_add_out("satp: {:#018x}".format(satp)) sstatus = get_register("sstatus") if sstatus is None: self.err_add_out("Failed to read $sstatus") return self.quiet_info_add_out("sstatus: {:#018x}".format(sstatus)) if is_riscv64(): mode = (satp >> 60) & 0b1111 # upper 4 bit pagewalk_base = (satp & 0xfff_ffff_ffff) * get_pagesize() # lower 44 bit else: mode = (satp >> 31) & 0b1 # upper 1 bit pagewalk_base = (satp & 0x3f_ffff) * get_pagesize() # lower 22 bit self.sstatus_sum = (sstatus >> 18) & 1 # virtual address base va_base = 0 flags = [] # do pagewalk self.PTE = [] self.TABLES = [(va_base, pagewalk_base, flags)] self.flags_strings_cache = {} if is_riscv64(): if mode == 0: self.err_add_out("RV64 bare page table is unsupported") elif mode == 11: # Sv64 is unsuppported self.err_add_out("RV64 Sv64 page table is unsupported") elif mode == 10: # Sv57 self.quiet_info_add_out("RV64 Sv57 page table") self.bits = { "ENTRY_SIZE": 8, "L5_BITS": 9, "L4_BITS": 9, "L3_BITS": 9, "L2_BITS": 9, "L1_BITS": 9, "OFFSET": 12, } if not self.args.use_cache or not self.mappings: self.mappings = None self.pagewalk_L5() self.pagewalk_L4() self.pagewalk_L3() self.pagewalk_L2() self.pagewalk_L1() self.merging() elif mode == 9: # Sv48 self.quiet_info_add_out("RV64 Sv48 page table") self.bits = { "ENTRY_SIZE": 8, "L4_BITS": 9, "L3_BITS": 9, "L2_BITS": 9, "L1_BITS": 9, "OFFSET": 12, } if not self.args.use_cache or not self.mappings: self.mappings = None self.pagewalk_L4() self.pagewalk_L3() self.pagewalk_L2() self.pagewalk_L1() self.merging() elif mode == 8: # Sv39 self.quiet_info_add_out("RV64 Sv39 page table") self.bits = { "ENTRY_SIZE": 8, "L3_BITS": 9, "L2_BITS": 9, "L1_BITS": 9, "OFFSET": 12, } if not self.args.use_cache or not self.mappings: self.mappings = None self.pagewalk_L3() self.pagewalk_L2() self.pagewalk_L1() self.merging() else: self.err_add_out("RV64 unknown mode") else: if mode == 0: self.err_add_out("RV32 bare page table is unsupported") elif mode == 1: # Sv32 self.quiet_info_add_out("RV32 Sv32 page table") self.bits = { "ENTRY_SIZE": 4, "L2_BITS": 10, "L1_BITS": 10, "OFFSET": 12, } if not self.args.use_cache or not self.mappings: self.mappings = None self.pagewalk_L2() self.pagewalk_L1() self.merging() self.flags_strings_cache = {} self.make_out(self.mappings) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("RISCV32", "RISCV64")) def do_invoke(self, args): if self.args.trace: # You should not modify the self.args.vrange directly. self.vrange = self.args.vrange + self.args.trace # merge vrange and trace self.args.print_each_level = True # overwrite self.args.use_cache = False # overwrite else: self.vrange = self.args.vrange self.out = [] self.cache = {} self.pagewalk() self.cache = {} # The cache is huge, so it will be released as soon as possible. self.print_output() return @register_command class PagewalkX64Command(PagewalkCommand): """Dump pagetable for x64/x86.""" _cmdline_ = "pagewalk x64" _category_ = "06-a. Qemu-system/KGDB Cooperation - Memory Map" _aliases_ = ["pagewalk x86"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-L", "--print-each-level", action="store_true", help="show all level pagetables.") parser.add_argument("-N", "--no-merge", action="store_true", help="do not merge similar/consecutive address.") parser.add_argument("-P", "--sort-by-phys", action="store_true", help="sort by physical address.") parser.add_argument("-Q", "--simple", action="store_true", help="merge with ignoring physical address consecutivness.") parser.add_argument("-f", "--filter", metavar="REGEX", action="append", type=re.compile, default=[], help="filter by REGEX pattern.") parser.add_argument("-v", "--vrange", metavar="VADDR", action="append", type=AddressUtil.parse_address, default=[], help="filter by map included specified virtual address.") parser.add_argument("-p", "--prange", metavar="PADDR", action="append", type=AddressUtil.parse_address, default=[], help="filter by map included specified physical address.") parser.add_argument("-t", "--trace", metavar="VADDR", action="append", type=AddressUtil.parse_address, default=[], help="show all level pagetables only associated specified address.") parser.add_argument("-i", "--include-esp-fixup-stacks", action="store_true", help="include `%%esp fixup stacks` area (sometimes heavy memory use; x64 only).") parser.add_argument("-U", "--user-pt", action="store_true", help="print userland pagetables (for KPTI, x64 only, in kernel context).") parser.add_argument("--cr3", dest="user_specified_cr3", type=AddressUtil.parse_address, help="use specified value as cr3.") parser.add_argument("--cr4", dest="user_specified_cr4", type=AddressUtil.parse_address, help="use specified value as cr4.") parser.add_argument("--ept", action="store_true", help="parse cr3 as EPT (Extended Page Table).") parser.add_argument("-D", "--disable-color", action="store_true", help="disable RWX colored output") parser.add_argument("-c", "--use-cache", action="store_true", help="use previous result.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) self.mappings = None return def format_flags(self, flag_info): flag_info_key = tuple(flag_info) x = self.flags_strings_cache.get(flag_info_key, None) if x is not None: return x flags = [] if self.args.ept: perm = "" perm += ["R", "-"]["NO_R" in flag_info] perm += ["W", "-"]["NO_W" in flag_info] perm += ["X", "-"]["NO_X" in flag_info] flags += [perm] else: if "NO_RW" in flag_info: if "XD" in flag_info: flags += ["R--"] else: flags += ["R-X"] else: if "XD" in flag_info: flags += ["RW-"] else: flags += ["RWX"] if "NO_US" in flag_info: flags += ["KERN"] else: flags += ["USER"] if not self.args.simple: if "A" in flag_info: flags += ["ACCESSED"] if "D" in flag_info: flags += ["DIRTY"] if "G" in flag_info: flags += ["GLOBAL"] flag_string = " ".join(flags) self.flags_strings_cache[flag_info_key] = flag_string return flag_string def pagewalk_PML5T(self): self.quiet_add_out(titlify("PML5E: Page Map Level 5 Entry")) PML5E = [] COUNT = 0 bit_shift = sum([ self.bits["PML4T_BITS"], self.bits["PDPT_BITS"], self.bits["PDT_BITS"], self.bits["PT_BITS"], self.bits["OFFSET"], ]) tqdm = GefUtil.get_tqdm(not self.args.quiet) for va_base, table_base, parent_flags in tqdm(self.TABLES, leave=False, desc="PML5E"): entries = self.read_physmem_cache(table_base, 2 ** self.bits["PML5T_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if (entry & 1) == 0: continue # calc virtual address sign_ext = 0xfe00_0000_0000_0000 if ((i >> (self.bits["PML5T_BITS"] - 1)) & 1) else 0 new_va = va_base + (sign_ext | (i << bit_shift)) new_va_end = new_va + (1 << bit_shift) # calc flags flags = parent_flags.copy() if self.args.ept: if ((entry >> 0) & 1) == 0: flags.append("NO_R") if ((entry >> 1) & 1) == 0: flags.append("NO_W") if ((entry >> 2) & 1) == 0: flags.append("NO_X") if ((entry >> 8) & 1) == 1: flags.append("A") else: if ((entry >> 1) & 1) == 0: flags.append("NO_RW") if ((entry >> 2) & 1) == 0: flags.append("NO_US") if ((entry >> 5) & 1) == 1: flags.append("A") if ((entry >> 63) & 1) == 1: flags.append("XD") # calc next table (drop the flag bits) next_level_table = entry & 0x000f_ffff_ffff_f000 # make entry PML5E.append([new_va, next_level_table, flags]) entry_type = "TABLE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("PML5 Entry: {:d}".format(len(PML5E))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(PML5E))) self.TABLES = PML5E return def pagewalk_PML4T(self): self.quiet_add_out(titlify("PML4E: Page Map Level 4 Entry")) PML4E = [] COUNT = 0 bit_shift = sum([ self.bits["PDPT_BITS"], self.bits["PDT_BITS"], self.bits["PT_BITS"], self.bits["OFFSET"], ]) tqdm = GefUtil.get_tqdm(not self.args.quiet) for va_base, table_base, parent_flags in tqdm(self.TABLES, leave=False, desc="PML4E"): entries = self.read_physmem_cache(table_base, 2 ** self.bits["PML4T_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if (entry & 1) == 0: continue # calc virtual address if "PML5T_BITS" in self.bits: new_va = va_base + (i << bit_shift) new_va_end = new_va + (1 << bit_shift) else: sign_ext = 0xffff_0000_0000_0000 if ((i >> (self.bits["PML4T_BITS"] - 1)) & 1) else 0 new_va = va_base + (sign_ext | (i << bit_shift)) new_va_end = new_va + (1 << bit_shift) # calc flags flags = parent_flags.copy() if self.args.ept: if ((entry >> 0) & 1) == 0: flags.append("NO_R") if ((entry >> 1) & 1) == 0: flags.append("NO_W") if ((entry >> 2) & 1) == 0: flags.append("NO_X") if ((entry >> 8) & 1) == 1: flags.append("A") else: if ((entry >> 1) & 1) == 0: flags.append("NO_RW") if ((entry >> 2) & 1) == 0: flags.append("NO_US") if ((entry >> 5) & 1) == 1: flags.append("A") if ((entry >> 63) & 1) == 1: flags.append("XD") # calc next table (drop the flag bits) next_level_table = entry & 0x000f_ffff_ffff_f000 # make entry PML4E.append([new_va, next_level_table, flags]) entry_type = "TABLE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("PML4 Entry: {:d}".format(len(PML4E))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(PML4E))) self.TABLES = PML4E return def pagewalk_PDPT(self): self.quiet_add_out(titlify("PDPE: Page Directory Pointer Entry")) def is_set_PS(entry): return ((entry >> 7) & 1) == 1 PDPTE = [] PTE = [] COUNT = 0 bit_shift = sum([ self.bits["PDT_BITS"], self.bits["PT_BITS"], self.bits["OFFSET"], ]) tqdm = GefUtil.get_tqdm(not self.args.quiet) for va_base, table_base, parent_flags in tqdm(self.TABLES, leave=False, desc="PDPE"): entries = self.read_physmem_cache(table_base, 2 ** self.bits["PDPT_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if (entry & 1) == 0: continue # calc virtual address new_va = va_base + (i << bit_shift) new_va_end = new_va + (1 << bit_shift) # calc flags flags = parent_flags.copy() if is_x86_64(): if self.args.ept: if ((entry >> 0) & 1) == 0: flags.append("NO_R") if ((entry >> 1) & 1) == 0: flags.append("NO_W") if ((entry >> 2) & 1) == 0: flags.append("NO_X") if ((entry >> 8) & 1) == 1: flags.append("A") if is_set_PS(entry) and ((entry >> 9) & 1) == 1: flags.append("D") else: if ((entry >> 1) & 1) == 0: flags.append("NO_RW") if ((entry >> 2) & 1) == 0: flags.append("NO_US") if ((entry >> 5) & 1) == 1: flags.append("A") if is_set_PS(entry) and ((entry >> 6) & 1) == 1: flags.append("D") if is_set_PS(entry) and ((entry >> 8) & 1) == 1: flags.append("G") if ((entry >> 63) & 1) == 1: flags.append("XD") else: # x86_32 and PAE pass # calc next table (drop the flag bits) if is_x86_64() and is_set_PS(entry): next_level_table = entry & 0x000f_ffff_ffff_e000 else: next_level_table = entry & 0x000f_ffff_ffff_f000 # make entry if is_set_PS(entry): virt_addr = new_va phys_addr = next_level_table page_size = 1 * 1024 * 1024 * 1024 page_count = 1 PTE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags(flags)]) entry_type = "1GB-PAGE" else: PDPTE.append([new_va, next_level_table, flags]) entry_type = "TABLE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("PDPT Entry: {:d}".format(len(PDPTE))) self.quiet_info_add_out("PT Entry (1GB): {:d}".format(len(PTE))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(PDPTE) - len(PTE))) self.TABLES = PDPTE self.PTE += PTE return def pagewalk_PDT(self): self.quiet_add_out(titlify("PDE: Page Directory Entry")) def is_set_PS(entry): return ((entry >> 7) & 1) == 1 PDE = [] PTE = [] COUNT = 0 bit_shift = sum([ self.bits["PT_BITS"], self.bits["OFFSET"], ]) tqdm = GefUtil.get_tqdm(not self.args.quiet) for va_base, table_base, parent_flags in tqdm(self.TABLES, leave=False, desc="PDE"): entries = self.read_physmem_cache(table_base, 2 ** self.bits["PDT_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) if not self.args.include_esp_fixup_stacks: if len({e & ~0b111 for e in entries}) == 1: continue for i, entry in enumerate(entries): # present flag if (entry & 1) == 0: continue # calc virtual address new_va = va_base + (i << bit_shift) new_va_end = new_va + (1 << bit_shift) # calc flags flags = parent_flags.copy() if self.args.ept: if ((entry >> 0) & 1) == 0: flags.append("NO_R") if ((entry >> 1) & 1) == 0: flags.append("NO_W") if ((entry >> 2) & 1) == 0: flags.append("NO_X") if ((entry >> 8) & 1) == 1: flags.append("A") if is_set_PS(entry) and ((entry >> 9) & 1) == 1: flags.append("D") else: if ((entry >> 1) & 1) == 0: flags.append("NO_RW") if ((entry >> 2) & 1) == 0: flags.append("NO_US") if ((entry >> 5) & 1) == 1: flags.append("A") if is_set_PS(entry) and ((entry >> 6) & 1) == 1: flags.append("D") if is_set_PS(entry) and ((entry >> 8) & 1) == 1: flags.append("G") if self.PAE and ((entry >> 63) & 1) == 1: flags.append("XD") # calc next table (drop the flag bits) if is_x86_64() and is_set_PS(entry): next_level_table = entry & 0x000f_ffff_ffff_e000 elif is_x86_32() and is_set_PS(entry): high = (entry >> 13) & 0xf low = (entry >> 22) & 0x3ff next_level_table = ((high << 10) | low) << 22 else: next_level_table = entry & 0x000f_ffff_ffff_f000 # make entry if is_set_PS(entry): virt_addr = new_va phys_addr = next_level_table if self.PAE: page_size = 2 * 1024 * 1024 entry_type = "2MB-PAGE" else: page_size = 4 * 1024 * 1024 entry_type = "4MB-PAGE" page_count = 1 PTE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags(flags)]) else: PDE.append([new_va, next_level_table, flags]) entry_type = "TABLE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("PD Entry: {:d}".format(len(PDE))) self.quiet_info_add_out("PT Entry ({:d}MB): {:d}".format(2 if self.PAE else 4, len(PTE))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(PDE) - len(PTE))) self.TABLES = PDE self.PTE += PTE return def pagewalk_PT(self): self.quiet_add_out(titlify("PTE: Page Table Entry")) PTE = [] COUNT = 0 bit_shift = self.bits["OFFSET"] flag_cache = {} tqdm = GefUtil.get_tqdm(not self.args.quiet) for va_base, table_base, parent_flags in tqdm(self.TABLES, leave=False, desc="PTE"): entries = self.read_physmem_cache(table_base, 2 ** self.bits["PT_BITS"] * self.bits["ENTRY_SIZE"]) entries = slice_unpack(entries, self.bits["ENTRY_SIZE"]) COUNT += len(entries) if not self.args.include_esp_fixup_stacks: if len({e & ~0b111 for e in entries}) == 1: continue for i, entry in enumerate(entries): # present flag if (entry & 1) == 0: continue # calc virtual address virt_addr = va_base + (i << bit_shift) virt_addr_end = virt_addr + (1 << bit_shift) # calc flags flags = parent_flags.copy() if self.args.ept: if ((entry >> 0) & 1) == 0: flags.append("NO_R") if ((entry >> 1) & 1) == 0: flags.append("NO_W") if ((entry >> 2) & 1) == 0: flags.append("NO_X") if ((entry >> 8) & 1) == 1: flags.append("A") if ((entry >> 9) & 1) == 1: flags.append("D") else: # This route passes many times, so make a memo entry_flags_key = entry & 0x8000_0000_0000_0166 x = flag_cache.get(entry_flags_key, None) if x is not None: flags.extend(x) else: flags_tmp = [] if ((entry >> 1) & 1) == 0: flags_tmp.append("NO_RW") if ((entry >> 2) & 1) == 0: flags_tmp.append("NO_US") if ((entry >> 5) & 1) == 1: flags_tmp.append("A") if ((entry >> 6) & 1) == 1: flags_tmp.append("D") if ((entry >> 8) & 1) == 1: flags_tmp.append("G") if self.PAE and ((entry >> 63) & 1) == 1: flags_tmp.append("XD") flag_cache[entry_flags_key] = flags_tmp flags.extend(flags_tmp) # calc physical addr (drop the flag bits) phys_addr = entry & 0x000f_ffff_ffff_f000 # make entry page_size = 4 * 1024 page_count = 1 PTE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags(flags)]) entry_type = "4KB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(virt_addr, virt_addr_end): continue addr = table_base + i * self.bits["ENTRY_SIZE"] line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, virt_addr, virt_addr_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("PT Entry (4KB): {:d}".format(len(PTE))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(PTE))) self.PTE += PTE self.quiet_add_out(titlify("Total")) self.quiet_info_add_out("PT Entry (Total): {:d}".format(len(self.PTE))) self.mappings = self.PTE return def pagewalk(self): # `info tlb` on qemu-monitor returns pagetable without intermediate pagetable information. # for printing it, we will pagewalk manually. if self.args.user_specified_cr3 is not None: cr3 = self.args.user_specified_cr3 else: cr3 = get_register("cr3", use_monitor=True, use_mbed_exec=True) if cr3 is None: self.quiet_err("Failed to resolve cr3") return if self.args.user_specified_cr4 is not None: cr4 = self.args.user_specified_cr4 else: cr4 = get_register("cr4", use_monitor=True, use_mbed_exec=True) if cr4 is None: self.quiet_err("Failed to resolve cr4") return if is_x86_64() and self.args.user_pt: cr3 += get_pagesize() self.quiet_info_add_out("cr3: {:#018x}".format(cr3)) self.quiet_info_add_out("cr4: {:#018x}".format(cr4)) # virtual address base va_base = 0 # pagewalk base is from CR3 register if self.args.user_specified_cr3 is not None: pagewalk_base = cr3 # without mask else: if is_x86_64(): # 64bit pagewalk_base = cr3 & ~0xfff elif ((cr4 >> 5) & 1) == 1: # 32bit PAE pagewalk_base = cr3 & ~0x1f else: # 32bit non-PAE pagewalk_base = cr3 & ~0xfff # we ignore PWT and PCD flags. flags = [] # do pagewalk self.PTE = [] self.TABLES = [(va_base, pagewalk_base, flags)] self.flags_strings_cache = {} if is_x86_64(): if (cr4 >> 12) & 1: # PML5T check # 64bit 5-level(4KB): 9,9,9,9,9,12 # 64bit 5-level(2MB): 9,9,9,9,0,21 # 64bit 5-level(1GB): 9,9,9,0,0,30 self.quiet_info_add_out("64-bit 5 level page table") self.bits = { "ENTRY_SIZE": 8, "PML5T_BITS": 9, "PML4T_BITS": 9, "PDPT_BITS": 9, "PDT_BITS": 9, "PT_BITS": 9, "OFFSET": 12, } self.PAE = True if not self.args.use_cache or not self.mappings: self.mappings = None self.pagewalk_PML5T() self.pagewalk_PML4T() self.pagewalk_PDPT() self.pagewalk_PDT() self.pagewalk_PT() self.merging() else: # 64bit 4-level(4KB): 9,9,9,9,12 # 64bit 4-level(2MB): 9,9,9,0,21 # 64bit 4-level(1GB): 9,9,0,0,30 self.quiet_info_add_out("64-bit 4 level page table") self.bits = { "ENTRY_SIZE": 8, "PML4T_BITS": 9, "PDPT_BITS": 9, "PDT_BITS": 9, "PT_BITS": 9, "OFFSET": 12, } self.PAE = True if not self.args.use_cache or not self.mappings: self.mappings = None self.pagewalk_PML4T() self.pagewalk_PDPT() self.pagewalk_PDT() self.pagewalk_PT() self.merging() elif is_x86_32() or is_x86_16(): if (cr4 >> 5) & 1: # PAE check # 32bit PAE(4KB): 2,9,9,12 (PTE Size: 64bit) # 32bit PAE(2MB): 2,9,0,21 (PTE Size: 64bit) self.quiet_info_add_out("32-bit {:s} page table".format(Color.boldify("PAE"))) self.bits = { "ENTRY_SIZE": 8, "PDPT_BITS": 2, "PDT_BITS": 9, "PT_BITS": 9, "OFFSET": 12, } self.PAE = True if not self.args.use_cache or not self.mappings: self.mappings = None self.pagewalk_PDPT() self.pagewalk_PDT() self.pagewalk_PT() self.merging() else: # 32bit(4KB): 10,10,12 # 32bit(4MB): 10,0,22 self.quiet_info_add_out("32-bit Non-PAE page table") self.bits = { "ENTRY_SIZE": 4, "PDT_BITS": 10, "PT_BITS": 10, "OFFSET": 12, } self.PAE = False if not self.args.use_cache or not self.mappings: self.mappings = None self.pagewalk_PDT() self.pagewalk_PT() self.merging() else: self.err_add_out("Unsupported CPU") return self.flags_strings_cache = None self.make_out(self.mappings) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_32", "x86_64", "x86_16")) def do_invoke(self, args): if self.args.include_esp_fixup_stacks and not is_x86_64(): err("Unsupported --include-esp-fixup-stacks option in this arch") return if self.args.trace: # You should not modify the self.args.vrange directly. self.vrange = self.args.vrange + self.args.trace # merge vrange and trace self.args.print_each_level = True # overwrite self.args.use_cache = False # overwrite else: self.vrange = self.args.vrange if not is_x86_64() or not is_in_kernel(): self.args.user_pt = False # support x64 only if args.ept: if not self.args.user_specified_cr3: err("Unsupported --ept option without --cr3 option") return self.out = [] self.cache = {} self.pagewalk() self.cache = {} # The cache is huge, so it will be released as soon as possible. self.print_output() return @register_command class PagewalkArmCommand(PagewalkCommand): """Dump pagetable for ARM Cortex-A. PL2 pagewalk is unsupported.""" _cmdline_ = "pagewalk arm" _category_ = "06-a. Qemu-system/KGDB Cooperation - Memory Map" _aliases_ = ["pagewalk arm32"] parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group() group.add_argument("-S", dest="force_secure", action="store_true", help="use TTBRn_ELm_S to parse start.") group.add_argument("-s", dest="force_normal", action="store_true", help="use TTBRn_ELm to parse start.") parser.add_argument("-L", "--print-each-level", action="store_true", help="show all level pagetables.") parser.add_argument("-N", "--no-merge", action="store_true", help="do not merge similar/consecutive address.") parser.add_argument("-P", "--sort-by-phys", action="store_true", help="sort by physical address.") parser.add_argument("-Q", "--simple", action="store_true", help="merge with ignoring physical address consecutivness.") parser.add_argument("-f", "--filter", metavar="REGEX", action="append", type=re.compile, default=[], help="filter by REGEX pattern.") parser.add_argument("-v", "--vrange", metavar="VADDR", action="append", type=AddressUtil.parse_address, default=[], help="filter by map included specified virtual address.") parser.add_argument("-p", "--prange", metavar="PADDR", action="append", type=AddressUtil.parse_address, default=[], help="filter by map included specified physical address.") parser.add_argument("-t", "--trace", metavar="VADDR", action="append", type=AddressUtil.parse_address, default=[], help="show all level pagetables only associated specified address.") parser.add_argument("--optee", action="store_true", help="show the secure world memory maps if used OP-TEE.") parser.add_argument("-D", "--disable-color", action="store_true", help="disable RWX colored output") parser.add_argument("-c", "--use-cache", action="store_true", help="use previous result.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) self.ttbr0_mappings = None self.ttbr1_mappings = None return def format_flags_short(self, flag_info): return self.__format_flags_short(flag_info, self.PXN) def __format_flags_short(self, flag_info, gPXN): flag_info_key = (tuple(flag_info), gPXN) x = self.flags_strings_cache.get(flag_info_key, None) if x is not None: return x flags = [] XN = "XN" in flag_info PXN = ("PXN" in flag_info) & gPXN # AP[2:0] access permissions model if "AP=000" in flag_info: if XN is False and PXN is False: flags += ["PL0/---", "PL1/---"] # elif XN is False and PXN is True: flags += ["PL0/---", "PL1/---"] # PXN elif XN is True and PXN is False: flags += ["PL0/---", "PL1/---"] # XN elif XN is True and PXN is True: flags += ["PL0/---", "PL1/---"] # XN, PXN elif "AP=001" in flag_info: if XN is False and PXN is False: flags += ["PL0/---", "PL1/RWX"] # elif XN is False and PXN is True: flags += ["PL0/---", "PL1/RW-"] # PXN elif XN is True and PXN is False: flags += ["PL0/---", "PL1/RW-"] # XN elif XN is True and PXN is True: flags += ["PL0/---", "PL1/RW-"] # XN, PXN elif "AP=010" in flag_info: if XN is False and PXN is False: flags += ["PL0/R-X", "PL1/RWX"] # elif XN is False and PXN is True: flags += ["PL0/R-X", "PL1/RW-"] # PXN elif XN is True and PXN is False: flags += ["PL0/R--", "PL1/RW-"] # XN elif XN is True and PXN is True: flags += ["PL0/R--", "PL1/RW-"] # XN, PXN elif "AP=011" in flag_info: if XN is False and PXN is False: flags += ["PL0/RWX", "PL1/RWX"] # elif XN is False and PXN is True: flags += ["PL0/RWX", "PL1/RW-"] # PXN elif XN is True and PXN is False: flags += ["PL0/RW-", "PL1/RW-"] # XN elif XN is True and PXN is True: flags += ["PL0/RW-", "PL1/RW-"] # XN, PXN elif "AP=100" in flag_info: flags += ["PL0/???", "PL1/???"] # undefined (reserved) elif "AP=101" in flag_info: if XN is False and PXN is False: flags += ["PL0/---", "PL1/R-X"] # elif XN is False and PXN is True: flags += ["PL0/---", "PL1/R--"] # PXN elif XN is True and PXN is False: flags += ["PL0/---", "PL1/R--"] # XN elif XN is True and PXN is True: flags += ["PL0/---", "PL1/R--"] # XN, PXN elif "AP=110" in flag_info: # deprecated if XN is False and PXN is False: flags += ["PL0/R-X", "PL1/R-X"] # elif XN is False and PXN is True: flags += ["PL0/R-X", "PL1/R--"] # PXN elif XN is True and PXN is False: flags += ["PL0/R--", "PL1/R--"] # XN elif XN is True and PXN is True: flags += ["PL0/R--", "PL1/R--"] # XN, PXN elif "AP=111" in flag_info: if XN is False and PXN is False: flags += ["PL0/R-X", "PL1/R-X"] # elif XN is False and PXN is True: flags += ["PL0/R-X", "PL1/R--"] # PXN elif XN is True and PXN is False: flags += ["PL0/R--", "PL1/R--"] # XN elif XN is True and PXN is True: flags += ["PL0/R--", "PL1/R--"] # XN, PXN # AP[2:1] access permissions model elif "AP=00" in flag_info: if XN is False and PXN is False: flags += ["PL0/---", "PL1/RWX"] # elif XN is False and PXN is True: flags += ["PL0/---", "PL1/RW-"] # PXN elif XN is True and PXN is False: flags += ["PL0/---", "PL1/RW-"] # XN elif XN is True and PXN is True: flags += ["PL0/---", "PL1/RW-"] # XN, PXN elif "AP=01" in flag_info: if XN is False and PXN is False: flags += ["PL0/RWX", "PL1/RWX"] # elif XN is False and PXN is True: flags += ["PL0/RWX", "PL1/RW-"] # PXN elif XN is True and PXN is False: flags += ["PL0/RW-", "PL1/RW-"] # XN elif XN is True and PXN is True: flags += ["PL0/RW-", "PL1/RW-"] # XN, PXN elif "AP=10" in flag_info: if XN is False and PXN is False: flags += ["PL0/---", "PL1/R-X"] # elif XN is False and PXN is True: flags += ["PL0/---", "PL1/R--"] # PXN elif XN is True and PXN is False: flags += ["PL0/---", "PL1/R--"] # XN elif XN is True and PXN is True: flags += ["PL0/---", "PL1/R--"] # XN, PXN elif "AP=11" in flag_info: if XN is False and PXN is False: flags += ["PL0/R-X", "PL1/R-X"] # elif XN is False and PXN is True: flags += ["PL0/R-X", "PL1/R--"] # PXN elif XN is True and PXN is False: flags += ["PL0/R--", "PL1/R--"] # XN elif XN is True and PXN is True: flags += ["PL0/R--", "PL1/R--"] # XN, PXN if "NS" in flag_info: flags += ["NS"] if not self.args.simple: # short description has no `AF` bit if "nG" not in flag_info: flags += ["GLOBAL"] flag_string = " ".join(flags) self.flags_strings_cache[flag_info_key] = flag_string return flag_string def format_flags_long(self, flag_info): return self.__format_flags_long(flag_info, self.PXN) def __format_flags_long(self, flag_info, gPXN): flag_info_key = (tuple(flag_info), gPXN) x = self.flags_strings_cache.get(flag_info_key, None) if x is not None: return x flags = [] # AP/APTable parsing if "AP=00" in flag_info: disable_write_access = 0 enable_unpriv_access = 0 elif "AP=01" in flag_info: disable_write_access = 0 enable_unpriv_access = 1 elif "AP=10" in flag_info: disable_write_access = 1 enable_unpriv_access = 0 elif "AP=11" in flag_info: disable_write_access = 1 enable_unpriv_access = 1 if "APTable2=00" in flag_info: pass elif "APTable2=01" in flag_info: enable_unpriv_access &= 0 elif "APTable2=10" in flag_info: disable_write_access |= 1 elif "APTable2=11" in flag_info: disable_write_access |= 1 enable_unpriv_access &= 0 if "APTable1=00" in flag_info: pass elif "APTable1=01" in flag_info: enable_unpriv_access &= 0 elif "APTable1=10" in flag_info: disable_write_access |= 1 elif "APTable1=11" in flag_info: disable_write_access |= 1 enable_unpriv_access &= 0 AP = (disable_write_access << 1) | enable_unpriv_access # XN/XNTable, PXN/PXNTable, NS/NSTable parsing XN = "XN" in flag_info XN |= "XNTable2" in flag_info XN |= "XNTable1" in flag_info PXN = "PXN" in flag_info PXN |= "PXNTable2" in flag_info PXN |= "PXNTable1" in flag_info PXN &= gPXN NS = "NS" in flag_info NS |= "NSTable2" in flag_info NS |= "NSTable1" in flag_info # AP[2:1] access permissions model if AP == 0b00: if XN is False and PXN is False: flags += ["PL0/---", "PL1/RWX"] # elif XN is False and PXN is True: flags += ["PL0/---", "PL1/RW-"] # PXN elif XN is True and PXN is False: flags += ["PL0/---", "PL1/RW-"] # XN elif XN is True and PXN is True: flags += ["PL0/---", "PL1/RW-"] # XN, PXN elif AP == 0b01: if XN is False and PXN is False: flags += ["PL0/RWX", "PL1/RWX"] # elif XN is False and PXN is True: flags += ["PL0/RWX", "PL1/RW-"] # PXN elif XN is True and PXN is False: flags += ["PL0/RW-", "PL1/RW-"] # XN elif XN is True and PXN is True: flags += ["PL0/RW-", "PL1/RW-"] # XN, PXN elif AP == 0b10: if XN is False and PXN is False: flags += ["PL0/---", "PL1/R-X"] # elif XN is False and PXN is True: flags += ["PL0/---", "PL1/R--"] # PXN elif XN is True and PXN is False: flags += ["PL0/---", "PL1/R--"] # XN elif XN is True and PXN is True: flags += ["PL0/---", "PL1/R--"] # XN, PXN elif AP == 0b11: if XN is False and PXN is False: flags += ["PL0/R-X", "PL1/R-X"] # elif XN is False and PXN is True: flags += ["PL0/R-X", "PL1/R--"] # PXN elif XN is True and PXN is False: flags += ["PL0/R--", "PL1/R--"] # XN elif XN is True and PXN is True: flags += ["PL0/R--", "PL1/R--"] # XN, PXN if NS: flags += ["NS"] if not self.args.simple: if "AF" in flag_info: flags += ["ACCESSED"] if "nG" not in flag_info: flags += ["GLOBAL"] flag_string = " ".join(flags) self.flags_strings_cache[flag_info_key] = flag_string return flag_string def do_pagewalk_short(self, table_base, va_base=0): self.mappings = [] def has_next_level(entry): return (entry & 0b11) == 0b01 def is_section(entry): return (entry & 0b11) in [0b10, 0b11] and ((entry >> 18) & 1) == 0 def is_super_section(entry): return self.XP and (entry & 0b11) in [0b10, 0b11] and ((entry >> 18) & 1) == 1 def is_large_page(entry): return (entry & 0b11) == 0b01 def is_small_page(entry): return (entry & 0b11) in [0b10, 0b11] # 1st level parse self.quiet_add_out(titlify("LEVEL 1")) LEVEL1 = [] SECTION = [] SUPER_SECTION = [] COUNT = 0 entries = self.read_physmem_cache(table_base, 4 * (2 ** (12 - self.N))) entries = slice_unpack(entries, 4) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if (entry & 0b11) == 0b00: continue # calc virtual address new_va = va_base + (i << 20) new_va_end = new_va + (1 << 20) # calc flags flags = [] if has_next_level(entry): if self.XP and ((entry >> 2) & 1) == 1: flags.append("PXN") if self.XP and ((entry >> 3) & 1) == 1: flags.append("NS") flags.append("domain={:#x}".format((entry >> 5) & 0b1111)) elif is_section(entry): if ((entry >> 0) & 1) == 1: flags.append("PXN") if ((entry >> 2) & 1) == 1: flags.append("B") if ((entry >> 3) & 1) == 1: flags.append("C") if self.XP and ((entry >> 4) & 1) == 1: flags.append("XN") flags.append("domain={:#x}".format((entry >> 5) & 0b1111)) ap = (((entry >> 15) & 1) << 2) + ((entry >> 10) & 0b11) if self.AFE: # AP[2:1] access permissions model # codespell:ignore flags.append("AP={:02b}".format(ap >> 1)) else: # AP[2:0] access permissions model flags.append("AP={:03b}".format(ap)) flags.append("TEX={:#x}".format((entry >> 12) & 0b111)) if self.XP and ((entry >> 16) & 1) == 1: flags.append("S") if self.XP and ((entry >> 17) & 1) == 1: flags.append("nG") if self.XP and ((entry >> 19) & 1) == 1: flags.append("NS") elif is_super_section(entry): if ((entry >> 0) & 1) == 1: flags.append("PXN") if ((entry >> 2) & 1) == 1: flags.append("B") if ((entry >> 3) & 1) == 1: flags.append("C") if ((entry >> 4) & 1) == 1: flags.append("XN") ap = (((entry >> 15) & 1) << 2) + ((entry >> 10) & 0b11) if self.AFE: # AP[2:1] access permissions model # codespell:ignore flags.append("AP={:02b}".format(ap >> 1)) else: # AP[2:0] access permissions model flags.append("AP={:03b}".format(ap)) flags.append("TEX={:#x}".format((entry >> 12) & 0b111)) if ((entry >> 16) & 1) == 1: flags.append("S") if ((entry >> 17) & 1) == 1: flags.append("nG") if ((entry >> 19) & 1) == 1: flags.append("NS") else: raise # calc next table (drop the flag bits) if has_next_level(entry): next_level_table = entry & 0xffff_fc00 elif is_section(entry): next_level_table = entry & 0xfff0_0000 elif is_super_section(entry): next_level_table = entry & 0xff00_0000 # PA[31:24] next_level_table += ((entry >> 20) & 0b1111) << 32 # PA[35:32] next_level_table += ((entry >> 5) & 0b1111) << 36 # PA[39:36] # make entry if has_next_level(entry): LEVEL1.append([new_va, next_level_table, flags]) entry_type = "TABLE" elif is_section(entry): virt_addr = new_va phys_addr = next_level_table page_size = 1 * 1024 * 1024 page_count = 1 SECTION.append([virt_addr, phys_addr, page_size, page_count, self.format_flags_short(flags)]) entry_type = "SECTION" elif is_super_section(entry): virt_addr = new_va phys_addr = next_level_table page_size = 16 * 1024 * 1024 page_count = 1 SUPER_SECTION.append([virt_addr, phys_addr, page_size, page_count, self.format_flags_short(flags)]) entry_type = "SUPER_SECTION" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * 4 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("Level 1 Entry: {:d}".format(len(LEVEL1))) self.quiet_info_add_out("PT Entry (supersection; 16MB): {:d}".format(len(SUPER_SECTION))) self.quiet_info_add_out("PT Entry (section; 1MB): {:d}".format(len(SECTION))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(LEVEL1) - len(SUPER_SECTION) - len(SECTION))) self.mappings += SECTION + SUPER_SECTION # 2nd level parse self.quiet_add_out(titlify("LEVEL 2")) LARGE = [] SMALL = [] COUNT = 0 tqdm = GefUtil.get_tqdm(not self.args.quiet) for va_base, table_base, parent_flags in tqdm(LEVEL1, leave=False, desc="LEVEL 2"): entries = self.read_physmem_cache(table_base, 4 * (2 ** 8)) entries = slice_unpack(entries, 4) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if (entry & 0b11) == 0b00: continue # calc virtual address virt_addr = va_base + (i << 12) virt_addr_end = virt_addr + (1 << 12) # calc flags flags = parent_flags.copy() if is_large_page(entry): if ((entry >> 2) & 1) == 1: flags.append("B") if ((entry >> 3) & 1) == 1: flags.append("C") ap = (((entry >> 9) & 1) << 2) + ((entry >> 4) & 0b11) if self.AFE: # AP[2:1] access permissions model # codespell:ignore flags.append("AP={:02b}".format(ap >> 1)) else: # AP[2:0] access permissions model flags.append("AP={:03b}".format(ap)) if ((entry >> 10) & 1) == 1: flags.append("S") if ((entry >> 11) & 1) == 1: flags.append("nG") flags.append("TEX={:#x}".format((entry >> 12) & 0b111)) if ((entry >> 15) & 1) == 1: flags.append("XN") elif is_small_page(entry): if ((entry >> 0) & 1) == 1: flags.append("XN") if ((entry >> 2) & 1) == 1: flags.append("B") if ((entry >> 3) & 1) == 1: flags.append("C") ap = (((entry >> 9) & 1) << 2) + ((entry >> 4) & 0b11) if self.AFE: # AP[2:1] access permissions model # codespell:ignore flags.append("AP={:02b}".format(ap >> 1)) else: # AP[2:0] access permissions model flags.append("AP={:03b}".format(ap)) flags.append("TEX={:#x}".format((entry >> 6) & 0b111)) if ((entry >> 10) & 1) == 1: flags.append("S") if ((entry >> 11) & 1) == 1: flags.append("nG") # calc physical addr (drop the flag bits) if is_large_page(entry): phys_addr = entry & 0xffff_0000 elif is_small_page(entry): phys_addr = entry & 0xffff_f000 # make entry if is_large_page(entry): page_size = 64 * 1024 page_count = 1 LARGE.append([virt_addr, phys_addr, page_size, page_count, self.format_flags_short(flags)]) entry_type = "LARGE" elif is_small_page(entry): page_size = 4 * 1024 page_count = 1 SMALL.append([virt_addr, phys_addr, page_size, page_count, self.format_flags_short(flags)]) entry_type = "SMALL" # dump if self.args.print_each_level: if self.is_not_trace_target(virt_addr, virt_addr_end): continue addr = table_base + i * 4 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, virt_addr, virt_addr_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("PT Entry (large; 64KB): {:d}".format(len(LARGE))) self.quiet_info_add_out("PT Entry (small; 4KB): {:d}".format(len(SMALL))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(LARGE) - len(SMALL))) self.mappings += LARGE + SMALL self.quiet_add_out(titlify("Total")) self.quiet_info_add_out("PT Entry (Total): {:d}".format(len(self.mappings))) self.mappings = sorted(self.mappings) return def do_pagewalk_long(self, table_base, va_base=0): self.mappings = [] def has_next_level(entry): return (entry & 0b11) == 0b11 def is_1GB_page(entry): return (entry & 0b11) == 0b01 def is_2MB_page(entry): return (entry & 0b11) == 0b01 self.quiet_add_out(titlify("LEVEL 1")) if self.N < 2: # 1st level parse LEVEL1 = [] GB = [] COUNT = 0 l1_count = 1 << max(0, 2 - self.N) entries = self.read_physmem_cache(table_base, 8 * l1_count) entries = slice_unpack(entries, 8) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if (entry & 1) == 0: continue # calc virtual address new_va = va_base | (i << 30) new_va_end = new_va + (1 << 30) # calc flags flags = [] if has_next_level(entry): if ((entry >> 59) & 1) == 1: flags.append("PXNTable1") if ((entry >> 60) & 1) == 1: flags.append("XNTable1") flags.append("APTable1={:02b}".format((entry >> 61) & 0b11)) if ((entry >> 63) & 1) == 1: flags.append("NSTable1") elif is_1GB_page(entry): flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 53) & 1) == 1: flags.append("PXN") if ((entry >> 54) & 1) == 1: flags.append("XN") # calc next table (drop the flag bits) if has_next_level(entry): next_level_table = entry & 0x0000_00ff_ffff_f000 elif is_1GB_page(entry): next_level_table = entry & 0x0000_00ff_c000_0000 # make entry if has_next_level(entry): LEVEL1.append([new_va, next_level_table, flags]) entry_type = "TABLE" elif is_1GB_page(entry): virt_addr = new_va phys_addr = next_level_table page_size = 1 * 1024 * 1024 * 1024 page_count = 1 GB.append([virt_addr, phys_addr, page_size, page_count, self.format_flags_long(flags)]) entry_type = "1GB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * 8 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("Level 1 Entry: {:d}".format(len(LEVEL1))) self.quiet_info_add_out("PT Entry (1GB): {:d}".format(len(GB))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(LEVEL1) - len(GB))) self.mappings += GB else: self.quiet_info_add_out("LEVEL 1 is skipped") flags = [] LEVEL1 = [[va_base, table_base, flags]] # 2nd level parse self.quiet_add_out(titlify("LEVEL 2")) LEVEL2 = [] MB = [] COUNT = 0 tqdm = GefUtil.get_tqdm(not self.args.quiet) for va_base, table_base, parent_flags in tqdm(LEVEL1, leave=False, desc="LEVEL 2"): entries = self.read_physmem_cache(table_base, 8 * (2 ** 9)) entries = slice_unpack(entries, 8) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if (entry & 1) == 0: continue # calc virtual address new_va = va_base | (i << 21) new_va_end = new_va + (1 << 21) # calc flags flags = parent_flags.copy() if has_next_level(entry): if ((entry >> 59) & 1) == 1: flags.append("PXNTable2") if ((entry >> 60) & 1) == 1: flags.append("XNTable2") flags.append("APTable2={:02b}".format((entry >> 61) & 0b11)) if ((entry >> 63) & 1) == 1: flags.append("NSTable2") elif is_2MB_page(entry): flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 53) & 1) == 1: flags.append("PXN") if ((entry >> 54) & 1) == 1: flags.append("XN") # calc next table (drop the flag bits) if has_next_level(entry): next_level_table = entry & 0x0000_00ff_ffff_f000 elif is_2MB_page(entry): next_level_table = entry & 0x0000_00ff_ffe0_0000 # make entry if has_next_level(entry): LEVEL2.append([new_va, next_level_table, flags]) entry_type = "TABLE" elif is_2MB_page(entry): virt_addr = new_va phys_addr = next_level_table page_size = 2 * 1024 * 1024 page_count = 1 MB.append([virt_addr, phys_addr, page_size, page_count, self.format_flags_long(flags)]) entry_type = "2MB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * 8 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("Level 2 Entry: {:d}".format(len(LEVEL2))) self.quiet_info_add_out("PT Entry (2MB): {:d}".format(len(MB))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(LEVEL2) - len(MB))) self.mappings += MB # 3rd level parse self.quiet_add_out(titlify("LEVEL 3")) KB = [] COUNT = 0 for va_base, table_base, parent_flags in tqdm(LEVEL2, leave=False, desc="LEVEL 3"): entries = self.read_physmem_cache(table_base, 8 * (2 ** 9)) entries = slice_unpack(entries, 8) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if (entry & 0b11) != 0b11: continue # calc virtual address virt_addr = va_base | (i << 12) virt_addr_end = virt_addr + (1 << 12) # calc flags flags = parent_flags.copy() flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 53) & 1) == 1: flags.append("PXN") if ((entry >> 54) & 1) == 1: flags.append("XN") # calc physical addr (drop the flag bits) phys_addr = entry & 0x0000_00ff_ffff_f000 # make entry page_size = 4 * 1024 page_count = 1 KB.append([virt_addr, phys_addr, page_size, page_count, self.format_flags_long(flags)]) entry_type = "4KB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(virt_addr, virt_addr_end): continue addr = table_base + i * 8 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, virt_addr, virt_addr_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("PT Entry (4KB): {:d}".format(len(KB))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(KB))) self.mappings += KB self.quiet_add_out(titlify("Total")) self.quiet_info_add_out("PT Entry (Total): {:d}".format(len(self.mappings))) self.mappings = sorted(self.mappings) return def pagewalk_short(self): self.out.append(titlify("$TTBR0_EL1{}".format(self.suffix), color="bold", msg_color="bold")) TTBR0_EL1 = get_register("$TTBR0_EL1{}".format(self.suffix)) if TTBR0_EL1 is None: TTBR0_EL1 = get_register("$TTBR0", use_mbed_exec=True) if TTBR0_EL1 is None: self.err_add_out("Could not find $TTBR0_EL1{}".format(self.suffix)) return TTBCR = get_register("$TTBCR{}".format(self.suffix)) if TTBCR is None: TTBCR = get_register("$TTBCR", use_mbed_exec=True) if TTBCR is None: self.err_add_out("Could not find $TTBCR{}".format(self.suffix)) return # pagewalk TTBR0_EL1 self.N = TTBCR & 0b111 x = 14 - self.N pl0_base = ((TTBR0_EL1 & 0xffff_ffff) >> x) << x self.quiet_info_add_out("$TTBR0_EL1{}: {:#x}".format(self.suffix, TTBR0_EL1)) self.quiet_info_add_out("$TTBCR{}: {:#x}".format(self.suffix, TTBCR)) self.quiet_info_add_out("PL0 base: {:#x}".format(pl0_base)) if not self.args.use_cache or not self.ttbr0_mappings: self.flags_strings_cache = {} self.do_pagewalk_short(pl0_base) self.flags_strings_cache = None self.merging() self.ttbr0_mappings = self.mappings.copy() self.make_out(self.ttbr0_mappings) # pagewalk TTBR1_EL1 self.out.append(titlify("$TTBR1_EL1{}".format(self.suffix), color="bold", msg_color="bold")) TTBR1_EL1 = get_register("$TTBR1_EL1{}".format(self.suffix)) if TTBR1_EL1 is None: TTBR1_EL1 = get_register("$TTBR1", use_mbed_exec=True) if TTBR1_EL1 is None: self.err_add_out("Could not find $TTBR1_EL1{}".format(self.suffix)) return if self.suffix: # The reason is unclear, but the vabase of PL1 appears to be 0x0 when TTBR1_EL1_S is used. pl1_vabase = 0 else: pl1_vabase = { 0: None, 1: 0x8000_0000, 2: 0x4000_0000, 3: 0x2000_0000, 4: 0x1000_0000, 5: 0x0800_0000, 6: 0x0400_0000, 7: 0x0200_0000, }[self.N] pl1_base = ((TTBR1_EL1 & 0xffff_ffff) >> x) << x # Whenever TTBCR.N is nonzero, the size of the translation table addressed by TTBR1 is 16KB (N=0). self.N = 0 if pl1_vabase is not None: self.quiet_info_add_out("$TTBR1_EL1{}: {:#x}".format(self.suffix, TTBR1_EL1)) self.quiet_info_add_out("$TTBCR{}: {:#x}".format(self.suffix, TTBCR)) self.quiet_info_add_out("PL1 base: {:#x}".format(pl1_base)) self.quiet_info_add_out("PL1 va_base: {:#x}".format(pl1_vabase)) if not self.args.use_cache or not self.ttbr1_mappings: self.flags_strings_cache = {} self.do_pagewalk_short(pl1_base, pl1_vabase) self.flags_strings_cache = None self.merging() self.ttbr1_mappings = self.mappings.copy() self.make_out(self.ttbr1_mappings) else: self.quiet_info_add_out("$TTBR1_EL1{} is unused".format(self.suffix)) return def pagewalk_long(self): self.out.append(titlify("$TTBR0_EL1{}".format(self.suffix), color="bold", msg_color="bold")) TTBR0_EL1 = get_register("$TTBR0_EL1{}".format(self.suffix)) if TTBR0_EL1 is None: TTBR0_EL1 = get_register("$TTBR0", use_mbed_exec=True) if TTBR0_EL1 is None: self.err_add_out("Could not find $TTBR0_EL1{}".format(self.suffix)) return TTBCR = get_register("$TTBCR{}".format(self.suffix)) if TTBCR is None: TTBCR = get_register("$TTBCR", use_mbed_exec=True) if TTBCR is None: self.err_add_out("Could not find $TTBCR{}".format(self.suffix)) return def get_x(TxSZ): if TxSZ > 1: return 14 - TxSZ else: return 5 - TxSZ # pagewalk TTBR0_EL1 T0SZ = TTBCR & 0b111 T1SZ = (TTBCR >> 16) & 0b111 self.N = T0SZ x0 = get_x(T0SZ) pl0_base = ((TTBR0_EL1 & 0xff_ffff_ffff) >> x0) << x0 self.quiet_info_add_out("$TTBR0_EL1{}: {:#x}".format(self.suffix, TTBR0_EL1)) self.quiet_info_add_out("$TTBCR{}: {:#x}".format(self.suffix, TTBCR)) self.quiet_info_add_out("T0SZ: {:#x}".format(T0SZ)) self.quiet_info_add_out("PL0 base: {:#x}".format(pl0_base)) if not self.args.use_cache or not self.ttbr0_mappings: self.flags_strings_cache = {} self.do_pagewalk_long(pl0_base) self.flags_strings_cache = None self.merging() self.ttbr0_mappings = self.mappings.copy() self.make_out(self.ttbr0_mappings) # pagewalk TTBR1_EL1 self.out.append(titlify("$TTBR1_EL1{}".format(self.suffix), color="bold", msg_color="bold")) TTBR1_EL1 = get_register("$TTBR1_EL1{}".format(self.suffix)) if TTBR1_EL1 is None: TTBR1_EL1 = get_register("$TTBR1", use_mbed_exec=True) if TTBR1_EL1 is None: self.err_add_out("Could not find $TTBR1_EL1{}".format(self.suffix)) return if T0SZ != 0 or T1SZ != 0: self.N = T1SZ x1 = get_x(T1SZ) pl1_base = ((TTBR1_EL1 & 0xff_ffff_ffff) >> x1) << x1 if T1SZ == 0: pl1_vabase = 2 ** (32 - T0SZ) else: pl1_vabase = (2 ** 32) - (2 ** (32 - T1SZ)) self.quiet_info_add_out("$TTBR1_EL1{}: {:#x}".format(self.suffix, TTBR1_EL1)) self.quiet_info_add_out("$TTBCR{}: {:#x}".format(self.suffix, TTBCR)) self.quiet_info_add_out("T1SZ: {:#x}".format(T1SZ)) self.quiet_info_add_out("PL1 base: {:#x}".format(pl1_base)) self.quiet_info_add_out("PL1 va_base: {:#x}".format(pl1_vabase)) if not self.args.use_cache or not self.ttbr1_mappings: self.flags_strings_cache = {} self.do_pagewalk_long(pl1_base, pl1_vabase) self.flags_strings_cache = None self.merging() self.ttbr1_mappings = self.mappings.copy() self.make_out(self.ttbr1_mappings) else: self.quiet_info_add_out("$TTBR1_EL1{} is unused".format(self.suffix)) return def pagewalk(self): # check use the register with`_S` suffix or not, and Seucre mode or not if self.FORCE_PREFIX_S is None: # auto detect SCR_S = get_register("$SCR_S") SCR = get_register("$SCR") if (SCR, SCR_S) == (None, None): self.SECURE = False self.suffix = "" elif SCR is not None and SCR_S is None: # do not use "_S" self.SECURE = (SCR & 0x1) == 0 # NS bit self.suffix = "" elif SCR is None and SCR_S is not None: # use "_S" self.SECURE = (SCR_S & 0x1) == 0 # NS bit self.suffix = "_S" elif SCR is not None and SCR_S is not None: r = gdb.execute("monitor info mtree -f", to_string=True) if ".secure-ram" in r: # do not use "_S" self.SECURE = (SCR & 0x1) == 0 # NS bit self.suffix = "" else: # use "_S" self.SECURE = (SCR_S & 0x1) == 0 # NS bit self.suffix = "_S" elif self.FORCE_PREFIX_S is True: # use "_S" SCR_S = get_register("$SCR_S") if SCR_S is not None: self.SECURE = (SCR_S & 0x1) == 0 # NS bit else: self.SECURE = False self.suffix = "_S" elif self.FORCE_PREFIX_S is False: # do not use "_S" SCR = get_register("$SCR") if SCR is not None: self.SECURE = (SCR & 0x1) == 0 # NS bit else: self.SECURE = False self.suffix = "" # check XP, AFE # codespell:ignore SCTLR = get_register("$SCTLR{}".format(self.suffix)) if SCTLR is not None: self.XP = ((SCTLR >> 23) & 0x1) == 1 self.AFE = ((SCTLR >> 29) & 0x1) == 1 # codespell:ignore else: self.XP = False self.AFE = False # codespell:ignore if not self.XP: self.quiet_info_add_out("VMSAv6 subpages is enabled") self.SECURE = False else: self.quiet_info_add_out("Secure world: {}".format(self.SECURE)) # check enabled LPAE TTBCR = get_register("$TTBCR{}".format(self.suffix)) if TTBCR is not None: self.LPAE = ((TTBCR >> 31) & 0x1) == 1 else: self.LPAE = False # check PXN supported ID_MMFR0 = get_register("$ID_MMFR0{}".format(self.suffix)) if ID_MMFR0 is not None: self.PXN = ((ID_MMFR0 >> 2) & 0x1) == 1 else: self.PXN = False if self.PXN: self.quiet_info_add_out("{:s} is supported".format(Color.boldify("PXN"))) else: self.quiet_info_add_out("PXN is unsupported") self.quiet_info_add_out("PAN is unimplemented on all ARMv7") # pagewalk if self.LPAE: self.quiet_info_add_out("{:s} is enabled (using long description)".format(Color.boldify("LPAE"))) self.pagewalk_long() else: self.quiet_info_add_out("LPAE is disabled (using short description)") self.pagewalk_short() return def arm32_optee_exact_pagewalk(self): res = PageMap.get_page_maps_by_pagewalk("pagewalk arm -S --quiet --no-pager --disable-color") if not res: return # extract lines entries = [] for line in res.splitlines(): if not line.startswith("0x"): continue vrange, prange, total_size, page_size, count, flags = line.split(None, 5) d = {} d["va_start"], d["va_end"] = [int(x, 16) for x in vrange.split("-")] d["pa_start"], d["pa_end"] = [int(x, 16) for x in prange.split("-")] d.update({ "total_size": int(total_size, 16), "page_size": int(page_size, 16), "count": int(count, 16), "flags": flags, }) Entry = collections.namedtuple("Entry", d.keys()) entry = Entry(*d.values()) entries.append(entry) fmt = "{:37s} {:37s} {:10s} {:20s} {:s}" legend = ["Virtual address start-end", "Physical address start-end", "Total size", "Flags", "Hint (Maybe)"] gef_print(GefUtil.make_legend(fmt.format(*legend))) """ gef> pagewalk --optee -n -q Virtual address start-end Physical address start-end Total size Flags Hint (Maybe) 0x000000000e100000-0x000000000e101000 0x000000000e100000-0x000000000e101000 0x1000 [PL0/--- PL1/R-X] TEE-OS bootstrap region 0x00000000be700000-0x00000000be900000 0x000000007fe00000-0x0000000080000000 0x200000 [PL0/--- PL1/RW- NS] NS<->S shared memory 0x00000000be9ab000-0x00000000bea00000 0x000000000e1ab000-0x000000000e200000 0x55000 [PL0/--- PL1/RW-] 0x00000000bea00000-0x00000000bf800000 0x000000000e200000-0x000000000f000000 0xe00000 [PL0/--- PL1/RW-] 0x00000000bf900000-0x00000000bfa00000 0x000000000e000000-0x000000000e100000 0x100000 [PL0/--- PL1/RW-] 0x00000000bfa00000-0x00000000bfb00000 0x0000000009000000-0x0000000009100000 0x100000 [PL0/--- PL1/RW-] UART0_BASE 0x00000000bfb00000-0x00000000bfc00000 0x0000000008000000-0x0000000008100000 0x100000 [PL0/--- PL1/RW-] GIC_BASE 0x00000000c2879000-0x00000000c2924000 0x000000000e100000-0x000000000e1ab000 0xab000 [PL0/--- PL1/R-X] TEE-OS .text 0x00000000c2924000-0x00000000c295f000 0x000000000e1ab000-0x000000000e1e6000 0x3b000 [PL0/--- PL1/RW-] TEE-OS .data / stack gef> """ text_end = None pl0_count = 0 after_ldelf = False after_ta = False for e in entries: if "PL0/---" in e.flags: after_ta = False # https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/plat-vexpress/conf.mk if e.pa_start == 0x0e10_0000: if e.va_start == 0x0e10_0000 and e.va_end - e.va_start == 0x1000: hint = "TEE-OS bootstrap region" else: hint = "TEE-OS .text" text_end = e.va_end elif text_end and e.va_start == text_end: hint = "TEE-OS .data / stack" elif e.pa_start == 0x7fe0_0000: hint = "NS<->S shared memory" # https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/plat-vexpress/platform_config.h elif e.pa_start == 0x0800_0000: hint = "GIC_BASE" elif e.pa_start == 0x0900_0000: hint = "UART0_BASE" elif e.pa_start == 0x0904_0000: hint = "UART1_BASE" elif e.pa_start == 0x0910_0000: hint = "PCSC_BASE" # others elif "[PL0/RW-" in e.flags and pl0_count == 0: hint = "ldelf" elif "[PL0/R-X" in e.flags: if pl0_count == 0: hint = "ldelf" after_ldelf = True else: hint = "TA" after_ta = True pl0_count += 1 elif after_ldelf: hint = "ldelf" after_ldelf = False elif after_ta: if "NS" in e.flags and e.total_size == 0x1000: hint = "TA (param)" else: hint = "TA .data / stack" else: hint = "" gef_print("{:#018x}-{:#018x} {:#018x}-{:#018x} {:<#10x} {:20s} {:s}".format( e.va_start, e.va_end, e.pa_start, e.pa_end, e.total_size, e.flags, hint, ).rstrip()) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM32",)) def do_invoke(self, args): if args.optee and is_qemu_system(): self.arm32_optee_exact_pagewalk() return if self.args.trace: # You should not modify the self.args.vrange directly. self.vrange = self.args.vrange + self.args.trace # merge vrange and trace self.args.print_each_level = True # overwrite self.args.use_cache = False # overwrite else: self.vrange = self.args.vrange self.FORCE_PREFIX_S = None if args.force_secure: self.FORCE_PREFIX_S = True elif args.force_normal: self.FORCE_PREFIX_S = False self.out = [] self.cache = {} self.pagewalk() self.cache = {} # The cache is huge, so it will be released as soon as possible. self.print_output() return @register_command class PagewalkArm64Command(PagewalkCommand): """Dump pagetable for ARM64 Cortex-A (ARM v8.7 base).""" _cmdline_ = "pagewalk arm64" _category_ = "06-a. Qemu-system/KGDB Cooperation - Memory Map" _aliases_ = [] # re-overwrite parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("target_el", metavar="TARGET_EL", nargs="?", type=int, help="target Exception Level. (default: current EL)") parser.add_argument("-L", "--print-each-level", action="store_true", help="show all level pagetables.") parser.add_argument("-N", "--no-merge", action="store_true", help="do not merge similar/consecutive address.") parser.add_argument("-P", "--sort-by-phys", action="store_true", help="sort by physical address.") parser.add_argument("-Q", "--simple", action="store_true", help="merge with ignoring physical address consecutivness.") parser.add_argument("-f", "--filter", metavar="REGEX", action="append", type=re.compile, default=[], help="filter by REGEX pattern.") parser.add_argument("-v", "--vrange", metavar="VADDR", action="append", type=AddressUtil.parse_address, default=[], help="filter by map included specified virtual address.") parser.add_argument("-p", "--prange", metavar="PADDR", action="append", type=AddressUtil.parse_address, default=[], help="filter by map included specified physical address.") parser.add_argument("-t", "--trace", metavar="VADDR", action="append", type=AddressUtil.parse_address, default=[], help="show all level pagetables only associated specified address.") parser.add_argument("--optee", action="store_true", help="show the secure world memory maps if used OP-TEE.") parser.add_argument("-0", "--only-TTBR0_EL1", action="store_true", help="Display only TTBR0_EL1 (if target==EL1)") parser.add_argument("-1", "--only-TTBR1_EL1", action="store_true", help="display only TTBR1_EL1 (if target==EL1)") parser.add_argument("-D", "--disable-color", action="store_true", help="disable RWX colored output") parser.add_argument("-c", "--use-cache", action="store_true", help="use previous result.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() # If you want to dump the secure world memory map, you need to break in the secure world. # This is because unlike ARMv7, TTBR0_EL1_S and TTBR1_EL1_S do not exist. # It is difficult to know the correct value of the secure world's system registers while in the normal world, # as the secure monitor saves all system registers to memory when the world changes. def __init__(self): super().__init__(prefix=False) self.ttbr0el1_mappings = None self.ttbr1el1_mappings = None self.ttbr0el2_mappings = None self.ttbr1el2_mappings = None self.vttbrel2_mappings = None self.ttbr0el3_mappings = None return def read_mem_wrapper(self, addr, size=8): """ When pagewalking EL0/EL1 of the guest OS, gdb pagewalks the physical memory according to $TTBR0_ELx. However, even if you try to read the physical memory, access to the address will fail because it is actually an intermediate physical memory. Therefore, in order to perform a pagewalk of EL0/EL1, EL2 mapping information is required. This function is for reading from physical memory with that in mind. """ if self.EL3_M and self.TargetEL == 3: return read_memory(addr, size) # translate via EL2 mappings if self.EL2_VM and self.TargetEL == 1 and self.el2_mappings: def search_pa(addr): for entry_info in self.el2_mappings: va, entry, sz, cnt, flags = entry_info if isinstance(va, str): va = int(va, 16) pa = entry & 0x0000_ffff_ffff_f000 if va <= addr < va + sz: offset = addr - va return pa + offset, sz - offset else: # not found raise out = b"" while size > 0: paddr, available_sz = search_pa(addr) out += self.read_physmem_cache(paddr, min([size, available_sz])) size -= min(size, available_sz) return out # direct physmem read else: return self.read_physmem_cache(addr, size) def format_flags_stage2(self, flag_info): flag_info_key = tuple(flag_info) x = self.flags_strings_cache.get(flag_info_key, None) if x is not None: return x flags = [] if "S2AP=00" in flag_info: if "XN=00" in flag_info: flags += ["EL0/---", "EL1/---"] elif "XN=01" in flag_info: flags += ["EL0/---", "EL1/---"] elif "XN=10" in flag_info: flags += ["EL0/---", "EL1/---"] elif "XN=11" in flag_info: flags += ["EL0/---", "EL1/---"] elif "S2AP=01" in flag_info: if "XN=00" in flag_info: flags += ["EL0/R-X", "EL1/R-X"] elif "XN=01" in flag_info: flags += ["EL0/R-X", "EL1/R--"] elif "XN=10" in flag_info: flags += ["EL0/R--", "EL1/R--"] elif "XN=11" in flag_info: flags += ["EL0/R--", "EL1/R-X"] elif "S2AP=10" in flag_info: if "XN=00" in flag_info: flags += ["EL0/-W-", "EL1/-W-"] elif "XN=01" in flag_info: flags += ["EL0/-W-", "EL1/-W-"] elif "XN=10" in flag_info: flags += ["EL0/-W-", "EL1/-W-"] elif "XN=11" in flag_info: flags += ["EL0/-W-", "EL1/-W-"] elif "S2AP=11" in flag_info: if "XN=00" in flag_info: flags += ["EL0/RWX", "EL1/RWX"] elif "XN=01" in flag_info: flags += ["EL0/RWX", "EL1/RW-"] elif "XN=10" in flag_info: flags += ["EL0/RW-", "EL1/RW-"] elif "XN=11" in flag_info: flags += ["EL0/RW-", "EL1/RWX"] if not self.args.simple: if "AF" in flag_info: flags += ["ACCESSED"] if "DBM" in flag_info: flags += ["DIRTY"] # stage2 has no `nG` bit flag_string = " ".join(flags) self.flags_strings_cache[flag_info_key] = flag_string return flag_string def format_flags(self, flag_info): return self.__format_flags(flag_info, self.TargetEL, self.EL1_WXN, self.EL2_WXN, self.EL2_M20, self.EL3_WXN) def __format_flags(self, flag_info, TargetEL, EL1_WXN, EL2_WXN, EL2_M20, EL3_WXN): flag_info_key = (tuple(flag_info), TargetEL, EL1_WXN, EL2_WXN, EL2_M20, EL3_WXN) x = self.flags_strings_cache.get(flag_info_key, None) if x is not None: return x flags = [] # AP/APTable parsing if "AP=00" in flag_info: disable_write_access = 0 enable_unpriv_access = 0 elif "AP=01" in flag_info: disable_write_access = 0 enable_unpriv_access = 1 elif "AP=10" in flag_info: disable_write_access = 1 enable_unpriv_access = 0 elif "AP=11" in flag_info: disable_write_access = 1 enable_unpriv_access = 1 if "APTable2=00" in flag_info: pass elif "APTable2=01" in flag_info: enable_unpriv_access &= 0 elif "APTable2=10" in flag_info: disable_write_access |= 1 elif "APTable2=11" in flag_info: disable_write_access |= 1 enable_unpriv_access &= 0 if "APTable1=00" in flag_info: pass elif "APTable1=01" in flag_info: enable_unpriv_access &= 0 elif "APTable1=10" in flag_info: disable_write_access |= 1 elif "APTable1=11" in flag_info: disable_write_access |= 1 enable_unpriv_access &= 0 if "APTable0=00" in flag_info: pass elif "APTable0=01" in flag_info: enable_unpriv_access &= 0 elif "APTable0=10" in flag_info: disable_write_access |= 1 elif "APTable0=11" in flag_info: disable_write_access |= 1 enable_unpriv_access &= 0 if "APTable-1=00" in flag_info: pass elif "APTable-1=01" in flag_info: enable_unpriv_access &= 0 elif "APTable-1=10" in flag_info: disable_write_access |= 1 elif "APTable-1=11" in flag_info: disable_write_access |= 1 enable_unpriv_access &= 0 # UXN/UXNTable, XN/XNTable, PXN/PXNTable, NS/NSTable parsing UXN = "UXN" in flag_info UXN |= "UXNTable2" in flag_info UXN |= "UXNTable1" in flag_info UXN |= "UXNTable0" in flag_info UXN |= "UXNTable-1" in flag_info XN = "XN" in flag_info XN |= "XNTable2" in flag_info XN |= "XNTable1" in flag_info XN |= "XNTable0" in flag_info XN |= "XNTable-1" in flag_info PXN = "PXN" in flag_info PXN |= "PXNTable2" in flag_info PXN |= "PXNTable1" in flag_info PXN |= "PXNTable0" in flag_info PXN |= "PXNTable-1" in flag_info NS = "NS" in flag_info NS |= "NSTable2" in flag_info NS |= "NSTable1" in flag_info NS |= "NSTable0" in flag_info NS |= "NSTable-1" in flag_info if TargetEL == 1: # always support 2VA ranges if UXN is False and PXN is False: if disable_write_access == 0 and enable_unpriv_access == 0: if not EL1_WXN: flags += ["EL0/--X", "EL1/RWX"] else: flags += ["EL0/--X", "EL1/RW-"] elif disable_write_access == 0 and enable_unpriv_access == 1: if not EL1_WXN: flags += ["EL0/RWX", "EL1/RW-"] else: flags += ["EL0/RW-", "EL1/RW-"] elif disable_write_access == 1 and enable_unpriv_access == 0: flags += ["EL0/--X", "EL1/R-X"] elif disable_write_access == 1 and enable_unpriv_access == 1: flags += ["EL0/R-X", "EL1/R-X"] elif UXN is False and PXN is True: if disable_write_access == 0 and enable_unpriv_access == 0: flags += ["EL0/--X", "EL1/RW-"] elif disable_write_access == 0 and enable_unpriv_access == 1: if not EL1_WXN: flags += ["EL0/RWX", "EL1/RW-"] else: flags += ["EL0/RW-", "EL1/RW-"] elif disable_write_access == 1 and enable_unpriv_access == 0: flags += ["EL0/--X", "EL1/R--"] elif disable_write_access == 1 and enable_unpriv_access == 1: flags += ["EL0/R-X", "EL1/R--"] elif UXN is True and PXN is False: if disable_write_access == 0 and enable_unpriv_access == 0: if not EL1_WXN: flags += ["EL0/---", "EL1/RWX"] else: flags += ["EL0/---", "EL1/RW-"] elif disable_write_access == 0 and enable_unpriv_access == 1: flags += ["EL0/RW-", "EL1/RW-"] elif disable_write_access == 1 and enable_unpriv_access == 0: flags += ["EL0/---", "EL1/R-X"] elif disable_write_access == 1 and enable_unpriv_access == 1: flags += ["EL0/R--", "EL1/R-X"] elif UXN is True and PXN is True: if disable_write_access == 0 and enable_unpriv_access == 0: flags += ["EL0/---", "EL1/RW-"] elif disable_write_access == 0 and enable_unpriv_access == 1: flags += ["EL0/RW-", "EL1/RW-"] elif disable_write_access == 1 and enable_unpriv_access == 0: flags += ["EL0/---", "EL1/R--"] elif disable_write_access == 1 and enable_unpriv_access == 1: flags += ["EL0/R--", "EL1/R--"] elif TargetEL == 2: if EL2_M20: # support 2VA ranges if HCR_EL2.{TGE,E2H} == {1,1} # codespell:ignore if UXN is False and PXN is False: if disable_write_access == 0 and enable_unpriv_access == 0: if not EL2_WXN: flags += ["EL0/--X", "EL2/RWX"] else: flags += ["EL0/--X", "EL2/RW-"] elif disable_write_access == 0 and enable_unpriv_access == 1: if not EL2_WXN: flags += ["EL0/RWX", "EL2/RW-"] else: flags += ["EL0/RW-", "EL2/RW-"] elif disable_write_access == 1 and enable_unpriv_access == 0: flags += ["EL0/--X", "EL2/R-X"] elif disable_write_access == 1 and enable_unpriv_access == 1: flags += ["EL0/R-X", "EL2/R-X"] elif UXN is False and PXN is True: if disable_write_access == 0 and enable_unpriv_access == 0: flags += ["EL0/--X", "EL2/RW-"] elif disable_write_access == 0 and enable_unpriv_access == 1: if not EL2_WXN: flags += ["EL0/RWX", "EL2/RW-"] else: flags += ["EL0/RW-", "EL2/RW-"] elif disable_write_access == 1 and enable_unpriv_access == 0: flags += ["EL0/--X", "EL2/R--"] elif disable_write_access == 1 and enable_unpriv_access == 1: flags += ["EL0/R-X", "EL2/R--"] elif UXN is True and PXN is False: if disable_write_access == 0 and enable_unpriv_access == 0: if not EL2_WXN: flags += ["EL0/---", "EL2/RWX"] else: flags += ["EL0/---", "EL2/RW-"] elif disable_write_access == 0 and enable_unpriv_access == 1: flags += ["EL0/RW-", "EL2/RW-"] elif disable_write_access == 1 and enable_unpriv_access == 0: flags += ["EL0/---", "EL2/R-X"] elif disable_write_access == 1 and enable_unpriv_access == 1: flags += ["EL0/R--", "EL2/R-X"] elif UXN is True and PXN is True: if disable_write_access == 0 and enable_unpriv_access == 0: flags += ["EL0/---", "EL2/RW-"] elif disable_write_access == 0 and enable_unpriv_access == 1: flags += ["EL0/RW-", "EL2/RW-"] elif disable_write_access == 1 and enable_unpriv_access == 0: flags += ["EL0/---", "EL2/R--"] elif disable_write_access == 1 and enable_unpriv_access == 1: flags += ["EL0/R--", "EL2/R--"] else: # not support 2VA ranges if HCR_EL2.{TGE,E2H} != {1,1} # codespell:ignore if XN is False: if disable_write_access == 0: if not EL2_WXN: flags += ["EL2/RWX"] else: flags += ["EL2/RW-"] elif disable_write_access == 1: flags += ["EL2/R-X"] elif XN is True: if disable_write_access == 0: flags += ["EL2/RW-"] elif disable_write_access == 1: flags += ["EL2/R--"] elif TargetEL == 3: if XN is False: if disable_write_access == 0: if not EL3_WXN: flags += ["EL3/RWX"] else: flags += ["EL3/RW-"] elif disable_write_access == 1: flags += ["EL3/R-X"] elif XN is True: if disable_write_access == 0: flags += ["EL3/RW-"] elif disable_write_access == 1: flags += ["EL3/R--"] if NS: flags += ["NS"] if not self.args.simple: if "AF" in flag_info: flags += ["ACCESSED"] if "DBM" in flag_info: flags += ["DIRTY"] if "nG" not in flag_info: flags += ["GLOBAL"] flag_string = " ".join(flags) self.flags_strings_cache[flag_info_key] = flag_string return flag_string """ Relation diagram when CPU uses Stage1 | Stage2 ------------------------------------------------------- +----------------------+ | +----------------------+ | Guest OS table | -|-> | Virtualization table | +----------------------+ | +----------------------+ TTBR0_EL1, TTBR1_EL1 | VTTBR0_EL2 | +----------------------+ | | Hypervisor table | | +----------------------+ | TTBR0_EL2, TTBR1_EL2 | | +----------------------+ | | Secure monitor table | | +----------------------+ | TTBR0_EL3 | | Since it is an implementation that dumps for each EL, consider as follows. TargetEL=1 | TargetEL=2 | TargetEL=3 --------------------------------------------------------------------------------- +----------------------+ | +----------------------+ | +----------------------+ | Guest OS table | | | Virtualization table | | | Secure monitor table | +----------------------+ | +----------------------+ | +----------------------+ TTBR0_EL1, TTBR1_EL1 | VTTBR0_EL2 | TTBR0_EL3 | | | +----------------------+ | | | Hypervisor table | | | +----------------------+ | | TTBR0_EL2, TTBR1_EL2 | | | """ def parse_bit_range(self, granule_bits, region_bits): IA_LVA_MAX = 52 if self.FEAT_LVA else 48 if granule_bits == 12: # 4KB granule self.LEVELM1_BIT_RANGE = [48, min(IA_LVA_MAX, region_bits)] if region_bits > 48 else None # no block descriptor self.LEVEL0_BIT_RANGE = [39, min(48, region_bits)] if region_bits > 39 else None # 512GB self.LEVEL1_BIT_RANGE = [30, min(39, region_bits)] if region_bits > 30 else None # 1GB self.LEVEL2_BIT_RANGE = [21, min(30, region_bits)] if region_bits > 21 else None # 2MB self.LEVEL3_BIT_RANGE = [12, min(21, region_bits)] if region_bits > 12 else None # 4KB self.OFFSET_BIT_RANGE = [0, 12] elif granule_bits == 14: # 16KB granule self.LEVELM1_BIT_RANGE = None self.LEVEL0_BIT_RANGE = [47, min(IA_LVA_MAX, region_bits)] if region_bits > 47 else None # no block descriptor self.LEVEL1_BIT_RANGE = [36, min(47, region_bits)] if region_bits > 36 else None # 64GB self.LEVEL2_BIT_RANGE = [25, min(36, region_bits)] if region_bits > 25 else None # 32MB self.LEVEL3_BIT_RANGE = [14, min(25, region_bits)] if region_bits > 14 else None # 16KB self.OFFSET_BIT_RANGE = [0, 14] elif granule_bits == 16: # 64KB granule self.LEVELM1_BIT_RANGE = None self.LEVEL0_BIT_RANGE = None self.LEVEL1_BIT_RANGE = [42, min(IA_LVA_MAX, region_bits)] if region_bits > 42 else None # 4TB self.LEVEL2_BIT_RANGE = [29, min(42, region_bits)] if region_bits > 29 else None # 512MB self.LEVEL3_BIT_RANGE = [16, min(29, region_bits)] if region_bits > 16 else None # 64KB self.OFFSET_BIT_RANGE = [0, 16] else: if not self.silent: self.err_add_out("Unsupported granule_bits") return if not self.silent: self.quiet_info_add_out("granule_bits: {:d}".format(granule_bits)) self.quiet_info_add_out("LEVELM1_BIT_RANGE: " + str(self.LEVELM1_BIT_RANGE)) self.quiet_info_add_out("LEVEL0_BIT_RANGE: " + str(self.LEVEL0_BIT_RANGE)) self.quiet_info_add_out("LEVEL1_BIT_RANGE: " + str(self.LEVEL1_BIT_RANGE)) self.quiet_info_add_out("LEVEL2_BIT_RANGE: " + str(self.LEVEL2_BIT_RANGE)) self.quiet_info_add_out("LEVEL3_BIT_RANGE: " + str(self.LEVEL3_BIT_RANGE)) self.quiet_info_add_out("OFFSET_BIT_RANGE: " + str(self.OFFSET_BIT_RANGE)) return def get_entries_per_table(self, BIT_RANGE, granule_bits, region_bits, is_first_level): if is_first_level and region_bits > BIT_RANGE[1]: idx_bits = {12: 9, 14: 11, 16: 13}[granule_bits] offset_bits = granule_bits remainder = (region_bits - offset_bits) % idx_bits entries_per_table = 1 << (idx_bits + remainder) else: used_from_ia = BIT_RANGE[1] - BIT_RANGE[0] entries_per_table = 1 << used_from_ia if not self.silent: self.quiet_info_add_out("Entries per table: {:d}".format(entries_per_table)) return entries_per_table def do_pagewalk(self, table_base, granule_bits, region_start, region_bits, start_level, is_stage2=False, is_2VAranges=False): # table_base: The start address of pagewalk. # granule_bits: One of [12, 14, 16]; It specifies how to separate the bits used for address translation. # region_start: The base address of translated address. # start_level: The start level of the pagewalking. # is_stage2: Whether VTTBR0_EL2 or not. Affects entry bitfield interpretation. # is_2VAranges: TTBR0/TTBR1 presence at target EL. Affects entry bitfield interpretation. self.mappings = [] is_4k_granule = granule_bits == 12 is_16k_granule = granule_bits == 14 is_64k_granule = granule_bits == 16 def has_next_level(entry): # for Level0, 1, 2 but not Level3 return (entry & 0b11) == 0b11 flags = [] TABLE_BASE = [[region_start, table_base, flags]] tqdm = GefUtil.get_tqdm(not self.args.quiet) # level -1 parse for 4KB granule if not self.silent: self.quiet_add_out(titlify("LEVEL -1")) if self.LEVELM1_BIT_RANGE is not None and start_level == -1: entries_per_table = self.get_entries_per_table( self.LEVELM1_BIT_RANGE, granule_bits, region_bits, is_first_level=(start_level == -1), ) LEVELM1 = [] COUNT = 0 for va_base, table_base, parent_flags in tqdm(TABLE_BASE, leave=False, desc="LEVEL -1"): entries = self.read_mem_wrapper(table_base, 8 * entries_per_table) entries = slice_unpack(entries, 8) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if entry & 1 == 0: continue # calc virtual address new_va = va_base + (i << self.LEVELM1_BIT_RANGE[0]) new_va_end = new_va + (1 << self.LEVELM1_BIT_RANGE[0]) # calc flags flags = parent_flags.copy() if has_next_level(entry): if is_stage2: # VTTBR_EL2 does not have level -1 raise elif is_2VAranges: if ((entry >> 59) & 1) == 1: flags.append("PXNTable-1") if ((entry >> 60) & 1) == 1: flags.append("UXNTable-1") flags.append("APTable-1={:02b}".format((entry >> 61) & 0b11)) if ((entry >> 63) & 1) == 1: flags.append("NSTable-1") else: if ((entry >> 60) & 1) == 1: flags.append("XNTable-1") # Use XNTable, not UXNTable # PXNTable is undefined flags.append("APTable-1={:02b}".format((entry >> 61) & 0b11 & 0b10)) # APTable[0] must be 0 if ((entry >> 63) & 1) == 1: flags.append("NSTable-1") else: # In ARMv8.7, level -1 has no block descriptors raise # calc next table / output phys addr (drop the flag bits) if has_next_level(entry): # In aRMv8.7, level -1 must be 4k_granule if self.TCR_ELx_DS: next_level_table = (entry & 0x0003_ffff_ffff_f000) | (((entry >> 8) & 0b11) << 50) else: next_level_table = entry & 0x0003_ffff_ffff_f000 else: # In ARMv8.7, level -1 has no block descriptors raise # make entry if has_next_level(entry): LEVELM1.append([new_va, next_level_table, flags]) entry_type = "TABLE" else: # In ARMv8.7, level -1 has no block descriptors raise # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * 8 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if not self.silent: if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("Level -1 Entry: {:d}".format(len(LEVELM1))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(LEVELM1))) self.mappings += [] else: if not self.silent: self.quiet_info_add_out("LEVEL -1 is skipped") LEVELM1 = TABLE_BASE # level 0 parse for 4KB/16KB granule if not self.silent: self.quiet_add_out(titlify("LEVEL 0")) if self.LEVEL0_BIT_RANGE is not None and start_level <= 0: entries_per_table = self.get_entries_per_table( self.LEVEL0_BIT_RANGE, granule_bits, region_bits, is_first_level=(start_level == 0), ) LEVEL0 = [] GB512 = [] COUNT = 0 for va_base, table_base, parent_flags in tqdm(LEVELM1, leave=False, desc="LEVEL 0"): entries = self.read_mem_wrapper(table_base, 8 * entries_per_table) entries = slice_unpack(entries, 8) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if entry & 1 == 0: continue # calc virtual address new_va = va_base + (i << self.LEVEL0_BIT_RANGE[0]) new_va_end = new_va + (1 << self.LEVEL0_BIT_RANGE[0]) # calc flags flags = parent_flags.copy() if has_next_level(entry): if is_stage2: # There are no flags in the table for VTTBR0_EL2. pass elif is_2VAranges: if ((entry >> 59) & 1) == 1: flags.append("PXNTable0") if ((entry >> 60) & 1) == 1: flags.append("UXNTable0") flags.append("APTable0={:02b}".format((entry >> 61) & 0b11)) if ((entry >> 63) & 1) == 1: flags.append("NSTable0") else: if ((entry >> 60) & 1) == 1: flags.append("XNTable0") # Use XNTable, not UXNTable # PXNTable is undefined flags.append("APTable0={:02b}".format((entry >> 61) & 0b11 & 0b10)) # APTable[0] must be 0 if ((entry >> 63) & 1) == 1: flags.append("NSTable0") else: if is_stage2: flags.append("MemAttr={:#x}".format((entry >> 2) & 0b1111)) flags.append("S2AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") flags.append("XN={:02b}".format((entry >> 53) & 0b11)) # Use XN, not UXN flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) elif is_2VAranges: flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 16) & 1) == 1: flags.append("nT") if ((entry >> 50) & 1) == 1: flags.append("GP") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 53) & 1) == 1: flags.append("PXN") if ((entry >> 54) & 1) == 1: flags.append("UXN") flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) else: flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11 & 0b10)) # AP[0] must be 0 flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 16) & 1) == 1: flags.append("nT") if ((entry >> 50) & 1) == 1: flags.append("GP") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 54) & 1) == 1: flags.append("XN") # Use XN, not UXN # PXN is undefined flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) # calc next table / output phys addr (drop the flag bits) if has_next_level(entry): if self.FEAT_LPA: # In aRMv8.7, level 0 must be 4k_granule or 16k_granule if self.TCR_ELx_DS: next_level_table = (entry & 0x0003_ffff_ffff_f000) | (((entry >> 8) & 0b11) << 50) else: next_level_table = entry & 0x0003_ffff_ffff_f000 else: next_level_table = entry & 0x0000_ffff_ffff_f000 else: if self.FEAT_LPA: # In aRMv8.7, level 0 must be 4k_granule or 16k_granule if self.TCR_ELx_DS: phys_addr = (entry & 0x0003_ffff_fffe_0000) | (((entry >> 8) & 0b11) << 50) else: phys_addr = entry & 0x0003_ffff_fffe_0000 else: # In ARMv8.7, level 0 + no-FEAT_LPA has no block descriptors raise # make entry if has_next_level(entry): LEVEL0.append([new_va, next_level_table, flags]) entry_type = "TABLE" else: virt_addr = new_va page_count = 1 if is_stage2: flag_string = self.format_flags_stage2(flags) else: flag_string = self.format_flags(flags) if is_4k_granule: page_size = 512 * 1024 * 1024 * 1024 GB512.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "512GB-PAGE" else: raise # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * 8 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if not self.silent: if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("Level 0 Entry: {:d}".format(len(LEVEL0))) self.quiet_info_add_out("PT Entry (512GB): {:d}".format(len(GB512))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(LEVEL0) - len(GB512))) self.mappings += GB512 else: if not self.silent: self.quiet_info_add_out("LEVEL 0 is skipped") LEVEL0 = TABLE_BASE # level 1 parse for 4KB/16KB/64KB granule if not self.silent: self.quiet_add_out(titlify("LEVEL 1")) if self.LEVEL1_BIT_RANGE is not None and start_level <= 1: entries_per_table = self.get_entries_per_table( self.LEVEL1_BIT_RANGE, granule_bits, region_bits, is_first_level=(start_level == 1), ) LEVEL1 = [] GB1 = [] TB4 = [] GB64 = [] COUNT = 0 for va_base, table_base, parent_flags in tqdm(LEVEL0, leave=False, desc="LEVEL 1"): entries = self.read_mem_wrapper(table_base, 8 * entries_per_table) entries = slice_unpack(entries, 8) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if entry & 1 == 0: continue # calc virtual address new_va = va_base + (i << self.LEVEL1_BIT_RANGE[0]) new_va_end = new_va + (1 << self.LEVEL1_BIT_RANGE[0]) # calc flags flags = parent_flags.copy() if has_next_level(entry): if is_stage2: # There are no flags in the table for VTTBR0_EL2. pass elif is_2VAranges: if ((entry >> 59) & 1) == 1: flags.append("PXNTable1") if ((entry >> 60) & 1) == 1: flags.append("UXNTable1") flags.append("APTable1={:02b}".format((entry >> 61) & 0b11)) if ((entry >> 63) & 1) == 1: flags.append("NSTable1") else: if ((entry >> 60) & 1) == 1: flags.append("XNTable1") # Use XNTable, not UXNTable # PXNTable is undefined flags.append("APTable1={:02b}".format((entry >> 61) & 0b11 & 0b10)) # APTable[0] must be 0 if ((entry >> 63) & 1) == 1: flags.append("NSTable1") else: if is_stage2: flags.append("MemAttr={:#x}".format((entry >> 2) & 0b1111)) flags.append("S2AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") flags.append("XN={:02b}".format((entry >> 53) & 0b11)) # Use XN, not UXN flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) elif is_2VAranges: flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 16) & 1) == 1: flags.append("nT") if ((entry >> 50) & 1) == 1: flags.append("GP") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 53) & 1) == 1: flags.append("PXN") if ((entry >> 54) & 1) == 1: flags.append("UXN") flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) else: flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11 & 0b10)) # AP[0] must be 0 flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 16) & 1) == 1: flags.append("nT") if ((entry >> 50) & 1) == 1: flags.append("GP") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 54) & 1) == 1: flags.append("XN") # Use XN, not UXN # PXN is undefined flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) # calc next table / output phys addr (drop the flag bits) if has_next_level(entry): if self.FEAT_LPA: if is_64k_granule: next_level_table = (entry & 0x0000_ffff_ffff_0000) | (((entry >> 12) & 0b1111) << 48) else: # 4k or 16k if self.TCR_ELx_DS: next_level_table = (entry & 0x0003_ffff_ffff_f000) | (((entry >> 8) & 0b11) << 50) else: next_level_table = entry & 0x0003_ffff_ffff_f000 else: next_level_table = entry & 0x0000_ffff_ffff_f000 else: if self.FEAT_LPA: if is_64k_granule: phys_addr = (entry & 0x0000_ffff_fffe_0000) | (((entry >> 12) & 0b1111) << 48) else: # 4k or 16k if self.TCR_ELx_DS: phys_addr = (entry & 0x0003_ffff_fffe_0000) | (((entry >> 8) & 0b11) << 50) else: phys_addr = entry & 0x0003_ffff_fffe_0000 else: phys_addr = entry & 0x0000_ffff_fffe_0000 # make entry if has_next_level(entry): LEVEL1.append([new_va, next_level_table, flags]) entry_type = "TABLE" else: virt_addr = new_va page_count = 1 if is_stage2: flag_string = self.format_flags_stage2(flags) else: flag_string = self.format_flags(flags) if is_4k_granule: page_size = 1 * 1024 * 1024 * 1024 GB1.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "1GB-PAGE" elif is_16k_granule: page_size = 64 * 1024 * 1024 * 1024 GB64.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "64GB-PAGE" elif is_64k_granule: page_size = 4 * 1024 * 1024 * 1024 * 1024 TB4.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "4TB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * 8 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if not self.silent: if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("Level 1 Entry: {:d}".format(len(LEVEL1))) self.quiet_info_add_out("PT Entry (1GB): {:d}".format(len(GB1))) self.quiet_info_add_out("PT Entry (64GB): {:d}".format(len(GB64))) self.quiet_info_add_out("PT Entry (4TB): {:d}".format(len(TB4))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(LEVEL1) - len(GB1) - len(GB64) - len(TB4))) self.mappings += GB1 + GB64 + TB4 else: if not self.silent: self.quiet_info_add_out("LEVEL 1 is skipped") LEVEL1 = LEVEL0 # level 2 parse for 4KB/16KB/64KB granule if not self.silent: self.quiet_add_out(titlify("LEVEL 2")) if self.LEVEL2_BIT_RANGE is not None and start_level <= 2: entries_per_table = self.get_entries_per_table( self.LEVEL2_BIT_RANGE, granule_bits, region_bits, is_first_level=(start_level == 2), ) LEVEL2 = [] MB2 = [] MB32 = [] MB512 = [] COUNT = 0 for va_base, table_base, parent_flags in tqdm(LEVEL1, leave=False, desc="LEVEL 2"): entries = self.read_mem_wrapper(table_base, 8 * entries_per_table) entries = slice_unpack(entries, 8) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if entry & 1 == 0: continue # calc virtual address new_va = va_base + (i << self.LEVEL2_BIT_RANGE[0]) new_va_end = new_va + (1 << self.LEVEL2_BIT_RANGE[0]) # calc flags flags = parent_flags.copy() if has_next_level(entry): if is_stage2: # There are no flags in the table for VTTBR0_EL2. pass elif is_2VAranges: if ((entry >> 59) & 1) == 1: flags.append("PXNTable2") if ((entry >> 60) & 1) == 1: flags.append("UXNTable2") flags.append("APTable2={:02b}".format((entry >> 61) & 0b11)) if ((entry >> 63) & 1) == 1: flags.append("NSTable2") else: if ((entry >> 60) & 1) == 1: flags.append("XNTable2") # Use XNTable, not UXNTable # PXNTable is undefined flags.append("APTable2={:02b}".format((entry >> 61) & 0b11 & 0b10)) # APTable[0] must be 0 if ((entry >> 63) & 1) == 1: flags.append("NSTable2") else: if is_stage2: flags.append("MemAttr={:#x}".format((entry >> 2) & 0b1111)) flags.append("S2AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") flags.append("XN={:02b}".format((entry >> 53) & 0b11)) # Use XN, not UXN flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) elif is_2VAranges: flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 16) & 1) == 1: flags.append("nT") if ((entry >> 50) & 1) == 1: flags.append("GP") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 53) & 1) == 1: flags.append("PXN") if ((entry >> 54) & 1) == 1: flags.append("UXN") flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) else: flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11 & 0b10)) # AP[0] must be 0 flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 16) & 1) == 1: flags.append("nT") if ((entry >> 50) & 1) == 1: flags.append("GP") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 54) & 1) == 1: flags.append("XN") # Use XN, not UXN # PXN is undefined flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) # calc next table / output phys addr (drop the flag bits) if has_next_level(entry): if self.FEAT_LPA: if is_64k_granule: next_level_table = (entry & 0x0000_ffff_ffff_0000) | (((entry >> 12) & 0b1111) << 48) else: # 4k or 16k if self.TCR_ELx_DS: next_level_table = (entry & 0x0003_ffff_ffff_f000) | (((entry >> 8) & 0b11) << 50) else: next_level_table = entry & 0x0003_ffff_ffff_f000 else: next_level_table = entry & 0x0000_ffff_ffff_f000 else: if self.FEAT_LPA: if is_64k_granule: phys_addr = (entry & 0x0000_ffff_fffe_0000) | (((entry >> 12) & 0b1111) << 48) else: # 4k or 16k if self.TCR_ELx_DS: phys_addr = (entry & 0x0003_ffff_fffe_0000) | (((entry >> 8) & 0b11) << 50) else: phys_addr = entry & 0x0003_ffff_fffe_0000 else: phys_addr = entry & 0x0000_ffff_fffe_0000 # make entry if has_next_level(entry): LEVEL2.append([new_va, next_level_table, flags]) entry_type = "TABLE" else: virt_addr = new_va page_count = 1 if is_stage2: flag_string = self.format_flags_stage2(flags) else: flag_string = self.format_flags(flags) if is_4k_granule: page_size = 2 * 1024 * 1024 MB2.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "2MB-PAGE" elif is_16k_granule: page_size = 32 * 1024 * 1024 MB32.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "32MB-PAGE" elif is_64k_granule: page_size = 512 * 1024 * 1024 MB512.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "512MB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(new_va, new_va_end): continue addr = table_base + i * 8 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, new_va, new_va_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if not self.silent: if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("Level 2 Entry: {:d}".format(len(LEVEL2))) self.quiet_info_add_out("PT Entry (2MB): {:d}".format(len(MB2))) self.quiet_info_add_out("PT Entry (32MB): {:d}".format(len(MB32))) self.quiet_info_add_out("PT Entry (512MB): {:d}".format(len(MB512))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(LEVEL2) - len(MB2) - len(MB32) - len(MB512))) self.mappings += MB2 + MB32 + MB512 else: if not self.silent: self.quiet_info_add_out("LEVEL 2 is skipped") LEVEL2 = LEVEL1 # level 3 parse for 4KB/16KB/64KB granule if not self.silent: self.quiet_add_out(titlify("LEVEL 3")) if self.LEVEL3_BIT_RANGE is not None and start_level <= 3: entries_per_table = self.get_entries_per_table( self.LEVEL3_BIT_RANGE, granule_bits, region_bits, is_first_level=False, ) KB4 = [] KB16 = [] KB64 = [] COUNT = 0 flag_cache = {} for va_base, table_base, parent_flags in tqdm(LEVEL2, leave=False, desc="LEVEL 3"): entries = self.read_mem_wrapper(table_base, 8 * entries_per_table) entries = slice_unpack(entries, 8) COUNT += len(entries) for i, entry in enumerate(entries): # present flag if entry & 1 == 0: continue # calc virtual address virt_addr = va_base + (i << self.LEVEL3_BIT_RANGE[0]) virt_addr_end = virt_addr + (1 << self.LEVEL3_BIT_RANGE[0]) # calc flags flags = parent_flags.copy() if (entry & 0b11) == 0b11: if is_stage2: flags.append("MemAttr={:#x}".format((entry >> 2) & 0b1111)) flags.append("S2AP={:02b}".format((entry >> 6) & 0b11)) flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") flags.append("XN={:02b}".format((entry >> 53) & 0b11)) # Use XN, not UXN flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) elif is_2VAranges: # This route passes many times, so make a memo entry_flags_key = entry & 0x787c_0000_0001_0ffc x = flag_cache.get(entry_flags_key, None) if x is not None: flags.extend(x) else: flags_tmp = [] flags_tmp.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags_tmp.append("NS") flags_tmp.append("AP={:02b}".format((entry >> 6) & 0b11)) flags_tmp.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags_tmp.append("AF") if ((entry >> 11) & 1) == 1: flags_tmp.append("nG") if ((entry >> 16) & 1) == 1: flags_tmp.append("nT") if ((entry >> 50) & 1) == 1: flags_tmp.append("GP") if ((entry >> 51) & 1) == 1: flags_tmp.append("DBM") if ((entry >> 52) & 1) == 1: flags_tmp.append("Contiguous") if ((entry >> 53) & 1) == 1: flags_tmp.append("PXN") if ((entry >> 54) & 1) == 1: flags_tmp.append("UXN") flags_tmp.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) flag_cache[entry_flags_key] = flags_tmp flags.extend(flags_tmp) else: flags.append("AttrIndx={:03b}".format((entry >> 2) & 0b111)) if ((entry >> 5) & 1) == 1: flags.append("NS") flags.append("AP={:02b}".format((entry >> 6) & 0b11 & 0b10)) # AP[0] must be 0 flags.append("SH={:02b}".format((entry >> 8) & 0b11)) if ((entry >> 10) & 1) == 1: flags.append("AF") if ((entry >> 11) & 1) == 1: flags.append("nG") if ((entry >> 16) & 1) == 1: flags.append("nT") if ((entry >> 50) & 1) == 1: flags.append("GP") if ((entry >> 51) & 1) == 1: flags.append("DBM") if ((entry >> 52) & 1) == 1: flags.append("Contiguous") if ((entry >> 54) & 1) == 1: flags.append("XN") # Use XN, not UXN # PXN is undefined flags.append("PBHA={:#x}".format((entry >> 59) & 0b1111)) else: # In ARMv8.7, level 3 has no table descriptors raise # calc next table / output phys addr (drop the flag bits) if is_4k_granule: if self.FEAT_LPA: if self.TCR_ELx_DS: phys_addr = (entry & 0x0003_ffff_ffff_f000) | (((entry >> 8) & 0b11) << 50) else: phys_addr = entry & 0x0003_ffff_ffff_f000 else: phys_addr = entry & 0x0000_ffff_ffff_f000 elif is_16k_granule: if self.FEAT_LPA: if self.TCR_ELx_DS: phys_addr = (entry & 0x0003_ffff_ffff_c000) | (((entry >> 8) & 0b11) << 50) else: phys_addr = entry & 0x0003_ffff_ffff_c000 else: phys_addr = entry & 0x0000_ffff_ffff_c000 elif is_64k_granule: if self.FEAT_LPA: phys_addr = (entry & 0x0000_ffff_ffff_0000) | (((entry >> 12) & 0b1111) << 48) else: phys_addr = entry & 0x0000_ffff_ffff_0000 # make entry page_count = 1 if is_stage2: flag_string = self.format_flags_stage2(flags) else: flag_string = self.format_flags(flags) if is_4k_granule: page_size = 4 * 1024 KB4.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "4KB-PAGE" elif is_16k_granule: page_size = 16 * 1024 KB16.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "16KB-PAGE" elif is_64k_granule: page_size = 64 * 1024 KB64.append([virt_addr, phys_addr, page_size, page_count, flag_string]) entry_type = "64KB-PAGE" # dump if self.args.print_each_level: if self.is_not_trace_target(virt_addr, virt_addr_end): continue addr = table_base + i * 8 line = "{:#018x}: {:#018x} (virt:{:#018x}-{:#018x},type:{:s}) {:s}".format( addr, entry, virt_addr, virt_addr_end, entry_type, " ".join(flags), ) if self.is_not_filter_target(line): continue self.out.append(line) if not self.silent: if self.args.print_each_level: self.out.append(titlify("")) self.quiet_info_add_out("Number of entries: {:d}".format(COUNT)) self.quiet_info_add_out("PT Entry (4KB): {:d}".format(len(KB4))) self.quiet_info_add_out("PT Entry (16KB): {:d}".format(len(KB16))) self.quiet_info_add_out("PT Entry (64KB): {:d}".format(len(KB64))) self.quiet_info_add_out("Invalid entries: {:d}".format(COUNT - len(KB4) - len(KB16) - len(KB64))) self.mappings += KB4 + KB16 + KB64 else: if not self.silent: self.quiet_info_add_out("LEVEL 3 is skipped") # finalize if not self.silent: self.quiet_add_out(titlify("Total")) self.quiet_info_add_out("PT Entry (Total): {:d}".format(len(self.mappings))) self.mappings = sorted(self.mappings) return def switch_el(self): self.SAVED_CPSR = 0 CPSR = get_register("$cpsr") & 0xffff_ffff CurrentEL = int((CPSR >> 2) & 0b11) # change EL try: if self.TargetEL < 1 or self.TargetEL > 3: self.err_add_out("Invalid argument (ELx>=1 && ELx<=3)") return if self.TargetEL != CurrentEL: self.SAVED_CPSR = CPSR CPSR = CPSR & ~(0b11 << 2) # clear EL CPSR |= self.TargetEL << 2 # set desired EL gdb.parse_and_eval("$cpsr = {:#x}".format(CPSR)) self.quiet_info_add_out("Moving to EL{:d}".format(self.TargetEL)) except ValueError: self.err_add_out("Invalid argument (ELx integer required)") return except gdb.error: self.err_add_out("Maybe unsupported to change to EL{:d}".format(self.TargetEL)) return # reload CPSR CPSR = get_register("$cpsr") & 0xffff_ffff CurrentEL = int((CPSR >> 2) & 0b11) self.quiet_info_add_out("CPSR: EL{:d}".format(CurrentEL)) return True def revert_el(self): if self.SAVED_CPSR: gdb.parse_and_eval("$cpsr = {:#x}".format(self.SAVED_CPSR)) SavedEL = (self.SAVED_CPSR >> 2) & 0b11 self.quiet_info_add_out("Moving back to EL{:d}".format(SavedEL)) return def get_granule_bits(self, TG, reg_name): if reg_name == "TG0": if TG == 0b00: return 12 # 4KB elif TG == 0b01: return 16 # 64KB elif TG == 0b10: return 14 # 16KB elif reg_name == "TG1": if TG == 0b01: return 14 # 16KB elif TG == 0b10: return 12 # 4KB elif TG == 0b11: return 16 # 64KB self.err_add_out("Unsupported {:s}".format(reg_name)) return None def get_pa_size_for_ps(self, ps, granule): if ps == 0b110: if granule == 16: return 52 if (granule == 14 or granule == 12) and self.TCR_ELx_DS: return 52 return 48 return { 0b000: 32, 0b001: 36, 0b010: 40, 0b011: 42, 0b100: 44, 0b101: 48, 0b110: 52, 0b111: 56, # unsupported for TCR_EL2 }[ps] def get_start_level(self, TnSZ, granule_bits): if self.FEAT_LPA2: if self.TCR_ELx_DS: if granule_bits == 12: if TnSZ < 0x10: return -1 return 0 def get_translation_base_addr(self, PS, TTBR): if PS == 0b110: if self.FEAT_LPA: high = TTBR & 0xffff_ffff_ffc0 low = (TTBR >> 2) & 0b1111 return high | (low << 48) else: self.err_add_out("Unsupported FEAT_LPA and IPS pair") return None return TTBR & 0xffff_ffff_fffe def pagewalk_TTBR0_EL1(self): self.out.append(titlify("$TTBR0_EL1", color="bold", msg_color="bold")) TTBR0_EL1 = get_register("$TTBR0_EL1", use_mbed_exec=True) TCR_EL1 = get_register("$TCR_EL1", use_mbed_exec=True) if TTBR0_EL1 == 0: self.warn_add_out("Maybe unused TTBR0_EL1") return self.TCR_ELx_DS = ((TCR_EL1 >> 59) & 1) == 1 IPS = (TCR_EL1 >> 32) & 0b111 TG0 = (TCR_EL1 >> 14) & 0b11 T0SZ = TCR_EL1 & 0b111111 granule_bits = self.get_granule_bits(TG0, "TG0") if granule_bits is None: return region_start = 0 region_end = region_start + (2 ** (64 - T0SZ)) region_bits = GefUtil.log2(region_end - region_start) page_size = 2 ** (granule_bits - 10) intermediate_pa_size = self.get_pa_size_for_ps(IPS, granule_bits) start_level = self.get_start_level(T0SZ, granule_bits) translation_base_addr = self.get_translation_base_addr(IPS, TTBR0_EL1) if translation_base_addr is None: return self.quiet_info_add_out("$TTBR0_EL1: {:#x}".format(TTBR0_EL1)) self.quiet_info_add_out("$TCR_EL1: {:#x}".format(TCR_EL1)) self.quiet_info_add_out("Intermediate Physical Address Size: {:d} bits".format(intermediate_pa_size)) self.quiet_info_add_out("EL1 User Region: {:#018x} - {:#018x} ({:d} bits)".format(region_start, region_end - 1, region_bits)) self.quiet_info_add_out("EL1 User Page Size: {:d}KB (per page)".format(page_size)) self.parse_bit_range(granule_bits, region_bits) if not self.args.use_cache or not self.ttbr0el1_mappings: self.flags_strings_cache = {} self.do_pagewalk(translation_base_addr, granule_bits, region_start, region_bits, start_level, is_2VAranges=True) self.flags_strings_cache = None self.merging() self.ttbr0el1_mappings = self.mappings.copy() self.make_out(self.ttbr0el1_mappings) return def pagewalk_TTBR1_EL1(self): self.out.append(titlify("$TTBR1_EL1", color="bold", msg_color="bold")) TTBR1_EL1 = get_register("$TTBR1_EL1", use_mbed_exec=True) TCR_EL1 = get_register("$TCR_EL1", use_mbed_exec=True) if TTBR1_EL1 == 0: self.warn_add_out("Maybe unused TTBR1_EL1") return self.TCR_ELx_DS = ((TCR_EL1 >> 59) & 1) == 1 IPS = (TCR_EL1 >> 32) & 0b111 TG1 = (TCR_EL1 >> 30) & 0b11 T1SZ = (TCR_EL1 >> 16) & 0b111111 granule_bits = self.get_granule_bits(TG1, "TG1") if granule_bits is None: return region_end = 2 ** 64 region_start = region_end - (2 ** (64 - T1SZ)) region_bits = GefUtil.log2(region_end - region_start) page_size = 2 ** (granule_bits - 10) intermediate_pa_size = self.get_pa_size_for_ps(IPS, granule_bits) start_level = self.get_start_level(T1SZ, granule_bits) translation_base_addr = self.get_translation_base_addr(IPS, TTBR1_EL1) if translation_base_addr is None: return self.quiet_info_add_out("$TTBR1_EL1: {:#x}".format(TTBR1_EL1)) self.quiet_info_add_out("$TCR_EL1: {:#x}".format(TCR_EL1)) self.quiet_info_add_out("Intermediate Physical Address Size: {:d} bits".format(intermediate_pa_size)) self.quiet_info_add_out("EL1 Kernel Region: {:#018x} - {:#018x} ({:d} bits)".format(region_start, region_end - 1, region_bits)) self.quiet_info_add_out("EL1 Kernel Page Size: {:d}KB (per page)".format(page_size)) self.parse_bit_range(granule_bits, region_bits) if not self.args.use_cache or not self.ttbr1el1_mappings: self.flags_strings_cache = {} self.do_pagewalk(translation_base_addr, granule_bits, region_start, region_bits, start_level, is_2VAranges=True) self.flags_strings_cache = None self.merging() self.ttbr1el1_mappings = self.mappings.copy() self.make_out(self.ttbr1el1_mappings) return def pagewalk_VTTBR_EL2(self): if not self.silent: self.out.append(titlify("$VTTBR_EL2", color="bold", msg_color="bold")) VTTBR_EL2 = get_register("$VTTBR_EL2") VTCR_EL2 = get_register("$VTCR_EL2") if VTTBR_EL2 == 0: if not self.silent: self.warn_add_out("Maybe unused VTTBR_EL2") return self.TCR_ELx_DS = ((VTCR_EL2 >> 32) & 1) == 1 SL2 = (VTCR_EL2 >> 33) & 0b1 PS = (VTCR_EL2 >> 16) & 0b111 TG0 = (VTCR_EL2 >> 14) & 0b11 SL0 = (VTCR_EL2 >> 6) & 0b11 T0SZ = VTCR_EL2 & 0b111111 granule_bits = self.get_granule_bits(TG0, "TG0") if granule_bits is None: return region_start = 0 region_end = region_start + (2 ** (64 - T0SZ)) region_bits = GefUtil.log2(region_end - region_start) page_size = 2 ** (granule_bits - 10) pa_size = self.get_pa_size_for_ps(PS, granule_bits) if self.FEAT_TTST: if SL0 == 0b00: if TG0 == 0b00: if self.FEAT_LPA2 and SL2 == 1: stage2_start_level = -1 else: stage2_start_level = 2 else: stage2_start_level = 3 elif SL0 == 0b01: if TG0 == 0b00: if self.FEAT_LPA2 and SL2 == 1: if not self.silent: self.err_add_out("Unsupported stage2 start level") return else: stage2_start_level = 1 else: stage2_start_level = 2 elif SL0 == 0b10: if TG0 == 0b00: if self.FEAT_LPA2 and SL2 == 1: if not self.silent: self.err_add_out("Unsupported stage2 start level") return else: stage2_start_level = 0 else: stage2_start_level = 1 elif SL0 == 0b11: if TG0 == 0b00: if self.FEAT_LPA2 and SL2 == 1: if not self.silent: self.err_add_out("Unsupported stage2 start level") return else: stage2_start_level = 3 else: stage2_start_level = 0 else: if SL0 == 0b00: if TG0 == 0b00: stage2_start_level = 2 else: stage2_start_level = 3 elif SL0 == 0b01: if TG0 == 0b00: stage2_start_level = 1 else: stage2_start_level = 2 elif SL0 == 0b10: if TG0 == 0b00: stage2_start_level = 0 else: stage2_start_level = 1 else: if not self.silent: self.err_add_out("Unsupported stage2 start level") return translation_base_addr = self.get_translation_base_addr(PS, VTTBR_EL2) if translation_base_addr is None: return if not self.silent: self.quiet_info_add_out("$VTTBR_EL2: {:#x}".format(VTTBR_EL2)) self.quiet_info_add_out("$VTCR_EL2: {:#x}".format(VTCR_EL2)) self.quiet_info_add_out("Physical Address Size: {:d} bits".format(pa_size)) self.quiet_info_add_out("EL2 Starting Level: {:d}".format(SL0)) self.quiet_info_add_out("EL2 Region: {:#018x} - {:#018x} ({:d} bits)".format(region_start, region_end - 1, region_bits)) self.quiet_info_add_out("EL2 Page Size: {:d}KB (per page)".format(page_size)) self.parse_bit_range(granule_bits, region_bits) if not self.args.use_cache or not self.vttbrel2_mappings: self.flags_strings_cache = {} self.do_pagewalk(translation_base_addr, granule_bits, region_start, region_bits, stage2_start_level, is_stage2=True) self.flags_strings_cache = None if not self.silent: self.merging() self.vttbrel2_mappings = self.mappings.copy() if not self.silent: self.make_out(self.vttbrel2_mappings) return def pagewalk_TTBR0_EL2(self): self.out.append(titlify("$TTBR0_EL2", color="bold", msg_color="bold")) TTBR0_EL2 = get_register("$TTBR0_EL2") TCR_EL2 = get_register("$TCR_EL2") if TTBR0_EL2 == 0: self.warn_add_out("Maybe unused TTBR0_EL2") return self.TCR_ELx_DS = ((TCR_EL2 >> 32) & 1) == 1 PS = (TCR_EL2 >> 16) & 0b111 TG0 = (TCR_EL2 >> 14) & 0b11 T0SZ = TCR_EL2 & 0b111111 granule_bits = self.get_granule_bits(TG0, "TG0") if granule_bits is None: return region_start = 0 region_end = region_start + (2 ** (64 - T0SZ)) region_bits = GefUtil.log2(region_end - region_start) page_size = 2 ** (granule_bits - 10) pa_size = self.get_pa_size_for_ps(PS, granule_bits) start_level = self.get_start_level(T0SZ, granule_bits) translation_base_addr = self.get_translation_base_addr(PS, TTBR0_EL2) if translation_base_addr is None: return self.quiet_info_add_out("$TTBR0_EL2: {:#x}".format(TTBR0_EL2)) self.quiet_info_add_out("$TCR_EL2: {:#x}".format(TCR_EL2)) if self.EL2_E2H: self.quiet_info_add_out("Intermediate Physical Address Size: {:d} bits".format(pa_size)) else: self.quiet_info_add_out("Physical Address Size: {:d} bits".format(pa_size)) if self.EL2_M20: self.quiet_info_add_out("EL2 User Region: {:#018x} - {:#018x} ({:d} bits)".format(region_start, region_end - 1, region_bits)) self.quiet_info_add_out("EL2 USer Page Size: {:d}KB (per page)".format(page_size)) else: self.quiet_info_add_out("EL2 Region: {:#018x} - {:#018x} ({:d} bits)".format(region_start, region_end - 1, region_bits)) self.quiet_info_add_out("EL2 Page Size: {:d}KB (per page)".format(page_size)) self.parse_bit_range(granule_bits, region_bits) if not self.args.use_cache or not self.ttbr0el2_mappings: self.flags_strings_cache = {} self.do_pagewalk(translation_base_addr, granule_bits, region_start, region_bits, start_level, is_2VAranges=self.EL2_M20) self.flags_strings_cache = None self.merging() self.ttbr0el2_mappings = self.mappings.copy() self.make_out(self.ttbr0el2_mappings) return def pagewalk_TTBR1_EL2(self): self.out.append(titlify("$TTBR1_EL2", color="bold", msg_color="bold")) TTBR1_EL2 = get_register("$TTBR1_EL2") TCR_EL2 = get_register("$TCR_EL2") if TTBR1_EL2 == 0: self.warn_add_out("Maybe unused TTBR1_EL2") return self.TCR_ELx_DS = ((TCR_EL2 >> 32) & 1) == 1 IPS = (TCR_EL2 >> 32) & 0b111 TG1 = (TCR_EL2 >> 30) & 0b11 T1SZ = (TCR_EL2 >> 16) & 0b111111 granule_bits = self.get_granule_bits(TG1, "TG1") if granule_bits is None: return region_end = 2 ** 64 region_start = region_end - (2 ** (64 - T1SZ)) region_bits = GefUtil.log2(region_end - region_start) page_size = 2 ** (granule_bits - 10) intermediate_pa_size = self.get_pa_size_for_ps(IPS, granule_bits) start_level = self.get_start_level(T1SZ, granule_bits) translation_base_addr = self.get_translation_base_addr(IPS, TTBR1_EL2) if translation_base_addr is None: return self.quiet_info_add_out("$TTBR1_EL2: {:#x}".format(TTBR1_EL2)) self.quiet_info_add_out("$TCR_EL2: {:#x}".format(TCR_EL2)) self.quiet_info_add_out("Intermediate Physical Address Size: {:d} bits".format(intermediate_pa_size)) self.quiet_info_add_out("EL2 Kernel Region: {:#018x} - {:#018x} ({:d} bits)".format(region_start, region_end - 1, region_bits)) self.quiet_info_add_out("EL2 Kernel Page Size: {:d}KB (per page)".format(page_size)) self.parse_bit_range(granule_bits, region_bits) if not self.args.use_cache or not self.ttbr1el2_mappings: self.flags_strings_cache = {} self.do_pagewalk(translation_base_addr, granule_bits, region_start, region_bits, start_level, is_2VAranges=self.EL2_M20) self.flags_strings_cache = None self.merging() self.ttbr1el2_mappings = self.mappings.copy() self.make_out(self.ttbr1el2_mappings) return def pagewalk_TTBR0_EL3(self): self.out.append(titlify("$TTBR0_EL3", color="bold", msg_color="bold")) TTBR0_EL3 = get_register("$TTBR0_EL3") TCR_EL3 = get_register("$TCR_EL3") if TTBR0_EL3 == 0: self.warn_add_out("Maybe unused TTBR0_EL3") return self.TCR_ELx_DS = ((TCR_EL3 >> 32) & 1) == 1 PS = (TCR_EL3 >> 16) & 0b111 TG0 = (TCR_EL3 >> 14) & 0b11 T0SZ = TCR_EL3 & 0b111111 granule_bits = self.get_granule_bits(TG0, "TG0") if granule_bits is None: return region_start = 0 region_end = region_start + (2 ** (64 - T0SZ)) region_bits = GefUtil.log2(region_end - region_start) page_size = 2 ** (granule_bits - 10) pa_size = self.get_pa_size_for_ps(PS, granule_bits) start_level = self.get_start_level(T0SZ, granule_bits) translation_base_addr = self.get_translation_base_addr(PS, TTBR0_EL3) if translation_base_addr is None: return self.quiet_info_add_out("$TTBR0_EL3: {:#x}".format(TTBR0_EL3)) self.quiet_info_add_out("$TCR_EL3: {:#x}".format(TCR_EL3)) self.quiet_info_add_out("Physical Address Size: {:d} bits".format(pa_size)) self.quiet_info_add_out("EL3 Region: {:#018x} - {:#018x} ({:d} bits)".format(region_start, region_end - 1, region_bits)) self.quiet_info_add_out("EL3 Page Size: {:d}KB (per page)".format(page_size)) self.parse_bit_range(granule_bits, region_bits) if not self.args.use_cache or not self.ttbr0el3_mappings: self.flags_strings_cache = {} self.do_pagewalk(translation_base_addr, granule_bits, region_start, region_bits, start_level) self.flags_strings_cache = None self.merging() self.ttbr0el3_mappings = self.mappings.copy() self.make_out(self.ttbr0el3_mappings) return def pagewalk_init(self): res = get_register("$TTBR0_EL1", use_mbed_exec=True) if res is None: self.err_add_out("Could not find system registers") return False SCTLR_EL1 = get_register("$SCTLR_EL1", use_mbed_exec=True) if SCTLR_EL1 is None: SCTLR_EL1 = get_register("$SCTLR") if SCTLR_EL1 is not None: self.EL1_M = (SCTLR_EL1 & 1) == 1 self.EL1_WXN = ((SCTLR_EL1 >> 19) & 1) == 1 else: self.EL1_M = False self.EL1_WXN = False HCR_EL2 = get_register("$HCR_EL2") if HCR_EL2 is not None: self.EL2_TGE = ((HCR_EL2 >> 27) & 1) == 1 self.EL2_E2H = ((HCR_EL2 >> 34) & 1) == 1 self.EL2_M20 = self.EL2_TGE and self.EL2_E2H self.EL2_VM = (HCR_EL2 & 1) == 1 else: self.EL2_TGE = False self.EL2_E2H = False self.EL2_M20 = False self.EL2_VM = False SCTLR_EL2 = get_register("$SCTLR_EL2") if SCTLR_EL2 is not None: self.EL2_M = (SCTLR_EL2 & 1) == 1 self.EL2_WXN = ((SCTLR_EL2 >> 19) & 1) == 1 else: self.EL2_M = False self.EL2_WXN = False SCTLR_EL3 = get_register("$SCTLR_EL3") if SCTLR_EL3 is not None: self.EL3_M = (SCTLR_EL3 & 1) == 1 self.EL3_WXN = ((SCTLR_EL3 >> 19) & 1) == 1 else: self.EL3_M = False self.EL3_WXN = False ID_AA64MMFR0_EL1 = get_register("$ID_AA64MMFR0_EL1", use_mbed_exec=True) if ID_AA64MMFR0_EL1 is not None: TGran4_2 = (ID_AA64MMFR0_EL1 >> 40) & 0b1111 TGran16_2 = (ID_AA64MMFR0_EL1 >> 32) & 0b1111 TGran4 = (ID_AA64MMFR0_EL1 >> 28) & 0b1111 TGran16 = (ID_AA64MMFR0_EL1 >> 20) & 0b1111 self.FEAT_LPA2 = (TGran4_2 == 0b0011) or (TGran16_2 == 0b0011) or (TGran4 == 0b0001) or (TGran16 == 0b0010) self.FEAT_LPA = (ID_AA64MMFR0_EL1 & 0b1111) == 0b0110 else: self.FEAT_LPA2 = False self.FEAT_LPA = False ID_AA64MMFR1_EL1 = get_register("$ID_AA64MMFR1_EL1", use_mbed_exec=True) if ID_AA64MMFR1_EL1 is not None: self.FEAT_PAN = ((ID_AA64MMFR1_EL1 >> 20) & 0b1111) != 0b0000 else: self.FEAT_PAN = False self.quiet_info_add_out("{:s} is supported on all ARMv8".format(Color.boldify("PXN"))) if self.FEAT_PAN: self.quiet_info_add_out("{:s} is supported".format(Color.boldify("PAN"))) else: self.quiet_info_add_out("PAN is unsupported") ID_AA64MMFR2_EL1 = get_register("$ID_AA64MMFR2_EL1", use_mbed_exec=True) if ID_AA64MMFR2_EL1 is not None: self.FEAT_TTST = ((ID_AA64MMFR2_EL1 >> 28) & 0b1111) == 0b0001 self.FEAT_LVA = ((ID_AA64MMFR2_EL1 >> 16) & 0b1111) == 0b0001 else: self.FEAT_TTST = False self.FEAT_LVA = False return True def pagewalk(self): # parse system registers if not self.pagewalk_init(): return self.silent = False self.mappings = None self.el2_mappings = None # TODO: implementation for VSTTBR_EL2, VSTCR_EL2 pattern # do pagewalk if self.TargetEL < 1 or 3 < self.TargetEL: self.warn_add_out("No paging in EL{:d}".format(self.TargetEL)) return if self.TargetEL == 1 and self.EL1_M: if self.EL2_VM: # el2_mapping is needed because read_mem() uses PA, but not IPA self.silent = True self.pagewalk_VTTBR_EL2() if self.mappings: self.el2_mappings = self.mappings.copy() self.mappings = None self.silent = False if self.args.only_TTBR0_EL1: self.pagewalk_TTBR0_EL1() elif self.args.only_TTBR1_EL1: self.pagewalk_TTBR1_EL1() else: self.pagewalk_TTBR0_EL1() self.pagewalk_TTBR1_EL1() if self.TargetEL == 1 and not self.EL1_M: self.quiet_info_add_out("EL1/0 translation is unused") if self.TargetEL == 2 and self.EL2_VM: self.pagewalk_VTTBR_EL2() if self.TargetEL == 2 and not self.EL2_VM: self.quiet_info_add_out("EL2(as stage2) translation is unused") if self.TargetEL == 2 and self.EL2_M: self.pagewalk_TTBR0_EL2() if self.EL2_M20: self.pagewalk_TTBR1_EL2() if self.TargetEL == 2 and self.EL2_VM: self.quiet_info_add_out("EL2(as stage1) translation is unused") if self.TargetEL == 3 and self.EL3_M: if not self.switch_el(): return self.pagewalk_TTBR0_EL3() self.revert_el() if self.TargetEL == 3 and not self.EL3_M: self.quiet_info_add_out("EL3 translation is unused") return def aarch64_optee_pseudo_pagewalk(self): Cache.reset_gef_caches() maps = PageMap.get_page_maps_arm64_optee_secure_memory(verbose=not self.args.quiet) if not maps: return fmt = "{:37s} {:37s} {:10s} {:s}" legend = ["Virtual address start-end", "Physical address start-end", "Total size", "Hint (Maybe)"] gef_print(GefUtil.make_legend(fmt.format(*legend))) """ [Newer version] gef> pagewalk --optee -n -q Virtual address start-end Physical address start-end Total size Hint (Maybe) 0x000000000e100000-0x000000000e102000 0x000000000e100000-0x000000000e102000 0x2000 TEE-OS bootstrap region 0x000000009e400000-0x000000009e600000 0x0000000042000000-0x0000000042200000 0x200000 NS<->S shared memory 0x000000009e600000-0x000000009e800000 0x0000000009000000-0x0000000009200000 0x200000 UART0_BASE 0x000000009e800000-0x000000009f800000 0x0000000008000000-0x0000000009000000 0x1000000 GIC_BASE 0x000000009fa00000-0x00000000a0400000 0x0000000000000000-0x0000000000a00000 0xa00000 0x00000000a0600000-0x00000000a2600000 0x0000000000000000-0x0000000002000000 0x2000000 0x00000000a2900000-0x00000000a3600000 0x000000000e300000-0x000000000f000000 0xd00000 0x00000000a362a000-0x00000000a36ae000 0x000000000e100000-0x000000000e184000 0x84000 TEE-OS .text 0x00000000a36ae000-0x00000000a382a000 0x000000000e184000-0x000000000e300000 0x17c000 TEE-OS .data / stack gef> [Older version] gef> pagewalk --optee -n -q Virtual address start-end Physical address start-end Total size Hint (Maybe) 0x000000000e100000-0x000000000e15d000 0x000000000e100000-0x000000000e15d000 0x5d000 TEE-OS .text 0x000000000e15d000-0x000000000e300000 0x000000000e15d000-0x000000000e300000 0x1a3000 TEE-OS .data / stack 0x000000000e300000-0x000000000f000000 0x000000000e300000-0x000000000f000000 0xd00000 0x000000000f200000-0x000000000fa00000 0x0000000000000000-0x0000000000800000 0x800000 0x000000000fa00000-0x0000000011a00000 0x0000000000000000-0x0000000002000000 0x2000000 0x0000000011a00000-0x0000000011c00000 0x0000000009000000-0x0000000009200000 0x200000 UART0_BASE 0x0000000011c00000-0x0000000011e00000 0x0000000042000000-0x0000000042200000 0x200000 NS<->S shared memory 0x000000000f000000-0x000000000f200000 0x0000000040000000-0x0000000040200000 0x200000 gef> """ text_end = None for va_start, va_end, pa_start, pa_end in maps: # https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/plat-vexpress/conf.mk if pa_start == 0x0e10_0000: if va_start == 0x0e10_0000 and va_end - va_start == 0x2000: hint = "TEE-OS bootstrap region" else: hint = "TEE-OS .text" text_end = va_end elif text_end and va_start == text_end: hint = "TEE-OS .data / stack" elif pa_start == 0x4200_0000: hint = "NS<->S shared memory" # https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/plat-vexpress/platform_config.h elif pa_start == 0x0800_0000: hint = "GIC_BASE" elif pa_start == 0x0900_0000: hint = "UART0_BASE" elif pa_start == 0x0904_0000: hint = "UART1_BASE" else: hint = "" gef_print("{:#018x}-{:#018x} {:#018x}-{:#018x} {:<#10x} {:s}".format( va_start, va_end, pa_start, pa_end, va_end - va_start, hint, ).rstrip()) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "kgdb")) @only_if_specific_arch(arch=("ARM64",)) def do_invoke(self, args): if args.optee and is_qemu_system(): self.aarch64_optee_pseudo_pagewalk() return if args.only_TTBR0_EL1 and args.only_TTBR1_EL1: err("Unsupported combination (-0 and -1)") return if self.args.trace: # You should not modify the self.args.vrange directly. self.vrange = self.args.vrange + self.args.trace # merge vrange and trace self.args.print_each_level = True # overwrite self.args.use_cache = False # overwrite else: self.vrange = self.args.vrange if args.target_el is None: CPSR = get_register("$cpsr") self.TargetEL = (CPSR >> 2) & 0b11 if self.TargetEL == 0: # Since $pc is in EL0 (unprivileged), temporarily elevate to EL1 # to inspect TTBR0_EL1 and TTBR1_EL1 page tables. self.TargetEL = 1 else: self.TargetEL = args.target_el if is_kgdb(): if self.TargetEL != 1: err("Unsupported target EL") return self.out = [] self.cache = {} self.pagewalk() self.cache = {} # The cache is huge, so it will be released as soon as possible. self.print_output() return @register_command class SwitchELCommand(GenericCommand): """Switch EL (Exception Level) on ARM64 architecture.""" _cmdline_ = "switch-el" _category_ = "06-b. Qemu-system/KGDB Cooperation - Register" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("target_el", metavar="TARGET_EL", nargs="?", type=int, help="Exception Level to change to.") _syntax_ = parser.format_help() def switch_el(self, target_el): # current EL CPSR = get_register("$cpsr") & 0xffff_ffff CurrentEL = (CPSR >> 2) & 0b11 # check argv if target_el is None: info("$cpsr = {:#x} (EL{:d})".format(CPSR, CurrentEL)) return # check target EL try: if target_el < 0 or target_el > 3: err("Invalid argument (ELx>=0 && ELx<=3)") return except ValueError: err("Invalid argument (ELx integer required)") return # change CPSR if target_el != CurrentEL: CPSR = CPSR & ~(0b11 << 2) # clear EL CPSR |= target_el << 2 # set desired EL gdb.parse_and_eval("$cpsr = {:#x}".format(CPSR)) info("Moving to EL{:d}".format(target_el)) else: info("Already at EL{:d}".format(target_el)) # reprint CPSR CPSR = int(gdb.parse_and_eval("$cpsr")) & 0xffff_ffff CurrentEL = (CPSR >> 2) & 0b11 info("$cpsr = {:#x} (EL{:d})".format(CPSR, CurrentEL)) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("ARM64",)) def do_invoke(self, args): self.switch_el(args.target_el) return @register_command class KernelVMMapCommand(GenericCommand, BufferingOutput): """Print kernel memory map.""" _cmdline_ = "kvmmap" _category_ = "06-a. Qemu-system/KGDB Cooperation - Memory Map" _aliases_ = ["pagewalk-with-hints"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-U", "--exclude-user", action="store_true", help="exclude userland memory.") parser.add_argument("-i", "--include-esp-fixup-stacks", action="store_true", help="include `%%esp fixup stacks` area (sometimes heavy memory use; x64 only).") parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity. (-v, -vv, -vvv)") parser.add_argument("address_filter", metavar="ADDRESS", nargs="*", type=AddressUtil.parse_address, help="filtering by specified address.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() class Region: def __init__(self, addr_start, addr_end, perm, description="", merge=True, addr_start_str=None, addr_end_str=None, size_str=None): self.addr_start = addr_start self.addr_end = addr_end self.perm = perm self.description = description self.merge = merge self.addr_start_str = addr_start_str self.addr_end_str = addr_end_str self.size_str = size_str return def add_description(self, description): if not self.description: self.description = description return if description in self.description: return self.description = "{:s}, {:s}".format(self.description, description) return @property def size(self): return self.addr_end - self.addr_start def __str__(self): line = "{:18s}-{:18s} {:18s} [{:s}] {:s}".format( self.addr_start_str if self.addr_start_str else "{:#018x}".format(self.addr_start), self.addr_end_str if self.addr_end_str else "{:#018x}".format(self.addr_end), self.size_str if self.size_str else "{:#018x}".format(self.size), self.perm, self.description, ).rstrip() # coloring if self.perm == "r--": line_color = Config.get_gef_setting("theme.address_readonly") elif self.perm == "rw-": line_color = Config.get_gef_setting("theme.address_writable") elif self.perm.endswith("x"): line_color = Config.get_gef_setting("theme.address_code") else: line_color = "" if self.perm == "rwx": line_color += " " + Config.get_gef_setting("theme.address_rwx") return Color.colorify(line, line_color) def page_start_align(self, x): return x & get_pagesize_mask_high() def page_end_align(self, x): if x & get_pagesize_mask_low(): return (x & get_pagesize_mask_high()) + get_pagesize() else: return x def insert_region(self, to_insert_address, to_insert_size, description, merge=True): target_address_start = to_insert_address target_address_end = to_insert_address + to_insert_size updated = False for _key, r in sorted(self.regions.items()): # no overwrap if r.addr_end <= target_address_start: continue if target_address_end <= r.addr_start: break # found, let's split # pattern1 # - target_address_start # (ignore) # - r.addr_start # (size_2nd) # - target_address_end # (size_3rd) # - r.addr_end # pattern2 # - target_address_start # (ignore) # - r.addr_start # (size_2nd) # - r.addr_end # (treat by next loop) # - target_address_end # pattern3 # - r.addr_start # (size_1st) # - target_address_start # (size_2nd) # - target_address_end # (size_3rd) # - r.addr_end # pattern4 # - r.addr_start # (size_1st) # - target_address_start # (size_2nd) # - r.addr_end # (treat by next loop) # - target_address_end size_1st = max(target_address_start - r.addr_start, 0) size_2nd = min(target_address_end, r.addr_end) - max(target_address_start, r.addr_start) size_3rd = max(r.addr_end - target_address_end, 0) # then insert (old one is overwritten) if size_1st > 0: addr_start_1st = r.addr_start addr_end_1st = addr_start_1st + size_1st self.regions[addr_start_1st] = self.Region(addr_start_1st, addr_end_1st, r.perm, r.description, merge) updated = True if size_2nd > 0: addr_start_2nd = max(target_address_start, r.addr_start) addr_end_2nd = addr_start_2nd + size_2nd if r.description: if description in r.description: new_description = r.description else: new_description = r.description + ", " + description else: new_description = description self.regions[addr_start_2nd] = self.Region(addr_start_2nd, addr_end_2nd, r.perm, new_description, merge) updated = True if size_3rd > 0: addr_start_3rd = target_address_end addr_end_3rd = addr_start_3rd + size_3rd self.regions[addr_start_3rd] = self.Region(addr_start_3rd, addr_end_3rd, r.perm, r.description, merge) updated = True if r.addr_end < target_address_end: target_address_start = r.addr_end return updated def insert_region_range(self, to_insert_address, to_insert_end, description, merge=True): return self.insert_region(to_insert_address, to_insert_end - to_insert_address, description, merge) def merge_region(self): new_regions = {} prev_key, prev_r = None, None for key, r in sorted(self.regions.items()): # for 1st element if prev_key is None: prev_key, prev_r = key, r continue # no_merge flag if not r.merge: new_regions[prev_key] = prev_r prev_key, prev_r = key, r continue if prev_r and not prev_r.merge: new_regions[prev_key] = prev_r prev_key, prev_r = key, r continue # not match, so append if prev_r.addr_end != r.addr_start: new_regions[prev_key] = prev_r prev_key, prev_r = key, r continue if prev_r.perm != r.perm: new_regions[prev_key] = prev_r prev_key, prev_r = key, r continue if prev_r.description != r.description: new_regions[prev_key] = prev_r prev_key, prev_r = key, r continue # match, so let's merge prev_r.addr_end += r.size # add last element if prev_key is not None: new_regions[prev_key] = prev_r # replace self.regions = new_regions return def filter_region(self): if not self.args.address_filter: return # filtering new_regions = {} for key, r in self.regions.items(): if any(r.addr_start <= a < r.addr_end for a in self.args.address_filter): new_regions[key] = r # replace self.regions = new_regions return def get_maps(self): option = "" if self.args.include_esp_fixup_stacks: option = " --include-esp-fixup-stacks" res = PageMap.get_page_maps_by_pagewalk("pagewalk --quiet --no-pager --disable-color" + option) res = sorted(set(res.splitlines())) res = list(filter(lambda line: line.endswith("]"), res)) res = list(filter(lambda line: "[+]" not in line, res)) def is_userland(line, addr_start): if is_x86(): return "USER" in line elif is_arm32(): if "[PL0/---" not in line and addr_start != 0xffff_0000: return True elif is_arm64(): return not AddressUtil.is_msb_on(addr_start) return False regions = {} # {addr_start: Region(), ...} for line in res: line = line.split() # parse address addr_start_str, addr_end_str = line[0].split("-") # Note that for espfix it looks like `0xffffff5b****e000-0xffffff5b****f000` addr_start = int(addr_start_str.replace("*", "0"), 16) addr_end = int(addr_end_str.replace("*", "f"), 16) # userland filter if self.args.exclude_user: if is_userland(line, addr_start): continue # parse permission if is_x86(): perm = Permission.from_process_maps(line[5][1:4].lower()) elif is_arm64() or is_arm32(): perm = Permission.from_process_maps(line[6][4:7].lower()) # add region if "*" in addr_start_str: regions[addr_start] = self.Region( addr_start, addr_end, str(perm), merge=False, description="esp_fixup (=0x1000*N)", addr_start_str=addr_start_str, addr_end_str=addr_end_str, size_str="0x0000000000001000", ) elif is_userland(line, addr_start): regions[addr_start] = self.Region( addr_start, addr_end, str(perm), description="userland", ) else: regions[addr_start] = self.Region( addr_start, addr_end, str(perm), ) return regions def resolve_kbase(self): self.quiet_info("Resolving kbase") kinfo = Kernel.get_kernel_layout() # .text stext = Symbol.get_ksymaddr("_stext") etext = Symbol.get_ksymaddr("_etext") if stext and etext: stext = self.page_start_align(stext) etext = self.page_end_align(etext) self.insert_region(stext, etext - stext, "kernel .text") else: if kinfo.text_base in self.regions: self.regions[kinfo.text_base].add_description("maybe kernel .text") # .rodata start_rodata = Symbol.get_ksymaddr("__start_rodata") end_rodata = Symbol.get_ksymaddr("__end_rodata") if start_rodata and end_rodata: start_rodata = self.page_start_align(start_rodata) end_rodata = self.page_end_align(end_rodata) self.insert_region(start_rodata, end_rodata - start_rodata, "kernel .rodata") else: # In a 32-bit environment, the range of rodata may not be measured correctly, so it is not used. if is_64bit(): if kinfo.ro_base in self.regions: self.regions[kinfo.ro_base].add_description("maybe kernel .rodata") # .data sdata = Symbol.get_ksymaddr("_sdata") edata = Symbol.get_ksymaddr("_edata") if sdata and edata: sdata = self.page_start_align(sdata) edata = self.page_end_align(edata) self.insert_region(sdata, edata - sdata, "kernel .data") else: # In a 32-bit environment, the range of rodata may not be measured correctly, so it is not used. if is_64bit(): if kinfo.rw_base in self.regions: self.regions[kinfo.rw_base].add_description("maybe kernel .data") return def resolve_direct_map(self): self.quiet_info("Resolving direct map") PAGE_OFFSET = KernelAddressHeuristicFinder.get_PAGE_OFFSET() PAGE_OFFSET_END = KernelAddressHeuristicFinder.get_PAGE_OFFSET_END() if PAGE_OFFSET and PAGE_OFFSET_END: self.insert_region_range(PAGE_OFFSET, PAGE_OFFSET_END, "physmap") return def resolve_vmalloc(self): self.quiet_info("Resolving vmalloc") VMALLOC_START = KernelAddressHeuristicFinder.get_VMALLOC_START() VMALLOC_END = KernelAddressHeuristicFinder.get_VMALLOC_END() if VMALLOC_END and VMALLOC_END: self.insert_region_range(VMALLOC_START, VMALLOC_END, "vmalloc") return def resolve_vmemmap(self): if is_x86_64() or is_arm64(): self.quiet_info("Resolving vmemmap") VMEMMAP_START = KernelAddressHeuristicFinder.get_VMEMMAP_START() VMEMMAP_END = KernelAddressHeuristicFinder.get_VMEMMAP_END() if VMEMMAP_START and VMEMMAP_END: self.insert_region_range(VMEMMAP_START, VMEMMAP_END, "vmemmap(=page[])") elif is_x86_32() or is_arm32(): if KernelAddressHeuristicFinder.consts().CONFIG_FLATMEM: self.quiet_info("Resolving mem_map") mem_map = KernelAddressHeuristicFinder.consts().mem_map mem_map &= get_pagesize_mask_high() # already there if mem_map in self.regions: self.regions[mem_map].add_description("mem_map(=page[])") return # require division for _key, r in sorted(self.regions.items()): if r.addr_start <= mem_map < r.addr_end: size = r.addr_end - mem_map self.insert_region(mem_map, size, "mem_map(=page[])") return elif KernelAddressHeuristicFinder.consts().CONFIG_SPARSEMEM: self.quiet_info("Resolving mem_section") mem_section = KernelAddressHeuristicFinder.consts().mem_section NR_MEM_SECTIONS = KernelAddressHeuristicFinder.consts().NR_MEM_SECTIONS sizeof_mem_section = KernelAddressHeuristicFinder.consts().sizeof_mem_section # parse mem_section for i in range(NR_MEM_SECTIONS): section_mem_map = read_int_from_memory(mem_section + sizeof_mem_section * i) section_mem_map &= get_pagesize_mask_high() if not is_valid_addr(section_mem_map): continue # already there if section_mem_map in self.regions: self.regions[section_mem_map].add_description("section_mem_map(=page[])") continue # require division for _key, r in sorted(self.regions.items()): if r.addr_start <= section_mem_map < r.addr_end: size = r.addr_end - section_mem_map self.insert_region(section_mem_map, size, "section_mem_map(=page[])") break return def resolve_ldt(self): if not is_x86(): return self.quiet_info("Resolving ldt") if is_x86_64() or is_x86_32(): LDT_BASE_ADDR = KernelAddressHeuristicFinder.consts().LDT_BASE_ADDR LDT_END_ADDR = KernelAddressHeuristicFinder.consts().LDT_END_ADDR if LDT_BASE_ADDR and LDT_END_ADDR: self.insert_region_range(LDT_BASE_ADDR, LDT_END_ADDR, "ldt") return def resolve_module(self): self.quiet_info("Resolving module") MODULES_VADDR = KernelAddressHeuristicFinder.consts().MODULES_VADDR MODULES_END = KernelAddressHeuristicFinder.consts().MODULES_END if MODULES_VADDR and MODULES_END: self.insert_region_range(MODULES_VADDR, MODULES_END, "modules") return def resolve_cpu_entry(self): if not is_x86(): return self.quiet_info("Resolving cpu entry") if is_x86_64() or is_x86_32(): CPU_ENTRY_AREA_BASE = KernelAddressHeuristicFinder.consts().CPU_ENTRY_AREA_BASE CPU_ENTRY_AREA_END = KernelAddressHeuristicFinder.consts().CPU_ENTRY_AREA_END if CPU_ENTRY_AREA_BASE and CPU_ENTRY_AREA_END: self.insert_region_range(CPU_ENTRY_AREA_BASE, CPU_ENTRY_AREA_END, "cpu_entry") return def resolve_efi(self): if not is_x86_64(): return self.quiet_info("Resolving efi") if is_x86_64(): EFI_VA_START = KernelAddressHeuristicFinder.consts().EFI_VA_START EFI_VA_END = KernelAddressHeuristicFinder.consts().EFI_VA_END if EFI_VA_START and EFI_VA_END: self.insert_region_range(EFI_VA_START, EFI_VA_END, "efi") return def resolve_dtb(self): if not is_arm32(): return self.quiet_info("Resolving device tree blob") if is_arm32(): DTB_START = KernelAddressHeuristicFinder.consts().DTB_START DTB_END = KernelAddressHeuristicFinder.consts().DTB_END if DTB_START and DTB_END: self.insert_region_range(DTB_START, DTB_END, "dtb") return def resolve_reserved(self): if not is_arm32(): return self.quiet_info("Resolving reserved") if is_arm32(): RESERVED_START = KernelAddressHeuristicFinder.consts().RESERVED_START RESERVED_END = KernelAddressHeuristicFinder.consts().RESERVED_END if RESERVED_START and RESERVED_END: self.insert_region_range(RESERVED_START, RESERVED_END, "reserved") return def resolve_fixmap(self): self.quiet_info("Resolving fixmap") FIXADDR_START = KernelAddressHeuristicFinder.consts().FIXADDR_START FIXADDR_TOP = KernelAddressHeuristicFinder.consts().FIXADDR_TOP if FIXADDR_START and FIXADDR_TOP: self.insert_region_range(FIXADDR_START, FIXADDR_TOP, "fixmap") return def resolve_vsyscall(self): if not is_x86_64(): return self.quiet_info("Resolving vsyscall") if is_x86_64(): VSYSCALL_ADDR = KernelAddressHeuristicFinder.consts().VSYSCALL_ADDR VSYSCALL_END = KernelAddressHeuristicFinder.consts().VSYSCALL_END if VSYSCALL_ADDR and VSYSCALL_END: self.insert_region_range(VSYSCALL_ADDR, VSYSCALL_END, "vsyscall") return def resolve_pci(self): if not is_arm64(): return self.quiet_info("Resolving pci") if is_arm64(): PCI_IO_START = KernelAddressHeuristicFinder.consts().PCI_IO_START PCI_IO_END = KernelAddressHeuristicFinder.consts().PCI_IO_END if PCI_IO_START and PCI_IO_END: self.insert_region_range(PCI_IO_START, PCI_IO_END, "pci") return def resolve_vector(self): if not is_arm32(): return self.quiet_info("Resolving vector") if is_arm32(): self.insert_region_range(0xffff_0000, 0xffff_1000, "vector") return def resolve_buddy(self): if self.args.verbose < 1: self.quiet_warn("Resolving buddy: skipped (args.verbose < 1)") return self.quiet_info("Resolving buddy") try: res = gdb.execute("buddy-dump --quiet --no-pager --sort", to_string=True) except gdb.error: return for line in res.splitlines(): line = Color.remove_color(line) if not line.startswith(" "): continue _page_str, size_str, virt_str, _phys_str, *pcp = line.split() if "???" in virt_str: # maybe x86 highmem continue size = int(size_str[5:], 16) virt = int(virt_str[5:].split("-")[0], 16) if pcp: description = "free page in buddy allocator {:s}".format(" ".join(pcp)) else: description = "free page in buddy allocator" self.insert_region(virt, size, description, merge=False) return def resolve_kstack(self): self.quiet_info("Resolving kstack") try: res = gdb.execute("ktask --quiet --no-pager --print-thread", to_string=True) except gdb.error: return # calc kstack address pattern kstacks = [] for line in res.splitlines(): line = line.split() if len(line) >= 2: kstack = int(line[-2], 16) kstacks.append(kstack & 0xffff) # calc kstack size kstacks = sorted(set(kstacks)) diffs = [] for i in range(len(kstacks) - 1): diff = kstacks[i + 1] - kstacks[i] diffs.append(diff) if len(diffs) == 0: kstack_size = get_pagesize() *2 else: kstack_size = min(diffs) # kstack for line in res.splitlines(): elems = line.split() pid, kstack = int(elems[3]), int(elems[-2], 16) process_name = line.split(maxsplit=4)[4][:16].strip() description = "kstack PID:{:d} ({:s})".format(pid, process_name) self.insert_region(kstack, kstack_size, description) return def resolve_userland(self): self.quiet_info("Resolving userland") # If current is a kernel thread, the userland memory map details will not be displayed. # Even if you are in a kernel thread, you may be able to see the userland memory map, # but it takes time to identify which process it belongs to. try: th_num = gdb.selected_thread().num res = gdb.execute("kcurrent --quiet", to_string=True) r = re.search(r"current \(cpu{:d}\): (0x\S+) .*".format(th_num - 1), res) if not r: return curr_task = int(r.group(1), 16) except Exception: return try: res = gdb.execute(f"ktask --quiet --no-pager -u --task-filter {curr_task:#x}", to_string=True) if not res: return res = gdb.execute(f"ktask --quiet --no-pager -u --task-filter {curr_task:#x} -m", to_string=True) except gdb.error: return pid = -1 comm = "?" for line in res.splitlines(): if not line.startswith("0x"): continue line = line.split() # process name if "-" not in line[0]: pid, comm = int(line[3]), line[4] continue # map name addr_start, addr_end = line[0].split("-") addr_start = int(addr_start, 16) addr_end = int(addr_end, 16) map_size = addr_end - addr_start description = "PID:{:d} ({:s}) {:s}".format(pid, comm, " ".join(line[2:])) self.insert_region(addr_start, map_size, description.rstrip()) return def resolve_full_slub(self): if self.args.verbose < 2: self.quiet_warn("Resolving full slub: skipped (args.verbose < 2)") return self.quiet_info("Resolving full slub (skip if target region size >= 0x200000)") old_regions = list(self.regions.items())[::] tqdm = GefUtil.get_tqdm() for _region_addr, region in tqdm(old_regions, leave=False): if "slab cache" in region.description: continue if region.perm != "rw-": continue if region.size >= 0x20_0000: # heuristic threshold continue current = region.addr_start while current < region.addr_end: try: res = Kernel.get_slab_contains(current) except gdb.error: break if not res: current += get_pagesize() continue r = re.search(r"name: (\S+) .+ num_pages: (\S+)", res) if not r: current += get_pagesize() continue name = r.group(1) # something is wrong if name and not all(x in String.STRING_PRINTABLE for x in name): current += get_pagesize() continue num_pages = int(r.group(2), 16) # something is wrong if num_pages == 0: current += get_pagesize() continue description = "slab cache ({:s}; full)".format(name) total_page_size = get_pagesize() * num_pages self.insert_region(current, total_page_size, description, merge=False) current += total_page_size return def resolve_slub(self): if self.args.verbose < 1: self.quiet_warn("Resolving slub: skipped (args.verbose < 1)") return self.quiet_info("Resolving slub") try: res = gdb.execute("slub-dump --quiet --no-pager -vv", to_string=True) except gdb.error: return name, address, size = None, None, None for line in res.splitlines(): r = re.search(r"name: (.+)", line) if r: name = Color.remove_color(r.group(1)) continue r = re.search(r"virtual address: (.+0x.+)", line) if r: address = Color.remove_color(r.group(1)) address = int(address, 16) continue r = re.search(r"num pages: (\d+)", line) if r: size = int(r.group(1)) * get_pagesize() description = "slab cache ({:s})".format(name) if address: self.insert_region(address, size, description, merge=False) address, size = None, None # for detect logic error. name will be reused until next parsing continue self.resolve_full_slub() return def resolve_slab(self): if self.args.verbose < 1: self.quiet_warn("Resolving slab: skipped (args.verbose < 1)") return self.quiet_info("Resolving slab") try: res = gdb.execute("slab-dump --quiet --no-pager", to_string=True) except gdb.error: return name, address, size = None, None, None for line in res.splitlines(): r = re.search(r"name: (.+)", line) if r: name = Color.remove_color(r.group(1)) continue r = re.search(r"virtual address \(s_mem & ~0xfff\): (.+0x.+)", line) if r: address = Color.remove_color(r.group(1)) address = int(address, 16) continue r = re.search(r"num pages: (\d+)", line) if r: size = int(r.group(1)) * get_pagesize() description = "slab cache ({:s})".format(name) if address: self.insert_region(address, size, description, merge=False) address, size = None, None # for detect logic error. name will be reused until next parsing continue return def resolve_slob(self): if self.args.verbose < 1: self.quiet_warn("Resolving slob: skipped (args.verbose < 1)") return self.quiet_info("Resolving slob") try: res = gdb.execute("slob-dump --quiet --no-pager", to_string=True) except gdb.error: return address, size = None, None for line in res.splitlines(): r = re.search(r"virtual address: (.+0x.+)", line) if r: address = Color.remove_color(r.group(1)) address = int(address, 16) continue r = re.search(r"num pages: (\d+)", line) if r: size = int(r.group(1)) * get_pagesize() description = "slab cache" if address: self.insert_region(address, size, description, merge=False) address, size = None, None # for detect logic error continue return def resolve_slub_tiny(self): if self.args.verbose < 1: self.quiet_warn("Resolving slub-tiny: skipped (args.verbose < 1)") return self.quiet_info("Resolving slub-tiny") try: res = gdb.execute("slub-tiny-dump --quiet --no-pager", to_string=True) except gdb.error: return name, address, size = None, None, None for line in res.splitlines(): r = re.search(r"name: (.+)", line) if r: name = Color.remove_color(r.group(1)) continue r = re.search(r"virtual address: (.+0x.+)", line) if r: address = Color.remove_color(r.group(1)) address = int(address, 16) continue r = re.search(r"num pages: (\d+)", line) if r: size = int(r.group(1)) * get_pagesize() description = "slab cache ({:s})".format(name) if address: self.insert_region(address, size, description, merge=False) address, size = None, None # for detect logic error. name will be reused continue return def resolve_each_slab(self): allocator = Kernel.get_slab_type() if allocator == "SLUB": self.resolve_slub() elif allocator == "SLUB_TINY": self.resolve_slub_tiny() elif allocator == "SLAB": self.resolve_slab() elif allocator == "SLOB": self.resolve_slob() return def resolve_each_module(self): self.quiet_info("Resolving each module") try: res = gdb.execute("kmod --quiet --no-pager", to_string=True) except gdb.error: return for line in res.splitlines(): if not line: continue line = line.split() module_name = line[1] module_base = int(line[2], 16) module_size = align_to_pagesize(int(line[3], 16)) description = "kernel module ({:s})".format(module_name) self.insert_region(module_base, module_size, description) return def resolve_vdso(self): if self.args.verbose < 1: self.quiet_warn("Resolving vdso: skipped (args.verbose < 1)") return self.quiet_info("Resolving vdso") if is_x86_64(): vdso_image_64 = KernelAddressHeuristicFinder.get_vdso_image_64() if vdso_image_64: vdso_start = read_int_from_memory(vdso_image_64) vdso_size = read_int_from_memory(vdso_image_64 + current_arch.ptrsize) self.insert_region(vdso_start, vdso_size, "vdso_image_64") if is_x86_64() or is_x86_32(): vdso_image_32 = KernelAddressHeuristicFinder.get_vdso_image_32() if vdso_image_32: vdso_start = read_int_from_memory(vdso_image_32) vdso_size = read_int_from_memory(vdso_image_32 + current_arch.ptrsize) self.insert_region(vdso_start, vdso_size, "vdso_image_32") if is_x86_64(): vdso_image_x32 = KernelAddressHeuristicFinder.get_vdso_image_x32() if vdso_image_x32: vdso_start = read_int_from_memory(vdso_image_x32) vdso_size = read_int_from_memory(vdso_image_x32 + current_arch.ptrsize) self.insert_region(vdso_start, vdso_size, "vdso_image_x32") if is_arm64() or is_arm32(): vdso_start = KernelAddressHeuristicFinder.get_vdso_start() if vdso_start: vdso_size = get_pagesize() self.insert_region(vdso_start, vdso_size, "vdso_start") if is_arm64(): vdso32_start = KernelAddressHeuristicFinder.get_vdso32_start() if vdso32_start: vdso_size = get_pagesize() self.insert_region(vdso32_start, vdso_size, "vdso32_start") return def resolve_device_physmem(self): if self.args.verbose < 2: self.quiet_warn("Resolving device physmem: skipped (args.verbose < 2)") return self.quiet_info("Resolving device physmem") try: res = gdb.execute("monitor info mtree -f", to_string=True) except gdb.error: return maps = PageMap.get_page_maps(None) if maps is None: return for line in res.splitlines(): if not line.startswith(" "): continue m = re.search(r" ([0-9a-f]+)-([0-9a-f]+) \(.+?\): (.+)", line) if not m: continue paddr = int(m.group(1), 16) if paddr % 0x1000: continue pend = int(m.group(2), 16) if (pend & 0xfff) == 0xfff: pend += 1 if pend % 0x1000: continue size = pend - paddr if size % 0x1000 or size == 0: continue device_name = "device ({:s})".format(m.group(3)) vaddrs = PageMap.p2v_from_map(paddr, maps) for vaddr in vaddrs: self.insert_region(vaddr, size, device_name) return def detect_zero_page(self): if self.args.verbose < 1: self.quiet_warn("Detecting zero page: skipped (args.verbose < 1)") return self.quiet_info("Detecting zero page") page_size = get_pagesize() z10 = b"\0" * 0x10 cc10 = b"\xcc" * 0x10 ff10 = b"\xff" * 0x10 z1000 = b"\0" * page_size ff1000 = b"\xff" * page_size cc1000 = b"\xcc" * page_size for _key, r in self.regions.copy().items(): if r.description != "": continue if r.size >= page_size * 0x10: continue for addr in range(r.addr_start, r.addr_end, page_size): try: x = read_memory(addr, 0x10) except gdb.MemoryError: self.insert_region(addr, page_size, "can not access") continue if x not in [z10, ff10, cc10]: continue try: x = read_memory(addr, page_size) except gdb.MemoryError: continue if x == z1000: self.insert_region(addr, page_size, "0x00-filled") elif x == cc1000: self.insert_region(addr, page_size, "0xcc-filled") elif x == ff1000: self.insert_region(addr, page_size, "0xff-filled") return def add_legend(self): if not self.args.quiet: fmt = "{:37s} {:18s} {:5s} {:s}" legend = ["Virtual address start-end", "Total size", "Perm", "Hint"] self.out.append(GefUtil.make_legend(fmt.format(*legend))) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_64", "x86_32", "ARM64", "ARM32")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): if self.args.include_esp_fixup_stacks and not is_x86_64(): err("Unsupported --include-esp-fixup-stacks option in this arch") return if Kernel.kernel_version() is None: err("Could not find Linux kernel") return # initial regions self.regions = self.get_maps() # add info self.out = [] self.add_legend() self.resolve_userland() self.resolve_ldt() self.resolve_direct_map() self.resolve_vmalloc() self.resolve_vmemmap() self.resolve_cpu_entry() self.resolve_efi() self.resolve_dtb() self.resolve_kbase() self.resolve_kstack() self.resolve_module() self.resolve_fixmap() self.resolve_vsyscall() self.resolve_pci() self.resolve_vector() self.resolve_reserved() self.resolve_device_physmem() self.resolve_buddy() self.resolve_each_slab() self.resolve_each_module() self.resolve_vdso() self.detect_zero_page() self.merge_region() self.filter_region() # make output for _, r in sorted(self.regions.items()): self.out.append(str(r)) self.print_output() return @register_command class PageCommand(GenericCommand): """The base command to convert between virtual addresses, physical addresses, and page addresses.""" _cmdline_ = "page" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-hh", "--help-simple", action="store_true", help="show help without ASCII diagram.") if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=False) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("to_virt") subparsers.add_parser("to_phys") subparsers.add_parser("from_virt") subparsers.add_parser("from_phys") _syntax_ = parser.format_help() _note_ = [ "Simplified page structure:", "", "[x86_64 / CONFIG_SPARSEMEM_VMEMMAP]", "VMEMMAP_START--------->+-struct page[]-+", " | pfn#0 page | --> physmem 0x0", " +---------------+", " | pfn#1 page | --> physmem 0x1000", " +---------------+", " | ... |", " +---------------+", " | pfn#N page | --> ...", " +---------------+", "", "", "[arm64 / CONFIG_SPARSEMEM_VMEMMAP]", "* This pattern uses `VMEMMAP_START`, but it needs `memstart_pfn` adjustment.", "vmemmap--------------->+-struct page[]-----------+", " | pfn#0 page | --> physmem 0x0", " +-------------------------+", " | ... | --> ...", "VMEMMAP_START--------->+-------------------------+", " | pfn#memstart_pfn page | --> physmem memstart_addr", " +-------------------------+", " | pfn#memstart_pfn+1 page | --> physmem memstart_addr+0x1000", " +-------------------------+", " | ... |", " +-------------------------+", " | pfn#memstart_pfn+N page | --> ...", " +-------------------------+", "", "", "[x86_32 / CONFIG_FLATMEM]", "mem_map--------------->+-struct page[]-+", " | pfn#0 page | --> physmem 0x0", " +---------------+", " | pfn#1 page | --> physmem 0x1000", " +---------------+", " | ... |", " +---------------+", " | pfn#N page | --> ...", " +---------------+", "", "", "[arm32 / CONFIG_FLATMEM]", "* `mem_map` starts at pfn#PHYS_PFN_OFFSET, not pfn#0.", "mem_map--------------->+-struct page[]--------------+", " | pfn#PHYS_PFN_OFFSET page | --> physmem PHYS_OFFSET", " +----------------------------+", " | pfn#PHYS_PFN_OFFSET+1 page | --> physmem PHYS_OFFSET+0x1000", " +----------------------------+", " | ... |", " +----------------------------+", " | pfn#PHYS_PFN_OFFSET+N page | --> ...", " +----------------------------+", "", "", "[x86_32 or arm32 / CONFIG_SPARSEMEM]", "* This pattern uses `mem_section[]`, i.e. multiple section-specific mem_maps.", "* `section_id` can be obtained from `page->flags`.", "* `section_mem_map` is encoded and used to locate the page descriptor array.", "+-------------------------------------------------------------------------------------------+", "| |", "| +-struct mem_section[]-+ |", "| | section_mem_map | +-->+-struct page[]----------------+ |", "| +----------------------+ | | pfn#section_start_pfn page | --> physmem ... |", "+->| section_mem_map |-----+ | flags | --> section_id (=idx)--+", " +----------------------+ +------------------------------+", " | ... | | pfn#section_start_pfn+1 page | --> physmem ...", " +----------------------+ | flags |", " | section_mem_map | +------------------------------+", " +----------------------+ | ... |", " +------------------------------+", " | pfn#section_start_pfn+N page | --> ...", " | flags |", " +------------------------------+", "", "* CONFIG_SPARSEMEM_EXTREME is currently unsupported by this command.", ] _note_ = "\n".join(_note_) def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) complete = kwargs.get("complete", gdb.COMPLETE_NONE) super().__init__(prefix=prefix, complete=complete) return def initialize(self): if hasattr(PageCommand, "initialized") and PageCommand.initialized: return True info("Wait for memory scan") PageCommand.PAGE_SHIFT = KernelAddressHeuristicFinder.consts().PAGE_SHIFT if is_x86_64(): PageCommand.VMEMMAP_START = KernelAddressHeuristicFinder.get_VMEMMAP_START() if self.VMEMMAP_START is None: err("Could not find VMEMMAP_START") return False PageCommand.sizeof_struct_page = KernelAddressHeuristicFinder.consts().sizeof_struct_page if self.sizeof_struct_page is None: err("Could not find sizeof(struct page)") return False elif is_x86_32(): if KernelAddressHeuristicFinder.consts().CONFIG_FLATMEM: PageCommand.mode = "FLATMEM" PageCommand.mem_map = KernelAddressHeuristicFinder.consts().mem_map sizeof_struct_page = KernelAddressHeuristicFinder.consts().sizeof_struct_page if sizeof_struct_page is None: return False PageCommand.sizeof_struct_page = sizeof_struct_page elif KernelAddressHeuristicFinder.consts().CONFIG_SPARSEMEM: PageCommand.mode = "SPARSEMEM" PageCommand.mem_section = KernelAddressHeuristicFinder.consts().mem_section sizeof_struct_page = KernelAddressHeuristicFinder.consts().sizeof_struct_page if sizeof_struct_page is None: return False PageCommand.sizeof_struct_page = sizeof_struct_page PageCommand.SECTION_HAS_MEM_MAP = KernelAddressHeuristicFinder.consts().SECTION_HAS_MEM_MAP PageCommand.sizeof_mem_section = KernelAddressHeuristicFinder.consts().sizeof_mem_section PageCommand.SECTIONS_PGSHIFT = KernelAddressHeuristicFinder.consts().SECTIONS_PGSHIFT PageCommand.SECTIONS_MASK = KernelAddressHeuristicFinder.consts().SECTIONS_MASK PageCommand.SECTION_MAP_MASK = KernelAddressHeuristicFinder.consts().SECTION_MAP_MASK PageCommand.PFN_SECTION_SHIFT = KernelAddressHeuristicFinder.consts().PFN_SECTION_SHIFT else: err("Could not find mem_map and mem_section") return False elif is_arm64(): PageCommand.VMEMMAP_START = KernelAddressHeuristicFinder.get_VMEMMAP_START() if self.VMEMMAP_START is None: err("Could not find VMEMMAP_START") return False PageCommand.sizeof_struct_page = KernelAddressHeuristicFinder.consts().sizeof_struct_page if self.sizeof_struct_page is None: err("Could not find sizeof(struct page)") return False memstart_addr = KernelAddressHeuristicFinder.consts().memstart_addr if memstart_addr is None: err("Could not find memstart_addr") return False pQ = lambda a: struct.pack("> self.SECTIONS_PGSHIFT) & self.SECTIONS_MASK mem_section_i = self.mem_section + self.sizeof_mem_section * section_id if not is_valid_addr(mem_section_i): return None section_mem_map_i = read_int_from_memory(mem_section_i) if (section_mem_map_i & self.SECTION_HAS_MEM_MAP) == 0: return None biased_map = section_mem_map_i & self.SECTION_MAP_MASK delta = page - biased_map if delta < 0: return None if delta % self.sizeof_struct_page != 0: return None pfn = delta // self.sizeof_struct_page else: return None elif is_arm64(): delta = page - self.VMEMMAP_START if delta < 0: return None if delta % self.sizeof_struct_page != 0: return None memstart_pfn = self.memstart_addr >> self.PAGE_SHIFT pfn = (delta // self.sizeof_struct_page) + memstart_pfn elif is_arm32(): if self.mode == "FLATMEM": delta = page - self.mem_map if delta < 0: return None if delta % self.sizeof_struct_page != 0: return None pfn = (delta // self.sizeof_struct_page) + self.PHYS_PFN_OFFSET elif self.mode == "SPARSEMEM": flags = read_int_from_memory(page) section_id = (flags >> self.SECTIONS_PGSHIFT) & self.SECTIONS_MASK mem_section_i = self.mem_section + self.sizeof_mem_section * section_id if not is_valid_addr(mem_section_i): return None section_mem_map_i = read_int_from_memory(mem_section_i) if (section_mem_map_i & self.SECTION_HAS_MEM_MAP) == 0: return None biased_map = section_mem_map_i & self.SECTION_MAP_MASK delta = page - biased_map if delta < 0: return None if delta % self.sizeof_struct_page != 0: return None pfn = delta // self.sizeof_struct_page else: return None else: return None if pfn < 0: return None return pfn << self.PAGE_SHIFT def phys2page(self, phys): if phys < 0: return None if phys & ((1 << self.PAGE_SHIFT) - 1): return None pfn = phys >> self.PAGE_SHIFT if is_x86_64(): page = self.VMEMMAP_START + (pfn * self.sizeof_struct_page) elif is_x86_32(): if self.mode == "FLATMEM": if pfn < 0: return None page = self.mem_map + (pfn * self.sizeof_struct_page) elif self.mode == "SPARSEMEM": section_id = pfn >> self.PFN_SECTION_SHIFT mem_section_i = self.mem_section + self.sizeof_mem_section * section_id if not is_valid_addr(mem_section_i): return None section_mem_map_i = read_int_from_memory(mem_section_i) if (section_mem_map_i & self.SECTION_HAS_MEM_MAP) == 0: return None biased_map = section_mem_map_i & self.SECTION_MAP_MASK page = biased_map + (pfn * self.sizeof_struct_page) else: return None elif is_arm64(): memstart_pfn = self.memstart_addr >> self.PAGE_SHIFT if pfn < memstart_pfn: return None page = self.VMEMMAP_START + ((pfn - memstart_pfn) * self.sizeof_struct_page) elif is_arm32(): if self.mode == "FLATMEM": if pfn < self.PHYS_PFN_OFFSET: return None page = self.mem_map + ((pfn - self.PHYS_PFN_OFFSET) * self.sizeof_struct_page) elif self.mode == "SPARSEMEM": section_id = pfn >> self.PFN_SECTION_SHIFT mem_section_i = self.mem_section + self.sizeof_mem_section * section_id if not is_valid_addr(mem_section_i): return None section_mem_map_i = read_int_from_memory(mem_section_i) if (section_mem_map_i & self.SECTION_HAS_MEM_MAP) == 0: return None biased_map = section_mem_map_i & self.SECTION_MAP_MASK page = biased_map + (pfn * self.sizeof_struct_page) else: return None else: return None if not is_valid_addr(page): err("Address in invalid range") return None return page @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_64", "x86_32", "ARM64", "ARM32")) @only_if_in_kernel def do_invoke(self, args): self.usage(simple=True) return @register_command class PageToVirtCommand(PageCommand, BufferingOutput): """Resolve virtual addresses mapped to the page.""" _cmdline_ = "page to_virt" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" _aliases_ = ["page2virt"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("page", metavar="ADDRESS", type=AddressUtil.parse_address, help="the page address to translate.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") _syntax_ = parser.format_help() _note_ = [ "One page may correspond to multiple virtual addresses.", ] _note_ = "\n".join(_note_) def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_64", "x86_32", "ARM64", "ARM32")) @only_if_in_kernel def do_invoke(self, args): if args.rescan: PageCommand.initialized = False if is_arm64(): kversion = Kernel.kernel_version() if kversion < "4.7": err("Unsupported before v4.7") return ret = self.initialize() if ret is False: err("Failed to initialize") return self.out = [] paddr = self.page2phys(args.page) if paddr is None: err("Failed to resolve phys") return # A page may be associated with multiple virtual addresses. vaddrs = Kernel.p2v(paddr) if not vaddrs: err("Failed to resolve virt") return for vaddr in vaddrs: self.out.append("Page: {:#x} -> Virt: {:#x}".format(args.page, vaddr)) self.print_output(check_terminal_size=True) return @register_command class PageFromVirtCommand(PageCommand, BufferingOutput): """Resolve the struct page for a virtual address.""" _cmdline_ = "page from_virt" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" _aliases_ = ["virt2page"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("virt", metavar="ADDRESS", type=AddressUtil.parse_address, help="the virtual address to translate.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") _syntax_ = parser.format_help() _note_ = None def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_64", "x86_32", "ARM64", "ARM32")) @only_if_in_kernel def do_invoke(self, args): if args.rescan: PageCommand.initialized = False if is_arm64(): kversion = Kernel.kernel_version() if kversion < "4.7": err("Unsupported before v4.7") return ret = self.initialize() if ret is False: err("Failed to initialize") return self.out = [] vaddr = args.virt if vaddr & 0xfff: warn("The address must be page aligned, round down and then calculate") vaddr &= get_pagesize_mask_high() paddr = Kernel.v2p(vaddr) if paddr is None: err("Failed to resolve phys") return page = self.phys2page(paddr) if page is None: err("Failed to resolve page") return self.out.append("Virt: {:#x} -> Page: {:#x}".format(vaddr, page)) self.print_output(check_terminal_size=True) return @register_command class PageToPhysCommand(PageCommand, BufferingOutput): """Resolve the physical address for a struct page.""" _cmdline_ = "page to_phys" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" _aliases_ = ["page2phys"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("page", metavar="ADDRESS", type=AddressUtil.parse_address, help="the page address to translate.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") _syntax_ = parser.format_help() _note_ = None def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_64", "x86_32", "ARM64", "ARM32")) @only_if_in_kernel def do_invoke(self, args): if args.rescan: PageCommand.initialized = False if is_arm64(): kversion = Kernel.kernel_version() if kversion < "4.7": err("Unsupported before v4.7") return ret = self.initialize() if ret is False: err("Failed to initialize") return self.out = [] paddr = self.page2phys(args.page) if paddr is None: err("Failed to resolve phys") return self.out.append("Page: {:#x} -> Phys: {:#x}".format(args.page, paddr)) self.print_output(check_terminal_size=True) return @register_command class PhysToPageCommand(PageCommand, BufferingOutput): """Resolve the struct page for a physical address.""" _cmdline_ = "page from_phys" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" _aliases_ = ["phys2page"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("phys", metavar="ADDRESS", type=AddressUtil.parse_address, help="the physical address to translate.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") _syntax_ = parser.format_help() _note_ = None def __init__(self): super().__init__(prefix=False, complete=gdb.COMPLETE_LOCATION) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware", "kgdb")) @only_if_specific_arch(arch=("x86_64", "x86_32", "ARM64", "ARM32")) @only_if_in_kernel def do_invoke(self, args): if args.rescan: PageCommand.initialized = False if is_arm64(): kversion = Kernel.kernel_version() if kversion < "4.7": err("Unsupported before v4.7") return ret = self.initialize() if ret is False: err("Failed to initialize") return self.out = [] paddr = args.phys if paddr & 0xfff: warn("The address must be page aligned, round down and then calculate") paddr &= get_pagesize_mask_high() page = self.phys2page(paddr) if page is None: err("Failed to resolve page") return self.out.append("Phys: {:#x} -> Page: {:#x}".format(paddr, page)) self.print_output(check_terminal_size=True) return @register_command class SlabVirtualCommand(GenericCommand): """Convert between slab-virtual addresses and page addresses.""" _cmdline_ = "slab-virtual" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" parser = argparse.ArgumentParser(prog=_cmdline_) modes = ["to_virt", "to_page", "from_virt", "from_page"] parser.add_argument("mode", choices=modes, help="conversion mode.") parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="the address to convert.") parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() _note_ = [ "This command works only in CONFIG_SLAB_VIRTUAL=y (implemented at https://github.com/thejh/linux).", "Used in the Google Kernel CTF mitigation instance.", "", "CONFIG_SLAB_VIRTUAL=n (normal kernel):", " Both `struct slab` and `struct page` directly manage physmap area.", "", " [physmap area]", " +------------+", " | virt | <-- size: 0x1000", " +------------+", " | ... |", " +------------+", " [vmemmap area]", " +------------+", " | page/slab | <-- size: sizeof(page) or sizeof(slab)", " +------------+", " | ... |", " +------------+", "", "CONFIG_SLAB_VIRTUAL=y (mitigated kernel):", " `struct slab` no longer manages physmap area. Instead, `struct slab` manages the slab_data area.", "", " [physmap area]", " +------------+", " | virt | <-- size: 0x1000", " +------------+", " | ... |", " +------------+", " [vmemmap area]", " +------------+", " | page | <-- size: sizeof(page)", " +------------+", " | ... |", " +------------+", " [slab_meta area]", " ^ +------------+ SLAB_BASE_ADDR (=0xfffffe8000000000)", " | | slab |", " | +------------+", " SLAB_META_SIZE | slab | <-- meta entry size: STRUCT_SLAB_SIZE # v6.1~v6.1.55", " | +------------+ or sizeof(struct slab) # v6.1.56~v6.6, v6.12~", " | | ... | or sizeof(struct virtual_slab) # v6.6~v6.12", " v +------------+ ", " [slab_data area]", " +------------+ SLAB_DATA_BASE_ADDR (=0xfffffe8800000000)", " | virt | ", " +------------+", " | virt | <-- size: 0x1000", " +------------+", " | ... |", " +------------+ SLAB_END_ADDR (=0xffffff0000000000)", ] _note_ = "\n".join(_note_) def initialize(self): if hasattr(self, "initialized") and self.initialized: return True self.PAGE_SHIFT = 12 P4D_SHIFT = 39 self.SLAB_BASE_ADDR = (-3 << P4D_SHIFT) & 0xffff_ffff_ffff_ffff self.quiet_info("SLAB_BASE_ADDR: {:#x}".format(self.SLAB_BASE_ADDR)) self.SLAB_END_ADDR = self.SLAB_BASE_ADDR + (1 << P4D_SHIFT) self.quiet_info("SLAB_END_ADDR: {:#x}".format(self.SLAB_END_ADDR)) SLAB_VPAGES = (self.SLAB_END_ADDR - self.SLAB_BASE_ADDR) >> self.PAGE_SHIFT self.quiet_info("SLAB_VPAGES: {:#x}".format(SLAB_VPAGES)) self.sizeof_struct_page = 0x40 self.quiet_info("sizeof(struct page): {:#x}".format(self.sizeof_struct_page)) kversion = Kernel.kernel_version() def align_kernel(x, alignment_size): mask = alignment_size - 1 return (x + mask) & (mask ^ 0xffff_ffff_ffff_ffff) # size of a single slab meta unit, SLAB_META_SIZE if kversion < "6.1.56": # branch: slub-virtual-v6.1, slub-virtual-v6.1-lts # include/linux/slab.h STRUCT_SLAB_SIZE = 24 * current_arch.ptrsize self.SLAB_META_SIZE = align_kernel(SLAB_VPAGES * STRUCT_SLAB_SIZE, 1 << self.PAGE_SHIFT) # mm/slub.c self.slab_meta_entry_size = STRUCT_SLAB_SIZE elif "6.1.56" <= kversion < "6.6": # branch: mitigations-v6.1.56 # arch/x86/include/asm/pgtable_64_types.h STRUCT_SLAB_SIZE = 32 * current_arch.ptrsize self.SLAB_META_SIZE = align_kernel(SLAB_VPAGES * STRUCT_SLAB_SIZE, 1 << self.PAGE_SHIFT) # mm/slab.h """ gef> dt slab struct slab { /* offset | size */ /* 0x0000 | 0x0008 */ struct slab * compound_slab_head; /* 0x0008 | 0x0008 */ struct folio * backing_folio; /* 0x0010 | 0x0004 */ struct kmem_cache_order_objects oo; /* 0x0014 | 0x0004 */ spinlock_t slab_lists_lock; /* 0x0018 | 0x0010 */ struct list_head flush_list_elem; /* 0x0028 | 0x0008 */ unsigned long align_mask; /* 0x0030 | 0x0004 */ atomic_t pinstate; /* 0x0038 | 0x0010 */ union {...} ; /* 0x0048 | 0x0008 */ struct kmem_cache * slab_cache; /* 0x0050 | 0x0010 */ struct {...} ; /* 0x0060 | 0x0004 */ unsigned int __unused; /* 0x0068 | 0x0008 */ unsigned long memcg_data; } // total: 0x70 bytes gef> """ self.slab_meta_entry_size = 0x70 # sizeof(struct slab) elif "6.6" <= kversion < "6.12": # branch: slub-virtual-v6.6 # arch/x86/include/asm/pgtable_64_types.h STRUCT_VIRTUAL_SLAB_SIZE = 32 * current_arch.ptrsize self.SLAB_META_SIZE = align_kernel(SLAB_VPAGES * STRUCT_VIRTUAL_SLAB_SIZE, 1 << self.PAGE_SHIFT) # mm/slab.h """ gef> dt slab struct slab { /* offset | size */ /* 0x0000 | 0x0008 */ union {...} ; /* 0x0008 | 0x0008 */ struct kmem_cache * slab_cache; /* 0x0010 | 0x0020 */ union {...} ; /* 0x0030 | 0x0004 */ struct kmem_cache_order_objects oo; /* 0x0034 | 0x0004 */ union {...} ; /* 0x0038 | 0x0008 */ unsigned long memcg_data; } // total: 0x40 bytes gef> dt virtual_slab struct virtual_slab { /* offset | size */ /* 0x0000 | 0x0040 */ struct slab slab; /* 0x0040 | 0x0008 */ struct virtual_slab * compound_slab_head; /* 0x0048 | 0x0008 */ unsigned long align_mask; } // total: 0x50 bytes gef> """ self.slab_meta_entry_size = 0x50 # sizeof(struct virtual_slab) else: # 6.12~ # branch: mitigations-next (linux-6.12 base) # arch/x86/include/asm/pgtable_64_types.h STRUCT_SLAB_SIZE = 32 * current_arch.ptrsize self.SLAB_META_SIZE = align_kernel(SLAB_VPAGES * STRUCT_SLAB_SIZE, 1 << self.PAGE_SHIFT) # mm/slab.h """ gef> dt slab struct slab { /* offset | size */ /* 0x0000 | 0x0008 */ struct slab * compound_slab_head; /* 0x0008 | 0x0008 */ struct folio * backing_folio; /* 0x0010 | 0x0004 */ struct kmem_cache_order_objects oo; /* 0x0018 | 0x0010 */ struct list_head flush_list_elem; /* 0x0028 | 0x0008 */ unsigned long align_mask; /* 0x0030 | 0x0004 */ spinlock_t slab_lock; /* 0x0038 | 0x0008 */ struct kmem_cache * slab_cache; /* 0x0040 | 0x0020 */ union {...} ; /* 0x0060 | 0x0008 */ unsigned long obj_exts; } // total: 0x70 bytes gef> """ self.slab_meta_entry_size = 0x70 # sizeof(struct slab) self.quiet_info("SLAB_META_SIZE: {:#x}".format(self.SLAB_META_SIZE)) self.quiet_info("single slab meta size: {:#x}".format(self.slab_meta_entry_size)) # offsetof(slab, compound_slab_head) if kernel != 6.6 # offsetof(virtual_slab, compound_slab_head) if kernel == 6.6 # offsetof(slab, backing_folio) if kversion < "6.6" or "6.12" <= kversion: self.slab_offset_compound_slab_head = 0 self.quiet_info("offsetof(slab, compound_slab_head): {:#x}".format(self.slab_offset_compound_slab_head)) self.slab_offset_backing_folio = current_arch.ptrsize else: # 6.6 self.slab_offset_compound_slab_head = current_arch.ptrsize * 8 self.quiet_info("offsetof(virtual_slab, compound_slab_head): {:#x}".format(self.slab_offset_compound_slab_head)) self.slab_offset_backing_folio = 0 self.quiet_info("offsetof(slab, backing_folio): {:#x}".format(self.slab_offset_backing_folio)) self.SLAB_DATA_BASE_ADDR = self.SLAB_BASE_ADDR + self.SLAB_META_SIZE self.quiet_info("SLAB_DATA_BASE_ADDR: {:#x}".format(self.SLAB_DATA_BASE_ADDR)) self.initialized = True return True def is_slab_virtual_meta(self, slab): return slab in range(self.SLAB_BASE_ADDR, self.SLAB_DATA_BASE_ADDR) def is_slab_virtual_addr(self, address): # for developer return address in range(self.SLAB_DATA_BASE_ADDR, self.SLAB_END_ADDR) def slab_to_virt(self, slab): """Convert slab-meta (aka slab-virtual) into virt (aka slab-data).""" if not is_valid_addr(slab): err("Memory Error") return None if not self.is_slab_virtual_meta(slab): err("Address is not in valid range") return None kversion = Kernel.kernel_version() if kversion < "6.1.56": slab_base = slab_data_base = self.SLAB_BASE_ADDR else: slab_base = self.SLAB_BASE_ADDR slab_data_base = self.SLAB_DATA_BASE_ADDR slab_idx, is_not_aligned = divmod(slab - slab_base, self.slab_meta_entry_size) if is_not_aligned: warn("Address is not aligned for the size of slab ({:#x})".format(self.slab_meta_entry_size)) return slab_data_base + (1 << self.PAGE_SHIFT) * slab_idx def virt_to_slab(self, virt): """Convert virt (aka slab-data) into slab (aka slab-meta or slab-virtual).""" if not is_valid_addr(virt): err("Memory error") return None if not self.is_slab_virtual_addr(virt): err("Address is not slab address") return None if virt & 0xfff: warn("Address is NOT aligned") kversion = Kernel.kernel_version() if kversion < "6.1.56": slab_base = slab_data_base = self.SLAB_BASE_ADDR else: slab_base = self.SLAB_BASE_ADDR slab_data_base = self.SLAB_DATA_BASE_ADDR slab_idx = (virt - slab_data_base) >> self.PAGE_SHIFT slab = slab_base + slab_idx * self.slab_meta_entry_size return read_int_from_memory(slab + self.slab_offset_compound_slab_head) # s->compound_head def slab_to_page(self, slab): """Convert slab (aka slab-meta) into page (aka backing_folio).""" if not is_valid_addr(slab): err("Memory error") return None return read_int_from_memory(slab + self.slab_offset_backing_folio) def get_vmemmap_base(self): # Do NOT use `KF.get_VMEMMAP_START()` here. # If CONFIG_KALLSYMS_ALL=n, `KF.get_VMEMMAP_START()` uses plan 3 and returns # slab-virtual address (NOT page address). # The returned value may be the first entry address of slab-virtual. # plan 1 (directly) addr = Symbol.get_ksymaddr("vmemmap_base") if addr: return read_int_from_memory(addr) # plan 2 (from `slab_virt_to_phys`) addr = Symbol.get_ksymaddr("slab_virt_to_phys") if addr: res = gdb.execute("x/30i {:#x}".format(addr), to_string=True) g = KernelAddressHeuristicFinderUtil.x64_qword_ptr_rip_base(res, read_valid=True) for x in g: return read_int_from_memory(x) kversion = Kernel.kernel_version() # plan 3 (available in 6.6-based only from `slub_addr_base` and `slub_addr_current`) # In 6.6-based, `slub_addr_base` is fixed after setup KASLR, and uses # `slub_addr_current` as watermark of once-allocated slabs. # Active range of slab-data area is [slub_addr_base, slub_addr_current). # So, slab-meta area in # virt_to_slab(slub_addr_base) ~ virt_to_slab(slub_addr_current - 1) # points vmemmmap area via member `backing_folio` in struct `slab`/`slab_virtual`. if "6.6" <= kversion < "6.12": addr = KernelAddressHeuristicFinder.get_slub_addr_base() if not addr: return None self.quiet_info("slub_addr_base @ {:#x}".format(addr)) slub_addr_base = read_int_from_memory(addr) slab_of_slub_addr_base = self.virt_to_slab(slub_addr_base) addr = KernelAddressHeuristicFinder.get_slub_addr_current() if not addr: return None self.quiet_info("slub_addr_current @ {:#x}".format(addr)) slub_addr_current = read_int_from_memory(addr) slab_of_slub_addr_current = self.virt_to_slab(slub_addr_current - 1) # Scan backing_folio referenced from slab-virtual area (so slow) vmemmap_entries = [] tqdm = GefUtil.get_tqdm(not self.args.quiet) self.quiet_info("Wait for memory scan") for slab in tqdm(range( slab_of_slub_addr_base, slab_of_slub_addr_current, self.slab_meta_entry_size, ), leave=False): backing_folio = read_int_from_memory(slab + self.slab_offset_backing_folio) if not is_valid_addr(backing_folio): continue vmemmap_entries.append(backing_folio) # The masked address of min page may be vmemmap_base(likely plan 3 of `KF.get_VMEMMAP_START()`) vmemmap_base = min(vmemmap_entries) & 0xffff_ffff_c000_0000 # ~((1 << PUD_SHIFT) - 1) return vmemmap_base return None def page_to_slab(self, page): """Convert page (aka backing_folio) into slab (aka slab-meta/slab-virtual).""" if not is_valid_addr(page): err("Memory error") return None # Step1: find `vmemmap_base` vmemmap_base = self.get_vmemmap_base() if vmemmap_base is None: return None self.quiet_info("vmemmap_base: {:#x}".format(vmemmap_base)) # Step2: get physical addr and mapped virtual address pfn = (page - vmemmap_base) // self.sizeof_struct_page phys_addr = pfn << self.PAGE_SHIFT # `p2v` returns 2 results (direct mapping area and slab data) r = Kernel.p2v(phys_addr) if not r or len(r) < 2: return None return self.virt_to_slab(max(r)) @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("x86_64",)) @only_if_in_kernel def do_invoke(self, args): kversion = Kernel.kernel_version() if kversion < "6.1": err("Unsupported before v6.1") return False # detect CONFIG_SLAB_VIRTUAL=y without `slub-dump` if KernelAddressHeuristicFinder.get_slub_tlbflush_queue() is None: err("CONFIG_SLAB_VIRTUAL=n is NOT supported") return if args.rescan: self.initialized = False ret = self.initialize() if ret is False: err("Failed to initialize") return out = [] if args.mode == "to_virt": data = self.slab_to_virt(args.address) if data is None: err("Failed to resolve") return out.append("Slab: {:#x} -> Virt: {:#x}".format(args.address, data)) elif args.mode == "to_page": page = self.slab_to_page(args.address) if page is None: err("Failed to resolve") return out.append("Slab: {:#x} -> Page: {:#x}".format(args.address, page)) elif args.mode == "from_virt": slab = self.virt_to_slab(args.address) if slab is None: err("Failed to resolve") return out.append("Virt: {:#x} -> Slab: {:#x}".format(args.address, slab)) elif args.mode == "from_page": slab = self.page_to_slab(args.address) if slab is None: err("Failed to resolve") return out.append("Page: {:#x} -> Slab: {:#x}".format(args.address, slab)) gef_print("\n".join(out)) return @register_command class PageInfoCommand(GenericCommand): """Dump struct page flags and page_type.""" _cmdline_ = "pageinfo" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("-p", "--page", type=AddressUtil.parse_address, help="page address to dump.") group.add_argument("virt", metavar="VIRT", nargs="?", type=AddressUtil.parse_address, help="virtual address to dump.") _syntax_ = parser.format_help() """ struct page { memdesc_flags_t flags; union { ... }; // 5 words union { unsigned int page_type; atomic_t _mapcount; }; atomic_t _refcount; ... }; """ @staticmethod def get_flags_str(flags_value): kversion = Kernel.kernel_version() # PG_uncached, PG_hwpoison, PG_young, PG_idle, PG_arch_2, PG_arch3: # Because it varies depending on the environment, GEF does not support it if "4.18" <= kversion < "4.20": flags_dic = { 0x0000_0000_0000_0001: "PG_locked", 0x0000_0000_0000_0002: "PG_error", 0x0000_0000_0000_0004: "PG_referenced", 0x0000_0000_0000_0008: "PG_uptodate", 0x0000_0000_0000_0010: "PG_dirty", 0x0000_0000_0000_0020: "PG_lru", 0x0000_0000_0000_0040: "PG_active", 0x0000_0000_0000_0080: "PG_waiters", 0x0000_0000_0000_0100: "PG_slab", 0x0000_0000_0000_0200: "PG_owner_priv_1", 0x0000_0000_0000_0400: "PG_arch_1", 0x0000_0000_0000_0800: "PG_reserved", 0x0000_0000_0000_1000: "PG_private", 0x0000_0000_0000_2000: "PG_private_2", 0x0000_0000_0000_4000: "PG_writeback", 0x0000_0000_0000_8000: "PG_head", 0x0000_0000_0001_0000: "PG_mappedtodisk", 0x0000_0000_0002_0000: "PG_reclaim", 0x0000_0000_0004_0000: "PG_swapbacked", 0x0000_0000_0008_0000: "PG_unevictable", 0x0000_0000_0010_0000: "PG_mlocked", # CONFIG_MMU is always defined } elif "4.20" <= kversion < "6.6": flags_dic = { 0x0000_0000_0000_0001: "PG_locked", 0x0000_0000_0000_0002: "PG_referenced", 0x0000_0000_0000_0004: "PG_uptodate", 0x0000_0000_0000_0008: "PG_dirty", 0x0000_0000_0000_0010: "PG_lru", 0x0000_0000_0000_0020: "PG_active", 0x0000_0000_0000_0040: "PG_workingset", 0x0000_0000_0000_0080: "PG_waiters", 0x0000_0000_0000_0100: "PG_error", 0x0000_0000_0000_0200: "PG_slab", 0x0000_0000_0000_0400: "PG_owner_priv_1", 0x0000_0000_0000_0800: "PG_arch_1", 0x0000_0000_0000_1000: "PG_reserved", 0x0000_0000_0000_2000: "PG_private", 0x0000_0000_0000_4000: "PG_private_2", 0x0000_0000_0000_8000: "PG_writeback", 0x0000_0000_0001_0000: "PG_head", 0x0000_0000_0002_0000: "PG_mappedtodisk", 0x0000_0000_0004_0000: "PG_reclaim", 0x0000_0000_0008_0000: "PG_swapbacked", 0x0000_0000_0010_0000: "PG_unevictable", 0x0000_0000_0020_0000: "PG_mlocked", # CONFIG_MMU is always defined } elif "6.6" <= kversion < "6.10": flags_dic = { 0x0000_0000_0000_0001: "PG_locked", 0x0000_0000_0000_0002: "PG_writeback", 0x0000_0000_0000_0004: "PG_referenced", 0x0000_0000_0000_0008: "PG_uptodate", 0x0000_0000_0000_0010: "PG_dirty", 0x0000_0000_0000_0020: "PG_lru", 0x0000_0000_0000_0040: "PG_head", 0x0000_0000_0000_0080: "PG_waiters", 0x0000_0000_0000_0100: "PG_active", 0x0000_0000_0000_0200: "PG_workingset", 0x0000_0000_0000_0400: "PG_error", 0x0000_0000_0000_0800: "PG_slab", 0x0000_0000_0000_1000: "PG_owner_priv_1", 0x0000_0000_0000_2000: "PG_arch_1", 0x0000_0000_0000_4000: "PG_reserved", 0x0000_0000_0000_8000: "PG_private", 0x0000_0000_0001_0000: "PG_private_2", 0x0000_0000_0002_0000: "PG_mappedtodisk", 0x0000_0000_0004_0000: "PG_reclaim", 0x0000_0000_0008_0000: "PG_swapbacked", 0x0000_0000_0010_0000: "PG_unevictable", 0x0000_0000_0020_0000: "PG_mlocked", # CONFIG_MMU is always defined } elif "6.10" <= kversion < "6.12": flags_dic = { 0x0000_0000_0000_0001: "PG_locked", 0x0000_0000_0000_0002: "PG_writeback", 0x0000_0000_0000_0004: "PG_referenced", 0x0000_0000_0000_0008: "PG_uptodate", 0x0000_0000_0000_0010: "PG_dirty", 0x0000_0000_0000_0020: "PG_lru", 0x0000_0000_0000_0040: "PG_head", 0x0000_0000_0000_0080: "PG_waiters", 0x0000_0000_0000_0100: "PG_active", 0x0000_0000_0000_0200: "PG_workingset", 0x0000_0000_0000_0400: "PG_error", 0x0000_0000_0000_0800: "PG_owner_priv_1", 0x0000_0000_0000_1000: "PG_arch_1", 0x0000_0000_0000_2000: "PG_reserved", 0x0000_0000_0000_4000: "PG_private", 0x0000_0000_0000_8000: "PG_private_2", 0x0000_0000_0001_0000: "PG_mappedtodisk", 0x0000_0000_0002_0000: "PG_reclaim", 0x0000_0000_0004_0000: "PG_swapbacked", 0x0000_0000_0008_0000: "PG_unevictable", 0x0000_0000_0010_0000: "PG_mlocked", # CONFIG_MMU is always defined } elif "6.12" <= kversion < "6.14": flags_dic = { 0x0000_0000_0000_0001: "PG_locked", 0x0000_0000_0000_0002: "PG_writeback", 0x0000_0000_0000_0004: "PG_referenced", 0x0000_0000_0000_0008: "PG_uptodate", 0x0000_0000_0000_0010: "PG_dirty", 0x0000_0000_0000_0020: "PG_lru", 0x0000_0000_0000_0040: "PG_head", 0x0000_0000_0000_0080: "PG_waiters", 0x0000_0000_0000_0100: "PG_active", 0x0000_0000_0000_0200: "PG_workingset", 0x0000_0000_0000_0400: "PG_owner_priv_1", 0x0000_0000_0000_0800: "PG_owner_2", 0x0000_0000_0000_1000: "PG_arch_1", 0x0000_0000_0000_2000: "PG_reserved", 0x0000_0000_0000_4000: "PG_private", 0x0000_0000_0000_8000: "PG_private_2", 0x0000_0000_0001_0000: "PG_reclaim", 0x0000_0000_0002_0000: "PG_swapbacked", 0x0000_0000_0004_0000: "PG_unevictable", 0x0000_0000_0008_0000: "PG_mlocked", # CONFIG_MMU is always defined } elif "6.14" <= kversion: flags_dic = { 0x0000_0000_0000_0001: "PG_locked", 0x0000_0000_0000_0002: "PG_writeback", 0x0000_0000_0000_0004: "PG_referenced", 0x0000_0000_0000_0008: "PG_uptodate", 0x0000_0000_0000_0010: "PG_dirty", 0x0000_0000_0000_0020: "PG_lru", 0x0000_0000_0000_0040: "PG_head", 0x0000_0000_0000_0080: "PG_waiters", 0x0000_0000_0000_0100: "PG_active", 0x0000_0000_0000_0200: "PG_workingset", 0x0000_0000_0000_0400: "PG_owner_priv_1", 0x0000_0000_0000_0800: "PG_owner_2", 0x0000_0000_0000_1000: "PG_arch_1", 0x0000_0000_0000_2000: "PG_reserved", 0x0000_0000_0000_4000: "PG_private", 0x0000_0000_0000_8000: "PG_private_2", 0x0000_0000_0001_0000: "PG_reclaim", 0x0000_0000_0002_0000: "PG_swapbacked", 0x0000_0000_0004_0000: "PG_unevictable", 0x0000_0000_0008_0000: "PG_dropbehind", 0x0000_0000_0010_0000: "PG_mlocked", # CONFIG_MMU is always defined } flags = [] for k, v in flags_dic.items(): if flags_value & k: flags.append(v) flags_str = " | ".join(flags) if flags_str == "": flags_str = "none" return flags_str @staticmethod def get_pagetype_str(flags_value): kversion = Kernel.kernel_version() if "4.18" <= kversion < "5.1": flags_dic = { 0x0000_0080: "PG_buddy", 0x0000_0100: "PG_balloon", 0x0000_0200: "PG_kmemcg", 0x0000_0400: "PG_table", } elif "5.1" <= kversion < "5.3": flags_dic = { 0x0000_0080: "PG_buddy", 0x0000_0100: "PG_offline", 0x0000_0200: "PG_kmemcg", 0x0000_0400: "PG_table", } elif "5.3" <= kversion < "5.11": flags_dic = { 0x0000_0080: "PG_buddy", 0x0000_0100: "PG_offline", 0x0000_0200: "PG_kmemcg", 0x0000_0400: "PG_table", 0x0000_0800: "PG_guard", } elif "5.11" <= kversion < "6.6": flags_dic = { 0x0000_0080: "PG_buddy", 0x0000_0100: "PG_offline", 0x0000_0200: "PG_table", 0x0000_0400: "PG_guard", } elif "6.6" <= kversion < "6.10": flags_dic = { 0x0000_0080: "PG_buddy", 0x0000_0100: "PG_offline", 0x0000_0200: "PG_table", 0x0000_0400: "PG_guard", 0x0000_0800: "PG_hugetlb", } elif "6.10" <= kversion < "6.11": flags_dic = { 0x0000_0080: "PG_buddy", 0x0000_0100: "PG_offline", 0x0000_0200: "PG_table", 0x0000_0400: "PG_guard", 0x0000_0800: "PG_hugetlb", 0x0000_1000: "PG_slab", } elif "6.11" <= kversion < "6.12": flags_dic = { 0x4000_0000: "PG_buddy", 0x2000_0000: "PG_offline", 0x1000_0000: "PG_table", 0x0800_0000: "PG_guard", 0x0400_0000: "PG_hugetlb", 0x0200_0000: "PG_slab", 0x0100_0000: "PG_zsmalloc", } elif "6.12" <= kversion: flags_dic = { 0xf0: "PGTY_buddy", 0xf1: "PGTY_offline", 0xf2: "PGTY_table", 0xf3: "PGTY_guard", 0xf4: "PGTY_hugetlb", 0xf5: "PGTY_slab", 0xf6: "PGTY_zsmalloc", 0xf7: "PGTY_unaccepted", 0xf8: "PGTY_large_kmalloc", # 6.15~ } if kversion < "6.12": flags = [] for k, v in flags_dic.items(): if ~flags_value & k: flags.append(v) flags_str = " | ".join(flags) if flags_str == "": flags_str = "none" else: type_value = (flags_value >> 24) & 0xff flags_str = flags_dic.get(type_value, "none") return flags_str def dump_page_info(self, page_addr, virt_addr): flags = read_int_from_memory(page_addr) compound_head = read_int32_from_memory(page_addr + current_arch.ptrsize * 1) page_type = read_int32_from_memory(page_addr + current_arch.ptrsize * 6) refcount = read_int32_from_memory(page_addr + current_arch.ptrsize * 6 + 4) gef_print("Page : {:#x}".format(page_addr)) if virt_addr is None: gef_print("Virt : None") else: gef_print("Virt : {:#x}".format(virt_addr)) gef_print("flags : {:#x} ({:s})".format(flags, PageInfoCommand.get_flags_str(flags))) gef_print("is_tailpage : {}".format(bool(compound_head & 1))) gef_print("page_type : {:#x} ({:s})".format(page_type, PageInfoCommand.get_pagetype_str(page_type))) if refcount == 0: gef_print("refcount : {:#x} (freed)".format(refcount)) else: gef_print("refcount : {:#x} (allocated)".format(refcount)) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_in_kernel def do_invoke(self, args): kversion = Kernel.kernel_version() if kversion < "4.18": err("Unsupported before v4.18") return if args.virt is not None: virt_addr = args.virt & get_pagesize_mask_high() page_addr = Kernel.virt2page(args.virt & get_pagesize_mask_high()) else: page_addr = args.page virt_addr = Kernel.page2virt(args.page) if not is_valid_addr(page_addr): err("Invalid page address") return self.dump_page_info(page_addr, virt_addr) return @register_command class HighMemDumpCommand(GenericCommand, BufferingOutput): """Dump HighMem mappings.""" _cmdline_ = "highmem-dump" _category_ = "06-d. Qemu-system/KGDB Cooperation - Virt/Phys/Page" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group() group.add_argument("-s", "--sort-by-virt", action="store_true", help="sort by virtual address.") group.add_argument("-S", "--sort-by-page", action="store_true", help="sort by page address.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="show result only.") _syntax_ = parser.format_help() def dump_entry(self, page, virt): heap_page_address_color = Config.get_gef_setting("theme.heap_page_address") virt_str = Color.colorify_hex(virt, heap_page_address_color) self.out.append("page:{:#010x} virt:{:s}".format(page, virt_str)) return def dump_slot(self, page_slot): seen = [page_slot] current = read_int_from_memory(page_slot) while current not in seen: seen.append(current) if not is_valid_addr(current): break try: page = read_int_from_memory(current - current_arch.ptrsize * 2) virt = read_int_from_memory(current - current_arch.ptrsize * 1) except gdb.MemoryError: self.err_add_out("Corrupted? ({:#x})".format(current)) break self.dump_entry(page, virt) current = read_int_from_memory(current) return def dump_table(self, page_address_htable): PA_HASH_ORDER = 7 sizeof_cache_align = 0x40 found = False for i in range(2 ** PA_HASH_ORDER): page_slot = page_address_htable + sizeof_cache_align * i if not is_double_link_list(page_slot, min_len=1): continue self.quiet_add_out(titlify("slot[{:d}] @ {:#x}".format(i, page_slot))) self.dump_slot(page_slot) found = True if not found: self.info_add_out("No highmem entries were found.") return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "ARM32")) @only_if_in_kernel_or_kpti_disabled def do_invoke(self, args): self.quiet_info("Wait for memory scan") page_address_htable = KernelAddressHeuristicFinder.get_page_address_htable() if page_address_htable is None: err("Could not find page_address_htable") return self.out = [] self.dump_table(page_address_htable) if self.args.sort_by_page: self.out = sorted(x for x in self.out if x.startswith("page:")) elif self.args.sort_by_virt: self.out = sorted([x for x in self.out if x.startswith("page:")], key=lambda x:x.split()[1]) self.print_output(check_terminal_size=True) return @register_command class QemuDeviceInfoCommand(GenericCommand, BufferingOutput): """Dump device information for qemu-escape.""" _cmdline_ = "qemu-device-info" _category_ = "06-k. Qemu-system/KGDB Cooperation - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-d", "--device", help="device name.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -d cydf-vga # Specify a device name", "{0:s} -d cydf # Specify a characteristic part of the device name", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "qemu-system must be running on the local host.", ] _note_ = "\n".join(_note_) def get_device_name(self): """Identify the device from qemu's command line arguments, or can force the use of the user specified device.""" # user specific if self.args.device: self.info_add_out("device name: {:s}".format(Color.boldify(self.args.device))) return self.args.device # scan from command line # check for existence qemu_cmdline_path = "/proc/{:d}/cmdline".format(Pid.get_pid()) if not os.path.exists(qemu_cmdline_path): err("Could not find {:s}".format(qemu_cmdline_path)) return None # check if it can be loaded try: content = open(qemu_cmdline_path, "rb").read() except Exception: err("Failed to read {:s}".format(qemu_cmdline_path)) return # check if it is from qemu-system cmdline = String.bytes2str(content).split("\0") if "qemu-system" not in " ".join(cmdline): err("Could not find `qemu-system` in {:s}".format(qemu_cmdline_path)) return None # check if the number of devices # the device specified by -device is likely to be the target of CTF attacks if cmdline.count("-device") == 0: err("Could not find `-device` option in qemu-system cmdline") return None # if multiple entries, it will be considered an error because it cannot be uniquely identified if cmdline.count("-device") >= 2: devices = [] for i in range(len(cmdline)): if cmdline[i] == "-device": devices.append(cmdline[i + 1]) devices_str = Color.boldify(", ".join(devices)) err("Multiple `-device` options are found in qemu-system cmdline: {:s}".format(devices_str)) return None # found device_name = cmdline[cmdline.index("-device") + 1] self.info_add_out("device name: {:s}".format(Color.boldify(device_name))) return device_name def dump_qdm(self, device_name): """Filter and display the target device from the list of devices recognized by qemu.""" res = gdb.execute("monitor info qdm", to_string=True) for line in res.splitlines(): if device_name in line: self.info_add_out("qdev device model: {:s}".format(Color.boldify(line))) return def dump_memmap(self, device_name): """Display information related to the target device from the memory managed by qemu.""" # get physmem map / IO map res = gdb.execute("monitor info mtree", to_string=True) self.info_add_out("Related memory address:") maps = [line.strip() for line in res.splitlines() if device_name in line and line.strip().startswith("0")] maps = sorted(set(maps)) # uniq for m in maps: self.out.append(" " + m) return def dump_symbol_related_device(self, device_name): """Show symbol information for the qemu-system related to the target device.""" # get nm try: nm = GefUtil.which(Config.get_gef_setting("gef.nm_command")) except FileNotFoundError as e: self.err_add_out("{}".format(e)) return # get qemu-system path qemu_path = os.readlink("/proc/{:d}/exe".format(Pid.get_pid())) self.info_add_out("qemu path: {:s}".format(qemu_path)) # get symbol related device try: result = GefUtil.gef_execute_external([nm, qemu_path], as_list=True) except subprocess.CalledProcessError: self.err_add_out("Executing `nm` error") return for line in result: if device_name not in line: continue if line.endswith(("read", "write")): index = line.rfind(" ") self.out.append(" {:s} {:s}".format(line[:index], Color.boldify(line[index + 1:]))) else: self.out.append(" {:s}".format(line)) return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) def do_invoke(self, args): self.out = [] device_name = self.get_device_name() if device_name is None: return self.dump_qdm(device_name) self.dump_memmap(device_name) self.dump_symbol_related_device(device_name) if not args.device: self.info_add_out("use `-d` if less information") self.print_output(check_terminal_size=True) return @register_command class QemuMemoryRegionDumpCommand(GenericCommand, BufferingOutput): """Dump memory regions for qemu-system.""" _cmdline_ = "qemu-system-memory-region-dump" _category_ = "07-g. Misc - Qemu-system" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-s", "--smart", action="store_true", help="show only entries where read or write is not the default.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() def get_rw_map(self): filepath = Path.get_filepath(append_proc_root_prefix=False) maps = ProcessMap.get_process_maps_linux(Pid.get_pid()) RW = Permission.READ | Permission.WRITE rw_maps = [] for m in maps: if m.permission.value != RW: continue if m.path == filepath: rw_maps.append(m) continue if len(rw_maps) > 0: if m.page_start == rw_maps[-1].page_end: # concat new_m = Section( page_start=rw_maps[-1].page_start, page_end=m.page_end, offset=rw_maps[-1].offset, permission=rw_maps[-1].permission, inode=rw_maps[-1].inode, path=rw_maps[-1].path, ) rw_maps[-1] = new_m if len(rw_maps) == 1: return rw_maps[0] return None def get_memory_region(self, name_target): """ static MemoryRegion *system_memory; static MemoryRegion *system_io; AddressSpace address_space_io; AddressSpace address_space_memory; struct AddressSpace { struct rcu_head rcu; // ptrsize * 2 bytes char *name; // -> "memory" or "I/O" MemoryRegion *root; struct FlatView *current_map; ... }; """ rw_map = self.get_rw_map() if rw_map is None: return None rw_content = read_memory(rw_map.page_start, rw_map.size) rw_content_sliced = slice_unpack(rw_content, current_arch.ptrsize) # Since it is near the end of the RW area, searching in reverse is faster for i, val in enumerate(rw_content_sliced[::-1], start=1): if not is_valid_addr(val): continue # name check name = read_cstring_from_memory(val) if name != name_target: continue ofs_name = rw_map.size - (current_arch.ptrsize * i) # root check ofs_root = ofs_name + current_arch.ptrsize root = read_int_from_memory(rw_map.page_start + ofs_root) if not is_valid_addr(root): continue root = ProcessMap.lookup_address(root) if not root.section.is_writable(): continue # current_map check ofs_current_map = ofs_root + current_arch.ptrsize current_map = read_int_from_memory(rw_map.page_start + ofs_current_map) if not is_valid_addr(current_map): continue current_map = ProcessMap.lookup_address(current_map) if not current_map.section.is_writable(): continue # found return root.value return None def get_system_memory(self): # fast path try: system_memory = AddressUtil.parse_address("&system_memory") return read_int_from_memory(system_memory) except gdb.error: pass # slow path return self.get_memory_region("memory") def get_system_io(self): # fast path try: system_io = AddressUtil.parse_address("&system_io") return read_int_from_memory(system_io) except gdb.error: pass # slow path return self.get_memory_region("I/O") def initialize(self): if hasattr(self, "initialized") and self.initialized: return True # root (system_memory, system_io) self.system_memory = self.get_system_memory() if self.system_memory is None: self.quiet_err("Could not find system_memory") return False self.quiet_info_add_out("system_memory: {:#x}".format(self.system_memory)) self.system_io = self.get_system_io() if self.system_io is None: self.quiet_err("Could not find system_io") return False self.quiet_info_add_out("system_io: {:#x}".format(self.system_io)) # name for offset in range(0, 0x100, current_arch.ptrsize): name_ptr_addr = self.system_memory + offset name_ptr = read_int_from_memory(name_ptr_addr) if not is_valid_addr(name_ptr): continue if read_cstring_from_memory(name_ptr) == "system": self.offset_name = offset break else: self.quiet_err("Could not find offsetof(MemoryRegion, name)") return False self.quiet_info_add_out("offsetof(MemoryRegion, name): {:#x}".format(self.offset_name)) # ops for offset in range(0, 0x80, current_arch.ptrsize): ops_addr = self.system_memory + offset ops = read_int_from_memory(ops_addr) # ops itself is r-x addr if not is_valid_addr(ops): continue if ProcessMap.lookup_address(ops).section.is_writable(): continue # ops->read: zero or r-x addr read_func = read_int_from_memory(ops + current_arch.ptrsize * 0) if read_func: if ProcessMap.lookup_address(read_func).section.is_writable(): continue # ops->write: zero or r-x addr write_func = read_int_from_memory(ops + current_arch.ptrsize * 1) if write_func: if ProcessMap.lookup_address(write_func).section.is_writable(): continue self.offset_ops = offset break else: self.quiet_err("Could not find offsetof(MemoryRegion, ops)") return False self.quiet_info_add_out("offsetof(MemoryRegion, ops): {:#x}".format(self.offset_ops)) # subregions, subregions_link self.offset_subregions = self.offset_name - current_arch.ptrsize * 6 self.quiet_info_add_out("offsetof(MemoryRegion, subregions): {:#x}".format(self.offset_subregions)) self.offset_subregions_link = self.offset_name - current_arch.ptrsize * 4 self.quiet_info_add_out("offsetof(MemoryRegion, subregions_link): {:#x}".format(self.offset_subregions_link)) self.initialized = True return def make_symbol_string(self, addr): addr = ProcessMap.lookup_address(addr) sym = Symbol.get_symbol_string(addr.value, nosymbol_string=" ") return "{!s}{:s}".format(addr, sym) def print_region_smart(self, name, ops, level): # skip if uninteresting if not ops: return # skip if seen if ops in self.seen: return self.seen.append(ops) # print indent = " " * level read_func = read_int_from_memory(ops + current_arch.ptrsize * 0) write_func = read_int_from_memory(ops + current_arch.ptrsize * 1) if read_func or write_func: self.out.append("{:s}MemoryRegion: {:s}".format(indent, Color.boldify(name))) self.out.append("{:s} ops:{:s}".format(indent, self.make_symbol_string(ops))) self.out.append("{:s} read:{:s}, write:{:s}".format( indent, self.make_symbol_string(read_func), self.make_symbol_string(write_func), )) return def print_region(self, name, ops, level): # print always indent = " " * level self.out.append("{:s}MemoryRegion: {:s}".format(indent, Color.boldify(name))) if not ops: self.out.append("{:s} ops:{:#x}".format(indent, ops)) return # print self.out.append("{:s} ops:{:s}".format(indent, self.make_symbol_string(ops))) read_func = read_int_from_memory(ops + current_arch.ptrsize * 0) write_func = read_int_from_memory(ops + current_arch.ptrsize * 1) if read_func or write_func: self.out.append("{:s} read:{:s}, write:{:s}".format( indent, self.make_symbol_string(read_func), self.make_symbol_string(write_func), )) return def dump_region(self, mr, level): name_ptr = read_int_from_memory(mr + self.offset_name) name = read_cstring_from_memory(name_ptr) or "" ops = read_int_from_memory(mr + self.offset_ops) # dump ops if self.args.smart: self.print_region_smart(name, ops, level) else: self.print_region(name, ops, level) # parse recursively link = read_int_from_memory(mr + self.offset_subregions) while link: self.dump_region(link, level + 1) link = read_int_from_memory(link + self.offset_subregions_link) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) @require_arch_set def do_invoke(self, args): self.out = [] if self.initialize() is False: return # dump system_memory self.seen = [] self.quiet_info_add_out("system_memory") self.dump_region(self.system_memory, 0) # dump system_io self.seen = [] self.quiet_info_add_out("system_io") self.dump_region(self.system_io, 0) self.print_output(check_terminal_size=True) return @register_command class XUntilCommand(GenericCommand): """Execute until specified address easily.""" _cmdline_ = "xuntil" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["exec-next", "stepover", "until-next"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", metavar="ADDRESS", nargs="?", type=AddressUtil.parse_address, help="the address to stop.") parser.add_argument("--from-wrapper", action="store_true", help="[FOR DEVELOPER] used internally in gef, please don't use it.") _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): if args.address is None: stop_addr = Disasm.gef_instruction_n(current_arch.pc, 1).address else: stop_addr = args.address # `until` command has a bug(?) because sometimes fail, # so we should use `tbreak` and `continue` instead of `until`. SimpleInternalTemporaryBreakpoint(loc=stop_addr) if args.from_wrapper: gdb.execute("continue") # do not use c wrapper because cycle reference else: gdb.execute("c") return @register_command class ExecUntilCommand(GenericCommand): """The base command to execute until specific condition.""" _cmdline_ = "exec-until" _category_ = "01-d. Debugging Support - Execution" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("call") subparsers.add_parser("jmp") subparsers.add_parser("syscall") subparsers.add_parser("ret") subparsers.add_parser("all-branch") subparsers.add_parser("indirect-branch") subparsers.add_parser("memaccess") subparsers.add_parser("keyword") subparsers.add_parser("cond") subparsers.add_parser("user-code") subparsers.add_parser("libc-code") subparsers.add_parser("secure-world") subparsers.add_parser("region-change") _syntax_ = parser.format_help() _example_ = [ "{0:s} call # execute until call instruction", "{0:s} jmp # execute until jmp instruction", "{0:s} syscall # execute until syscall instruction", "{0:s} ret # execute until ret instruction", "{0:s} all-branch # execute until call/jmp/ret instruction", "{0:s} indirect-branch # execute until indirect branch instruction (x64/x86 only)", "{0:s} memaccess # execute until '[' is included by the instruction", '{0:s} keyword "call +r[ab]x" # execute until specified keyword (regex)', '{0:s} cond "$rax==0xdead && $rbx==0xcafe" # execute until specified condition is filled', "{0:s} user-code # execute until user code", "{0:s} libc-code # execute until libc code", "{0:s} secure-world # execute until secure world (ARM/ARM64 only)", "{0:s} region-change # execute until different region (e.g., binary itself -> libc)", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) super().__init__(prefix=prefix) return def close_stdout_stderr(self): self.stdout = 1 self.stdout_bak = os.dup(self.stdout) f = open("/dev/null") os.dup2(f.fileno(), self.stdout) f.close() self.stderr = 2 self.stderr_bak = os.dup(self.stderr) f = open("/dev/null") os.dup2(f.fileno(), self.stderr) f.close() return def revert_stdout_stderr(self): os.dup2(self.stdout_bak, self.stdout) os.close(self.stdout_bak) os.dup2(self.stderr_bak, self.stderr) os.close(self.stderr_bak) return def force_write_stdout(self, msg): open("/proc/self/fd/0", "wb").write(msg) return def check_jump_taken(self, insn): if not current_arch.is_jump(insn): return False if (self.args.only_taken, self.args.only_not_taken) == (False, False): return True if (self.args.only_taken, self.args.only_not_taken) == (True, False): if current_arch.is_conditional_branch(insn): taken, _reason = current_arch.is_branch_taken(insn) return taken else: return True # non-conditional, so always jump if (self.args.only_taken, self.args.only_not_taken) == (False, True): if current_arch.is_conditional_branch(insn): taken, _reason = current_arch.is_branch_taken(insn) return not taken else: return False # non-conditional, so always jump raise def get_breakpoint_list(self): lines = gdb.execute("info breakpoints", to_string=True).splitlines() if lines[0] == "No breakpoints or watchpoints.": return [] if lines[0] == "No breakpoints, watchpoints, tracepoints, or catchpoints.": # gdb 15.x ~ return [] enable_idx = lines[0].index("Enb") addr_idx = lines[0].index("Address") bp_list = [] for line in lines[1:]: try: if line[0] == "\t": continue enable = line[enable_idx] addr = int(line[addr_idx:].split()[0], 16) if enable == "y": bp_list.append(addr) except Exception: pass # breakpoint with condition is unsupported return bp_list def exec_next(self): bp_list = self.get_breakpoint_list() EventHooking.gef_on_stop_unhook(EventHandler.hook_stop_handler) self.close_stdout_stderr() self.err = None prev_addr = -1 try: count = 0 while True: # progress if not self.args.print_insn and count % 100 == 0: self.force_write_stdout([b"\r|", b"\r/", b"\r-", b"\r\\"][count // 100 % 4]) # backup prev_prev_addr = prev_addr prev_addr = current_arch.pc # execute 1 instruction insn = get_insn() if self.args.use_ni or (self.args.skip_lib and "@plt>" in str(insn)): gdb.execute("ni") # use ni wrapper else: gdb.execute("si") # use si wrapper # check breakpoint insn = get_insn() if current_arch.pc in bp_list: break # $pc is not changed if prev_prev_addr == prev_addr == current_arch.pc: # for faster, repeat insn is skip # infinity self loop if current_arch.is_call(insn) or current_arch.is_jump(insn) or current_arch.is_ret(insn): self.err = "Detected infinity loop prev_addr ({:#x})".format(prev_addr) break # maybe rep prefix gdb.execute("xuntil") # recheck if prev_prev_addr == prev_addr == current_arch.pc: self.err = "Detected infinity loop prev_addr ({:#x})".format(prev_addr) break insn = get_insn() if self.args.print_insn: self.force_write_stdout((str(insn) + "\n").encode()) # found and break if self.is_target_insn(insn) and current_arch.pc not in self.args.exclude: if not self.args.print_insn: self.force_write_stdout(b"\r \r") break count += 1 except KeyboardInterrupt: pass except Exception: if is_alive(): exc_type, exc_value, exc_traceback = sys.exc_info() self.err = exc_value else: pass finally: self.revert_stdout_stderr() # anytime needed EventHooking.gef_on_stop_hook(EventHandler.hook_stop_handler) # anytime needed Cache.reset_gef_caches() if self.err: err(self.err) else: gdb.execute("context") return @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.usage() return @register_command class ExecUntilCallCommand(ExecUntilCommand): """Execute until call instruction.""" _cmdline_ = "exec-until call" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-call"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") _syntax_ = parser.format_help() _example_ = None def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): return current_arch.is_call(insn) @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.exec_next() return @register_command class ExecUntilJumpCommand(ExecUntilCommand): """Execute until jmp instruction.""" _cmdline_ = "exec-until jmp" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-jmp"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") group = parser.add_mutually_exclusive_group() group.add_argument("-t", "--only-taken", action="store_true", help="break only if jump will be taken.") group.add_argument("-T", "--only-not-taken", action="store_true", help="break only if jump will be not taken.") _syntax_ = parser.format_help() _example_ = None def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): return self.check_jump_taken(insn) @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.exec_next() return @register_command class ExecUntilIndirectBranchCommand(ExecUntilCommand): """Execute until indirect call/jmp instruction (x64/x86 only).""" _cmdline_ = "exec-until indirect-branch" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-indirect-branch"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") group = parser.add_mutually_exclusive_group() group.add_argument("-t", "--only-taken", action="store_true", help="break only if jump will be taken.") group.add_argument("-T", "--only-not-taken", action="store_true", help="break only if jump will be not taken.") _syntax_ = parser.format_help() _example_ = None def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): if current_arch.is_call(insn) or self.check_jump_taken(insn): if "[" in str(insn): return True for reg in current_arch.general_registers: if reg.replace("$", "") in str(insn): return True return False @parse_args @only_if_gdb_running @only_if_specific_arch(arch=("x86_32", "x86_64")) def do_invoke(self, args): self.exec_next() return @register_command class ExecUntilAllBranchCommand(ExecUntilCommand): """Execute until call/jump/ret instruction.""" _cmdline_ = "exec-until all-branch" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-all-branch"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") group = parser.add_mutually_exclusive_group() group.add_argument("-t", "--only-taken", action="store_true", help="break only if jump will be taken.") group.add_argument("-T", "--only-not-taken", action="store_true", help="break only if jump will be not taken.") _syntax_ = parser.format_help() _example_ = None def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): return current_arch.is_call(insn) or self.check_jump_taken(insn) or current_arch.is_ret(insn) @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.exec_next() return @register_command class ExecUntilSyscallCommand(ExecUntilCommand): """Execute until syscall instruction.""" _cmdline_ = "exec-until syscall" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-syscall"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-f", "--filter", action="append", default=[], help="filter by specified syscall.") parser.add_argument("-i", "--ignore", action="append", default=[], help="ignore specified syscall.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") _syntax_ = parser.format_help() _example_ = None def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): if current_arch.is_syscall(insn): if not self.args.filter and not self.args.ignore: return True _reg, nr = SyscallArgsCommand.get_nr() syscall_table = get_syscall_table() if syscall_table is None: return True # for debug if nr not in syscall_table.nr_table: return True # for debug syscall_name = syscall_table.nr_table[nr].name if self.args.ignore and syscall_name in self.args.ignore: return False if self.args.filter: return syscall_name in self.args.filter else: return True return False @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("wine",)) @require_arch_set def do_invoke(self, args): self.exec_next() return @register_command class ExecUntilRetCommand(ExecUntilCommand): """Execute until ret instruction.""" _cmdline_ = "exec-until ret" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-ret"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") _syntax_ = parser.format_help() _example_ = None def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): return current_arch.is_ret(insn) @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.exec_next() return @register_command class ExecUntilMemaccessCommand(ExecUntilCommand): """Execute until memory access instruction.""" _cmdline_ = "exec-until memaccess" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-mem"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") _syntax_ = parser.format_help() _example_ = None def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): return "[" in str(insn) @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.exec_next() return @register_command class ExecUntilKeywordReCommand(ExecUntilCommand): """Execute until specified keyword instruction.""" _cmdline_ = "exec-until keyword" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-keyword"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") parser.add_argument("keyword", metavar="KEYWORD", type=re.compile, nargs="+", help="filter by specified regex keyword.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "call +r[ab]x" # execute until specified keyword', '{0:s} "(push|pop) +(r[a-d]x|r[ds]i|r[sb]p)" # another example', '{0:s} "mov +rax, QWORD PTR \\\\[" # another example (need double escape)', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): for re_pattern in self.args.keyword: if re_pattern.search(str(insn)): return True return False @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): self.exec_next() return @register_command class ExecUntilCondCommand(ExecUntilCommand): """Execute until specified condition is filled.""" _cmdline_ = "exec-until cond" _category_ = "01-d. Debugging Support - Execution" _repeat_ = True _aliases_ = ["next-cond"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-I", "--print-insn", action="store_true", help="print each instruction during execution.") parser.add_argument("-n", "--use-ni", action="store_true", help="use `ni` instead of `si`.") parser.add_argument("-N", "--skip-lib", action="store_true", help="use `ni` instead of `si` if instruction is `call xxx@plt`.") parser.add_argument("-e", "--exclude", action="append", type=AddressUtil.parse_address, default=[], help="the address to exclude from breakpoints.") parser.add_argument("condition", metavar="CONDITION", help="filter by condition.") _syntax_ = parser.format_help() _example_ = [ '{0:s} "$rax==0xdead && $rbx==0xcafe" # execute until specified condition is filled', '{0:s} "*(int*)$rbx==0x12" # memory access is supported', '{0:s} "$ALL_REG==0x34" # compare with all regs. e.g., `($rax==0x34||$rbx==0x34||...)`', ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return def is_target_insn(self, insn): try: v = gdb.parse_and_eval(self.condition) except gdb.error: return False if v not in [0x0, 0x1]: self.err = "condition result should be True or False" return True return bool(v) @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): condition = args.condition if re.search(r"[^> '{:s}'".format(ptr1, addr1, path)) gef_print("{:s}: {:#x} -> [{:s}]".format(ptr2, addr2, ",".join(argv))) return False # continue @register_command class UsermodehelperTracerCommand(GenericCommand): """Collect and display information that is executed by call_usermodehelper_setup.""" _cmdline_ = "usermodehelper-tracer" _category_ = "06-i. Qemu-system/KGDB Cooperation - Linux Dynamic Inspection" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel def do_invoke(self, args): info("Resolving the function addresses") addr = Symbol.get_ksymaddr("call_usermodehelper_setup") if addr is None: err("Could not find call_usermodehelper_setup") return CallUsermodehelperSetupBreakpoint(addr) info("Setup is complete. Try `continue`") return class ThunkBreakpoint(gdb.Breakpoint): """Create a breakpoint to print caller address for thunk function.""" def __init__(self, loc, sym, reg, maps): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=False) self.loc = loc self.sym = sym self.reg = reg self.maps = maps self.seen = [] return def search_perm(self, target): for m in self.maps: addr, size, perm = m if addr <= target < addr + size: return perm.lower() return "?" def stop(self): try: return_address = gdb.selected_frame().older().pc() caller_address = Disasm.gdb_get_nth_previous_instruction_address(return_address, 1) target_address = get_register(self.reg) except gdb.error: return False # continue # duplicate, check if (caller_address, target_address) in self.seen: return False # continue else: self.seen.append((caller_address, target_address)) # get caller address, symbol caller_symbol = Symbol.get_symbol_string(caller_address, nosymbol_string=" ") # get callee address, symbol target_symbol = Symbol.get_symbol_string(target_address, nosymbol_string=" ") # print information if caller_address is None: info("{:s}{:s} -> {:#x} <{:s}> -> {:#x}{:s}".format( "???(unknown)", caller_symbol, self.loc, self.sym, target_address, target_symbol, )) else: info("{:#x}{:s} -> {:#x} <{:s}> -> {:#x}{:s}".format( caller_address, caller_symbol, self.loc, self.sym, target_address, target_symbol, )) # print preferred register condition pattern = [0] + [(x + 1) * y for x, y in itertools.product(range(0x100), [1, -1])] # [0, 1, -1, 2, -2, ...] for reg in current_arch.general_registers: reg_value = get_register(reg) for i in pattern: slide = current_arch.ptrsize * i reg_value_slided = reg_value + slide try: mem_value = read_int_from_memory(reg_value_slided) except (gdb.MemoryError, OverflowError): continue if mem_value != target_address: continue perm = self.search_perm(reg_value_slided) reg_value_slided_symbol = Symbol.get_symbol_string(reg_value_slided, nosymbol_string=" ") mem_value_symbol = Symbol.get_symbol_string(mem_value, nosymbol_string=" ") info(" {:s}{:+#x}: {:#x}{:s} [{:s}] -> {:#x}{:s}".format( reg, slide, reg_value_slided, reg_value_slided_symbol, perm, mem_value, mem_value_symbol, )) break return False # continue @register_command class ThunkTracerCommand(GenericCommand): """Collect and display the thunk addresses that are called automatically (x64/x86 only).""" _cmdline_ = "thunk-tracer" _category_ = "06-i. Qemu-system/KGDB Cooperation - Linux Dynamic Inspection" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("x86_32", "x86_64")) @only_if_in_kernel def do_invoke(self, args): info("Wait for memory scan") maps = Kernel.get_maps() # [vaddr, size, perm] info("Resolving thunk function addresses") for reg in current_arch.general_registers: if reg in ["$esp", "$rsp", "$eip", "$rip"]: continue sym = "__x86_indirect_thunk_{}".format(reg.replace("$", "")) addr = Symbol.get_ksymaddr(sym) if addr is None: continue gef_print(sym + ": ", end="") ThunkBreakpoint(addr, sym, reg, maps) info("Setup is complete, try `continue`") return class KmallocBreakpoint(gdb.Breakpoint): """Create a breakpoint to print information of kmalloc.""" def __init__(self, loc, sym, index_of_size_arg, option, extra): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=False) self.sym = sym self.index_of_size_arg = index_of_size_arg self.option = option self.extra = extra self.enabled = False return def check_nested(self, task_addr): for bp in gdb.breakpoints(): try: if bp.__class__.__name__ in ["KmallocRetBreakpoint"]: if bp.enabled and bp.task_addr == task_addr: return True except Exception: pass return False def stop(self): Cache.reset_gef_caches() # fast return if nested break task_addr, task_name = KmallocTracerCommand.get_task() if self.check_nested(task_addr): return False # filtering by task addr if self.option.target_task and task_addr != self.option.target_task: return False # filtering by task name if self.option.task_name and task_name not in self.option.task_name: return False # get size from arguments if self.index_of_size_arg >= 0: # e.g., kmalloc(size, ...) _, size = current_arch.get_ith_parameter(self.index_of_size_arg) else: # e.g., kmem_cache_alloc_node has no `size` argument _, kmem_cache = current_arch.get_ith_parameter(0) slab_cache_name_ptr = read_int_from_memory(kmem_cache + self.extra.kmem_cache_offset_name) slab_cache_name = read_cstring_from_memory(slab_cache_name_ptr) if not slab_cache_name.startswith("kmalloc-"): return False size = read_int32_from_memory(kmem_cache + self.extra.kmem_cache_offset_size) # Use gdb.Breakpoint instead of gdb.FinishBreakpoint because gdb.FinishBreakpoint is buggy ret_addr = gdb.newest_frame().older().pc() KmallocRetBreakpoint(ret_addr, self.sym, size, task_addr, self.option, self.extra) return False class KmallocRetBreakpoint(gdb.Breakpoint): """Create a breakpoint to print information of kmalloc.""" def __init__(self, loc, sym, size, task_addr, option, extra): # Note that specifying temporary=True does not remove the breakpoint if stop() returns False. super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=True) self.size = size self.sym = sym self.task_addr = task_addr self.option = option self.extra = extra KmallocTracerCommand.clear_disabled_breakpoints("KmallocRetBreakpoint") return def stop(self): Cache.reset_gef_caches() # check if another thread is detected task_addr, task_name = KmallocTracerCommand.get_task() if self.task_addr != task_addr: return False task_prefix = Color.boldify("[task:{:#018x} {:16s}]".format(task_addr, task_name)) allocated = AddressUtil.parse_address(current_arch.return_register) allocated_s = Color.colorify_hex(allocated, Config.get_gef_setting("theme.heap_chunk_address_used")) if self.extra: ret = KmallocTracerCommand.virt2name_and_size(allocated) if ret: # print more info name, chunk_size = ret if not name.startswith("kmalloc-"): self.enabled = False return False if self.option.filter and name not in self.option.filter: self.enabled = False return False name_s = Color.colorify(name, Config.get_gef_setting("theme.heap_chunk_label")) chunk_size_s = Color.colorify("{:<#6x}".format(chunk_size), Config.get_gef_setting("theme.heap_chunk_size")) gef_print("{:s} {:40s}: {:s} (size: {:s} name: {:s})".format( task_prefix, self.sym, allocated_s, chunk_size_s, name_s, )) KmallocTracerCommand.print_backtrace(self.option.backtrace) KmallocTracerCommand.dump_chunk(self.option.dump_chunk, allocated) self.enabled = False return False # fall through # print less info gef_print("{:s} {:40s}: {:s} (size: {:<#6x})".format(task_prefix, self.sym, allocated_s, self.size)) KmallocTracerCommand.print_backtrace(self.option.backtrace) KmallocTracerCommand.dump_chunk(self.option.dump_chunk, allocated) self.enabled = False return False class KfreeBreakpoint(gdb.Breakpoint): """Create a breakpoint to print information of kfree.""" def __init__(self, loc, sym, index_of_addr_arg, option, extra): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=False) self.sym = sym self.index_of_addr_arg = index_of_addr_arg self.option = option self.extra = extra self.enabled = False return def stop(self): Cache.reset_gef_caches() # check if NULL _, to_free = current_arch.get_ith_parameter(self.index_of_addr_arg) if not self.option.print_null and to_free == 0: return False # filtering by task addr task_addr, task_name = KmallocTracerCommand.get_task() if self.option.target_task and task_addr != self.option.target_task: return False # filtering by task name if self.option.task_name and task_name not in self.option.task_name: return False task_prefix = Color.boldify("[task:{:#018x} {:16s}]".format(task_addr, task_name)) to_free_s = Color.colorify_hex(to_free, Config.get_gef_setting("theme.heap_chunk_address_freed")) if self.extra: ret = KmallocTracerCommand.virt2name_and_size(to_free) if ret: # print more info name, chunk_size = ret if not name.startswith("kmalloc-"): return False if self.option.filter and name not in self.option.filter: return False name_s = Color.colorify(name, Config.get_gef_setting("theme.heap_chunk_label")) chunk_size_s = Color.colorify("{:<#6x}".format(chunk_size), Config.get_gef_setting("theme.heap_chunk_size")) gef_print("{:s} {:40s}: {:s} (size: {:s} name: {:s})".format( task_prefix, self.sym, to_free_s, chunk_size_s, name_s, )) KmallocTracerCommand.print_backtrace(self.option.backtrace) KmallocTracerCommand.dump_chunk(self.option.dump_chunk, to_free) return False # fall through # print less info gef_print("{:s} {:40s}: {:s}".format(task_prefix, self.sym, to_free_s)) KmallocTracerCommand.print_backtrace(self.option.backtrace) KmallocTracerCommand.dump_chunk(self.option.dump_chunk, to_free) return False class AllocPagesBreakpoint(gdb.Breakpoint): """Create a breakpoint to print information of __alloc_pages.""" def __init__(self, loc, sym, index_of_order_arg, option, extra): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=False) self.sym = sym self.index_of_order_arg = index_of_order_arg self.option = option self.extra = extra self.enabled = False return def check_nested(self, task_addr): for bp in gdb.breakpoints(): try: if bp.__class__.__name__ in ["AllocPagesRetBreakpoint"]: if bp.enabled and bp.task_addr == task_addr: return True except Exception: pass return False def stop(self): Cache.reset_gef_caches() # fast return if nested break task_addr, task_name = KmallocTracerCommand.get_task() if self.check_nested(task_addr): return False # filtering by task addr if self.option.target_task and task_addr != self.option.target_task: return False # filtering by task name if self.option.task_name and task_name not in self.option.task_name: return False # get order from arguments _, order = current_arch.get_ith_parameter(self.index_of_order_arg) # Use gdb.Breakpoint instead of gdb.FinishBreakpoint because gdb.FinishBreakpoint is buggy ret_addr = gdb.newest_frame().older().pc() AllocPagesRetBreakpoint(ret_addr, self.sym, order, task_addr, self.option, self.extra) return False class AllocPagesRetBreakpoint(gdb.Breakpoint): """Create a breakpoint to print information of __alloc_pages.""" def __init__(self, loc, sym, order, task_addr, option, extra): # Note that specifying temporary=True does not remove the breakpoint if stop() returns False. super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=True) self.order = order self.sym = sym self.task_addr = task_addr self.option = option self.extra = extra KmallocTracerCommand.clear_disabled_breakpoints("AllocPagesRetBreakpoint") return def stop(self): Cache.reset_gef_caches() # check if another thread is detected task_addr, task_name = KmallocTracerCommand.get_task() if self.task_addr != task_addr: return False task_prefix = Color.boldify("[task:{:#018x} {:16s}]".format(task_addr, task_name)) allocated_page = AddressUtil.parse_address(current_arch.return_register) if not is_valid_addr(allocated_page): allocated_virt = 0 else: allocated_virt = Kernel.page2virt(allocated_page) if allocated_virt is None: allocated_virt = 0 allocated_virt_s = Color.colorify_hex( allocated_virt, Config.get_gef_setting("theme.heap_page_address"), ) size = KernelAddressHeuristicFinder.consts().PAGE_SIZE * (2 ** self.order) # print less info gef_print("{:s} {:40s}: {:s} (page: {:#x}, order: {:d}, size: {:#x})".format( task_prefix, self.sym, allocated_virt_s, allocated_page, self.order, size, )) KmallocTracerCommand.print_backtrace(self.option.backtrace) KmallocTracerCommand.dump_chunk(self.option.dump_chunk, allocated_virt) self.enabled = False return False class FreePagesBreakpoint(gdb.Breakpoint): """Create a breakpoint to print information of __free_pages.""" def __init__(self, loc, sym, index_of_addr_arg, index_of_order_arg, option, extra): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=False) self.sym = sym self.index_of_addr_arg = index_of_addr_arg self.index_of_order_arg = index_of_order_arg self.option = option self.extra = extra self.enabled = False return def stop(self): Cache.reset_gef_caches() _, to_free_page = current_arch.get_ith_parameter(self.index_of_addr_arg) _, order = current_arch.get_ith_parameter(self.index_of_order_arg) # filtering by task addr task_addr, task_name = KmallocTracerCommand.get_task() if self.option.target_task and task_addr != self.option.target_task: return False # filtering by task name if self.option.task_name and task_name not in self.option.task_name: return False task_prefix = Color.boldify("[task:{:#018x} {:16s}]".format(task_addr, task_name)) if not is_valid_addr(to_free_page): to_free_virt = 0 else: to_free_virt = Kernel.page2virt(to_free_page) if to_free_virt is None: to_free_virt = 0 to_free_virt_s = Color.colorify_hex( to_free_virt, Config.get_gef_setting("theme.heap_page_address") ) size = KernelAddressHeuristicFinder.consts().PAGE_SIZE * (2 ** order) # print less info gef_print("{:s} {:40s}: {:s} (page: {:#x}, order: {:d}, size: {:#x})".format( task_prefix, self.sym, to_free_virt_s, to_free_page, order, size, )) KmallocTracerCommand.print_backtrace(self.option.backtrace) KmallocTracerCommand.dump_chunk(self.option.dump_chunk, to_free_virt) return False @register_command class KmallocTracerCommand(GenericCommand): """Collect and display information when kmalloc/kfree.""" _cmdline_ = "kmalloc-tracer" _category_ = "06-i. Qemu-system/KGDB Cooperation - Linux Dynamic Inspection" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--filter", action="append", default=[], help="filter specified slab name (e.g., kmalloc-XX)") parser.add_argument("-T", "--task-name", action="append", default=[], help="filter specified task name (e.g., sh)") parser.add_argument("-N", "--print-null", action="store_true", help="display free(NULL).") parser.add_argument("-t", "--backtrace", action="store_true", help="display backtrace.") parser.add_argument("-d", "--dump-chunk", action="store_true", help="dump the first 0x40 bytes of each chunk.") parser.add_argument("-p", "--enable-page-allocator-trace", action="store_true", help="in addition to kmalloc and kfree, it also monitors __alloc_pages and __free_pages.") parser.add_argument("-v", "--verbose", action="store_true", help="print meta information.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # simple output", "{0:s} -dtv # useful output", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Disable `-enable-kvm` option for qemu-system (#PF may occur).", "Append `tsc=unstable` option for kernel cmdline.", "Tracing `kmem_cache_alloc` type is not supported.", "This command requires CONFIG_RANDSTRUCT=n.", ] _note_ = "\n".join(_note_) @staticmethod def create_option_info(args, target_task=None): dic = { "print_null": args.print_null, "backtrace": args.backtrace, "filter": args.filter, "dump_chunk": args.dump_chunk, "task_name": getattr(args, "task_name", []), "target_task": target_task, } OptionInfo = collections.namedtuple("OptionInfo", dic.keys()) option_info = OptionInfo(*dic.values()) return option_info @staticmethod def clear_disabled_breakpoints(name, force=False): for bp in gdb.breakpoints(): try: if bp.__class__.__name__ != name: continue if force is False and bp.enabled: continue bp.delete() except Exception: pass return @staticmethod def initialize(allocator, verbose): if allocator != "SLUB": # Do nothing other than SLUB. return None res = gdb.execute("slub-dump --meta", to_string=True) r = re.search(r"offsetof\((?:page|slab), slab_cache\): (0x\S+)", res) if not r: return False page_offset_slab_cache = int(r.group(1), 16) if verbose: info("offsetof({:s}, slab_cache): {:#x}".format(Kernel.slab_page_str(), page_offset_slab_cache)) r = re.search(r"offsetof\(kmem_cache, name\): (0x\S+)", res) if not r: return False kmem_cache_offset_name = int(r.group(1), 16) if verbose: info("offsetof(kmem_cache, name): {:#x}".format(kmem_cache_offset_name)) r = re.search(r"offsetof\(kmem_cache, size\): (0x\S+)", res) if not r: return False kmem_cache_offset_size = int(r.group(1), 16) if verbose: info("offsetof(kmem_cache, size): {:#x}".format(kmem_cache_offset_size)) # create extra_info dic = { "kmem_cache_offset_name": kmem_cache_offset_name, "kmem_cache_offset_size": kmem_cache_offset_size, "page_offset_slab_cache": page_offset_slab_cache, } ExtraInfo = collections.namedtuple("ExtraInfo", dic.keys()) extra_info = ExtraInfo(*dic.values()) return extra_info @staticmethod def get_task(): th_num = gdb.selected_thread().num res = gdb.execute("kcurrent --quiet", to_string=True) r = re.search(r"current \(cpu{:d}\): (0x\S+) (.*)".format(th_num - 1), res) if r: task = int(r.group(1), 16) name = r.group(2) return task, name return 0, "" @staticmethod def virt2name_and_size(vaddr): ret = Kernel.get_slab_contains(vaddr) if not ret: return None r = re.search(r"name: (\S+) object_size: (\S+)", ret) if not r: return None slab_cache_name = r.group(1) slab_cache_size = int(r.group(2), 16) return slab_cache_name, slab_cache_size @staticmethod def print_backtrace(backtrace): if not backtrace: return try: frame = gdb.newest_frame() while frame and frame.is_valid(): addr = frame.pc() if not is_valid_addr(addr): break sym = Symbol.get_symbol_string(addr, nosymbol_string=" ") gef_print(" {:#018x}{:s}".format(addr, sym)) frame = frame.older() except gdb.error: return @staticmethod def dump_chunk(dump, loc): if not dump: return if not is_valid_addr(loc): err("Invalid address") return gdb.execute("dereference -n {:#x} 8".format(loc)) return @staticmethod def set_bp_to_kmalloc_kfree(option_info, extra_info): # `kmalloc` is always inlined and not exported, so its symbol cannot be identified. # Therefore, you must set a breakpoint in a function that is exported instead of kmalloc. # This can be done by checking EXPORT_SYMBOL, and a tool to automate this is dev/update-kmalloc-tracer. # The same symbol may be found multiple times from different *.c files, and they can be treated as the same. """ [3.0~3.15] EXPORT_SYMBOL(__kmalloc); EXPORT_SYMBOL(__kmalloc_node); EXPORT_SYMBOL(__kmalloc_node_track_caller); EXPORT_SYMBOL(__kmalloc_track_caller); EXPORT_SYMBOL(__krealloc); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kmalloc_order_trace); EXPORT_SYMBOL(kmem_cache_alloc); EXPORT_SYMBOL(kmem_cache_alloc_node); EXPORT_SYMBOL(kmem_cache_alloc_node_trace); EXPORT_SYMBOL(kmem_cache_alloc_trace); EXPORT_SYMBOL(krealloc); [3.16~5.5] EXPORT_SYMBOL(__kmalloc); EXPORT_SYMBOL(__kmalloc_node); EXPORT_SYMBOL(__kmalloc_node_track_caller); EXPORT_SYMBOL(__kmalloc_track_caller); EXPORT_SYMBOL(__krealloc); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kmalloc_order); EXPORT_SYMBOL(kmalloc_order_trace); EXPORT_SYMBOL(kmem_cache_alloc); EXPORT_SYMBOL(kmem_cache_alloc_node); EXPORT_SYMBOL(kmem_cache_alloc_node_trace); EXPORT_SYMBOL(kmem_cache_alloc_trace); EXPORT_SYMBOL(krealloc); [5.6~5.17] EXPORT_SYMBOL(__kmalloc); EXPORT_SYMBOL(__kmalloc_node); EXPORT_SYMBOL(__kmalloc_node_track_caller); EXPORT_SYMBOL(__kmalloc_track_caller); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kmalloc_order); EXPORT_SYMBOL(kmalloc_order_trace); EXPORT_SYMBOL(kmem_cache_alloc); EXPORT_SYMBOL(kmem_cache_alloc_node); EXPORT_SYMBOL(kmem_cache_alloc_node_trace); EXPORT_SYMBOL(kmem_cache_alloc_trace); EXPORT_SYMBOL(krealloc); [5.18~6.0] EXPORT_SYMBOL(__kmalloc); EXPORT_SYMBOL(__kmalloc_node); EXPORT_SYMBOL(__kmalloc_node_track_caller); EXPORT_SYMBOL(__kmalloc_track_caller); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kmalloc_order); EXPORT_SYMBOL(kmalloc_order_trace); EXPORT_SYMBOL(kmem_cache_alloc); EXPORT_SYMBOL(kmem_cache_alloc_lru); EXPORT_SYMBOL(kmem_cache_alloc_node); EXPORT_SYMBOL(kmem_cache_alloc_node_trace); EXPORT_SYMBOL(kmem_cache_alloc_trace); EXPORT_SYMBOL(krealloc); [6.1~6.9] EXPORT_SYMBOL(__kmalloc); EXPORT_SYMBOL(__kmalloc_node); EXPORT_SYMBOL(__kmalloc_node_track_caller); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kmalloc_large); EXPORT_SYMBOL(kmalloc_large_node); EXPORT_SYMBOL(kmalloc_node_trace); EXPORT_SYMBOL(kmalloc_trace); EXPORT_SYMBOL(kmem_cache_alloc); EXPORT_SYMBOL(kmem_cache_alloc_lru); EXPORT_SYMBOL(kmem_cache_alloc_node); EXPORT_SYMBOL(krealloc); [6.10] EXPORT_SYMBOL(__kmalloc_node_noprof); EXPORT_SYMBOL(__kmalloc_noprof); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kmalloc_large_node_noprof); EXPORT_SYMBOL(kmalloc_large_noprof); EXPORT_SYMBOL(kmalloc_node_trace_noprof); EXPORT_SYMBOL(kmalloc_node_track_caller_noprof); EXPORT_SYMBOL(kmalloc_trace_noprof); EXPORT_SYMBOL(kmem_cache_alloc_lru_noprof); EXPORT_SYMBOL(kmem_cache_alloc_node_noprof); EXPORT_SYMBOL(kmem_cache_alloc_noprof); EXPORT_SYMBOL(krealloc_noprof); [6.11~6.17] EXPORT_SYMBOL(__kmalloc_cache_node_noprof); EXPORT_SYMBOL(__kmalloc_cache_noprof); EXPORT_SYMBOL(__kmalloc_large_node_noprof); EXPORT_SYMBOL(__kmalloc_large_noprof); EXPORT_SYMBOL(__kmalloc_node_noprof); EXPORT_SYMBOL(__kmalloc_node_track_caller_noprof); EXPORT_SYMBOL(__kmalloc_noprof); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kmem_cache_alloc_lru_noprof); EXPORT_SYMBOL(kmem_cache_alloc_node_noprof); EXPORT_SYMBOL(kmem_cache_alloc_noprof); EXPORT_SYMBOL(krealloc_noprof); [6.18~] EXPORT_SYMBOL(__kmalloc_cache_node_noprof); EXPORT_SYMBOL(__kmalloc_cache_noprof); EXPORT_SYMBOL(__kmalloc_large_node_noprof); EXPORT_SYMBOL(__kmalloc_large_noprof); EXPORT_SYMBOL(__kmalloc_node_noprof); EXPORT_SYMBOL(__kmalloc_node_track_caller_noprof); EXPORT_SYMBOL(__kmalloc_noprof); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kmem_cache_alloc_lru_noprof); EXPORT_SYMBOL(kmem_cache_alloc_node_noprof); EXPORT_SYMBOL(kmem_cache_alloc_noprof); EXPORT_SYMBOL(krealloc_node_align_noprof); EXPORT_SYMBOL_GPL(kfree_nolock); EXPORT_SYMBOL_GPL(kmalloc_nolock_noprof); """ # This list may be incomplete. # If you know of any memory-allocating functions that may be freed with kfree, please let me know. # The number is the argument index of the size. -1 means index 0 is `struct kmem_cache*`. kversion = Kernel.kernel_version() if kversion < "3.16": kmalloc_syms = [ ["__kmalloc", 0], ["__kmalloc_node", 0], ["__kmalloc_node_track_caller", 0], ["__kmalloc_track_caller", 0], ["__krealloc", 1], ["kmalloc_order_trace", 0], ["kmem_cache_alloc", -1], ["kmem_cache_alloc_node", -1], ["kmem_cache_alloc_node_trace", 3], ["kmem_cache_alloc_trace", 2], ["krealloc", 1], ] elif kversion < "5.6": kmalloc_syms = [ ["__kmalloc", 0], ["__kmalloc_node", 0], ["__kmalloc_node_track_caller", 0], ["__kmalloc_track_caller", 0], ["__krealloc", 1], ["kmalloc_order", 0], ["kmalloc_order_trace", 0], ["kmem_cache_alloc", -1], ["kmem_cache_alloc_node", -1], ["kmem_cache_alloc_node_trace", 3], ["kmem_cache_alloc_trace", 2], ["krealloc", 1], ] elif kversion < "5.18": kmalloc_syms = [ ["__kmalloc", 0], ["__kmalloc_node", 0], ["__kmalloc_node_track_caller", 0], ["__kmalloc_track_caller", 0], ["kmalloc_order", 0], ["kmalloc_order_trace", 0], ["kmem_cache_alloc", -1], ["kmem_cache_alloc_node", -1], ["kmem_cache_alloc_node_trace", 3], ["kmem_cache_alloc_trace", 2], ["krealloc", 1], ] elif kversion < "6.1": kmalloc_syms = [ ["__kmalloc", 0], ["__kmalloc_node", 0], ["__kmalloc_node_track_caller", 0], ["__kmalloc_track_caller", 0], ["kmalloc_order", 0], ["kmalloc_order_trace", 0], ["kmem_cache_alloc", -1], ["kmem_cache_alloc_lru", -1], ["kmem_cache_alloc_node", -1], ["kmem_cache_alloc_node_trace", 3], ["kmem_cache_alloc_trace", 2], ["krealloc", 1], ] elif kversion < "6.10": kmalloc_syms = [ ["__kmalloc", 0], ["__kmalloc_node", 0], ["__kmalloc_node_track_caller", 0], ["kmalloc_large", 0], ["kmalloc_large_node", 0], ["kmalloc_node_trace", 3], ["kmalloc_trace", 2], ["kmem_cache_alloc", -1], ["kmem_cache_alloc_lru", -1], ["kmem_cache_alloc_node", -1], ["krealloc", 1], ] elif kversion < "6.11": kmalloc_syms = [ ["__kmalloc_node_noprof", 0], ["__kmalloc_noprof", 0], ["kmalloc_large_node_noprof", 0], ["kmalloc_large_noprof", 0], ["kmalloc_node_trace_noprof", 3], ["kmalloc_node_track_caller_noprof", 0], ["kmalloc_trace_noprof", 2], ["kmem_cache_alloc_lru_noprof", -1], ["kmem_cache_alloc_node_noprof", -1], ["kmem_cache_alloc_noprof", -1], ["krealloc_noprof", 1], ] elif kversion < "6.18": kmalloc_syms = [ ["__kmalloc_cache_node_noprof", 3], ["__kmalloc_cache_noprof", 2], ["__kmalloc_large_node_noprof", 0], ["__kmalloc_large_noprof", 0], ["__kmalloc_node_noprof", 0], ["__kmalloc_node_track_caller_noprof", 0], ["__kmalloc_noprof", 0], ["kmem_cache_alloc_lru_noprof", -1], ["kmem_cache_alloc_node_noprof", -1], ["kmem_cache_alloc_noprof", -1], ["krealloc_noprof", 1], ] else: kmalloc_syms = [ ["__kmalloc_cache_node_noprof", 3], ["__kmalloc_cache_noprof", 2], ["__kmalloc_large_node_noprof", 0], ["__kmalloc_large_noprof", 0], ["__kmalloc_node_noprof", 0], ["__kmalloc_node_track_caller_noprof", 0], ["__kmalloc_noprof", 0], ["kmem_cache_alloc_lru_noprof", -1], ["kmem_cache_alloc_node_noprof", -1], ["kmem_cache_alloc_noprof", -1], ["krealloc_node_align_noprof", 1], ["kmalloc_nolock_noprof", 0], ] if kversion < "6.18": kfree_syms = [ ["kfree", 0], ] else: kfree_syms = [ ["kfree", 0], ["kfree_nolock", 0], ] breakpoints = [] for sym, index_of_size_arg in kmalloc_syms: func_addr = Symbol.get_ksymaddr(sym) if func_addr: gef_print(sym + ": ", end="") bp = KmallocBreakpoint(func_addr, sym, index_of_size_arg, option_info, extra_info) breakpoints.append(bp) for sym, index_of_addr_arg in kfree_syms: func_addr = Symbol.get_ksymaddr(sym) if func_addr: gef_print(sym + ": ", end="") bp = KfreeBreakpoint(func_addr, sym, index_of_addr_arg, option_info, extra_info) breakpoints.append(bp) return breakpoints @staticmethod def set_bp_to_alloc_free_pages(option_info, extra_info): """ [3.0~5.12] EXPORT_SYMBOL(__alloc_pages_nodemask); EXPORT_SYMBOL(__free_pages); [5.13~6.9] EXPORT_SYMBOL(__alloc_pages); EXPORT_SYMBOL(__free_pages); [6.10~6.13] EXPORT_SYMBOL(__alloc_pages_noprof); EXPORT_SYMBOL(__free_pages); [6.14~] EXPORT_SYMBOL(__alloc_frozen_pages_noprof); EXPORT_SYMBOL(__free_pages); """ # This list may be incomplete. # If you know of any memory-allocating functions that may be freed with kfree, please let me know. # The number is the argument index of the order. kversion = Kernel.kernel_version() if kversion < "5.13": alloc_pages_sym = [ ["__alloc_pages_nodemask", 1], ] elif kversion < "6.10": alloc_pages_sym = [ ["__alloc_pages", 1], ] elif kversion < "6.14": alloc_pages_sym = [ ["__alloc_pages_noprof", 1], ] else: alloc_pages_sym = [ ["__alloc_frozen_pages_noprof", 1], ] free_pages_sym = [ ["__free_pages", 0, 1], ] breakpoints = [] for sym, index_of_order_arg in alloc_pages_sym: func_addr = Symbol.get_ksymaddr(sym) if func_addr: gef_print(sym + ": ", end="") bp = AllocPagesBreakpoint(func_addr, sym, index_of_order_arg, option_info, extra_info) breakpoints.append(bp) for sym, index_of_addr_arg, index_of_order_arg in free_pages_sym: func_addr = Symbol.get_ksymaddr(sym) if func_addr: gef_print(sym + ": ", end="") bp = FreePagesBreakpoint(func_addr, sym, index_of_addr_arg, index_of_order_arg, option_info, extra_info) breakpoints.append(bp) return breakpoints @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel @only_if_kvm_disabled def do_invoke(self, args): info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion < "3.0": err("Unsupported before v3.0") return allocator = Kernel.get_slab_type() if allocator == "Unknown": err("Unsupported: Unknown allocator") return if allocator != "SLUB": warn("Unsupported viewing detailed information for SLAB, SLOB, SLUB_TINY") # fall through # initialize if not hasattr(self, "initialized"): ret = KmallocTracerCommand.initialize(allocator, args.verbose) if ret is False: err("Failed to initialize") return self.initialized = True self.extra_info = ret # allow None else: if args.verbose and self.extra_info: info("offsetof({:s}, slab_cache): {:#x}".format(Kernel.slab_page_str(), self.extra_info.page_offset_slab_cache)) info("offsetof(kmem_cache, name): {:#x}".format(self.extra_info.kmem_cache_offset_name)) info("offsetof(kmem_cache, size): {:#x}".format(self.extra_info.kmem_cache_offset_size)) # create option_info option_info = KmallocTracerCommand.create_option_info(args) # set breakpoints breakpoints = KmallocTracerCommand.set_bp_to_kmalloc_kfree(option_info, self.extra_info) if args.enable_page_allocator_trace: breakpoints += KmallocTracerCommand.set_bp_to_alloc_free_pages(option_info, self.extra_info) for bp in breakpoints: bp.enabled = True # doit info("Setup is complete. continuing...") gdb.execute("continue") # clean up info("kmalloc-tracer is complete, cleaning up...") for bp in breakpoints: bp.delete() KmallocTracerCommand.clear_disabled_breakpoints("KmallocRetBreakpoint", force=True) if args.enable_page_allocator_trace: KmallocTracerCommand.clear_disabled_breakpoints("AllocPagesRetBreakpoint", force=True) return class KmallocAllocatedBy_UserlandHardwareBreakpoint(gdb.Breakpoint): """Breakpoint to userland `sleep` process for KmallocAllocatedByCommand.""" def __init__(self, loc): super().__init__("*{:#x}".format(loc), gdb.BP_HARDWARE_BREAKPOINT, internal=False) self.silent = True return def stop(self): return True # stop @register_command class KmallocAllocatedByCommand(GenericCommand): """Call predefined system-calls and print kmalloc-N chunks allocated and freed (x64 only).""" _cmdline_ = "kmalloc-allocated-by" _category_ = "06-i. Qemu-system/KGDB Cooperation - Linux Dynamic Inspection" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--filter", action="append", default=[], help="filter specified name (e.g., kmalloc-XX)") parser.add_argument("-N", "--print-null", action="store_true", help="display free(NULL).") parser.add_argument("-t", "--backtrace", action="store_true", help="display backtrace.") parser.add_argument("-d", "--dump-chunk", action="store_true", help="dump the first 0x40 bytes of each chunk.") parser.add_argument("-v", "--verbose", action="store_true", help="print meta information.") _syntax_ = parser.format_help() _example_ = [ "{0:s} # simple output", "{0:s} -dtv # useful output", ] _example_ = "\n".join(_example_).format(_cmdline_) _note_ = [ "Disable `-enable-kvm` option for qemu-system (#PF may occur).", "Disable `-smp N` option for qemu-system (write memory error may occur).", "Append `tsc=unstable` option for kernel cmdline.", "This command requires CONFIG_RANDSTRUCT=n.", ] _note_ = "\n".join(_note_) def setup_syscall(self, syscall_name, args): gdb.execute("set $pc-={:#x}".format(len(current_arch.syscall_insn)), to_string=True) nr = self.syscall_table[syscall_name] gdb.execute("set $rax={:#x}".format(nr), to_string=True) sp = current_arch.sp for reg, arg in zip(current_arch.syscall_parameters, args): if arg is None: break if isinstance(arg, str): arg = String.str2bytes(arg) if isinstance(arg, bytes): write_memory(sp, arg) gdb.execute("set {:s}={:#x}".format(reg, sp), to_string=True) sp = align(sp + len(arg), current_arch.ptrsize * 2) else: gdb.execute("set {:s}={:#x}".format(reg, arg), to_string=True) self.tested_syscall.add(syscall_name) return def dump_untested_syscall(self): valid_syscall = [] for line in self.syscall_table_view_ret.splitlines(): tag, _, valid, name, *__ = Color.remove_color(line).split() if tag != "x86_64": continue if valid != "valid": continue valid_syscall.append(name) invalid_syscall = [] tested_syscall = [] untested_syscall = [] skipped_syscall = [] # sort by index and translate from set to list for name, _nr in self.syscall_table.items(): if name not in valid_syscall: invalid_syscall.append(name) elif name in self.tested_syscall: tested_syscall.append(name) elif name in self.scheduled_syscall: skipped_syscall.append(name) elif name in self.skipped_syscall: skipped_syscall.append(name) else: untested_syscall.append(name) gef_print(titlify("Tested syscall")) gef_print(", ".join(tested_syscall)) gef_print(titlify("Untested syscall")) gef_print(", ".join(untested_syscall)) gef_print(titlify("Skipped syscall")) gef_print(", ".join(skipped_syscall)) gef_print(titlify("Invalid (Unsupported) syscall")) gef_print(", ".join(invalid_syscall)) return def test_syscall(self, breakpoints): def u2i(x): x = struct.pack(" msgsnd -> msgrcv -> msgctl" yield ("msqid = msgget(IPC_PRIVATE, IPC_CREAT|0666)", "msgget", [0, 0o1000 | 0o666]) msgsize = 0x100 msg = p64(1) + b"A" * msgsize if u2i(ret_history[-1]) >= 0: msqid = ret_history[-1] yield ("msgsnd(msqid, &msg, msgsize, 0)", "msgsnd", [msqid, msg, msgsize, 0]) yield ("msgrcv(msqid, &msg, msgsize, 0, 0)", "msgrcv", [msqid, msg, msgsize, 0, 0]) yield ("msgctl(msqid, IPC_RMID, 0)", "msgctl", [msqid, 0, 0]) yield "shmget -> shmat -> shmdt -> shmctl" yield ("shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT|0666)", "shmget", [0, 0x400, 0o1000 | 0o666]) if u2i(ret_history[-1]) >= 0: shmid = ret_history[-1] yield ("addr = shmat(shmid, NULL, 0)", "shmat", [shmid, 0, 0]) addr = ret_history[-1] yield ("shmdt(addr)", "shmdt", [addr]) yield ("shmctl(shmid, IPC_RMID, 0)", "shmctl", [shmid, 0, 0]) yield "semget -> semop -> semctl" yield ("semid = semget(IPC_PRIVATE, 1, IPC_CREAT|0666)", "semget", [0, 1, 0o1000 | 0o666]) if u2i(ret_history[-1]) >= 0: semid = ret_history[-1] sembuf = p16(0) # sem_num sembuf += p16(2) # sem_op sembuf += p16(0) # sem_flg yield ("semop(semid, &sembuf, 1)", "semop", [semid, sembuf, 1]) self.skipped_syscall.add("semtimedop") yield ("semctl(semid, 0, IPC_RMID)", "semctl", [semid, 0, 0]) yield "mq_open -> mq_timedsend -> mq_timedreceive -> mq_notify -> mq_getsetattr -> mq_unlink -> close" MQ_NAME = "mq_test\0" attr = p64(0o4000) # mq_flags: O_NONBLOCK attr += p64(10) # mq_maxmsg attr += p64(0x100) # mq_msgsize attr += p64(0) # mq_curmsgs attr += p64(0) * 4 # __reserved[4] yield ( 'fd = mq_open("mq_test", O_RDWR|O_CREAT, 0700, &attr)', "mq_open", [MQ_NAME, 0o2 | 0o100, 0o700, attr], ) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] msg = "A" * 0x100 yield ("mq_timedsend(fd, &msg, sizeof(msg), 0, NULL)", "mq_timedsend", [fd, msg, len(msg), 0, 0]) buf = "\0" * 0x100 prio = p32(0) timeout = p64(0) # tv_sec timeout += p64(0) # tv_nsec yield ( "mq_timedreceive(fd, &buf, sizeof(buf), &prio, &timeout)", "mq_timedreceive", [fd, buf, len(buf), prio, timeout], ) sigevent = p32(0) # sigev_notify: SIGEV_SIGNAL sigevent += p32(10) # sigev_signo: SIGUSR1 sigevent += p64(0) # sigev_value sigevent += p64(0) # sigev_notify_function sigevent += p64(0) # sigev_notify_attributes sigevent += p64(0) # sigev_notify_thread_id yield ("mq_notify(fd, &sigevent)", "mq_notify", [fd, sigevent]) attr = p64(0) # mq_flags attr += p64(0) # mq_maxmsg attr += p64(0) # mq_msgsize attr += p64(0) # mq_curmsgs attr += p64(0) * 4 # __reserved[4] yield ("mq_getsetattr(fd, NULL, &attr)", "mq_getsetattr", [fd, 0, attr]) yield ('mq_unlink("mq_test")', "mq_unlink", [MQ_NAME]) yield ("close(fd)", "close", [fd]) yield "signalfd4 -> close" mask = "\0" * 8 yield ("fd = signalfd4(-1, &mask, sizeof(mask), 0)", "signalfd4", [-1, mask, len(mask), 0]) self.skipped_syscall.add("signalfd") if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) yield "setitimer -> getitimer" new_value = p64(0) # it_interval.tv_sec new_value += p64(0) # it_interval.tv_nsec new_value += p64(0) # it_value.tv_sec new_value += p64(0) # it_value.tv_nsec old_value = p64(0) # it_interval.tv_sec old_value += p64(0) # it_interval.tv_nsec old_value += p64(0) # it_value.tv_sec old_value += p64(0) # it_value.tv_nsec yield ("setitimer(ITIMER_REAL, &new_value, &old_value)", "setitimer", [0, new_value, old_value]) curr_value = p64(0) # it_interval.tv_sec curr_value += p64(0) # it_interval.tv_nsec curr_value += p64(0) # it_value.tv_sec curr_value += p64(0) # it_value.tv_nsec yield ("getitimer(ITIMER_REAL, &curr_value)", "getitimer", [0, curr_value]) yield "timerfd_create -> timerfd_settime -> timerfd_gettime -> close" yield ("fd = timerfd_create(CLOCK_MONOTONIC, 0)", "timerfd_create", [1, 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] new_value = p64(0) # it_interval.tv_sec new_value += p64(0) # it_interval.tv_nsec new_value += p64(0) # it_value.tv_sec new_value += p64(0) # it_value.tv_nsec old_value = p64(0) # it_interval.tv_sec old_value += p64(0) # it_interval.tv_nsec old_value += p64(0) # it_value.tv_sec old_value += p64(0) # it_value.tv_nsec yield ( "timerfd_settime(fd, 0, &new_value, &old_value)", "timerfd_settime", [fd, 0, new_value, old_value], ) curr_value = p64(0) # it_interval.tv_sec curr_value += p64(0) # it_interval.tv_nsec curr_value += p64(0) # it_value.tv_sec curr_value += p64(0) # it_value.tv_nsec yield ("timerfd_gettime(fd, &curr_value)", "timerfd_gettime", [fd, curr_value]) yield ("close(fd)", "close", [fd]) yield "timer_create -> timer_settime -> timier_gettime -> timer_getoverrun -> timer_delete" timerid = p64(0) yield ("timer_create(CLOCK_MONOTONIC, NULL, &timerid)", "timer_create", [1, 0, timerid]) if u2i(ret_history[-1]) == 0: timerid = read_int_from_memory(current_arch.sp) new_value = p64(0) # it_interval.tv_sec new_value += p64(0) # it_interval.tv_nsec new_value += p64(0) # it_value.tv_sec new_value += p64(0) # it_value.tv_nsec old_value = p64(0) # it_interval.tv_sec old_value += p64(0) # it_interval.tv_nsec old_value += p64(0) # it_value.tv_sec old_value += p64(0) # it_value.tv_nsec yield ( "timer_settime(timerid, 0, &new_value, &old_value)", "timer_settime", [timerid, 0, new_value, old_value], ) curr_value = p64(0) # it_interval.tv_sec curr_value += p64(0) # it_interval.tv_nsec curr_value += p64(0) # it_value.tv_sec curr_value += p64(0) # it_value.tv_nsec yield ("timer_gettime(timerid, &curr_value)", "timer_gettime", [timerid, curr_value]) yield ("timer_getoverrun(timerid)", "timer_getoverrun", [timerid]) yield ("timer_delete(timerid)", "timer_delete", [timerid]) yield "epoll_create1 -> epoll_ctl -> epoll_wait -> close" yield ("epfd = epoll_create1(0)", "epoll_create1", [0]) self.skipped_syscall.add("epoll_create") if u2i(ret_history[-1]) >= 0: epfd = ret_history[-1] event = p32(1) # events: EPOLLIN event += p64(0) # data yield ("epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &event)", "epoll_ctl", [epfd, 1, 0, event]) yield ("epoll_wait(epfd, &events, 1, 0)", "epoll_wait", [epfd, event, 1, 0]) self.skipped_syscall.add("epoll_pwait") self.skipped_syscall.add("epoll_pwait2") yield ("close(epfd)", "close", [epfd]) yield "eventfd2 -> close" yield ("fd = eventfd2(0, 0)", "eventfd2", [0, 0]) self.skipped_syscall.add("eventfd") if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) yield "perf_event_open (need kernel.perf_event_paranoid <= 2) -> close" attr = p32(1) # type: PERF_TYPE_SOFTWARE attr += p32(0x80) # size: sizeof(attr) attr += p64(9) # config: PERF_COUNT_SW_DUMMY attr += p64(0) # sample_period or sample_freq attr += p64(0) # sample_type attr += p64(0) # read_format flags = 1 << 5 # execlude_kernel=1 flags |= 1 << 6 # execlude_hv=1 flags |= 1 << 8 # mmap=1 flags |= 1 << 17 # mmap_data=1 attr += p64(flags) # flags attr += p32(0) # wakeup_events or wakeup_watermalk attr += p32(0) # bp_type attr += p64(0) # bp_addr or kprobe_func or uprobe_path or config1 attr += p64(0) # bp_len or kprobe_addr or probe_offset or config2 attr += p64(0) # branch_sample_type attr += p64(0) # sample_regs_user attr += p32(0) # sample_stack_user attr += p32(0) # clockid attr += p64(0) # sample_regs_intr attr += p32(0) # aux_watermark attr += p16(0) # sample_max_stack attr += p16(0) # __reserved_2 attr += p32(0) # aux_sample_size attr += p32(0) # __reserved_3 attr += p64(0) # sig_data attr += p64(0) # config3 yield ("fd = perf_event_open(&attr, 0, -1, -1, 0)", "perf_event_open", [attr, 0, -1, -1, 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) yield "bpf (need kernel.unprivileged_bpf_disabled == 0) -> close" attr = p32(2) # map_type: BPF_MAP_TYPE_ARRAY attr += p32(4) # key_size attr += p32(0x10) # value_size attr += p32(0x10) # max_entries yield ("bpf(BPF_MAP_CREATE, &attr, sizeof(attr))", "bpf", [0, attr, len(attr)]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) yield "mmap -> mprotect -> mremap -> msync -> madvise -> munmap" size = get_pagesize() yield ( "addr = mmap(NULL, 0x1000, RWX, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)", "mmap", [0, size, 7, 0x20 | 0x2, -1, 0], ) if u2i(ret_history[-1]) >= 0: addr = ret_history[-1] yield ("mprotect(addr, 0x1000, R--)", "mprotect", [addr, size, 1]) size2 = size * 2 yield ("addr2 = mremap(addr, 0x1000, 0x2000, MREMAP_MAYMOVE)", "mremap", [addr, size, size2, 1]) if u2i(ret_history[-1]) >= 0: addr2 = ret_history[-1] yield ("msync(addr2, 0x2000, MS_SYNC)", "msync", [addr2, size2, 4]) yield ("madvise(addr2, 0x2000, MADV_DONTNEED)", "madvise", [addr2, size2, 4]) self.skipped_syscall.add("process_madvise") yield ("munmap(addr2, 0x2000)", "munmap", [addr2, size2]) yield "mmap -> mincore -> mbind -> move_pages -> migrate_pages -> munmap" size = get_pagesize() yield ( "addr = mmap(NULL, 0x1000, RWX, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)", "mmap", [0, size, 7, 0x20 | 0x2, -1, 0], ) if u2i(ret_history[-1]) >= 0: addr = ret_history[-1] vec = "\0" * 0x100 yield ("mincore(addr, 0x1000, &vec)", "mincore", [addr, size, vec]) yield ("mbind(addr, 0x1000, MPOL_DEFAULT, NULL, 0, 0)", "mbind", [addr, size, 0, 0, 0, 0]) pages = p64(addr) status = p64(0) yield ("move_pages(0, 1, &pages, NULL, &status, 0)", "move_pages", [0, 1, pages, 0, status, 0]) maxnode = 8 old_nodes = "\x02" new_nodes = "\x01" yield ( "migrate_pages(0, 8, old_nodes, new_nodes)", "migrate_pages", [0, maxnode, old_nodes, new_nodes], ) yield ("munmap(addr, 0x1000)", "munmap", [addr, size]) yield "brk -> userfaultfd -> ioctl -> close" yield ("addr = brk(0)", "brk", [0]) last_page = ret_history[-1] - get_pagesize() yield ("fd = userfaultfd(O_CLOEXEC|O_NONBLOCK)", "userfaultfd", [0o2000000 | 0o4000]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] uffdio_api = p64(0xaa) # api: UFFD_API uffdio_api += p64(0) # features uffdio_api += p64(0) # ioctls yield ("ioctl(fd, UFFDIO_API, &uffdio_api)", "ioctl", [fd, 0xc018aa3f, uffdio_api]) uffdio_register = p64(last_page) # range.start uffdio_register += p64(get_pagesize()) # range.len uffdio_register += p64(1) # mode: UFFDIO_REGISTER_MODE_MISSING uffdio_register += p64(0) # ioctls yield ("ioctl(fd, UFFDIO_REGISTER, &uffdio_register)", "ioctl", [fd, 0xc020aa00, uffdio_register]) yield ("ioctl(fd, UFFDIO_UNREGISTER, &uffdio_register)", "ioctl", [fd, 0x8010aa01, uffdio_register]) yield ("close(fd)", "close", [fd]) yield "mlockall -> munlockall" yield ("mlockall(MCL_CURRENT)", "mlockall", [1]) self.skipped_syscall.add("mlock") self.skipped_syscall.add("mlock2") yield ("munlockall()", "munlockall", []) self.skipped_syscall.add("munlock") yield "get_robust_list -> set_robust_list" head_ptr = p64(0) len_ptr = p64(0) yield ("get_robust_list(0, &head_ptr, &len_ptr)", "get_robust_list", [0, head_ptr, len_ptr]) if u2i(ret_history[-1]) >= 0: head = read_int_from_memory(current_arch.sp) len_ = read_int_from_memory(current_arch.sp + 0x10) yield ("set_robust_list(head, len)", "set_robust_list", [head, len_]) yield "prctl -> arch_prctl -> set_tid_address" buf = p32(0) yield ("prctl(PR_GET_PDEATHSIG, &buf)", "prctl", [2, buf]) yield ("prctl(PR_GET_DUMPABLE)", "prctl", [3]) yield ("prctl(PR_GET_KEEPCAPS)", "prctl", [7]) yield ("prctl(PR_GET_TIMING)", "prctl", [13]) buf = "\0" * 16 yield ("prctl(PR_GET_NAME, &buf)", "prctl", [16, buf]) yield ("prctl(PR_GET_SECCOMP)", "prctl", [21]) yield ("prctl(PR_CAPBSET_READ, CAP_CHOWN)", "prctl", [23, 0]) buf = p32(0) yield ("prctl(PR_GET_TSC, &buf)", "prctl", [25, buf]) yield ("prctl(PR_GET_SECUREBITS)", "prctl", [27]) yield ("prctl(PR_GET_TIMERSLACK)", "prctl", [30]) yield ("prctl(PR_TASK_PERF_EVENTS_DISABLE)", "prctl", [31]) yield ("prctl(PR_MCE_KILL_GET, 0, 0, 0, 0)", "prctl", [34, 0, 0, 0, 0]) buf = p32(0) yield ("prctl(PR_GET_CHILD_SUBREAPER, &buf)", "prctl", [37, buf]) yield ("prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)", "prctl", [39, 0, 0, 0, 0]) yield ("prctl(PR_GET_TID_ADDRESS)", "prctl", [40, buf]) yield ("prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0)", "prctl", [42, 0, 0, 0, 0]) yield ("arch_prctl(ARCH_GET_CPUID)", "arch_prctl", [0x1011]) buf = p64(0) yield ("arch_prctl(ARCH_GET_GS, &buf)", "arch_prctl", [0x1001, buf]) buf = p64(0) yield ("arch_prctl(ARCH_GET_FS, &buf)", "arch_prctl", [0x1003, buf]) fsbase = ret_history[-1] yield ("set_tid_address(fsbase)", "set_tid_address", [fsbase]) yield "futex" uaddr = p64(0) yield ("futex(&uaddr, FUTEX_WAKE, 1, &timeout, 0, 0)", "futex", [uaddr, 1, 1, 0, 0, 0]) yield "time" yield ("time(NULL)", "time", [0]) yield "times" buf = p64(0) # tms_utime buf += p64(0) # tms_stime buf += p64(0) # tms_cutime buf += p64(0) # tms_cstime yield ("times(&buf)", "times", [buf]) yield "gettimeofday" tv = p64(0) # tv_sec tv += p64(0) # tv_usec tz = p32(0) # tz_minuteswest tz += p32(0) # tz_dsttime yield ("gettimeofday(&tv, &tz)", "gettimeofday", [tv, tz]) yield "nanosleep" req = p64(0) # tv_sec req += p64(1000) # tv_nsec rem = p64(0) # tv_sec rem += p64(0) # tv_nsec yield ("nanosleep(&req, &rem)", "nanosleep", [req, rem]) self.skipped_syscall.add("clock_nanosleep") yield "clock_getres -> clock_gettime" res = p64(0) # tv_sec res += p64(0) # tv_nsec yield ("clock_getres(CLOCK_PROCESS_CPUTIME_ID, &res)", "clock_getres", [2, res]) tp = p64(0) # tv_sec tp += p64(0) # tv_nsec yield ("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp)", "clock_gettime", [2, tp]) yield "adjtimex" buf = p64(0) # modes buf += p64(0) # offset buf += p64(0) # freq buf += p64(0) # maxerror buf += p64(0) # esterror buf += p64(0) # status buf += p64(0) # constant buf += p64(0) # precision buf += p64(0) # tolerance buf += p64(0) # time.tv_sec buf += p64(0) # time.tv_usec buf += p64(0) # tick buf += p64(0) # ppsfreq buf += p64(0) # jitter buf += p64(0) # shift buf += p64(0) # stabil buf += p64(0) # jitcnt buf += p64(0) # calcnt buf += p64(0) # errcnt buf += p64(0) # stbcnt buf += p64(0) # tai yield ("adjtimex(&buf)", "adjtimex", [buf]) self.skipped_syscall.add("clock_adjtime") yield "membarrier" yield ("membarrier(MEMBARRIER_CMD_QUERY, 0, 0)", "membarrier", [0, 0, 0]) yield "getcwd" buf = "\0" * 0x100 yield ("getcwd(&buf, sizeof(buf))", "getcwd", [buf, len(buf)]) yield "uname" buf = "\0" * 0x400 yield ("uname(&buf)", "uname", [buf]) yield "getrandom" buf = "\0" * 0x100 yield ("getrandom(&buf, sizeof(buf), 0)", "getrandom", [buf, len(buf), 0]) yield "getrlimit -> setrlimit" rlim = p64(0) # rlim_cur rlim += p64(0) # rlim_max yield ("getrlimit(RLIMIT_STACK, &rlim, sizeof(rlim))", "getrlimit", [3, rlim, len(rlim)]) rlim = read_memory(current_arch.sp, 16) yield ("setrlimit(RLIMIT_STACK, &rlim, sizeof(rlim))", "setrlimit", [3, rlim, len(rlim)]) self.skipped_syscall.add("prlimit64") yield "sched_yield" yield ("sched_yield()", "sched_yield", []) yield "sched_getscheduler -> sched_setscheduler" yield ("sched_getscheduler(0)", "sched_getscheduler", [0]) param = p32(0) yield ("sched_setscheduler(0, SCHED_OTHER, ¶m)", "sched_setscheduler", [0, 0, param]) yield "sched_getparam -> sched_setparam" param = p32(0) yield ("sched_getparam(0, ¶m)", "sched_getparam", [0, param]) yield ("sched_setparam(0, ¶m)", "sched_setparam", [0, param]) yield "sched_getaffinity -> sched_setaffinity" mask = "\0" * 0x80 yield ("sched_getaffinity(0, sizeof(mask), &mask)", "sched_getaffinity", [0, len(mask), mask]) mask = read_memory(current_arch.sp, 0x80) yield ("sched_setaffinity(0, sizeof(mask), &mask)", "sched_setaffinity", [0, len(mask), mask]) yield "sched_get_priority_max -> sched_get_priority_min" yield ("sched_get_priority_max(SCHED_OTHER)", "sched_get_priority_max", [0]) yield ("sched_get_priority_min(SCHED_OTHER)", "sched_get_priority_min", [0]) yield "sched_rr_get_interval" tp = p64(0) # tv_sec tp += p64(0) # tv_nsec yield ("sched_rr_get_interval(0, &tp)", "sched_rr_get_interval", [0, tp]) yield "sched_getattr -> sched_setattr" attr = p32(8 * 6) # size attr += p32(0) # sched_policy: SCHED_OTHER attr += p64(0) # sched_flags attr += p32(0) # sched_nice attr += p32(0) # sched_priority attr += p64(0) # sched_runtime attr += p64(0) # sched_deadline attr += p64(0) # sched_period yield ("sched_getattr(0, &attr, sizeof(attr), 0)", "sched_getattr", [0, attr, len(attr), 0]) # sched_setattr must be called before setpriority, or failed. yield ("sched_setattr(0, &attr, 0)", "sched_setattr", [0, attr, 0]) yield "getpriority -> setpriority" yield ("getpriority(PRIO_PROCESS, 0)", "getpriority", [0, 0]) yield ("setpriority(PRIO_PROCESS, 0, 1)", "setpriority", [0, 0, 1]) yield "ioprio_get -> ioprio_set" yield ("ioprio_get(IOPRIO_WHO_PROCESS, 0)", "ioprio_get", [1, 0]) yield ("ioprio_set(IOPRIO_WHO_PROCESS, 0, IOPRIO_PRIO_VALUE(2, 0))", "ioprio_set", [1, 0, 0x4000]) yield "getrusage" buf = p64(0) # ru_utime.tv_sec buf += p64(0) # ru_utime.tv_usec buf += p64(0) # ru_stime.tv_sec buf += p64(0) # ru_stime.tv_usec buf += p64(0) # ru_maxrss buf += p64(0) # ru_ixrss buf += p64(0) # ru_idrss buf += p64(0) # ru_isrss buf += p64(0) # ru_minflt buf += p64(0) # ru_majflt buf += p64(0) # ru_nswap buf += p64(0) # ru_inblock buf += p64(0) # ru_oublock buf += p64(0) # ru_msgsnd buf += p64(0) # ru_msgrcv buf += p64(0) # ru_nsignals buf += p64(0) # ru_nvcsw buf += p64(0) # ru_nivcsw yield ("getrusage(RUSAGE_SELF, &buf)", "getrusage", [0, buf]) yield "personality" yield ("personality(0xffffffff)", "personality", [0xffff_ffff]) yield "get_mempolicy -> set_mempolicy" nodemask = p64(0) yield ("get_mempolicy(0, &nodemask, 8, 0, MPOL_F_MEMS_ALLOWED)", "get_mempolicy", [0, nodemask, 8, 0, 4]) yield ("set_mempolicy(MPOL_DEFAULT, NULL, 8)", "set_mempolicy", [0, 0, 8]) yield "getcpu" cpu = p32(0) node = p32(0) yield ("getcpu(&cpu, &node, NULL)", "getcpu", [cpu, node, 0]) yield "sysinfo" buf = p64(0) # uptime buf += p64(0) * 3 # loads[3] buf += p64(0) # totalram buf += p64(0) # freeram buf += p64(0) # sharedram buf += p64(0) # bufferram buf += p64(0) # totalswap buf += p64(0) # freeswap buf += p16(0) # procs buf += p8(0) * 22 # padding yield ("sysinfo(&buf)", "sysinfo", [buf]) yield "sysfs" buf = "\0" * 0x100 yield ("sysfs(2, 0, &buf)", "sysfs", [2, 0, buf]) yield "sigaltstack" oss = p64(0) # ss_sp oss += p32(0) # ss_flags oss += p32(0) # padding oss += p64(0) # ss_size yield ("sigaltstack(NULL, &oss)", "sigaltstack", [0, oss]) yield "rt_sigprocmask -> rt_sigpending" oldset = "\0" * 0x100 sigsetsize = 8 yield ("rt_sigprocmask(0, NULL, &oldset, sigsetsize)", "rt_sigprocmask", [0, 0, oldset, sigsetsize]) set_ = "\0" * 0x100 yield ("rt_sigpending(&set)", "rt_sigpending", [set_]) yield "getpid -> getppid -> getsid -> gettid -> getpgid -> setpgid -> getpgrp -> kcmp" yield ("pid = getpid()", "getpid", []) pid = ret_history[-1] yield ("getppid()", "getppid", []) yield ("getsid(pid)", "getsid", [pid]) yield ("gettid()", "gettid", []) tid = ret_history[-1] yield ("pgid = getpgid(pid)", "getpgid", [pid]) pgid = ret_history[-1] yield ("setpgid(pid, pgid)", "setpgid", [pid, pgid]) yield ("getpgrp()", "getpgrp", []) yield ("kcmp(pid, pid, KCMP_FS, 0, 0)", "kcmp", [pid, pid, 3, 0, 0]) yield "getuid -> setuid -> setreuid -> setfsuid -> geteuid -> getresuid -> setresuid" yield ("uid = getuid()", "getuid", []) uid = ret_history[-1] yield ("setuid(uid)", "setuid", [uid]) yield ("setreuid(uid, uid)", "setreuid", [uid, uid]) yield ("setfsuid(uid)", "setfsuid", [uid]) yield ("geteuid()", "geteuid", []) ruid = euid = suid = p32(0) yield ("getresuid(&ruid, &euid, &suid)", "getresuid", [ruid, euid, suid]) yield ("setresuid(uid, uid, uid)", "setresuid", [uid, uid, uid]) yield "getgid -> setgid -> setregid -> setfsgid -> getegid -> getresgid -> setresgid -> getgroups" yield ("gid = getgid()", "getgid", []) gid = ret_history[-1] yield ("setgid(gid)", "setgid", [gid]) yield ("setregid(gid, gid)", "setregid", [gid, gid]) yield ("setfsgid(uid)", "setfsgid", [gid]) yield ("getegid()", "getegid", []) rgid = egid = sgid = p32(0) yield ("getresgid(&rgid, &egid, &sgid)", "getresgid", [rgid, egid, sgid]) yield ("setresgid(gid, gid, gid)", "setresgid", [gid, gid, gid]) yield ("getgroups(0, NULL)", "getgroups", [0, 0]) yield "rt_sigaction -> alarm -> kill -> tkill -> rt_sigtimedwait" act = p64(1) # sa_handler: SIG_IGN act += p64(0) # sa_flags act += p64(0) # sa_restorer act += p64(0xe) # sa_mask: SIGALRM oldact = p64(0) # sa_handler oldact += p64(0) # sa_flags oldact += p64(0) # sa_restorer oldact += p64(0) # sa_mask: SIGALRM sigsetsize = 8 yield ("rt_sigaction(SIGALRM, &act, &oldact, sigsetsize)", "rt_sigaction", [14, act, oldact, sigsetsize]) yield ("alarm(1000)", "alarm", [1000]) yield ("kill(pid, SIGALRM)", "kill", [pid, 14]) yield ("tkill(tid, SIGALRM)", "kill", [tid, 14]) self.skipped_syscall.add("tgkill") self.skipped_syscall.add("rt_sigqueueinfo") self.skipped_syscall.add("rt_tgsigqueueinfo") set_ = "\0" * 0x100 info = p32(0) # si_signo info += p32(0) # si_code info += p64(0) # si_value info += p32(0) # si_errno info += p32(0) # si_pid info += p32(0) # si_uid info += p32(0) # padding info += p64(0) # si_addr info += p32(0) # si_status info += p32(0) # si_band timeout = p64(0) # tv_sec timeout += p64(0) # tv_nsec sigsetsize = 8 yield ( "rt_sigtimedwait(&set, &info, &timeout, sigsetsize)", "rt_sigtimedwait", [set_, info, timeout, sigsetsize], ) yield "pidfd_open -> pidfd_getfd -> pidfd_send_signal -> close" yield ("pfdfd = pidfd_open(pid, 0)", "pidfd_open", [pid, 0]) if u2i(ret_history[-1]) >= 0: pidfd = ret_history[-1] yield ("fd = pidfd_getfd(pidfd, STDIN_FILENO, 0)", "pidfd_getfd", [pidfd, 0, 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) yield ("pidfd_send_signal(pidfd, SIGALRM, NULL, 0)", "pidfd_send_signal", [pidfd, 14, 0, 0]) yield ("close(pidfd)", "close", [pidfd]) yield "capget -> capset" hdrp = p32(0x20080522) # version hdrp += p32(pid) # pid datap = p32(0) # effective datap += p32(0) # permitted datap += p32(0) # inheritable yield ("capget(&hdrp, &datap)", "capget", [hdrp, datap]) datap = read_memory(current_arch.sp + 0x10, 4 * 3) yield ("capset(&hdrp, &datap)", "capset", [hdrp, datap]) yield "umask" yield ("umask(022)", "umask", [0o022]) yield "open -> fallocate -> write -> fdatasync -> fsync -> syncfs -> fadvise64 -> close" TMP_XXX = "/tmp/xxx\0" yield ('fd = open("/tmp/xxx", 0_WRONLY|O_CREAT, 0666)', "open", [TMP_XXX, 0o1 | 0o100, 0o666]) self.skipped_syscall.add("creat") self.skipped_syscall.add("openat") self.skipped_syscall.add("openat2") if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("fallocate(fd, 0, 0, 0x100)", "fallocate", [fd, 0, 0, 0x100]) buf = "A" * 4 yield ('write(fd, "AAAA", 4)', "write", [fd, buf, len(buf)]) self.skipped_syscall.add("writev") self.skipped_syscall.add("pwrite64") self.skipped_syscall.add("pwritev") self.skipped_syscall.add("pwritev2") self.skipped_syscall.add("process_vm_writev") yield ("fdatasync(fd)", "fdatasync", [fd]) yield ("fsync(fd)", "fsync", [fd]) yield ("syncfs(fd)", "syncfs", [fd]) self.skipped_syscall.add("sync") yield ("fadvise64(fd, 0, 0x100, POSIX_FADV_DONTNEED)", "fadvise64", [fd, 0, 0x100, 4]) yield ("close(fd)", "close", [fd]) yield "open -> flock -> lseek -> readahead -> poll -> read -> dup -> close_range" yield ('fd = open("/tmp/xxx", 0_RDONLY)', "open", [TMP_XXX, 0o0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("flock(fd, LOCK_SH|LOCK_NB)", "flock", [fd, 1 | 4]) yield ("lseek(fd, SEEK_SET, 0)", "lseek", [fd, 0, 0]) yield ("readahead(fd, 0, 0x1000)", "readahead", [fd, 0, 0x1000]) fds = p32(fd) # fd fds += p16(0) # events fds += p16(0) # revents yield ("poll(&fds, 1, 0)", "poll", [fds, 1, 0]) self.skipped_syscall.add("ppoll") buf = "\0" * 4 yield ("read(fd, &buf, 4)", "read", [fd, buf, len(buf)]) self.skipped_syscall.add("readv") self.skipped_syscall.add("pread64") self.skipped_syscall.add("preadv") self.skipped_syscall.add("preadv2") self.skipped_syscall.add("process_vm_readv") yield ("fd2 = dup(fd)", "dup", [fd]) self.skipped_syscall.add("dup2") self.skipped_syscall.add("dup3") fd2 = ret_history[-1] yield ("close_range(fd, fd2, 0)", "close_range", [fd, fd2, 0]) yield "open -> mmap -> remap_file_pages -> munmap -> close" yield ('fd = open("/tmp/xxx", 0_RDONLY)', "open", [TMP_XXX, 0o0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] size = get_pagesize() yield ( "addr = mmap(NULL, 0x1000, R--, MAP_ANONYMOUS|MAP_SHARED, -1, 0)", "mmap", [0, size, 1, 0x20 | 0x1, fd, 0], ) if u2i(ret_history[-1]) >= 0: addr = ret_history[-1] yield ("remap_file_pages(addr, 0x1000, 0, 0, 0)", "remap_file_pages", [addr, size, 0, 0, 0]) yield ("munmap(addr, 0x1000)", "munmap", [addr, size]) yield ("close(fd)", "close", [fd]) yield "chmod -> chown" yield ('chmod("/tmp/xxx", 0o664)', "chmod", [TMP_XXX, 0o664]) self.skipped_syscall.add("fchmod") self.skipped_syscall.add("fchmodat") yield ('chown("/tmp/xxx", -1, -1)', "chown", [TMP_XXX, -1, -1]) self.skipped_syscall.add("fchown") self.skipped_syscall.add("lchown") self.skipped_syscall.add("fchownat") yield "open -> pipe -> sendfile -> splice -> select -> vmsplice -> close_range -> close" yield ('fd = open("/tmp/xxx", 0_RDONLY)', "open", [TMP_XXX, 0o0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] pipefd_array = p32(0) * 2 # pipefd[2] yield ("pipe(&pipefd[])", "pipe", [pipefd_array]) self.skipped_syscall.add("pipe2") if u2i(ret_history[-1]) >= 0: pipefd0 = read_int32_from_memory(current_arch.sp) pipefd1 = read_int32_from_memory(current_arch.sp + 4) offset = p64(0) yield ("sendfile(pipefd[1], fd, &offset, 4)", "sendfile", [pipefd1, fd, offset, 4]) off_in = p64(0) yield ("splice(fd, &off_in, pipefd[1], NULL, 4, 0)", "splice", [fd, off_in, pipefd1, 0, 4, 0]) readfds = [0] * 1024 readfds[pipefd0] = 1 readfds = slicer("".join([str(x) for x in readfds]), 64) readfds = b"".join([p64(int(x[::-1], 2)) for x in readfds]) nfds = pipefd0 + 1 yield ("select(nfds, readfds, NULL, NULL, NULL)", "select", [nfds, readfds, 0, 0, 0]) self.skipped_syscall.add("pselect6") iov = p64(current_arch.sp + 0x10) # iov_base iov += p64(0x100) # iov_len yield ("vmsplice(pipefd[0], &iov, 1, 0)", "vmsplice", [pipefd0, iov, 1, 0]) yield ("close_range(pipefd[0], pipefd[1], 0)", "close_range", [pipefd0, pipefd1, 0]) yield ("close(fd)", "close", [fd]) yield "pipe -> pipe -> tee -> close_range" pipefd_array = p32(0) * 2 # pipefd[2] yield ("pipe(&pipefd[])", "pipe", [pipefd_array]) pipefd0 = read_int32_from_memory(current_arch.sp) _pipefd1 = read_int32_from_memory(current_arch.sp + 4) yield ("pipe(&pipefd[])", "pipe", [pipefd_array]) _pipefd2 = read_int32_from_memory(current_arch.sp) pipefd3 = read_int32_from_memory(current_arch.sp + 4) yield ("tee(pipefd[0], pipefd[3], 0", "tee", [pipefd0, pipefd3]) yield ("close_range(pipefd[0], pipefd[3], 0)", "close_range", [pipefd0, pipefd3, 0]) yield "mknod -> unlink" TMP_PIPE = "/tmp/pipe\0" yield ('mknod("/tmp/pipe", S_IFIFO|0644, 0)', "mknod", [TMP_PIPE, 0o10000 | 0o644, 0]) self.skipped_syscall.add("mknodat") if u2i(ret_history[-1]) >= 0: yield ('unlink("/tmp/pipe")', "unlink", [TMP_PIPE]) yield "open -> sync_file_range -> copy_file_range -> close -> unlink -> close" TMP_XXX2 = "/tmp/xxx2\0" yield ('fd_in = open("/tmp/xxx", 0_RDONLY)', "open", [TMP_XXX, 0o0]) if u2i(ret_history[-1]) >= 0: fd_in = ret_history[-1] yield ('fd_out = open("/tmp/xxx2", 0_WRONLY|O_CREAT, 0666)', "open", [TMP_XXX2, 0o1 | 0o100, 0o666]) if u2i(ret_history[-1]) >= 0: fd_out = ret_history[-1] yield ( "sync_file_range(fd_out, 0, 4, SYNC_FILE_RANGE_WAIT_AFTER)", "sync_file_range", [fd_out, 0, 4, 4], ) yield ( "copy_file_range(fd_in, 0, fd_out, 0, 4, 0)", "copy_file_range", [fd_in, 0, fd_out, 0, 4, 0], ) yield ("close(fd_out)", "close", [fd_out]) yield ('unlink("/tmp/xxx2")', "unlink", [TMP_XXX2]) yield ("close(fd_in)", "close", [fd_in]) yield "access -> utime -> stat -> statx -> truncate" yield ('access("/tmp/xxx", F_OK)', "access", [TMP_XXX, 0]) self.skipped_syscall.add("faccessat") self.skipped_syscall.add("faccessat2") if u2i(ret_history[-1]) >= 0: yield ('utime("/tmp/xxx", NULL)', "utime", [TMP_XXX, 0]) self.skipped_syscall.add("utimes") self.skipped_syscall.add("futimesat") self.skipped_syscall.add("utimensat") buf = p64(0) # st_dev buf += p64(0) # st_ino buf += p64(0) # st_nlink buf += p32(0) # st_mode buf += p32(0) # st_uid buf += p32(0) # st_gid buf += p32(0) # __pad0 buf += p64(0) # st_rdev buf += p64(0) # st_size buf += p64(0) # st_blksize buf += p64(0) # st_blocks buf += p64(0) # st_atim.tv_sec buf += p64(0) # st_atim.tv_nsec buf += p64(0) # st_mtim.tv_sec buf += p64(0) # st_mtim.tv_nsec buf += p64(0) # st_ctim.tv_sec buf += p64(0) # st_ctim.tv_nsec buf += p64(0) * 3 # __glibc_reserved[3] yield ('stat("/tmp/xxx", &buf)', "stat", [TMP_XXX, buf]) self.skipped_syscall.add("fstat") self.skipped_syscall.add("lstat") self.skipped_syscall.add("newfstatat") statxbuf = p32(0) # stx_mask statxbuf += p32(0) # stx_blksize statxbuf += p64(0) # stx_attributes statxbuf += p32(0) # stx_nlink statxbuf += p32(0) # stx_uid statxbuf += p32(0) # stx_gid statxbuf += p16(0) # stx_mode statxbuf += p16(0) # padding statxbuf += p64(0) # stx_ino statxbuf += p64(0) # stx_size statxbuf += p64(0) # stx_blocks statxbuf += p64(0) # stx_attributes_mask statxbuf += p64(0) # stx_atime.tv_sec statxbuf += p32(0) # stx_atime.tv_nsec statxbuf += p32(0) # stx_atime.padding statxbuf += p64(0) # stx_btime.tv_sec statxbuf += p32(0) # stx_btime.tv_nsec statxbuf += p32(0) # stx_btime.padding statxbuf += p64(0) # stx_ctime.tv_sec statxbuf += p32(0) # stx_ctime.tv_nsec statxbuf += p32(0) # stx_ctime.padding statxbuf += p64(0) # stx_mtime.tv_sec statxbuf += p32(0) # stx_mtime.tv_nsec statxbuf += p32(0) # stx_mtime.padding statxbuf += p32(0) # stx_rdev_major statxbuf += p32(0) # stx_rdev_minor statxbuf += p32(0) # stx_dev_major statxbuf += p32(0) # stx_dev_minor statxbuf += p64(0) # stx_mnt_id statxbuf += p32(0) # stx_dio_mem_align statxbuf += p32(0) # stx_dio_offset_align yield ('statx(0, "/tmp/xxx", 0, 0, &statxbuf)', "statx", [0, TMP_XXX, 0, 0, statxbuf]) yield ('truncate("/tmp/xxx", 10)', "truncate", [TMP_XXX, 10]) self.skipped_syscall.add("ftruncate") yield "access -> setxattr -> getxattr -> listxattr -> removexattr" yield ('access("/tmp/xxx", F_OK)', "access", [TMP_XXX, 0]) if u2i(ret_history[-1]) >= 0: buf = "A" * 0xff + "\0" userx = "user.x\0" yield ( 'setxattr("/tmp/xxx", "user.x", &buf, sizeof(buf), 0)', "setxattr", [TMP_XXX, userx, buf, len(buf), 0], ) self.skipped_syscall.add("lsetxattr") self.skipped_syscall.add("fsetxattr") if u2i(ret_history[-1]) >= 0: buf= "\0" * 0x100 yield ( 'getxattr("/tmp/xxx", "user.x", &buf, sizeof(buf))', "getxattr", [TMP_XXX, userx, buf, len(buf)], ) self.skipped_syscall.add("lgetxattr") self.skipped_syscall.add("fgetxattr") buf = "\0" * 0x100 yield ('listxattr("/tmp/xxx", &buf, sizeof(buf))', "listxattr", [TMP_XXX, buf, len(buf)]) self.skipped_syscall.add("llistxattr") self.skipped_syscall.add("flistxattr") yield ('removexattr("/tmp/xxx", "user.x")', "removexattr", [TMP_XXX, userx]) self.skipped_syscall.add("lremovexattr") self.skipped_syscall.add("fremovexattr") yield "link -> unlink" TMP_XXX3 = "/tmp/xxx3\0" yield ('link("/tmp/xxx", "/tmp/xxx3")', "link", [TMP_XXX, TMP_XXX3]) self.skipped_syscall.add("linkat") if u2i(ret_history[-1]) >= 0: yield ('unlink("/tmp/xxx3")', "unlink", [TMP_XXX3]) self.skipped_syscall.add("unlinkat") yield "symlink -> readlink -> rename -> unlink" TMP_XXX4 = "/tmp/xxx4\0" yield ('symlink("/tmp/xxx", "/tmp/xxx4")', "symlink", [TMP_XXX, TMP_XXX4]) self.skipped_syscall.add("symlinkat") if u2i(ret_history[-1]) >= 0: buf = "\0" * 0x100 yield ('readlink("/tmp/xxx4", &buf, sizeof(buf))', "readlink", [TMP_XXX4, buf, len(buf)]) self.skipped_syscall.add("readlinkat") TMP_XXX5 = "/tmp/xxx5\0" yield ('rename("/tmp/xxx4", "/tmp/xxx5")', "rename", [TMP_XXX4, TMP_XXX5]) self.skipped_syscall.add("renameat") self.skipped_syscall.add("renameat2") if u2i(ret_history[-1]) >= 0: yield ('unlink("/tmp/xxx5")', "unlink", [TMP_XXX5]) self.skipped_syscall.add("unlinkat") yield "inotify_init1 -> inotify_add_watch -> inotify_rm_watch -> close" yield ("fd = inotify_init1(0)", "inotify_init1", [0]) self.skipped_syscall.add("inotify_init") if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ( 'wd = inotify_add_watch(fd, "/tmp/xxx", IN_MOVE_SELF)', "inotify_add_watch", [fd, TMP_XXX, 0x800], ) if u2i(ret_history[-1]) >= 0: wd = ret_history[-1] yield ("inotify_rm_watch(fd, wd)", "inotify_rm_watch", [fd, wd]) yield ("close(fd)", "close", [fd]) yield "open -> io_setup -> io_submit -> io_getevents -> close -> io_destroy" yield ('fd = open("/tmp/xxx", 0_RDONLY)', "open", [TMP_XXX, 0o0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] ctx_idp = p64(0) yield ("io_setup(1, &ctx_idp)", "io_setup", [1, ctx_idp]) ctx_id = read_int_from_memory(current_arch.sp) iocb = p64(0) # aio_data iocb += p32(0) # aio_key iocb += p32(0) # aio_rw_flags iocb += p16(0) # aio_lio_opcode: IOCB_CMD_PREAD iocb += p16(0) # aio_reqprio iocb += p32(fd) # aio_fildes iocb += p64(current_arch.sp + 0x70) # aio_buf iocb += p64(0x100) # aio_nbytes iocb += p64(0) # aio_offset iocb += p64(0) # aio_reserved2 iocb += p32(0) # aio_flags iocb += p32(0) # aio_resfd iocb += b"\0" * 0x100 # data buffer <-- aio_buf iocbp = p64(current_arch.sp + 0x30) # iocb iocbp += p64(0) * 5 # padding iocbp += iocb yield ("io_submit(ctx_id, 1, &iocbp)", "io_submit", [ctx_id, 1, iocbp]) events = p64(0) # data events += p64(0) # obj events += p64(0) # res events += p64(0) # res2 timeout = p64(0) # tv_sec timeout += p64(0) # tv_nsec yield ( "io_getevents(ctx_id, 1, 1, &events, &timeout)", "io_getevents", [ctx_id, 1, 1, events, timeout], ) self.tested_syscall.add("io_pgetevents") yield ("clode(fd)", "close", [fd]) yield ("io_destroy(ctx_id)", "io_destroy", [ctx_id]) yield "io_uring_setup -> mmap -> io_uring_enter -> io_uring_register -> munmap" params = p32(0) # sq_entries params += p32(0) # cq_entries params += p32(0) # flags params += p32(0) # sq_thread_cpu params += p32(0) # sq_thread_idle params += p32(0) # features params += p32(0) # wq_fd params += p32(0) * 3 # resv[3] params += p32(0) # sq_off.head params += p32(0) # sq_off.tail params += p32(0) # sq_off.ring_mask params += p32(0) # sq_off.ring_entries params += p32(0) # sq_off.flags params += p32(0) # sq_off.dropped params += p32(0) # sq_off.array params += p32(0) * 3 # sq_off.resv[3] params += p32(0) # cq_off.head params += p32(0) # cq_off.tail params += p32(0) # cq_off.ring_mask params += p32(0) # cq_off.ring_entries params += p32(0) # cq_off.overflow params += p32(0) # cq_off.cqes params += p32(0) # cq_off.flags params += p32(0) * 3 # cq_off.resv[3] yield ("ring_fd = io_uring_setup(1, ¶ms)", "io_uring_setup", [1, params]) params = slice_unpack(read_memory(current_arch.sp, len(params)), 4) if u2i(ret_history[-1]) >= 0 and params[5] & 1: # IORING_FEAT_SINGLE_MMAP (available linux v5.4~) ring_fd = ret_history[-1] sring_sz = params[16] + params[0] * 4 # sq_off.array + sq_entries * sizeof(uint) cring_sz = params[25] + params[1] * 0x10 # cq_off.cqes + cq_entries * sizeof(struct io_uring_cqe) sring_sz = max(sring_sz, cring_sz) yield ( "mmap(0, sring_sz, RW-, MAP_SHARED|MAP_POPULATE, ring_fd, IORING_OFF_SQ_RING)", "mmap", [0, sring_sz, 3, 0x1 | 0x8000, ring_fd, 0], ) if u2i(ret_history[-1]) >= 0: sq_ptr = ret_history[-1] yield ( "io_uring_enter(ring_fd, 0, 0, 0, NULL, 8)", "io_uring_enter", [ring_fd, 0, 0, 0, 0, 8], ) arg = p32(0) yield ( "io_uring_register(ring_fd, IORING_REGISTER_FILES, &arg, 1)", "io_uring_register", [ring_fd, 2, arg, 1], ) yield ( "io_uring_register(ring_fd, IORING_UNREGISTER_FILES, NULL, 0)", "io_uring_register", [ring_fd, 3, 0, 0], ) yield ("munmap(sq_ptr, sring_sz)", "munmap", [sq_ptr, sring_sz]) yield ("close(ring_fd)", "close", [ring_fd]) yield "unshare" yield ("unshare(CLONE_FS|CLONE_FILES)", "unshare", [0x200 | 0x400]) yield "name_to_handle_at -> unlink" handle = p32(0x80) # handle_bytes: MAX_HANDLE_SZ handle += p32(0) # handle_type handle += b"\0" * 0x80 # f_handle mntid = p32(0) yield ( 'name_to_handle_at(0, "/tmp/xxx", &handle, &mntid, 0)', "name_to_handle_at", [0, TMP_XXX, handle, mntid, 0], ) yield ('unlink("/tmp/xxx")', "unlink", [TMP_XXX]) yield "mkdir -> open_tree -> close -> chdir -> rmdir" TMP_YYY = "/tmp/yyy\0" yield ('mkdir("/tmp/yyy", 0777)', "mkdir", [TMP_YYY, 0o777]) self.tested_syscall.add("mkdirat") if u2i(ret_history[-1]) >= 0: yield ('fd = open_tree(-1, "/tmp/yyy", 0)', "open_tree", [-1, TMP_YYY, 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) yield ('chdir("/tmp/yyy")', "chdir", [TMP_YYY]) self.skipped_syscall.add("fchdir") yield ('chdir("..")', "chdir", ["..\0"]) yield ('rmdir("/tmp/yyy")', "rmdir", [TMP_YYY]) yield "statfs" buf = p64(0) # f_type buf += p64(0) # f_bsize buf += p64(0) # f_blocks buf += p64(0) # f_bfree buf += p64(0) # f_bavail buf += p64(0) # f_files buf += p64(0) # f_ffree buf += p64(0) # f_fsid buf += p64(0) # f_namelen buf += p64(0) # f_frsize buf += p64(0) # f_flags buf += p64(0) * 4 # f_spare[4] yield ('statfs("/", &buf)', "statfs", ["/\0", buf]) self.skipped_syscall.add("fstatfs") self.skipped_syscall.add("ustat") yield "open -> getdents -> fcntl -> close" yield ('fd = open("/", 0_RDONLY)', "open", ["/\0", 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] buf = "\0" * 0x200 yield ("getdents(fd, &buf, sizeof(buf))", "getdents", [fd, buf, len(buf)]) self.skipped_syscall.add("getdents64") yield ("fcntl(fd, F_GETFD)", "fcntl", [fd, 1]) yield ("fcntl(fd, F_GETFL)", "fcntl", [fd, 3]) flock = "\0" * 0x200 yield ("fcntl(fd, F_GETLK, &flock)", "fcntl", [fd, 5, flock]) yield ("fcntl(fd, F_OFD_GETLK, &flock)", "fcntl", [fd, 36, flock]) yield ("close(fd)", "close", [fd]) yield "invalid socket -> close" yield ("socket(22, SOCK_STREAM, 0)", "socket", [22, 1, 0]) yield "socket AF_INET/TCP -> bind -> listen -> setsockopt -> getsockopt -> close" yield ("fd = socket(AF_INET, SOCK_STREAM, 0)", "socket", [2, 1, 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] import socket sockaddr = p16(socket.AF_INET) # sin_len, sin_family sockaddr += p16(socket.htons(13337)) # sin_port sockaddr += socket.inet_aton("0.0.0.0") # sin_addr.s_addr sockaddr += b"\0" * 8 # sin_zero[8] yield ("bind(fd, &sockaddr, sizeof(sockaddr))", "bind", [fd, sockaddr, len(sockaddr)]) yield ("listen(fd, 16)", "listen", [fd, 16]) opt = p32(0) yield ( "setsockopt(fd, SOL_SOCKET, SO_DEBUG, &opt, sizeof(opt))", "setsockopt", [fd, 1, 1, opt, len(opt)], ) optlen = p32(len(opt)) yield ("getsockopt(fd, SOL_SOCKET, SO_DEBUG, &opt, &optlen)", "getsockopt", [fd, 1, 1, opt, optlen]) yield ("close(fd)", "close", [fd]) yield "socket AF_INET/UDP -> connect -> getsockname -> getpeername -> close" yield ("fd = socket(AF_INET, SOCK_DGRAM , 0)", "socket", [2, 2, 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] import socket sockaddr = p16(socket.AF_INET) # sin_len, sin_family sockaddr += p16(socket.htons(13337)) # sin_port sockaddr += socket.inet_aton("0.0.0.0") # sin_addr.s_addr sockaddr += b"\0" * 8 # sin_zero[8] yield ("connect(fd, &sockaddr, sizeof(sockaddr))", "connect", [fd, sockaddr, len(sockaddr)]) sockaddr = p16(0) # sin_len, sin_family sockaddr += p16(0) # sin_port sockaddr += p32(0) # sin_addr.s_addr sockaddr += b"\0" * 8 # sin_zero[8] addr_len = p64(len(sockaddr)) yield ("getsockname(fd, &sockaddr, &addrlen)", "getsockname", [fd, sockaddr, addr_len]) yield ("getpeername(fd, &sockaddr, &addrlen)", "getpeername", [fd, sockaddr, addr_len]) yield ("close(fd)", "close", [fd]) yield "socketpair AF_UNIX -> sendto -> recvfrom -> sendmsg -> recvmsg -> shutdown -> close" sv_array = p32(0) * 2 # sv[2] yield ("socketpair(AF_UNIX, SOCK_STREAM, 0, &sv[])", "socketpair", [1, 1, 0, sv_array]) if u2i(ret_history[-1]) >= 0: sv0 = read_int32_from_memory(current_arch.sp) sv1 = read_int32_from_memory(current_arch.sp + 4) buf = "A" * 4 yield ('sendto(sv[0], "AAAA", 4, 0, NULL, 0)', "sendto", [sv0, buf, len(buf), 0, 0, 0]) buf = "\0" * 4 yield ("recvfrom(sv[1], buf, 4, 0, NULL, NULL)", "recvfrom", [sv1, buf, len(buf), 0, 0, 0]) msg = p64(0) # msg_name msg += p64(0) # msg_melen msg += p64(current_arch.sp + 0x40) # msg_iov msg += p64(1) # msg_iov_len msg += p64(0) # msg_control msg += p64(0) # msg_controllen msg += p64(0) # msg_flags msg += p64(0) # padding msg += p64(current_arch.sp + 0x50) # iov_base msg += p64(4) # iov_len msg += b"AAAA" # data yield ("sendmsg(sv[0], &msg, 0)", "sendmsg", [sv0, msg, 0]) self.skipped_syscall.add("sendmmsg") yield ("recvmsg(sv[1], &msg, 0)", "sendmsg", [sv1, msg, 0]) self.skipped_syscall.add("recvmmsg") yield ("shutdown(sv[0], SHUT_RDWR)", "shutdown", [sv0, 2]) yield ("close(sv[0])", "close", [sv0]) yield ("close(sv[1])", "close", [sv1]) yield "memfd_create -> close" yield ('fd = memfd_create("test", 0)', "memfd_create", ["test\0", 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) yield "add_key -> request_key -> keyctl" user = "user\0" tkey = "test:testkey\0" payload = "payload\0" yield ('add_key("user", "test:testkey", "payload", plen, KEY_SPEC_PROCESS_KEYRING)', "add_key", [user, tkey, payload, len(payload) - 1, 0xffff_fffe]) callout_info = "\0" * 0x100 yield ('request_key("user", "test:testkey", &callout_info, KEY_SPEC_PROCESS_KEYRING)', "request_key", [user, tkey, callout_info, 0xffff_fffe]) if u2i(ret_history[-1]) >= 0: key_serial = ret_history[-1] yield ("keyctl(KEYCTL_REVOKE, key_serial)", "keyctl", [3, key_serial]) yield "invalid add_key" tkey = "A" * 0x100 yield ('add_key("user", "AAAAAAAA...", NULL, 0, KEY_SPEC_PROCESS_KEYRING)', "add_key", [user, tkey, 0, 0, 0xffff_fffe]) yield "open /dev/ptmx -> close" yield ('fd = open("/dev/ptmx", O_RDWR|O_NOCTTY)', "open", ["/dev/ptmx\0", 0o2 | 0o400]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) yield "open /proc/self/stat -> close" yield ('fd = open("/proc/self/stat", O_RDONLY)', "open", ["/proc/self/stat\0", 0]) if u2i(ret_history[-1]) >= 0: fd = ret_history[-1] yield ("close(fd)", "close", [fd]) self.skipped_syscall.add("restart_syscall") # difficult to implement generically self.skipped_syscall.add("pause") # difficult to implement generically self.skipped_syscall.add("rt_sigreturn") # difficult to implement generically self.skipped_syscall.add("rt_sigsuspend") # difficult to implement generically self.skipped_syscall.add("clone") # difficult to implement generically self.skipped_syscall.add("clone3") # difficult to implement generically self.skipped_syscall.add("execve") # difficult to implement generically self.skipped_syscall.add("execveat") # difficult to implement generically self.skipped_syscall.add("fork") # difficult to implement generically self.skipped_syscall.add("vfork") # difficult to implement generically self.skipped_syscall.add("exit") # difficult to implement generically self.skipped_syscall.add("exit_group") # difficult to implement generically self.skipped_syscall.add("wait4") # difficult to implement generically self.skipped_syscall.add("waitid") # difficult to implement generically self.skipped_syscall.add("accept") # difficult to implement generically self.skipped_syscall.add("accept4") # difficult to implement generically self.skipped_syscall.add("setsid") # difficult to implement generically self.skipped_syscall.add("io_cancel") # difficult to implement generically self.skipped_syscall.add("seccomp") # affect subsequent system calls self.skipped_syscall.add("rseq") # affect subsequent system calls self.skipped_syscall.add("pkey_alloc") # maybe unsupported by qemu HW self.skipped_syscall.add("pkey_mprotect") # maybe unsupported by qemu HW self.skipped_syscall.add("pkey_free") # maybe unsupported by qemu HW self.skipped_syscall.add("modify_ldt") # supported i386 only self.skipped_syscall.add("lookup_dcookie") # deprecated self.skipped_syscall.add("quotactl") # supported ufs only self.skipped_syscall.add("setgroups") # need CAP_SETGID self.skipped_syscall.add("chroot") # need CAP_SYS_CHROOT self.skipped_syscall.add("acct") # need CAP_SYS_PACCT self.skipped_syscall.add("ptrace") # need CAP_SYS_PTRACE self.skipped_syscall.add("iopl") # need CAP_SYS_RAWIO self.skipped_syscall.add("ioperm") # need CAP_SYS_RAWIO self.skipped_syscall.add("vhangup") # need CAP_SYS_TTY_CONFIG self.skipped_syscall.add("reboot") # need CAP_SYS_BOOT self.skipped_syscall.add("kexec_load") # need CAP_SYS_BOOT self.skipped_syscall.add("kexec_file_load") # need CAP_SYS_BOOT self.skipped_syscall.add("init_module") # need CAP_SYS_MODULE self.skipped_syscall.add("finit_module") # need CAP_SYS_MODULE self.skipped_syscall.add("delete_module") # need CAP_SYS_MODULE self.skipped_syscall.add("syslog") # need CAP_SYS_ADMIN self.skipped_syscall.add("pivot_root") # need CAP_SYS_ADMIN self.skipped_syscall.add("mount") # need CAP_SYS_ADMIN self.skipped_syscall.add("umount2") # need CAP_SYS_ADMIN self.skipped_syscall.add("swapon") # need CAP_SYS_ADMIN self.skipped_syscall.add("swapoff") # need CAP_SYS_ADMIN self.skipped_syscall.add("sethostname") # need CAP_SYS_ADMIN self.skipped_syscall.add("setdomainname") # need CAP_SYS_ADMIN self.skipped_syscall.add("setns") # need CAP_SYS_ADMIN self.skipped_syscall.add("move_mount") # need CAP_SYS_ADMIN self.skipped_syscall.add("fsopen") # need CAP_SYS_ADMIN self.skipped_syscall.add("fspick") # need CAP_SYS_ADMIN self.skipped_syscall.add("fsconfig") # need CAP_SYS_ADMIN self.skipped_syscall.add("fsmount") # need CAP_SYS_ADMIN self.skipped_syscall.add("fanotify_init") # need CAP_SYS_ADMIN self.skipped_syscall.add("fanotify_mark") # need CAP_SYS_ADMIN self.skipped_syscall.add("clock_settime") # need CAP_SYS_TIME self.skipped_syscall.add("settimeofday") # need CAP_SYS_TIME self.skipped_syscall.add("open_by_handle_at") # need CAP_DAC_READ_SEARCH return None self.scheduled_syscall = set() self.tested_syscall = set() self.skipped_syscall = set() ret_history = [] for testcase in gen_testcase(): if isinstance(testcase, str): gef_print(titlify(testcase, msg_color="bold")) self.scheduled_syscall |= set(testcase.split(" -> ")) continue desc, syscall_name, args = testcase gef_print(titlify("{:s};".format(desc))) self.setup_syscall(syscall_name, args) for bp in breakpoints: bp.enabled = True gdb.execute("continue") # here, stop at hw breakpoint ret = get_register(current_arch.return_register) gef_print("ret: {:#x}".format(ret)) if u2i(ret) < 0: gef_print(Color.colorify("WARNING: r < 0", "bold red underline")) gdb.execute("errno {:#x}".format(ret)) ret_history.append(ret) for bp in breakpoints: bp.enabled = False self.dump_untested_syscall() self.setup_syscall("exit", [0]) return def cleanup(self, hwbp, breakpoints): # clean up hwbp.delete() for bp in breakpoints: bp.delete() KmallocTracerCommand.clear_disabled_breakpoints("KmallocRetBreakpoint", force=True) ContextCommand.unhide_context() info("Exiting `sleep` process... (Please issue Ctrl+C)") gdb.execute("continue") return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("x86_64",)) @only_if_in_kernel @only_if_kvm_disabled @only_if_smp_disabled def do_invoke(self, args): info("Wait for memory scan") kversion = Kernel.kernel_version() if kversion < "3.0": err("Unsupported before v3.0") return allocator = Kernel.get_slab_type() if allocator == "Unknown": err("Unsupported: Unknown allocator") return if allocator != "SLUB": warn("Unsupported viewing detailed information for SLAB, SLOB, SLUB_TINY") # fall through # initialize if not hasattr(self, "initialized"): ret = KmallocTracerCommand.initialize(allocator, args.verbose) if ret is False: err("Failed to initialize") return self.initialized = True self.extra_info = ret # allow None else: if args.verbose and self.extra_info: info("offsetof({:s}, slab_cache): {:#x}".format(Kernel.slab_page_str(), self.extra_info.page_offset_slab_cache)) info("offsetof(kmem_cache, name): {:#x}".format(self.extra_info.kmem_cache_offset_name)) info("offsetof(kmem_cache, size): {:#x}".format(self.extra_info.kmem_cache_offset_size)) # get syscall table syscall_table = get_syscall_table() if syscall_table is None: err("Could not find the syscall table") return self.syscall_table = {e.name: n for n, e in syscall_table.nr_table.items() if n < 0x1000} self.syscall_table_view_ret = gdb.execute("syscall-table-view --no-pager --quiet", to_string=True) # get task res = gdb.execute("ktask --print-regs --no-pager --quiet --filter sleep", to_string=True) r = re.findall(r"(?:^|\n)(0x\S+)", res) if not r: err("Could not find `sleep` process, unable to continue") info("Do `/bin/sleep 5` in the guest (use full path), then `{:s}` again".format( self._cmdline_, )) return if len(r) != 1: err("Multiple sleep processes are found, unable to continue") return target_task = int(r[0], 16) info("The task of `sleep`: {:#x}".format(target_task)) # create option_info option_info = KmallocTracerCommand.create_option_info(args, target_task) # get rip for breakpoint # get rsp for checking process r1 = re.search(r"rip\s*: (0x\S+)", res) r2 = re.search(r"rsp\s*: (0x\S+)", res) if not r1 or not r2: err("Failed to get rip and rsp") return rip_of_sleep = int(r1.group(1), 16) rsp_of_sleep = int(r2.group(1), 16) info("The sleep's $rip: {:#x}, rsp: {:#x} (after return from nanosleep syscall)".format( rip_of_sleep, rsp_of_sleep, )) # set a hw breakpoints hwbp = KmallocAllocatedBy_UserlandHardwareBreakpoint(rip_of_sleep) # set kmalloc breakpoints (but disabled) breakpoints = KmallocTracerCommand.set_bp_to_kmalloc_kfree(option_info, self.extra_info) # wait to stop at userland `sleep` process ContextCommand.hide_context() info("Setup is complete. continuing...") gdb.execute("continue") # here, stop in userland `sleep` process if current_arch.sp != rsp_of_sleep: err("Stack pointer is different from expected. Unable to continue") self.cleanup(hwbp, breakpoints) return # rsp align gdb.execute("set $rsp = {:#x}".format(rsp_of_sleep & ~0xf)) # For some reason, setting rsp can break rip. Here's a workaround for that. gdb.execute("set $rip = {:#x}".format(rip_of_sleep)) # do test self.test_syscall(breakpoints) info("Syscall test is complete, cleaning up...") self.cleanup(hwbp, breakpoints) return class KtraceBreakpoint(gdb.Breakpoint): """Create a breakpoint to print information for kernel functions.""" def __init__(self, loc, sym, task_name_filter, task_addr_filter): super().__init__("*{:#x}".format(loc), gdb.BP_BREAKPOINT, internal=True) self.loc = loc self.sym = sym self.task_name_filter = task_name_filter self.task_addr_filter = task_addr_filter return def stop(self): Cache.reset_gef_caches() # check task task_addr, task_name = KmallocTracerCommand.get_task() if self.task_name_filter and task_name not in self.task_name_filter: return False if self.task_addr_filter and task_addr not in self.task_addr_filter: return False # get args arg_key_color = Config.get_gef_setting("theme.registers_register_name") args = [] nb_argument = 6 # guessed for i in range(nb_argument): try: key, value = current_arch.get_ith_parameter(i, in_func=True) value = AddressUtil.recursive_dereference_to_string(value) except Exception: break args.append(" {} = {}".format(Color.colorify(key, arg_key_color), value)) # random id to distinguish between nested functions import random random_id = random.randint(1, 0xffff_ffff) # print task_prefix = Color.boldify("[task:{:#018x} {:16s}]".format(task_addr, task_name)) gef_print("{:s} {:#x} <{:s}> ( // enter // random_id:{:#x}".format( task_prefix, self.loc, self.sym, random_id, )) for arg in args: gef_print(arg) gef_print(")") # set bp for return value try: KtraceRetBreakpoint(self.loc, self.sym, self.task_name_filter, self.task_addr_filter, random_id) except gdb.error: # The case is following (why?): # Warning: # Cannot insert breakpoint -440. # Cannot access memory at address 0x0 pass return False class KtraceRetBreakpoint(gdb.FinishBreakpoint): """Create a breakpoint to print information for kernel functions.""" def __init__(self, loc, sym, task_name_filter, task_addr_filter, random_id): super().__init__(gdb.newest_frame(), internal=True) self.loc = loc self.sym = sym self.task_name_filter = task_name_filter self.task_addr_filter = task_addr_filter self.random_id = random_id KernelTraceCommand.finish_breakpoints.append(self) return def stop(self): Cache.reset_gef_caches() # check task task_addr, task_name = KmallocTracerCommand.get_task() if self.task_name_filter and task_name not in self.task_name_filter: return False if self.task_addr_filter and task_addr not in self.task_addr_filter: return False # get return value arg_key_color = Config.get_gef_setting("theme.registers_register_name") # self.return_value unavailable since no type information. use current_arch.return register reg = current_arch.return_register value = AddressUtil.recursive_dereference_to_string(get_register(reg)) msg = " {} = {}".format(Color.colorify(reg, arg_key_color), value) # print task_prefix = Color.boldify("[task:{:#018x} {:16s}]".format(task_addr, task_name)) gef_print("{:s} {:#x} <{:s}> ( // return // random_id:{:#x}".format( task_prefix, self.loc, self.sym, self.random_id, )) gef_print(msg) gef_print(")") return False def out_of_scope(self): # noqa if self.enabled: self.enabled = False # print task_addr, task_name = KmallocTracerCommand.get_task() task_prefix = Color.boldify("[task:{:#018x} {:16s}]".format(task_addr, task_name)) warn("{:s} {:#x} <{:s}> (random_id{:#x}) has been disabled due to out of scope".format( task_prefix, self.loc, self.sym, self.random_id, )) return @register_command class KernelTraceCommand(GenericCommand): """Trace kernel functions and arguments.""" _cmdline_ = "ktrace" _category_ = "06-i. Qemu-system/KGDB Cooperation - Linux Dynamic Inspection" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--task-name", action="append", default=[], help="task name (from `ktask`) for filtering.") parser.add_argument("--task-addr", action="append", type=AddressUtil.parse_address, default=[], help="task address for filtering.") parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="function include filter (REGEXP).") parser.add_argument("-e", "--exclude", action="append", type=re.compile, default=[], help="function exclude filter (REGEXP).") parser.add_argument("-c", "--commit", action="store_true", help="actually perform ktrace.") parser.add_argument("-q", "--quiet", action="store_true", help="skip tqdm and displaying function name.") _syntax_ = parser.format_help() _note_ = [ "If you set breakpoints in some commonly called functions, it became too slow to be useful.", "Use filtering options to reduce the number of functions targeted by breakpoints as much as possible.", ] _note_ = "\n".join(_note_) finish_breakpoints = [] def is_valid_addr(self, addr): page_start = addr & get_pagesize_mask_low() if page_start in self.addr_range_ok_cache: return True if page_start in self.addr_range_ng_cache: return False if is_valid_addr(addr): self.addr_range_ok_cache.append(page_start) return True else: self.addr_range_ng_cache.append(page_start) return False @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64")) @only_if_in_kernel @only_if_kvm_disabled def do_invoke(self, args): info("Wait for memory scan") # check if text base is available text_base = Symbol.get_ksymaddr("_stext") if text_base is None: err("Failed to get kernel base (_stext)") return self.addr_range_ok_cache = [] self.addr_range_ng_cache = [] # list target functions res = gdb.execute("ksymaddr-remote --quiet --no-pager --type t", to_string=True) target_functions = [] tqdm = GefUtil.get_tqdm(not args.quiet) for line in tqdm(res.splitlines(), leave=False): func_addr, _, func_name = line.split() func_addr = int(func_addr, 16) # user specified filtering if args.filter and not any(filt.search(func_name) for filt in args.filter): continue if args.exclude and any(filt.search(func_name) for filt in args.exclude): continue # lower address is percpu-relative. if func_addr < text_base: continue # The function with `init` attribute may no longer exist. if not self.is_valid_addr(func_addr): continue target_functions.append([func_addr, func_name]) # check num of breakpoints info("Num of breakpoint targets: {:d}".format(len(target_functions))) if len(target_functions) > 1000: err("Too many breakpoints cause this to not work properly (>1000)") return if len(target_functions) > 100: warn("Too many breakpoints may cause this to not work properly (>100)") # debug print if not args.quiet: for func_addr, func_name in target_functions: info("{:#x} {:s}".format(func_addr, func_name)) # not commit if not args.commit: warn('This dry run mode skips executing; add "--commit" to proceed') return # set break points info("Set breakpoints in all functions that match the specified criteria") breakpoints = [] for func_addr, func_name in target_functions: bp = KtraceBreakpoint(func_addr, func_name, args.task_name, args.task_addr) breakpoints.append(bp) # Locking a thread can have disadvantages: such as making it impossible to enter commands # from the guest's terminal. Therefore, we decided not to disable it. # doit info("Setup is complete (set {:d} braekpoints). continuing...".format(len(breakpoints))) gdb.execute("continue") # clean up info("ktrace is complete, cleaning up...") for bp in breakpoints: bp.delete() while KernelTraceCommand.finish_breakpoints: bp = KernelTraceCommand.finish_breakpoints.pop() if bp.is_valid(): bp.delete() return @register_command class UefiOvmfInfoCommand(GenericCommand): """Print UEFI OVMF info.""" # https://github.com/tianocore/tianocore.github.io/wiki/OVMF-Boot-Overview # https://github.com/tianocore/edk2/blob/master/OvmfPkg/Sec/SecMain.c # https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/Pei/PeiMain/PeiMain.c # https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c # https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/BdsDxe/BdsEntry.c # https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf _cmdline_ = "uefi-ovmf-info" _category_ = "06-k. Qemu-system/KGDB Cooperation - Other" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() def check_crc32(self, addr): import crccheck size = u32(read_physmem(addr + 0xc, 0x4)) if size <= 0 or size > 0x1000: return False crc = u64(read_physmem(addr + 0x10, 0x8)) if crc == 0: return False data = read_physmem(addr, 0x10) data += p64(0x0) # crc is zero when calculate data += read_physmem(addr + 0x18, size - 0x18) calculated_crc = crccheck.crc.Crc32().calc(data) return calculated_crc == crc def read_structure(self, addr, structure): d = {} d["__addr"] = addr for size, name in structure: unpack = u32 if size == 4 else u64 d[name] = unpack(read_physmem(addr, size)) addr += size return d def search_mem_backward_iter(self, keyword): # search backward for keyword from higher address (0x800_0000), it is more likely START_ADDR = 0x800_0000 END_ADDR = 0x700_0000 current = START_ADDR - get_pagesize() data = read_physmem(current, get_pagesize()) end = len(data) while True: pos = data.rfind(keyword, 0, end) if pos == -1: current -= get_pagesize() if current < END_ADDR: return None if len(data) > len(keyword): size_of_cut = len(data) - len(keyword) data = read_physmem(current, get_pagesize()) + data[:len(keyword)] end += get_pagesize() - size_of_cut else: data = read_physmem(current, get_pagesize()) + data end += get_pagesize() continue yield current + pos end = pos def read_gPs(self): for _addr in self.search_mem_backward_iter(b"PEI SERV"): # EFI_TABLE_HEADER.Signature addr = _addr break else: return None structure = [ [8, "Hdr.Signature"], [4, "Hdr.Revision"], [4, "Hdr.HeaderSize"], [4, "Hdr.CRC32"], [4, "Hdr.Reserved"], [8, "InstallPpi"], [8, "ReInstallPpi"], [8, "LocatePpi"], [8, "NotifyPpi"], [8, "GetBootMode"], [8, "SetBootMode"], [8, "GetHobList"], [8, "CreateHob"], [8, "FfsFindNextVolume"], [8, "FfsFindNextFile"], [8, "FfsFindSectionData"], [8, "InstallPeiMemory"], [8, "AllocatePages"], [8, "AllocatePool"], [8, "CopyMem"], [8, "SetMem"], [8, "ReportStatusCode"], [8, "ResetSystem"], [8, "CpuIo"], [8, "PciCfg"], [8, "FfsFindFileByName"], [8, "FfsGetFileInfo"], [8, "FfsGetVolumeInfo"], [8, "RegisterForShadow"], [8, "FindSectionData3"], [8, "FfsGetFileInfo2"], [8, "ResetSystem2"], [8, "FreePages"], ] return self.read_structure(addr, structure) def dump_gPs(self): self.gPs = self.read_gPs() if self.gPs is None: err("Could not find gPs") return info("gPs: {:#x}".format(self.gPs["__addr"])) for k, v in self.gPs.items(): if k.startswith("__"): continue if k == "Hdr.Signature": gef_print(" {:40s}{:#x} ({!s})".format(k + ":", v, p64(v))) else: gef_print(" {:40s}{:#x}".format(k + ":", v)) return def read_mBootServices(self): for addr in self.search_mem_backward_iter(b"BOOTSERV"): # EFI_TABLE_HEADER.Signature if self.check_crc32(addr): break else: return None structure = [ [8, "Hdr.Signature"], [4, "Hdr.Revision"], [4, "Hdr.HeaderSize"], [4, "Hdr.CRC32"], [4, "Hdr.Reserved"], [8, "RaiseTPL"], [8, "RestoreTPL"], [8, "AllocatePages"], [8, "FreePages"], [8, "GetMemoryMap"], [8, "AllocatePool"], [8, "FreePool"], [8, "CreateEvent"], [8, "SetTimer"], [8, "WaitForEvent"], [8, "SignalEvent"], [8, "CloseEvent"], [8, "CheckEvent"], [8, "InstallProtocolInterface"], [8, "ReinstallProtocolInterface"], [8, "UninstallProtocolInterface"], [8, "HandleProtocol"], [8, "Reserved"], [8, "RegisterProtocolNotify"], [8, "LocateHandle"], [8, "LocateDevicePath"], [8, "InstallConfigurationTable"], [8, "LoadImage"], [8, "StartImage"], [8, "Exit"], [8, "UnloadImage"], [8, "ExitBootServices"], [8, "GetNextMonotonicCount"], [8, "Stall"], [8, "SetWatchdogTimer"], [8, "ConnectController"], [8, "DisconnectController"], [8, "OpenProtocol"], [8, "CloseProtocol"], [8, "OpenProtocolInformation"], [8, "ProtocolsPerHandle"], [8, "LocateHandleBuffer"], [8, "LocateProtocol"], [8, "InstallMultipleProtocolInterfaces"], [8, "UninstallMultipleProtocolInterfaces"], [8, "CalculateCrc32"], [8, "CopyMem"], [8, "SetMem"], [8, "CreateEventEx"], ] return self.read_structure(addr, structure) def dump_mBootServices(self): self.mBootServices = self.read_mBootServices() if self.mBootServices is None: err("Could not find mBootServices") return info("mBootServices: {:#x}".format(self.mBootServices["__addr"])) for k, v in self.mBootServices.items(): if k.startswith("__"): continue if k == "Hdr.Signature": gef_print(" {:40s}{:#x} ({!s})".format(k + ":", v, p64(v))) else: gef_print(" {:40s}{:#x}".format(k + ":", v)) return def read_mDxeServices(self): for addr in self.search_mem_backward_iter(b"DXE_SERV"): # EFI_TABLE_HEADER.Signature if self.check_crc32(addr): break else: return None structure = [ [8, "Hdr.Signature"], [4, "Hdr.Revision"], [4, "Hdr.HeaderSize"], [4, "Hdr.CRC32"], [4, "Hdr.Reserved"], [8, "AddMemorySpace"], [8, "AllocateMemorySpace"], [8, "FreeMemorySpace"], [8, "RemoveMemorySpace"], [8, "GetMemorySpaceDescriptor"], [8, "SetMemorySpaceAttributes"], [8, "GetMemorySpaceMap"], [8, "AddIoSpace"], [8, "AllocateIoSpace"], [8, "FreeIoSpace"], [8, "RemoveIoSpace"], [8, "GetIoSpaceDescriptor"], [8, "GetIoSpaceMap"], [8, "Dispatch"], [8, "Schedule"], [8, "Trust"], [8, "ProcessFirmwareVolume"], [8, "SetMemorySpaceCapabilities"], ] return self.read_structure(addr, structure) def dump_mDxeServices(self): self.mDxeServices = self.read_mDxeServices() if self.mDxeServices is None: err("Could not find mDxeServices") return info("mDxeServices: {:#x}".format(self.mDxeServices["__addr"])) for k, v in self.mDxeServices.items(): if k.startswith("__"): continue if k == "Hdr.Signature": gef_print(" {:40s}{:#x} ({!s})".format(k + ":", v, p64(v))) else: gef_print(" {:40s}{:#x}".format(k + ":", v)) return def read_mEfiSystemTable(self): for addr in self.search_mem_backward_iter(b"IBI SYST"): # EFI_TABLE_HEADER.Signature if self.check_crc32(addr): break else: return None structure = [ [8, "Hdr.Signature"], [4, "Hdr.Revision"], [4, "Hdr.HeaderSize"], [4, "Hdr.CRC32"], [4, "Hdr.Reserved"], [8, "FirmwareVendor"], [8, "FirmwareRevision"], [8, "ConsoleInHandle"], [8, "ConIn"], [8, "ConsoleOutHandle"], [8, "ConOut"], [8, "StandardErrorHandle"], [8, "StdErr"], [8, "RuntimeServices"], [8, "BootServices"], [8, "NumberOfConfigurationTableEntries"], [8, "ConfigurationTable"], ] return self.read_structure(addr, structure) def dump_mEfiSystemTable(self): self.mEfiSystemTable = self.read_mEfiSystemTable() if self.mEfiSystemTable is None: err("Could not find *gDxeCoreST(=mEfiSystemTable)") return info("*gDxeCoreST(=mEfiSystemTable): {:#x}".format(self.mEfiSystemTable["__addr"])) for k, v in self.mEfiSystemTable.items(): if k.startswith("__"): continue if k == "Hdr.Signature": gef_print(" {:40s}{:#x} ({!s})".format(k + ":", v, p64(v))) else: gef_print(" {:40s}{:#x}".format(k + ":", v)) return def read_mEfiRuntimeServicesTable(self): for addr in self.search_mem_backward_iter(b"RUNTSERV"): # EFI_TABLE_HEADER.Signature if self.check_crc32(addr): break else: return None structure = [ [8, "Hdr.Signature"], [4, "Hdr.Revision"], [4, "Hdr.HeaderSize"], [4, "Hdr.CRC32"], [4, "Hdr.Reserved"], [8, "GetTime"], [8, "SetTime"], [8, "GetWakeupTime"], [8, "SetWakeupTime"], [8, "SetVirtualAddressMap"], [8, "ConvertPointer"], [8, "GetVariable"], [8, "GetNextVariableName"], [8, "SetVariable"], [8, "GetNextHighMonotonicCount"], [8, "ResetSystem"], [8, "UpdateCapsule"], [8, "QueryCapsuleCapabilities"], [8, "QueryVariableInfo"], ] return self.read_structure(addr, structure) def dump_mEfiRuntimeServicesTable(self): self.mEfiRuntimeServicesTable = self.read_mEfiRuntimeServicesTable() if self.mEfiRuntimeServicesTable is None: err("Could not find *gDxeCoreRT(=mEfiRuntimeServicesTable)") return info("*gDxeCoreRT(=mEfiRuntimeServicesTable): {:#x}".format(self.mEfiRuntimeServicesTable["__addr"])) for k, v in self.mEfiRuntimeServicesTable.items(): if k.startswith("__"): continue if k == "Hdr.Signature": gef_print(" {:40s}{:#x} ({!s})".format(k + ":", v, p64(v))) else: gef_print(" {:40s}{:#x}".format(k + ":", v)) return def read_gMemoryMap(self): # gMemoryMap is just around mDxeServices if not self.mDxeServices: return None base = self.mDxeServices["__addr"] & ~0xf for diff in range(-0x1000, 0x1000, 8): addr = base + diff try: a = u64(read_physmem(addr, 8)) b = u64(read_physmem(a + 8, 8)) asig = u64(read_physmem(a - 8, 8)) c = u64(read_physmem(addr + 8, 8)) d = u64(read_physmem(c, 8)) csig = u64(read_physmem(c - 8, 8)) if addr == b == d and asig == csig == u32(b"mmap"): break except (gdb.MemoryError, ValueError, OverflowError): pass else: return None structure = [ [8, "ForwardLink"], [8, "BackLink"], ] return self.read_structure(addr, structure) def read_Entry(self, addr): structure = [ [8, "Signature"], [8, "Link.ForwardLink"], [8, "Link.BackLink"], [4, "FromPages"], # with pad [4, "Type"], [8, "Start"], [8, "End"], [8, "VirtualStart"], [8, "Attribute"], ] offset_of_link = 8 return self.read_structure(addr - offset_of_link, structure) def dump_memory_map(self): self.gMemoryMap = self.read_gMemoryMap() if self.gMemoryMap is None: err("Could not find gMemoryMap") return info("gMemoryMap: {:#x}".format(self.gMemoryMap["__addr"])) type_names = [ "EfiReservedMemoryType", "EfiLoaderCode", "EfiLoaderData", "EfiBootServicesCode", "EfiBootServicesData", "EfiRuntimeServicesCode", "EfiRuntimeServicesData", "EfiConventionalMemory", "EfiUnusableMemory", "EfiACPIReclaimMemory", "EfiACPIMemoryNVS", "EfiMemoryMappedIO", "EfiMemoryMappedIOPortSpace", "EfiPalCode", "EfiPersistentMemory", "EfiMaxMemoryType", ] att_list = { 0x1: "UC", 0x2: "WC", 0x4: "WT", 0x8: "WB", 0x10: "UCE", 0x1000: "WP", 0x2000: "RP", 0x4000: "XP", 0x8000: "NV", 0x1_0000: "MORE_RELIABLE", 0x2_0000: "RO", 0x4_0000: "SPM", 0x8_0000: "CPU_CRYPTO", 0x8000_0000_0000_0000: "RUNTIME" } def att2str(att): s = [] for k, v in att_list.items(): if k & att: s.append(v) return ",".join(s) fmt = "{:21s} {:10s} {:10s} {:30s} {:s}" legend = ["Paddr Start-End", "Vaddr", "Size", "Type:TypeName", "Attribute"] gef_print(GefUtil.make_legend(fmt.format(*legend))) current = self.gMemoryMap["ForwardLink"] entries = [] while current != self.gMemoryMap["__addr"]: entry = self.read_Entry(current) paddr_s = entry["Start"] paddr_e = entry["End"] + 1 vaddr = entry["VirtualStart"] size = paddr_e - paddr_s typ = entry["Type"] memtype = type_names[entry["Type"]] att = entry["Attribute"] att_s = att2str(att) entry_text = "{:#010x}-{:#010x} {:#010x} {:#010x} {:#x}:{:26s} {:#x}:[{:s}]".format( paddr_s, paddr_e, vaddr, size, typ, memtype, att, att_s, ) entries.append(entry_text) if entry["Signature"] != u32(b"mmap"): err("Signature does not match. Corrupted?") break current = entry["Link.ForwardLink"] for entry_text in sorted(set(entries)): gef_print(entry_text) gef_print("Legend for attribute") gef_print("UC: It supports being configured as Un-Cacheable") gef_print("WC: It supports being configured as Write-Combining") gef_print("WT: It supports being configured as Write-Through") gef_print("WB: It supports being configured as Write-Back") gef_print("UCE: It supports being configured as Un-Cacheable and Exportable") gef_print("WP: It supports being configured as Write-Protected") gef_print("RP: It supports being configured as Read-Protected") gef_print("XP: It supports being configured as eXecute-Protected") gef_print("NV: It refers to persistent memory(Non-Volatile-Memory)") gef_print("MORE_RELIABLE: it has higher reliability than other") gef_print("RO: It supports being configured as Read-Only") gef_print("SP: Specific-Purpose memory") gef_print("CPU_CRYPTO: Encrypted and protected by CPU function") gef_print("RUNTIME: It will be mapped by OS when SetVirtualAddressMap() is called") return @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system",)) @only_if_specific_arch(arch=("x86_32", "x86_64")) @ModuleLoader.load_crccheck def do_invoke(self, args): gef_print(titlify("SEC (Security) phase variables")) gef_print("Unimplemented") gef_print(titlify("PEI (Pre EFI Initialization) phase variables")) self.dump_gPs() gef_print(titlify("DXE (Driver Execution Environment) phase variables")) self.dump_mBootServices() self.dump_mDxeServices() self.dump_mEfiSystemTable() self.dump_mEfiRuntimeServicesTable() gef_print(titlify("Memory map for UEFI")) self.dump_memory_map() gef_print(titlify("BDS (Boot Device Selection) phase variables")) gef_print("gBS: See `mBootServices` in the DXE phase") gef_print("gST: See `mEfiSystemTable` in the DXE phase") gef_print("gRT: See `mEfiRuntimeServicesTable` in the DXE phase") return @register_command class AddSymbolTemporaryCommand(GenericCommand): """Add symbol from command temporarily.""" _cmdline_ = "add-symbol-temporary" _category_ = "01-i. Debugging Support - Other" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("function_name", metavar="FUNCTION_NAME", help="new symbol name to add.") parser.add_argument("function_start", metavar="START_ADDR", type=AddressUtil.parse_address, help="start address to add a symbol.") parser.add_argument("function_end", metavar="END_ADDR", type=AddressUtil.parse_address, nargs="?", help="end address to add a symbol.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() _example_ = [ "{0:s} your_func_name $rip $rip+0x20", ] _example_ = "\n".join(_example_).format(_cmdline_) @staticmethod def create_blank_elf(text_base, text_end): try: objcopy = GefUtil.which(Config.get_gef_setting("gef.objcopy_command")) except FileNotFoundError as e: err("{}".format(e)) return None try: gcc = GefUtil.which("gcc") except FileNotFoundError: gcc = None # create light ELF if gcc: fd, fname = GefUtil.mkstemp(prefix="add-symbol-temporary", suffix=".c") blank_elf = fname + ".elf" os.fdopen(fd, "w").write("int main() {}") # When adding symbols, it is not necessary to match the architecture of the ELF to be created # and the architecture of the debugged kernel. Regardless of the architecture of the kernel # you are debugging, create an ELF using gcc in the host environment. os.system("{!r} {!r} -no-pie -o {!r}".format(gcc, fname, blank_elf)) os.unlink(fname) # delete unneeded section for faster (`ksymaddr-remote-apply` will embed many symbols) os.system("{!r} --only-keep-debug {!r}".format(objcopy, blank_elf)) os.system("{!r} --strip-all {!r}".format(objcopy, blank_elf)) elf = Elf.get_elf(blank_elf) for s in elf.shdrs: if s.sh_name == "": # null, skip continue if s.sh_name == ".text": # .text is needed, don't remove continue if s.sh_name == ".interp": # broken if remove continue if s.sh_name == ".rela.dyn": # cannot remove continue if s.sh_name == ".dynamic": # cannot remove continue if s.sh_name == ".data": # broken if remove (e.g., add-symbol-temporary hoge 0x1234) continue if s.sh_name == ".bss": # broken if remove continue os.system("{!r} --remove-section={!r} {!r} 2>/dev/null".format( objcopy, s.sh_name, blank_elf, )) else: # not found gcc. we use pre-built elf for x64 blank_elf_skelton = [ "7f45 4c46 0201 0100 0000 0000 0000 0000 0200 3e00 0100 0000 2010 4000 0000 0000", "4000 0000 0000 0000 1803 0000 0000 0000 0000 0000 4000 3800 0c00 4000 0800 0700", "0600 0000 0400 0000 4000 0000 0000 0000 4000 4000 0000 0000 4000 4000 0000 0000", "a002 0000 0000 0000 a002 0000 0000 0000 0800 0000 0000 0000 0300 0000 0400 0000", "1803 0000 0000 0000 1803 4000 0000 0000 1803 4000 0000 0000 0000 0000 0000 0000", "1c00 0000 0000 0000 0100 0000 0000 0000 0100 0000 0400 0000 0000 0000 0000 0000", "0000 4000 0000 0000 0000 4000 0000 0000 e002 0000 0000 0000 a804 0000 0000 0000", "0010 0000 0000 0000 0100 0000 0500 0000 2000 0000 0000 0000 2010 4000 0000 0000", "2010 4000 0000 0000 0000 0000 0000 0000 f500 0000 0000 0000 0010 0000 0000 0000", "0100 0000 0400 0000 e002 0000 0000 0000 2820 4000 0000 0000 0000 0000 0000 0000", "0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 0000 0000 0100 0000 0600 0000", "480e 0000 0000 0000 483e 4000 0000 0000 483e 4000 0000 0000 0000 0000 0000 0000", "d001 0000 0000 0000 0010 0000 0000 0000 0200 0000 0600 0000 480e 0000 0000 0000", "483e 4000 0000 0000 483e 4000 0000 0000 0000 0000 0000 0000 9001 0000 0000 0000", "0800 0000 0000 0000 0400 0000 0400 0000 0000 0000 0000 0000 3803 4000 0000 0000", "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0800 0000 0000 0000", "0400 0000 0400 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000", "0000 0000 0000 0000 0000 0000 0000 0000 0800 0000 0000 0000 53e5 7464 0400 0000", "0000 0000 0000 0000 3803 4000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000", "0000 0000 0000 0000 0800 0000 0000 0000 50e5 7464 0400 0000 0000 0000 0000 0000", "0420 4000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000", "0800 0000 0000 0000 51e5 7464 0600 0000 0000 0000 0000 0000 0000 0000 0000 0000", "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0800 0000 0000 0000", "002e 7368 7374 7274 6162 002e 696e 7465 7270 002e 7265 6c61 2e64 796e 002e 7465", "7874 002e 6479 6e61 6d69 6300 2e64 6174 6100 2e62 7373 0000 0000 0000 0000 0000", "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000", "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0b00 0000 0800 0000", "0200 0000 0000 0000 1803 4000 0000 0000 1803 0000 0000 0000 1c00 0000 0000 0000", "0000 0000 0000 0000 0100 0000 0000 0000 0000 0000 0000 0000 1300 0000 0800 0000", "0200 0000 0000 0000 7804 4000 0000 0000 1803 0000 0000 0000 3000 0000 0000 0000", "0000 0000 0000 0000 0800 0000 0000 0000 1800 0000 0000 0000 1d00 0000 0800 0000", "0600 0000 0000 0000 2010 4000 0000 0000 2010 0000 0000 0000 f500 0000 0000 0000", "0000 0000 0000 0000 1000 0000 0000 0000 0000 0000 0000 0000 2300 0000 0800 0000", "0300 0000 0000 0000 483e 4000 0000 0000 480e 0000 0000 0000 9001 0000 0000 0000", "0000 0000 0000 0000 0800 0000 0000 0000 1000 0000 0000 0000 2c00 0000 0800 0000", "0300 0000 0000 0000 0040 4000 0000 0000 480e 0000 0000 0000 1000 0000 0000 0000", "0000 0000 0000 0000 0800 0000 0000 0000 0000 0000 0000 0000 3200 0000 0800 0000", "0300 0000 0000 0000 1040 4000 0000 0000 480e 0000 0000 0000 0800 0000 0000 0000", "0000 0000 0000 0000 0100 0000 0000 0000 0000 0000 0000 0000 0100 0000 0300 0000", "0000 0000 0000 0000 0000 0000 0000 0000 e002 0000 0000 0000 3700 0000 0000 0000", "0000 0000 0000 0000 0100 0000 0000 0000 0000 0000 0000 0000", ] blank_elf_skelton = bytes.fromhex("".join(blank_elf_skelton).replace(" ", "")) fd, blank_elf = GefUtil.mkstemp(prefix="add-symbol-temporary", suffix=".elf") os.fdopen(fd, "wb").write(blank_elf_skelton) elf = Elf.get_elf(blank_elf) # fix .text base address os.system("{!r} --change-section-address .text={:#x} {!r} 2>/dev/null".format( objcopy, text_base, blank_elf, )) # fix .text section size (objcopy doesn't support it, so fix it manually) data = open(blank_elf, "rb").read() new_size = text_end - text_base if elf.e_class == Elf.ELF_64_BITS: # host is 64bit seq_to_find = p64(text_base) target_offset = data.rfind(seq_to_find) + 0x10 seq_to_write = p64(new_size) else: if text_base > 0xffff_ffff: err("Unsupported adding 64 bit guest symbols when you use 32 bit host") return None seq_to_find = p32(text_base) target_offset = data.rfind(seq_to_find) + 0x8 seq_to_write = p32(new_size) data = data[:target_offset] + seq_to_write + data[target_offset + len(seq_to_write):] open(blank_elf, "wb").write(data) return blank_elf @parse_args @only_if_gdb_running def do_invoke(self, args): try: objcopy = GefUtil.which(Config.get_gef_setting("gef.objcopy_command")) except FileNotFoundError as e: err("{}".format(e)) return # check address validity max_address = AddressUtil.get_vmem_end_mask() if args.function_start > max_address: self.quiet_err("The function start address must be {:#x} or less".format(max_address)) return if args.function_end is not None: if args.function_end > max_address: self.quiet_err("The function end address must be {:#x} or less".format(max_address)) return if args.function_start > args.function_end: self.quiet_err("The function start address must be equal or less than the function end address") return # make blank ELF text_base = args.function_start & get_pagesize_mask_high() sym_elf = self.create_blank_elf(text_base, args.function_end or args.function_start + 1) if sym_elf is None: err("Failed to create blank ELF") return self.quiet_info("1 entries will be added") # embedding symbols relative_addr = args.function_start - text_base os.system("{!r} --add-symbol {!r}=.text:{:#x},global,function {!r} 2>/dev/null".format( objcopy, args.function_name, relative_addr, sym_elf, )) self.quiet_info("1 entries were processed") # add symbol to gdb try: gdb.execute("add-symbol-file {!r} {:#x}".format(sym_elf, text_base), to_string=True) except gdb.error as e: err(e) os.unlink(sym_elf) return @register_command class KsymaddrRemoteApplyCommand(GenericCommand): """Apply symbol from kallsyms in memory.""" _cmdline_ = "ksymaddr-remote-apply" _category_ = "06-e. Qemu-system/KGDB Cooperation - Linux Symbol/Type" _aliases_ = ["ks-apply"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--rescan", action="store_true", help="do not use cache.") parser.add_argument("-q", "--quiet", action="store_true", help="enable quiet mode.") _syntax_ = parser.format_help() def create_symboled_elf(self, sym_elf_path): # get .kernel range text_base = Symbol.get_ksymaddr("_stext") if text_base is None: err("Failed to get kernel base (_stext)") return False res = gdb.execute("ksymaddr-remote --quiet --no-pager", to_string=True) text_end = int(res.splitlines()[-1].split()[0], 16) # make blank ELF text_base &= get_pagesize_mask_high() blank_elf = AddSymbolTemporaryCommand.create_blank_elf(text_base, text_end) if blank_elf is None: err("Failed to create blank ELF") return False # parse kernel symbol cmd_string_arr = [] for line in res.splitlines(): addr, typ, func_name = line.split() addr = int(addr, 16) if addr < text_base: # lower address is percpu-relative. It is meaningless to import the address, so it is skipped. continue if typ in ["T", "t", "W", None]: type_flag = "function" else: type_flag = "object" if typ and typ in "abcdefghijklmnopqrstuvwxyz": global_flag = "local" else: global_flag = "global" # higher address needs relative relative_addr = addr - text_base cmd_string_arr.append("--add-symbol") cmd_string_arr.append("{:s}=.text:{:#x},{:s},{:s}".format(func_name, relative_addr, global_flag, type_flag)) self.quiet_info("{:d} entries will be added".format(len(cmd_string_arr) // 2)) # embedding symbols objcopy = GefUtil.which(Config.get_gef_setting("gef.objcopy_command")) processed_count = 0 for cmd_string_arr_sliced in slicer(cmd_string_arr, 10000 * 2): subprocess.check_output([objcopy] + cmd_string_arr_sliced + [blank_elf]) processed_count += len(cmd_string_arr_sliced) // 2 # debug print if processed_count and processed_count % 10000 == 0: self.quiet_info("{:d} entries were processed".format(processed_count)) self.quiet_info("{:d} entries were processed".format(processed_count)) os.rename(blank_elf, sym_elf_path) return True @parse_args @only_if_gdb_running @only_if_specific_gdb_mode(mode=("qemu-system", "vmware")) @only_if_specific_arch(arch=("x86_32", "x86_64", "ARM32", "ARM64", "RISCV32", "RISCV64")) @only_if_in_kernel def do_invoke(self, args): try: GefUtil.which(Config.get_gef_setting("gef.objcopy_command")) except FileNotFoundError as e: err("{}".format(e)) return self.quiet_info("Wait for memory scan") # resolve kversion for saved file name kversion = Kernel.kernel_version() h = hashlib.sha256(String.str2bytes(kversion.version_string)).hexdigest()[-16:] sym_elf_path = os.path.join(GEF_TEMP_DIR, "ks-apply-{:s}.elf".format(h)) if (not args.rescan) and os.path.exists(sym_elf_path) and os.path.getsize(sym_elf_path) > 0: self.quiet_info("A previously used file found, will be reused") else: if os.path.exists(sym_elf_path): os.unlink(sym_elf_path) ret = self.create_symboled_elf(sym_elf_path) if not ret: return # add symbol to gdb text_base = Symbol.get_ksymaddr("_stext") & get_pagesize_mask_high() cmd = "add-symbol-file {:s} {:#x}".format(sym_elf_path, text_base) self.quiet_warn("Execute `{:s}`".format(cmd)) gdb.execute(cmd) return @register_command class WalkLinkListCommand(GenericCommand, BufferingOutput): """Walk the link list.""" _cmdline_ = "walk-link-list" _category_ = "03-b. Memory - View" _aliases_ = ["chain"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-o", dest="next_offset", type=AddressUtil.parse_address, default=0, help="offset of the next(or prev) pointer in the target structure.") parser.add_argument("-A", dest="dump_bytes_after", type=AddressUtil.parse_address, default=0, help="dump bytes after link-list location.") parser.add_argument("-B", dest="dump_bytes_before", type=AddressUtil.parse_address, default=0, help="dump bytes before link-list location.") parser.add_argument("--adjust-output", type=AddressUtil.parse_address, default=0, help="displays the result of subtracting a specific value to the output.") parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="start address to walk.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0xffff9c60800597e0 # walk list_head.next", "{0:s} -o 8 0xffff9c60800597e0 # walk list_head.prev", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(complete=gdb.COMPLETE_LOCATION) return def walk_link_list(self, head, offset): indent = " " * 12 current = head seen = [current] idx = 1 while True: try: flink = read_int_from_memory(AddressUtil.align_address(current + offset)) except gdb.MemoryError: self.err_add_out("memory corrupted") return if self.args.dump_bytes_before: source = read_memory(current - self.args.dump_bytes_before, self.args.dump_bytes_before) dump = hexdump(source, base=current - self.args.dump_bytes_before, unit=current_arch.ptrsize) for line in dump.splitlines(): self.out.append(indent + line) if self.args.dump_bytes_after: source = read_memory(current, self.args.dump_bytes_after) dump = hexdump(source, base=current, unit=current_arch.ptrsize) for line in dump.splitlines(): self.out.append(indent + line) la_flink = ProcessMap.lookup_address(flink) if self.args.adjust_output: la_flink_adjusted = ProcessMap.lookup_address(flink - self.args.adjust_output) self.out.append("[{:d}] -> {!s} (adjusted: {!s})".format(idx, la_flink, la_flink_adjusted)) else: self.out.append("[{:d}] -> {!s}".format(idx, la_flink)) if flink == 0: break if flink == head: self.out[-1] += " (head)" break if flink in seen[1:]: self.err_add_out("loop detected") break seen.append(current) current = flink idx += 1 return @parse_args def do_invoke(self, args): self.out = [] self.info_add_out("head address: {:#x}".format(args.address)) self.info_add_out("next pointer offset: {:#x}".format(args.next_offset)) self.walk_link_list(args.address, args.next_offset) self.print_output() return @register_command class PeekPageFrameCommand(GenericCommand, BufferingOutput): """Read page frame data from a single address or an address range.""" _cmdline_ = "peek-pageframe" _category_ = "03-g. Memory - Investigation" _aliases_ = ["ppf"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", metavar="ADDRESS", nargs="?", type=AddressUtil.parse_address, help="address for which the pfn is read.") parser.add_argument("-f", "--from-addr", type=AddressUtil.parse_address, help="start of range.") parser.add_argument("-t", "--to-addr", type=AddressUtil.parse_address, help="end of range.") parser.add_argument("-i", "--ignore-non-present", action="store_true", help="ignores pages which are not present in the output.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x555555555060 # read pagemap of single address", "{0:s} -f 0x7ffffffdd000 -t 0x7ffffffff000 # read pagemap of an address range", ] _example_ = "\n".join(_example_).format(_cmdline_) ENTRY_SIZE = 8 def get_bit(self, x, bit): return (x >> bit) & 1 def get_pfn(self, x): return x & 0x7f_ffff_ffff_ffff def append_pfn_zero_warn(self): warn_messages = [ Color.yellowify("Pages are present but the PFN field is zeroed out"), Color.yellowify("Since kernel 4.0 only users with the CAP_SYS_ADMIN capability can get PFNs"), Color.yellowify("In kernel versions 4.2+ the PFN field is zeroed if the user does not have CAP_SYS_ADMIN"), Color.yellowify("Consider running gdb as root/sudo or adding the CAP_SYS_ADMIN capability to gdb via setcap\n"), ] self.out = warn_messages + self.out return def read_pagemap_with_virt_address(self, address, pid): page_size = get_pagesize() file_offset = (address // page_size) * self.ENTRY_SIZE path = "/proc/{:d}/pagemap".format(pid) try: with open(path, "rb") as file: file.seek(file_offset) return file.read(8) except FileNotFoundError: err("Opening {:s} failed! Could not find file".format(path)) except OSError as e: if e.errno == 1: # 1 = EPERM err("No permission to open the pagemap file") err("Only users with the CAP_SYS_ADMIN capability can get PFNs") err("In kernel versions 4.0 and 4.1 unprivileged opens fail with -EPERM") else: err("Opening {:s} failed!".format(path)) return None def get_pagemap_entry(self, address, pid): entry_bytes = self.read_pagemap_with_virt_address(address, pid) if entry_bytes is None or len(entry_bytes) < 8: err("Reading pagemap entry for address {:#x} wasn't successful".format(address)) return None entry = struct.unpack("Q", entry_bytes)[0] pfn = self.get_pfn(entry) data = { "address": address, "pfn": pfn, "entry": entry, "present": self.get_bit(entry, 63), "swapped": self.get_bit(entry, 62), "file_mapped": self.get_bit(entry, 61), "uffd_wp": self.get_bit(entry, 57), "exclusive": self.get_bit(entry, 56), "soft_dirty": self.get_bit(entry, 55), } return data def handle_address(self, address, pid): data = self.get_pagemap_entry(address, pid) if data is None: return present = data["present"] swapped = data["swapped"] file_mapped = data["file_mapped"] uffd_wp = data["uffd_wp"] exclusive = data["exclusive"] soft_dirty = data["soft_dirty"] if self.args.ignore_non_present and not present: self.out.append("Non-present page is ignored") return pfn = data["pfn"] if pfn == 0 and present: # Show the warning message only once if not getattr(self, "pfn_zero_warned", False): self.append_pfn_zero_warn() self.pfn_zero_warned = True green_yes = Color.greenify("yes") red_no = Color.redify("no") self.out.append("present: {:s}".format(green_yes if present else red_no)) self.out.append("swapped: {:s}".format(green_yes if swapped else red_no)) self.out.append("file-mapped: {:s}".format(green_yes if file_mapped else red_no)) self.out.append("soft-dirty: {:s}".format(green_yes if soft_dirty else red_no)) self.out.append("exclusively-mapped: {:s}".format(green_yes if exclusive else red_no)) self.out.append("uffd-wp: {:s}".format(green_yes if uffd_wp else red_no)) if present: self.out.append(Color.boldify("PFN: {:#x}").format(pfn)) return def handle_address_range(self, from_addr, to_addr, pid): page_size = get_pagesize() start_page = from_addr // page_size end_page = to_addr // page_size for page_num in range(start_page, end_page + 1): address = page_num * page_size data = self.get_pagemap_entry(address, pid) if data is None: continue if self.args.ignore_non_present and not data["present"]: continue pfn = data["pfn"] if pfn == 0 and data["present"]: # Show the warning message only once if not getattr(self, "pfn_zero_warned", False): self.append_pfn_zero_warn() self.pfn_zero_warned = True flags = [] flags.append("P" if data["present"] else "-") flags.append("S" if data["swapped"] else "-") flags.append("F" if data["file_mapped"] else "-") flags.append("D" if data["soft_dirty"] else "-") flags.append("E" if data["exclusive"] else "-") flags.append("U" if data["uffd_wp"] else "-") flags_str = "".join(flags) if data["pfn"] != 0: pfn_str = "{:#x}".format(data["pfn"]) else: pfn_str = "0x0" self.out.append("{:#018x} {:>8} {:s}".format(data["address"], pfn_str, flags_str)) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): pid = Pid.get_pid() if pid is None: err("Failed to read pid") return self.out = [] if args.address is not None: self.handle_address(args.address, pid) elif args.from_addr is not None and args.to_addr is not None: self.handle_address_range(args.from_addr, args.to_addr, pid) else: err("You must provide either a single address or both --from-addr and --to-addr") return self.print_output(check_terminal_size=True) return @register_command class PeekPageFlagsCommand(GenericCommand, BufferingOutput): """Read the page flags of a page frame (needs root).""" _cmdline_ = "peek-pageflags" _category_ = "03-g. Memory - Investigation" _aliases_ = ["ppfl"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("pfn", metavar="PFN", type=AddressUtil.parse_address, help="pfn of which to read flags.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} 0x6b2ae3", ] _example_ = "\n".join(_example_).format(_cmdline_) class KPageFlags: FLAGS = { "LOCKED": 1 << 0, "ERROR": 1 << 1, "REFERENCED": 1 << 2, "UPTODATE": 1 << 3, # codespell:ignore "DIRTY": 1 << 4, "LRU": 1 << 5, "ACTIVE": 1 << 6, "SLAB": 1 << 7, "WRITEBACK": 1 << 8, "RECLAIM": 1 << 9, "BUDDY": 1 << 10, "MMAP": 1 << 11, "ANON": 1 << 12, "SWAPCACHE": 1 << 13, "SWAPBACKED": 1 << 14, "COMPOUND_HEAD": 1 << 15, "COMPOUND_TAIL": 1 << 16, "HUGE": 1 << 17, "UNEVICTABLE": 1 << 18, "HWPOISON": 1 << 19, "NOPAGE": 1 << 20, "KSM": 1 << 21, "THP": 1 << 22, "OFFLINE": 1 << 23, "ZERO_PAGE": 1 << 24, "IDLE": 1 << 25, "PGTABLE": 1 << 26, } def __init__(self, value): self.value = value def get_flag(self): return self.value def get_set_flags(self): return [flag for flag, bit in self.FLAGS.items() if self.value & bit] def read_file(self, path, pfn): try: with open(path, "rb") as f: f.seek(pfn * 8) data = f.read(8) if len(data) == 8: return struct.unpack("Q", data)[0] else: raise ValueError("Could not read kpagecount for PFN {:#x}".format(pfn)) except FileNotFoundError: err("Could not open {:s}".format(path)) except PermissionError: err("No permissions to read {:s}".format(path)) err("Only the owner of {:s} (root) is able to read it, rerun gdb with proper permissions".format(path)) except Exception as e: err("Error reading kpagecount: {}".format(e)) return None def read_kpagecount(self, pfn): path = "/proc/kpagecount" return self.read_file(path, pfn) def read_kpageflags(self, pfn): path = "/proc/kpageflags" return self.read_file(path, pfn) @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware", "wine")) def do_invoke(self, args): if args.pfn is None: err("You must provide a PFN") return count = self.read_kpagecount(args.pfn) if count is None: return flags_value = self.read_kpageflags(args.pfn) if flags_value is None: return flags = self.KPageFlags(flags_value) set_flags = flags.get_set_flags() self.out = [] self.out.append("/proc/kpagecount: {:d}".format(count)) self.out.append("Pageflags: {:#x}".format(flags.get_flag())) self.out.append("Flags:") for flag in set_flags: self.out.append(" {:s}".format(flag)) self.print_output(check_terminal_size=True) return @register_command class StackFrameCommand(GenericCommand): """Display the entire stack of the current frame.""" _cmdline_ = "stack-frame" _category_ = "02-d. Process Information - Trivial Information" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args @only_if_gdb_running @require_arch_set def do_invoke(self, args): ptrsize = current_arch.ptrsize try: frame = gdb.selected_frame() except gdb.error: # gdb.selected_frame() may error for unknown reasons (often during kernel startup). err("Failed to get frame information") return if not frame.older(): reason_str = gdb.frame_stop_reason_string(frame.unwind_stop_reason()) warn("Cannot determine frame boundary, reason: {:s}".format(reason_str)) return saved_ip = frame.older().pc() stack_hi = int(frame.older().read_register("sp")) stack_lo = int(frame.read_register("sp")) results = [] if not current_arch.stack_grow_down: addr_lo = stack_lo addr_hi = stack_hi else: addr_lo = stack_hi + current_arch.ptrsize addr_hi = stack_lo + current_arch.ptrsize for offset, address in enumerate(range(addr_lo, addr_hi, ptrsize)): pprint_str = DereferenceCommand.pprint_dereferenced(addr_lo, offset) if AddressUtil.dereference(address) == saved_ip: pprint_str += " ($savedip)" results.append(pprint_str) if current_arch.stack_grow_down: results.reverse() gef_print(titlify("Stack top (higher address)")) else: gef_print(titlify("Stack top (lower address)")) for res in results: gef_print(res) if current_arch.stack_grow_down: gef_print(titlify("Stack bottom (lower address)")) else: gef_print(titlify("Stack bottom (higher address)")) return @register_command class XRefTelescopeCommand(SearchPatternCommand, BufferingOutput): """Recursively search for cross-references to a pattern in memory.""" _cmdline_ = "xref-telescope" _category_ = "03-a. Memory - Search" _repeat_ = False # re-overwrite _aliases_ = [] # re-overwrite parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("pattern", metavar="PATTERN", help="search pattern.") parser.add_argument("depth", metavar="DEPTH", nargs="?", type=int, default=1, help="max recursive depth. (default: %(default)s)") parser.add_argument("-v", "--verbose", action="store_true", help="shows the section currently being searched.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} AAAA 2 # search string with depth level 2", '{0:s} "\\\\x41\\\\x41\\\\x41\\\\x41" 2 # use double-escape string', "{0:s} 0x555555554000 2 # search value", ] _example_ = "\n".join(_example_).format(_cmdline_) def xref_telescope(self, pattern, depth, history): """Recursively search for a pattern within the whole userland memory.""" if depth <= 0: # print history for i, h in enumerate(history): if i == 0: prefix = "" else: prefix = " " * i + " -> " if isinstance(h, int): addr = ProcessMap.lookup_address(h) path = addr.section.path perm = addr.section.permission self.out.append("{:s}{!s} {:s} [{!s}]".format(prefix, addr, path, perm)) else: self.out.append("{:s}{:s}".format(prefix, h)) return if String.is_hex(pattern): if Endian.get_endian() == Elf.BIG_ENDIAN: pattern = "".join(["\\x" + pattern[i:i + 2] for i in range(2, len(pattern), 2)]) else: pattern = "".join(["\\x" + pattern[i:i + 2] for i in range(len(pattern) - 2, 0, -2)]) locs = [] for section in ProcessMap.get_process_maps_exclude_special_regions(allow_vdso=True): if not section.permission & Permission.READ: continue locs += self.search_pattern_by_address(pattern, section.page_start, section.page_end) for loc, _ustr in locs: self.xref_telescope(AddressUtil.format_address(loc), depth - 1, [loc] + history) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) def do_invoke(self, args): self.found_count = 0 # Since it inherits SearchPatternCommand, set the values to be used there. args.aligned = False args.interval = False args.limit = False args.phys = False args.hex_regex = False self.out = [] self.out.append("Recursively searching for '{:s}' in memory (depth: {:d})".format( Color.yellowify(args.pattern), args.depth, )) self.xref_telescope(args.pattern, args.depth, [args.pattern]) self.print_output(check_terminal_size=True) return @register_command class BytearrayCommand(GenericCommand): """Generate a bytearray to be compared with possible badchars (ported from mona.py).""" _cmdline_ = "bytearray" _category_ = "07-c. Misc - Generation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-b", dest="badchars", default=[], action="append", help="characters to exclude.") parser.add_argument("-d", dest="dump", action="store_true", help="dump to /tmp/gef/bytearray.{txt,bin}.") _syntax_ = parser.format_help() _example_ = [ "{0:s} -b 414243 -b 51-53 -b 61..63", ] _example_ = "\n".join(_example_).format(_cmdline_) def expand_hex(self, x): """4142..45 -> 4142434445""" if ".." not in x: return x if len(x) < 6: return False if "..." in x: return False while ".." in x: pos = x.find("..") if pos % 2 != 0: return False if pos < 2 or len(x) - 4 < pos: return False xa, xb = int(x[pos - 2:pos], 16), int(x[pos + 2:pos + 4], 16) if xa >= xb: return False middle = "".join("{:02x}".format(c) for c in range(xa, xb)) x = x[:pos - 2] + middle + x[pos + 2:] return x @parse_args def do_invoke(self, args): excluded = set() for b in args.badchars: b = b.lower().replace("-", "..") if not re.match(r"[0-9a-f]+", b): err("{:s} is not valid hex (not match `[0-9a-f]+`)".format(b)) return if (len(b) % 2) != 0: err("{:s} is not valid hex (odd length)".format(b)) return eb = self.expand_hex(b) if eb is False: err("{:s} is not valid hex (failed to expand `..`)".format(b)) return excluded |= {int(c, 16) for c in slicer(eb, 2)} info("Generating table, excluding {:d} bad chars...".format(len(excluded))) included = sorted(set(range(0, 256)) - excluded) if len(included) == 0: info("Nothing to dump") return info("Dumping table") outt_arr = [] outb_arr = [] for c in included: outt_arr.append("\\x{:02x}".format(c)) outb_arr.append(bytes([c])) bytesperline = 32 outt = "" for s in slicer(outt_arr, bytesperline): outt += '"{:s}"\n'.format("".join(s)) outb = b"".join(outb_arr) gef_print(outt.rstrip()) if args.dump: arrayfile = os.path.join(GEF_TEMP_DIR, "bytearray.txt") open(arrayfile, "w").write(outt) info("Done, wrote {:d} bytes to file {:s}".format(len(outt_arr), arrayfile)) binfilename = os.path.join(GEF_TEMP_DIR, "bytearray.bin") open(binfilename, "wb").write(outb) info("Binary output saved in {:s}".format(binfilename)) return @register_command class SixelMemoryCommand(GenericCommand): """Show image (png, jpg, bmp, etc.) to terminal by imagemagick.""" _cmdline_ = "sixel-memory" _category_ = "03-g. Memory - Investigation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address of the image.") parser.add_argument("size", metavar="SIZE", nargs="?", type=AddressUtil.parse_address, help="the size of the image.") parser.add_argument("-b", "--decode-barcode", action="store_true", help="decode barcode if found.") _syntax_ = parser.format_help() def get_jpg_size(self, start_address): # parse header pos = start_address + 2 while True: marker = read_memory(pos, 2) pos += 2 if marker[0] != 0xff: return None length = struct.unpack(">H", read_memory(pos, 2))[0] pos += length if marker == b"\xff\xda": break header_size = pos - start_address # saerch EOI if pos % get_pagesize(): read_size = get_pagesize() - (pos % get_pagesize()) else: read_size = get_pagesize() MAX_FILE_SIZE = get_pagesize() * 4096 # 16MB jpg_data = b"" # except header while len(jpg_data) < MAX_FILE_SIZE: try: jpg_data += read_memory(pos, read_size) except (gdb.MemoryError, MemoryError): return None if b"\xff\xd9" in jpg_data: image_data_size = jpg_data.index(b"\xff\xd9") + 2 return header_size + image_data_size pos += read_size read_size = get_pagesize() return None def get_png_size(self, start_address): pos = start_address if start_address % get_pagesize(): read_size = get_pagesize() - (start_address % get_pagesize()) else: read_size = get_pagesize() MAX_FILE_SIZE = get_pagesize() * 4096 # 16MB png_data = b"" while len(png_data) < MAX_FILE_SIZE: try: png_data += read_memory(pos, read_size) except (gdb.MemoryError, MemoryError): return None if b"IEND" in png_data: image_data_size = png_data.index(b"IEND") + 4 return image_data_size + 4 # crc pos += read_size read_size = get_pagesize() return None def decode_barcode(self, path): try: import PIL.Image import pyzbar.pyzbar except ImportError as e: err("Import error {}".format(e)) return image = PIL.Image.open(path) decoded = pyzbar.pyzbar.decode(image) if decoded == []: err("Not found") return for i, data in enumerate(decoded): gef_print("[{}] type:{} data:{}".format(i, data.type, data.data)) return @parse_args @only_if_gdb_running def do_invoke(self, args): try: convert_command = GefUtil.which("convert") # imagemagick except FileNotFoundError as e: err("{}".format(e)) return if not is_valid_addr(args.location): err("Memory read error") return try: if args.size is not None: size = args.size elif read_memory(args.location, 2) == b"BM": # BMP size = read_int32_from_memory(args.location + 2) elif read_memory(args.location, 2) == b"\xff\xd8": # JPG size = self.get_jpg_size(args.location) elif read_memory(args.location, 4) == b"\x89PNG": # PNG size = self.get_png_size(args.location) else: err("Specify the SIZE parameter") return data = read_memory(args.location, size) except (gdb.MemoryError, MemoryError, TypeError): err("Memory read error") return tmp_fd, tmp_path = GefUtil.mkstemp(prefix="sixel-memory", suffix=".img") os.fdopen(tmp_fd, "wb").write(data) os.system("{!r} {!r} sixel:-".format(convert_command, tmp_path)) if args.decode_barcode: self.decode_barcode(tmp_path) os.unlink(tmp_path) return @register_command class FrequencyAnalysisCommand(GenericCommand, BufferingOutput): """Visualize the frequency of occurrence of each byte.""" _cmdline_ = "freq-analysis" _category_ = "03-g. Memory - Investigation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address to analyze.") parser.add_argument("size", metavar="SIZE", nargs="?", type=AddressUtil.parse_address, help="the size to analyze; if omitted, calculated from the end of the area.") parser.add_argument("-e", "--exclude", action="append", default=[], type=lambda x: int(x, 16), help="exclude character in hex.") parser.add_argument("-a", "--ascii-gradation", action="store_true", help="show heatmap with ascii range word.") parser.add_argument("-t", "--topn", type=AddressUtil.parse_address, default=16, help="outputs the top N numbers. (default: %(default)s)") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rax 0x1000 # ragne: $rax ~ $rax + 0x1000", "{0:s} $rax # range: $rax ~ end of the region to which $rax belongs", "{0:s} $rax 0x1000 -a # use ascii compatible result", "{0:s} $rax 0x1000 -e 00 -e 01 # exclude some characters", ] _example_ = "\n".join(_example_).format(_cmdline_) def hist256(self, data): h = [0] * 256 for b in data: if b in self.args.exclude: continue h[b] += 1 return h def print_heatmap(self, h): if self.args.ascii_gradation: GRAD = ".:-=+*$#%@" else: GRAD = u" _\u2582\u2583\u2584\u2585\u2586\u2587\u2588" def scale_to_grad(value, vmax): if vmax <= 0: return GRAD[0] idx = int((value * (len(GRAD) - 1)) / vmax) return GRAD[idx] legend = "Legend: [few] {!r} [many]".format(GRAD) self.out.append(legend) col_labels = " " + " ".join(f"{x:X}" for x in range(16)) self.out.append(col_labels) vmax = max(h) if h else 0 for hi in range(16): line = [f"{hi:X} "] for lo in range(16): idx = (hi << 4) | lo ch = scale_to_grad(h[idx], vmax) line.append(ch * 1) self.out.append(" ".join(line)) self.out.append(f"Total bytes: {sum(h)} Max bin count: {vmax}") self.out.append("") return def top_n(self, h, n): order = sorted(range(256), key=lambda b: (-h[b], b)) rows = [] for i in range(min(n, 256)): b = order[i] c = h[b] if c == 0: break rows.append((b, c)) return rows def print_top_n(self, h, n): rows = self.top_n(h, n) if not rows: self.out.append("No data.") return maxc = rows[0][1] width = 40 self.out.append("Top frequencies:") for b, c in rows: bar_len = int(width * c / maxc) if maxc > 0 else 0 bar = "#" * bar_len self.out.append(f" {b:02X}: {c:>10d} |{bar}") return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.size is None: loc = ProcessMap.lookup_address(args.location) if loc.valid: size = loc.section.page_end - args.location else: err("The size could not be calculated") return else: size = args.size try: data = read_memory(args.location, size) except (gdb.MemoryError, MemoryError, TypeError): err("Memory read error") return self.out = [] hist = self.hist256(data) self.print_heatmap(hist) self.print_top_n(hist, self.args.topn) self.print_output() return @register_command class VisualDumpCommand(GenericCommand): """Visualize memory data like an image.""" _cmdline_ = "vdump" _category_ = "03-g. Memory - Investigation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("location", metavar="LOCATION", type=AddressUtil.parse_address, help="start address to dump.") parser.add_argument("size", metavar="SIZE", nargs="?", type=AddressUtil.parse_address, help="the size to dump; if omitted, calculated from the end of the area.") parser.add_argument("-d", "--disable-autoscale", action="store_true", help="disable autoscaling to fit the terminal.") parser.add_argument("-w", "--width", type=AddressUtil.parse_address, help="the number of wrap bytes. (default: sqrt(len(content)))") parser.add_argument("-c", "--color", choices=("r", "g", "b"), help="convert the grayscale tone to either r,g,b.") parser.add_argument("-n", "--negate", action="store_true", help="negate the grayscale tone.") parser.add_argument("-A", "--auto-width-inclement", action="store_true", help="repeat the display while shifting the interpretation of the width.") parser.add_argument("-Ab", "--auto-inclement-begin-width", type=AddressUtil.parse_address, default=16, help="auto inclement begin width. (default: %(default)s)") parser.add_argument("-Ae", "--auto-inclement-end-width", type=AddressUtil.parse_address, help="auto inclement end width. (default: min(len(data) // begin_width, 512)") parser.add_argument("-As", "--auto-inclement-step-width", type=AddressUtil.parse_address, default=2, help="auto inclement step width. (default: %(default)s)") _syntax_ = parser.format_help() _example_ = [ "{0:s} $rsp 0x1000 # width =~ sqrt(len(content))", "{0:s} -w 0x100 $rsp 0x1000 # use fixed width", "{0:s} -c r $rsp 0x1000 # change color: gray -> red", "{0:s} -c r -n $rsp 0x1000 # change color: gray -> red and negate", "{0:s} -A $rsp 0x1000 # bruteforce the width", "{0:s} -A -Ab 0x100 -Ae 0x200 -As 0x10 $rsp 0x1000 # bruteforce the width (w=0x100; w<0x200; w+=0x10)", ] _example_ = "\n".join(_example_).format(_cmdline_) def make_command_line(self, img_width, img_height, tmp_path): command_options = [ "-size {:d}x{:d}".format(img_width, img_height), "-depth 8", ] if not self.args.disable_autoscale: # terminal size (number of characters) term_height, term_width = GefUtil.get_terminal_size() # it's too tight, so make it slightly smaller. term_width = int(term_width * 0.95) term_height = int(term_height * 0.95) # number of pixels per character font_width_px = 6 font_height_px = 12 # pixel dimensions of the terminal term_width_px = term_width * font_width_px term_height_px = term_height * font_height_px # scaling factor to fit the terminal scale_width = term_width_px * 100 / img_width scale_height = term_height_px * 100 / img_height resize_scale = min(scale_height, scale_width) # convert option command_options.extend([ "-filter Box", "-resize {:d}%".format(int(resize_scale)), ]) if self.args.color == "r": command_options.append("-colorspace Gray -colorize 0,100,100") elif self.args.color == "g": command_options.append("-colorspace Gray -colorize 100,0,100") elif self.args.color == "b": command_options.append("-colorspace Gray -colorize 100,100,0") if self.args.negate: command_options.append("-negate") cmd = "{!r} {:s} GRAY:{!r} sixel:-".format( GefUtil.which("convert"), " ".join(command_options), tmp_path, ) return cmd @parse_args @only_if_gdb_running def do_invoke(self, args): try: GefUtil.which("convert") # imagemagick except FileNotFoundError as e: err("{}".format(e)) return if args.size is None: loc = ProcessMap.lookup_address(args.location) if loc.valid: size = loc.section.page_end - args.location else: err("The size could not be calculated") return else: size = args.size try: data = read_memory(args.location, size) except (gdb.MemoryError, MemoryError, TypeError): err("Memory read error") return if args.width and args.width > 0: img_width = args.width else: import math img_width = int(math.sqrt(len(data))) while len(data) % img_width: data += b"\0" img_height = len(data) // img_width tmp_fd, tmp_path = GefUtil.mkstemp(prefix="vhexdump", suffix=".raw") os.fdopen(tmp_fd, "wb").write(data) if args.auto_width_inclement: # if the width is too large, processing will be slow min_width = args.auto_inclement_begin_width if args.auto_inclement_end_width is not None: max_width = args.auto_inclement_end_width else: max_width = min(len(data) // min_width, 512) step = args.auto_inclement_step_width # processing while changing the width for img_width in range(min_width, max_width + 1, step): img_height = len(data) // img_width cmd = self.make_command_line(img_width, img_height, tmp_path) info(cmd) # Ctrl+C is consumed by os.system, so it cannot escape from the python loop. # Therefore, it is judged by the execution result. e = os.system(cmd) if e != 0: break else: cmd = self.make_command_line(img_width, img_height, tmp_path) info(cmd) os.system(cmd) os.unlink(tmp_path) return @register_command class FiletypeMemoryCommand(GenericCommand): """Scan memory by file and magika.""" _cmdline_ = "filetype-memory" _category_ = "03-g. Memory - Investigation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="target address.") parser.add_argument("end_address", metavar="END_ADDRESS", nargs="?", type=AddressUtil.parse_address, help="target end address. (default: the end of section of ADDRESS)") _syntax_ = parser.format_help() def filetype_memory(self, start_address, end_address, size): try: data = read_memory(start_address, size) except gdb.MemoryError: err("Memory read error") return dumpfile_name = "filetype_{:#x}-{:#x}.dat".format(start_address, end_address) filepath = os.path.join(GEF_TEMP_DIR, dumpfile_name) open(filepath, "wb").write(data) try: gef_print(titlify("file {!r}".format(filepath))) file_command = GefUtil.which("file") os.system("{!r} {!r}".format(file_command, filepath)) except FileNotFoundError as e: warn("{}".format(e)) try: gef_print(titlify("magika {!r}".format(filepath))) magika_command = GefUtil.which("magika") os.system("{!r} {!r}".format(magika_command, filepath)) except FileNotFoundError as e: warn("{}".format(e)) os.unlink(filepath) return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) def do_invoke(self, args): if not is_valid_addr(args.address): err("Memory read error") return try: start_address = args.address if args.end_address is not None: size = args.end_address - args.address else: section = ProcessMap.lookup_address(args.address).section size = section.page_end - args.address end_address = start_address + size except (AttributeError, ValueError): self.usage() return self.filetype_memory(start_address, end_address, size) return @register_command class BinwalkMemoryCommand(GenericCommand): """Scan memory by binwalk.""" _cmdline_ = "binwalk-memory" _category_ = "03-g. Memory - Investigation" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="REGEXP include filter.") parser.add_argument("-e", "--exclude", action="append", type=re.compile, default=[], help="REGEXP exclude filter.") parser.add_argument("-m", "--maxsize", type=AddressUtil.parse_address, default=0x1000_0000, help="maximum size of a section to be dumped. (default: 256 MB)") parser.add_argument("-c", "--commit", action="store_true", help="actually perform binwalk.") _syntax_ = parser.format_help() def memory_binwalk(self): import binwalk maps = ProcessMap.get_process_maps_exclude_special_regions(allow_vdso=True) if maps is None: err("Failed to get maps") return addr_len = current_arch.ptrsize * 2 for entry in maps: start = entry.page_start end = entry.page_end perm = str(entry.permission) if entry.size > self.args.maxsize: continue if not entry.path.startswith(("[", "<")): path = os.path.basename(entry.path) else: path = entry.path path = path.replace("[", "").replace("]", "") # consider [heap], [stack], [vdso] path = path.replace("<", "").replace(">", "") # consider , path = path.replace(" ", "_") # consider deleted case. e.g., /path/to/file (deleted) dumpfile_name = "binwalk-{:0{}x}-{:0{}x}_{:s}_{:s}.raw".format( start, addr_len, end, addr_len, perm, path, ) if self.args.filter and not any(filt.search(dumpfile_name) for filt in self.args.filter): continue if self.args.exclude and any(ex.search(dumpfile_name) for ex in self.args.exclude): continue filepath = os.path.join(GEF_TEMP_DIR, dumpfile_name) if self.args.commit: gef_print(titlify("{:#x}-{:#x} [{}] {:s}".format( entry.page_start, entry.page_end, entry.permission, entry.path, ))) try: data = read_memory(start, end - start) except gdb.MemoryError: continue open(filepath, "wb").write(data) binwalk.scan(filepath, signature=True) os.unlink(filepath) else: gef_print(dumpfile_name) if not self.args.commit: warn('This dry run mode skips executing binwalk; add "--commit" to proceed') return @parse_args @only_if_gdb_running @exclude_specific_gdb_mode(mode=("qemu-system", "kgdb", "vmware")) @ModuleLoader.load_binwalk def do_invoke(self, args): self.memory_binwalk() return @register_command class BincompareCommand(GenericCommand, BufferingOutput): """Compare an binary file with the memory position looking for badchars.""" _cmdline_ = "bincompare" _category_ = "03-c. Memory - Compare" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("filename", metavar="FILENAME", help="specifies the binary file to be compared.") parser.add_argument("address", metavar="ADDRESS", type=AddressUtil.parse_address, help="specifies the memory address.") parser.add_argument("size", metavar="SIZE", nargs="?", type=AddressUtil.parse_address, help="specifies the size.") parser.add_argument("--file-offset", type=AddressUtil.parse_address, default=0, help="specifies the file offset.") parser.add_argument("-f", "--full", action="store_true", help="display the same line without omitting.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete=gdb.COMPLETE_FILENAME) return def compare(self, from1data, from2data, size): diff_found = False asterisk = False hex_pad_len = { 1: 37, 2: 35, 3: 32, 4: 30, 5: 27, 6: 25, 7: 22, 8: 20, 9: 17, 10: 15, 11: 12, 12: 9, 13: 7, 14: 5, 15: 2, 16: 0, } width = len(hex(size)) for pos in range(0, size, 16): # determining continuity f1_bin = from1data[pos : pos + 16] f2_bin = from2data[pos : pos + 16] if not self.args.full: if f1_bin == f2_bin: if asterisk is False: self.out.append("*") asterisk = True continue diff_found = True asterisk = False # coloring f1_hex = [] f2_hex = [] f1_ascii = [] f2_ascii = [] for i in range(min(len(f1_bin), 16)): if f1_bin[i] == f2_bin[i]: color_func = lambda x: x else: color_func = Color.boldify f1_hex.append(color_func("{:02x}".format(f1_bin[i]))) f2_hex.append(color_func("{:02x}".format(f2_bin[i]))) f1_ascii.append(color_func(chr(f1_bin[i]) if 0x20 <= f1_bin[i] < 0x7f else ".")) f2_ascii.append(color_func(chr(f2_bin[i]) if 0x20 <= f2_bin[i] < 0x7f else ".")) # formatting # ["00", "00", "00" "00", ...] -> ["0000", "0000", ...] f1_hex2 = ["".join(x) for x in slicer(f1_hex, 2)] f2_hex2 = ["".join(x) for x in slicer(f2_hex, 2)] # padding # ["0000", "0000", ...] -> "0000 0000 ..." f1_hex_s = " ".join(f1_hex2) + " " * hex_pad_len[len(f1_hex)] f2_hex_s = " ".join(f2_hex2) + " " * hex_pad_len[len(f2_hex)] # [".", ".", ...] -> "................" f1_ascii_s = "".join(f1_ascii) + " " * (16 - len(f1_ascii)) f2_ascii_s = "".join(f2_ascii) + " " * (16 - len(f2_ascii)) # make line addr1 = "{:#{:d}x}".format(self.args.file_offset + pos, width) addr2 = ProcessMap.lookup_address(self.args.address + pos) self.out.append("{:s}: {:s} |{:s}| {!s}: {:s} |{:s}|".format( addr1, f1_hex_s, f1_ascii_s, addr2, f2_hex_s, f2_ascii_s, )) if diff_found is False: self.info_add_out("No difference") return @parse_args @only_if_gdb_running def do_invoke(self, args): # file_data if not os.path.isfile(args.filename): err("Specified file '{:s}' does not exist".format(args.filename)) return file_data = open(args.filename, "rb").read() file_data = file_data[args.file_offset:] file_size = len(file_data) # size if args.size is None: size = file_size else: if args.size > file_size: err("The file size is too short") return size = args.size file_data = file_data[:size] if size == 0: err("Comparing size is 0, nothing to do") return # memory_data try: memory_data = read_memory(args.address, size) except gdb.MemoryError: err("Cannot reach memory {:#x}".format(args.address)) return self.out = [] self.compare(file_data, memory_data, size) self.print_output(check_terminal_size=True) return @register_command class SymbolsCommand(GenericCommand, BufferingOutput): """List all symbols (shortcut for `maintenance print msymbols`) with coloring.""" _cmdline_ = "symbols" _category_ = "02-g. Process Information - Symbol" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-t", "--type", action="append", default=[], help="filter by symbol type.") parser.add_argument("-c", "--use-cache", action="store_true", help="use previous result.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() def get_build_id(self, filename): e = Elf.get_elf(filename) if e is None or not e.is_valid(): return None try: build_id = e.read_shdr(".note.gnu.build-id") except Exception: return None return build_id def get_build_ids(self): build_id_dict = {} for filename in ProcessMap.get_loaded_files(): build_id = self.get_build_id(filename) if build_id is None: continue build_id_dict[build_id] = filename return build_id_dict def get_symbols(self): """Parse and display symbol information from GDB, including file/object info and symbol addresses, with filtering.""" ret = gdb.execute("maintenance print msymbols", to_string=True).strip() SYMBOL_INFO_LINE_PATTERN = re.compile(r"^(\[ ?\d+\]) (.) (0x[0-9a-f]+) (.*)") SYMBOL_FILE_PATTERN = re.compile(r"^Object file (/.*):$") build_id_dict = self.get_build_ids() tqdm = GefUtil.get_tqdm(not self.args.quiet) for line in tqdm(ret.splitlines(), leave=False): line = line.strip() # blank line if not line: self.out.append(line) continue # filename line # e.g., Object file /root/.cache/debuginfod_client/1c8db5f83bba514f8fd5f1fb6d7be975be1bb855/debuginfo: r = SYMBOL_FILE_PATTERN.search(line) if r: self.out.append(line) filename = r.group(1) build_id = self.get_build_id(filename) if build_id in build_id_dict: if build_id_dict[build_id] != filename: self.out.append("(={:s})".format(build_id_dict[build_id])) continue # symbol info line # e.g., [2875] T 0x7ffff7cad650 malloc section .text sofini.c r = SYMBOL_INFO_LINE_PATTERN.search(line) if r: # type filtering typ = r.group(2) if self.args.type: if typ.lower() not in self.args.type: continue # invalid address filtering addr = int(r.group(3), 16) if not is_valid_addr(addr): self.out.append(line) continue addr = ProcessMap.lookup_address(addr) index = r.group(1) remain = r.group(4) self.out.append("{:s} {:s} {!s} {:s}".format(index, typ, addr, remain)) continue # other line self.out.append(line) return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.use_cache and hasattr(self, "cache") and self.cache: self.out = self.cache[::] self.print_output() return self.out = [] self.get_symbols() self.print_output() self.cache = self.out[::] return @register_command class TypesCommand(GenericCommand, BufferingOutput): """List all types (shortcut for `info types`) with compaction.""" _cmdline_ = "types" _category_ = "02-h. Process Information - Type" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-s", "--smart", action="store_true", help="temporarily override by `context.smart_cpp_function_name = True`.") parser.add_argument("-f", "--filter", action="append", type=re.compile, default=[], help="REGEXP include filter.") parser.add_argument("-e", "--exclude", action="append", type=re.compile, default=[], help="REGEXP exclude filter.") parser.add_argument("-E", "--no-enum", action="store_true", help="without enum.") parser.add_argument("-S", "--no-struct", action="store_true", help="without struct.") parser.add_argument("-T", "--no-typedef", action="store_true", help="without typedef.") parser.add_argument("-U", "--no-union", action="store_true", help="without union.") parser.add_argument("-c", "--use-cache", action="store_true", help="use previous result.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") parser.add_argument("-v", "--verbose", action="store_true", help="with the output of `dt` command.") _syntax_ = parser.format_help() def get_type_names(self): """Parse and filter type names from GDB's 'info types', skipping basic types and applying user-specified filters.""" basic_types = [ "char", "unsigned char", "signed char", "int", "unsigned int", "signed int", "short", "unsigned short", "signed short", "long", "unsigned long", "signed long", "long long", "unsigned long long", "signed long long", "float", "double", "long double", "void", "bool", ] ret = gdb.execute("info types", to_string=True).strip() type_names = [] for line in ret.splitlines(): if line == "All defined types:": continue if line == "": continue if line.startswith("File "): continue line = re.sub(r"^\d+:|;$", "", line).strip() if line in basic_types: continue if self.args.filter and not any(filt.search(line) for filt in self.args.filter): continue if self.args.exclude and any(filt.search(line) for filt in self.args.exclude): continue if self.args.no_enum and line.startswith("enum "): continue if self.args.no_struct and line.startswith("struct "): continue if self.args.no_typedef and line.startswith("typedef "): continue if self.args.no_union and line.startswith("union "): continue type_names.append(line) type_names = sorted(set(type_names)) return type_names def get_types(self): """Display type information for each type name, optionally using verbose output and smart formatting.""" type_names = self.get_type_names() # temporarily changed if self.args.smart: old_smart_setting = Config.get_gef_setting("context.smart_cpp_function_name") Config.set_gef_setting("context.smart_cpp_function_name", True) # formatting typenames tqdm = GefUtil.get_tqdm() for type_name in tqdm(type_names, leave=False): if self.args.verbose: ret = gdb.execute("dt -n {!r}".format(type_name), to_string=True) if not ret or (" is not struct or union" in ret) or ("Could not find " in ret): self.out.append(Instruction.smartify_text(type_name)) self.out.append("") continue self.out.append(ret) else: self.out.append(Instruction.smartify_text(type_name)) # revert if self.args.smart: Config.set_gef_setting("context.smart_cpp_function_name", old_smart_setting) return @parse_args @only_if_gdb_running def do_invoke(self, args): if args.use_cache and hasattr(self, "cache") and self.cache: self.out = self.cache[::] self.print_output() return self.out = [] self.get_types() self.print_output() self.cache = self.out[::] return @register_command class GefCommand(GenericCommand): """The base command of GEF maintenance.""" _cmdline_ = "gef" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) subparsers = parser.add_subparsers(title="command") subparsers.add_parser("missing") subparsers.add_parser("config") subparsers.add_parser("save") subparsers.add_parser("restore") subparsers.add_parser("reload") subparsers.add_parser("reset-breakpoint") subparsers.add_parser("reset-cache") subparsers.add_parser("arch-list") subparsers.add_parser("raise-exception") subparsers.add_parser("pyobj-list") subparsers.add_parser("avail-comm-list") subparsers.add_parser("set-arch") subparsers.add_parser("status") subparsers.add_parser("version") subparsers.add_parser("check-update") subparsers.add_parser("tmux-setup") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=True) self.add_setting("follow_child", False, "Automatically set GDB to follow child when forking") self.add_setting("disable_color", False, "Disable all colors in GEF") self.add_setting("always_no_pager", False, "Always disable pager in gef_print()") self.add_setting("pager_min_lines", 11, "Show pager only if output is longer than this value") self.add_setting("keep_pager_result", False, "Leaves temporary files in gef_print()") self.add_setting("less_option", "-Rf -j.5", "LESS command option used in gef_print()") # binutils for cross-arch self.add_setting("nm_command", "nm", "nm command (executable name or path)") self.add_setting("objcopy_command", "objcopy", "objcopy command (executable name or path)") self.add_setting("objdump_command", "objdump", "objdump command (executable name or path)") self.add_setting("readelf_command", "readelf", "readelf command (executable name or path)") self.add_setting("cppfilt_command", "c++filt", "c++filt command (executable name or path)") # workarounds self.add_setting("readline_compat", False, "Workaround for readline SOH/ETX issue (SEGV)") self.add_setting("read_memory_work_around_for_aarch64_secure_memory", False, "Workaround for AArch64 secure memory read_memory failures") self.add_setting("physmap_base_for_read_physmem_kgdb_work_around", 0, "Use this address as physmap_base to read physmem if read_physmem is slow in KGDB") # other self.add_setting("kgdb_force", False, "Force-enable KGDB mode (for agent-proxy)") self.add_setting("kgdb_system_registers", False, "Whether KGDB provides access to system registers") return @parse_args def do_invoke(self, args): gdb.execute("gef help") # for frequent use return @register_command class GefHelpCommand(GenericCommand, BufferingOutput): """Display GEF command list.""" _cmdline_ = "gef help" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def generate_help(self): """Generate builtin commands documentation.""" # get maxlen of cmdline maxlen = max(len(x) for x in __gef_command_instances__.keys()) # docs of each command docs = [] for cmdline, instance in __gef_command_instances__.items(): # category category = getattr(instance, "_category_", "Uncategorized") # document doc = getattr(instance.__class__, "__doc__", "").lstrip() doc = [Color.greenify(x) for x in doc.splitlines()] # Adjust the position assuming there are multiple lines doc = ("\n{:<{:d}s} ".format("", maxlen)).join(doc).rstrip() # alias if hasattr(instance._aliases_, "__iter__"): if not instance._aliases_: aliases = "" elif isinstance(instance._aliases_, str): aliases = " (alias: {:s})".format(instance._aliases_) else: aliases = " (alias: {:s})".format(", ".join(instance._aliases_)) else: aliases = "" msg = " {:<{:d}s} -- {:s}{:s}".format(cmdline, maxlen, doc, aliases) docs.append([category, msg]) docs = sorted(docs) def get_major_category(x): if x is None: return None return x.split(". ")[1].split(" - ")[0] # merging output = [] old_category = None for category, msg in docs: # separator draw_separator = False if get_major_category(old_category) != get_major_category(category): draw_separator = True if draw_separator: separator = titlify(get_major_category(category)) output.append(separator) # headings if old_category != category: category_headings = "[{:s}]".format(Color.colorify(category, "bold yellow")) output.append(category_headings) old_category = category output.append(msg) return output @parse_args def do_invoke(self, args): self.out = [] self.out.append(titlify("GEF - GDB Enhanced Features")) self.out.extend(self.generate_help()) self.print_output() return @register_command class GefConfigCommand(GenericCommand): """Display or change GEF configuration.""" _cmdline_ = "gef config" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("setting_name", metavar="SETTING_NAME", nargs="?", help="setting name.") parser.add_argument("setting_value", metavar="SETTING_VALUE", nargs="?", help="setting value.") parser.add_argument("-s", "--show-only-changes", action="store_true", help="show only changed settings.") _syntax_ = parser.format_help() def __init__(self): super().__init__(complete="use_user_complete") return def print_setting(self, config_name, with_description=False, show_only_changes=False): """Print a GEF configuration setting, with optional description and original value, highlighting changes.""" res = Config.__gef_config__.get(config_name) res_orig = Config.__gef_config_orig__.get(config_name) # something is wrong if not res or not res_orig: return string_color = Config.get_gef_setting("theme.dereference_string") misc_color = Config.get_gef_setting("theme.dereference_base_address") value, type_, desc = res value_orig, _, _ = res_orig setting = Color.colorify(config_name, "green") type_name = type_.__name__ if type_name == "str": value = '"{:s}"'.format(Color.colorify(value, string_color)) value_orig = '"{:s}"'.format(Color.colorify(value_orig, string_color)) else: value = Color.colorify(value, misc_color) value_orig = Color.colorify(value_orig, misc_color) if show_only_changes: if value != value_orig: gef_print("{:s} ({:s}) = {:s} (orig: {:s})".format(setting, type_name, value, value_orig)) # do not print the description return gef_print("{:s} ({:s}) = {:s}".format(setting, type_name, value)) if with_description: if value != value_orig: gef_print("") gef_print(Color.colorify("Original value:", "bold underline")) gef_print("{:s} ({:s}) = {:s}".format(setting, type_name, value_orig)) gef_print("") gef_print(Color.colorify("Description:", "bold underline")) gef_print("{:s}".format(desc)) return def set_setting(self, config_name, config_value): """Set a GEF configuration value, validating type and command, and updating the cache.""" if "." not in config_name: err("Invalid command format") return loaded_cmdlines = [x.replace(" ", "_").replace("-", "_") for x in __gef_command_instances__.keys()] command_name = config_name.split(".", 1)[0] if command_name not in loaded_cmdlines: err("Unknown command '{:s}'".format(command_name)) return type_ = Config.__gef_config__.get(config_name, [None, None, None])[1] if type_ is None: err("Failed to get '{:s}' config setting".format(config_name)) return try: if type_ is bool: if config_value.upper() in ("TRUE", "T", "1"): newval = True else: newval = False else: newval = type_(config_value) except Exception: err("{} expects type '{}'".format(config_name, type_.__name__)) return Config.__gef_config__[config_name][0] = newval Cache.reset_gef_caches(all=True) return def complete(self, text, word): # noqa """Provide tab-completion suggestions for GEF config settings based on user input.""" settings = sorted(Config.__gef_config__) if text.strip() in settings: # already matched return [] if text == "": # no prefix: example: `gef config TAB` return [s for s in settings if ((word is None) or (s and word in s))] if "." not in text: # if looking for possible prefix return [s for s in settings if s.startswith(text.strip())] # finally, look for possible values for given prefix return [s.split(".", 1)[1] for s in settings if s and s.startswith(text.strip())] @parse_args def do_invoke(self, args): # list all configs if (args.setting_name, args.setting_value) == (None, None): gef_print(titlify("GEF configuration settings")) for name in sorted(Config.__gef_config__): self.print_setting(name, show_only_changes=args.show_only_changes) return # show name-matched config(s) if args.setting_name and args.setting_value is None: names = [x for x in Config.__gef_config__.keys() if x.startswith(args.setting_name)] if not names: return if len(names) == 1 or (args.setting_name in Config.__gef_config__): # uniquely identified or exact match gef_print(titlify("GEF configuration setting: {:s}".format(names[0]))) self.print_setting(names[0], with_description=True, show_only_changes=args.show_only_changes) else: gef_print(titlify("GEF configuration settings matching '{:s}'".format(args.setting_name))) for name in names: self.print_setting(name, show_only_changes=args.show_only_changes) return # set config value self.set_setting(args.setting_name, args.setting_value) return @register_command class GefSaveCommand(GenericCommand): """Save the current settings to '~/.gef.rc'.""" _cmdline_ = "gef save" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): cfg = configparser.RawConfigParser() old_sect = None # save the configuration for key in sorted(Config.__gef_config__): sect, optname = key.split(".", 1) value = Config.__gef_config__.get(key, None) value = value[0] if value else None if old_sect != sect: cfg.add_section(sect) old_sect = sect cfg.set(sect, optname, value) # save the aliases cfg.add_section("user-defined-aliases") cfg.add_section("user-defined-aliases.repeat") for alias in __gef_alias_instances__.values(): # check pre-defined alias or not if alias._command_ in __gef_command_instances__: instance = __gef_command_instances__[alias._command_] if alias._alias_ in instance._aliases_: continue cfg.set("user-defined-aliases", alias._alias_, alias._command_) cfg.set("user-defined-aliases.repeat", alias._alias_, str(alias._repeat_)) with open(GEF_RC, "w") as fd: cfg.write(fd) self.quiet_ok("Configuration saved to '{:s}'".format(GEF_RC)) return @register_command class GefRestoreCommand(GenericCommand): """Load settings from '~/.gef.rc'.""" _cmdline_ = "gef restore" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-q", "--quiet", action="store_true", help="quiet execution.") _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): if not os.access(GEF_RC, os.R_OK): self.quiet_info("Could not find {:s}, GEF uses default settings".format(GEF_RC)) return cfg = configparser.ConfigParser() cfg.read(GEF_RC) for section in cfg.sections(): if section == "user-defined-aliases.repeat": continue if section == "user-defined-aliases": # load the aliases for key in cfg.options(section): repeat = cfg.get("user-defined-aliases.repeat", key) GefAlias(key, cfg.get("user-defined-aliases", key), force_repeat=repeat) continue # load the other options for optname in cfg.options(section): # warn unused setting key = "{:s}.{:s}".format(section, optname) if key not in Config.__gef_config__: err("Config '{:s}' is no longer in use, skipping...".format(Color.boldify(key))) continue # restore type Type = Config.__gef_config__.get(key)[1] new_value = cfg.get(section, optname) try: if Type is bool: if new_value == "True": new_value = True elif new_value == "False": new_value = False else: raise ValueError else: new_value = Type(new_value) except ValueError: err("Config '{:s}' has bad value, skipping...".format(Color.boldify(key))) continue # set Config.__gef_config__[key][0] = new_value # ensure that the temporary directory always exists abspath = os.path.expanduser(GEF_TEMP_DIR) abspath = os.path.realpath(abspath) if not os.path.isdir(abspath): os.makedirs(abspath, mode=0o755, exist_ok=True) self.quiet_ok("Configuration from '{:s}' restored".format(Color.colorify(GEF_RC, "bold blue"))) return @register_command class GefMissingCommand(GenericCommand): """Display the GEF commands that could not be loaded with the reason.""" _cmdline_ = "gef missing" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() _note_ = [ "This command only detects commands that could not be loaded when GEF started.", "To speed up startup, some commands lazy load required modules and dependencies.", "These command cannot be detected.", ] _note_ = "\n".join(_note_) @parse_args def do_invoke(self, args): missing_commands = Gef.missing_commands.keys() if not missing_commands: ok("No missing command") return for missing_command in missing_commands: reason = Gef.missing_commands[missing_command] warn("Command `{}` is missing, reason -> {}".format(missing_command, reason)) return @register_command class GefReloadCommand(GenericCommand): """Reload the GEF.""" _cmdline_ = "gef reload" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): info("Check syntax {:s}".format(GEF_FILEPATH)) try: pythonbin = GefUtil.which("python3") except FileNotFoundError as e: err("{}, failed to reload".format(e)) return try: subprocess.check_output([pythonbin, GEF_FILEPATH]) except subprocess.CalledProcessError: err("Reload aborted") return EventHooking.gef_on_continue_unhook(EventHandler.continue_handler) EventHooking.gef_on_stop_unhook(EventHandler.hook_stop_handler) EventHooking.gef_on_new_unhook(EventHandler.new_objfile_handler) EventHooking.gef_on_exit_unhook(EventHandler.exit_handler) EventHooking.gef_on_memchanged_unhook(EventHandler.memchanged_handler) EventHooking.gef_on_regchanged_unhook(EventHandler.regchanged_handler) Cache.reset_gef_caches(all=True) info("Reload {:s}".format(GEF_FILEPATH)) s = gdb.execute("source {:s}".format(GEF_FILEPATH), to_string=True) for line in s.splitlines(): if ".gnu_debugaltlink" in line: continue if "No debugging symbols" in line: continue gef_print(line) if current_arch is None: set_arch(get_arch()) if not (is_qemu_user() or is_pin()): gdb.execute("define c\ncontinue\nend") Cache.reset_gef_caches(all=True) return @register_command class GefResetCacheCommand(GenericCommand): """Reset all caches (both Cache.cache_until_next and Cache.cache_this_session).""" _cmdline_ = "gef reset-cache" _category_ = "99. GEF Maintenance Command" _aliases_ = ["reset-cache"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--hard", action="store_true", help="also delete under {:s}.".format(GEF_TEMP_DIR)) _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): Cache.reset_gef_caches(all=True) if args.hard: GefUtil.rmdir(GEF_TEMP_DIR, verbose=True, keep_root=True) return @register_command class GefResetBreakpointsCommand(GenericCommand): """Show and reset all breakpoints (include internal breakpoints).""" _cmdline_ = "gef reset-breakpoint" _category_ = "99. GEF Maintenance Command" _aliases_ = ["reset-breakpoint"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-c", "--commit", action="store_true", help="actually perform delete.") _syntax_ = parser.format_help() def get_breakpoint(self, b): if hasattr(b, "locations"): # gdb 13.1~ for bl in b.locations: if bl and bl.address is not None: return bl.address else: # for old gdb if b.location and b.location.startswith("*"): pos = b.location.lstrip("*") try: return int(pos, 16) except ValueError: pass return None @parse_args def do_invoke(self, args): breakpoints = gdb.breakpoints() n = len(breakpoints) for bp in breakpoints: bp_str = repr(bp) bp_addr = self.get_breakpoint(bp) if args.commit: bp.delete() if bp_addr is not None: gef_print("Delete successfully: {:s} (@{:#x})".format(bp_str, bp_addr)) else: gef_print("Delete successfully: {:s}".format(bp_str)) else: if bp_addr is not None: info("Breakpoint is found: {:s} (@{:#x})".format(bp_str, bp_addr)) else: info("Breakpoint is found: {:s}".format(bp_str)) if not args.commit and n > 0: warn('This dry run mode skips deleting breakpoint; add "--commit" to proceed') return @register_command class GefArchListCommand(GenericCommand, BufferingOutput): """Display defined architecture information.""" _cmdline_ = "gef arch-list" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def dump_arch_info(self, arch): """Display detailed architecture information, including bit length, endianness, registers, and feature support.""" if arch.arch == "HPPA" and arch.mode == "64": # Currently, HPPA-64 is unsupported. # A definition for displaying syscalls is provided, but it is provisional. return # title if arch.arch == "ARM": arch_name = "ARM (ARM/THUMB)" elif arch.arch == arch.mode: arch_name = arch.arch else: arch_name = "{:s} {:s}".format(arch.arch, arch.mode) self.out.append(titlify(arch_name)) # settings self.out.append("{:30s} -> {!s}".format("bit length", arch.bit_length)) self.out.append("{:30s} -> {!s}".format("endianness", arch.endianness)) if arch.arch == "ARM": inst_len = "ARM:4 / THUMB:2or4" elif arch.instruction_length is None: inst_len = "variable length" else: inst_len = str(arch.instruction_length) self.out.append("{:30s} -> {!s}".format("instruction length", inst_len)) if arch.return_register is None: ret_regs = "different for each system call" else: ret_regs = str(arch.return_register) self.out.append("{:30s} -> {!s}".format("return register", ret_regs)) fparams = ", ".join(arch.function_parameters) if len(arch.function_parameters) == 1: fparams += " (passing via stack)" self.out.append("{:30s} -> {!s}".format("function parameters", fparams)) self.out.append("{:30s} -> {!s}".format("syscall register", arch.syscall_register)) if arch.syscall_parameters is None: sparams = "different for each system call" else: sparams = ", ".join(arch.syscall_parameters) self.out.append("{:30s} -> {!s}".format("syscall parameters", sparams)) self.out.append("{:30s} -> {!s}".format("Has a call/jump delay slot", arch.has_delay_slot)) self.out.append("{:30s} -> {!s}".format("Has a syscall delay slot", arch.has_syscall_delay_slot)) self.out.append("{:30s} -> {!s}".format("Has a ret delay slot", arch.has_ret_delay_slot)) self.out.append("{:30s} -> {!s}".format("Stack grow down", arch.stack_grow_down)) self.out.append("{:30s} -> {!s}".format("Thread Local Storage support", arch.tls_supported)) self.out.append("{:30s} -> {!s}".format("keystone support", arch.keystone_support)) self.out.append("{:30s} -> {!s}".format("capstone support", arch.capstone_support)) self.out.append("{:30s} -> {!s}".format("unicorn support", arch.unicorn_support)) return def listup_arch_info(self): queue = Architecture.__subclasses__() while queue: cls = queue.pop(0) self.dump_arch_info(cls()) queue = cls.__subclasses__() + queue return @parse_args def do_invoke(self, args): self.out = [] self.listup_arch_info() self.print_output() return @register_command class GefRaiseExceptionCommand(GenericCommand): """Raise an exception for development.""" _cmdline_ = "gef raise-exception" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): raise RuntimeError("Test exception") @register_command class GefPyObjListCommand(GenericCommand, BufferingOutput): """Display defined global python object.""" _cmdline_ = "gef pyobj-list" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def listup_pyobject(self): skip_name_list = [ "__name__", "__loader__", "__doc__", "__spec__", "__package__", "__annotations__", "__warningregistry__", "GdbRemoveReadlineFinder", ] skip_type_list = [ type(re.compile("")), # regex object type(sys), # module ] function_type = type(lambda x:x) class_type = type(GefCommand) arch_list = [] queue = Architecture.__subclasses__() while queue: cls = queue.pop(0) arch_list.append(cls) queue = cls.__subclasses__() + queue global_configs = [] classes = [] command_classes = [] bp_classes = [] arch_classes = [] arch_determinations = [] gdb_mode_determinations = [] decorators = [] syscall_defines = [] gef_print_wrappers = [] read_write_mems = [] others = [] for gobj in dir(sys.modules["__main__"]): # for global object # skip specific if gobj in skip_name_list: continue obj = getattr(sys.modules["__main__"], gobj) t = type(obj) # skip specific type if t in skip_type_list: continue # classify if gobj.startswith("__") and t is not function_type: global_configs.append("{!s} {!s}".format(t, gobj)) elif gobj in ["current_arch"]: t = type(get_current_arch()) global_configs.append("{!s} {!s}".format(t, gobj)) elif gobj.upper() == gobj and t is not class_type: global_configs.append("{!s} {!s}".format(t, gobj)) elif t is class_type: if gobj.endswith("Command"): command_classes.append("{!s} {!s}".format(t, gobj)) elif gobj.endswith("Breakpoint") or gobj.endswith("Watchpoint"): bp_classes.append("{!s} {!s}".format(t, gobj)) elif obj in arch_list: arch_classes.append("{!s} {!s}".format(t, gobj)) else: classes.append("{!s} {!s}".format(t, gobj)) elif obj.__doc__ and obj.__doc__.startswith("Architecture determination function"): arch_determinations.append("{!s} {!s}".format(t, gobj)) elif obj.__doc__ and obj.__doc__.startswith("GDB mode determination function"): gdb_mode_determinations.append("{!s} {!s}".format(t, gobj)) elif obj.__doc__ and obj.__doc__.startswith("Decorator"): decorators.append("{!s} {!s}".format(t, gobj)) elif gobj.endswith(("syscall_tbl", "syscall_list")) or gobj.startswith("syscall_defs"): syscall_defines.append("{!s} {!s}".format(t, gobj)) elif obj.__doc__ and obj.__doc__.startswith("The wrapper of gef_print"): gef_print_wrappers.append("{!s} {!s}".format(t, gobj)) elif re.match(r"(read|write)_.*(memory|physmem).*", gobj): read_write_mems.append("{!s} {!s}".format(t, gobj)) else: others.append("{!s} {!s}".format(t, gobj)) self.out.append(titlify("GEF global configs")) self.out.extend(sorted(global_configs)) self.out.append(titlify("Command classes")) self.out.extend(sorted(command_classes)) self.out.append(titlify("Breakpoint classes")) self.out.extend(sorted(bp_classes)) self.out.append(titlify("Architecture classes")) self.out.extend(sorted(arch_classes)) self.out.append(titlify("Architecture determination function")) self.out.extend(sorted(arch_determinations)) self.out.append(titlify("GDB mode determination function")) self.out.extend(sorted(gdb_mode_determinations)) self.out.append(titlify("Classes")) self.out.extend(sorted(classes)) self.out.append(titlify("Syscall defines")) self.out.extend(sorted(syscall_defines)) self.out.append(titlify("Decorators")) self.out.extend(sorted(decorators)) self.out.append(titlify("gef_print wrapper")) self.out.extend(sorted(gef_print_wrappers)) self.out.append(titlify("read/write memory functions")) self.out.extend(sorted(read_write_mems)) self.out.append(titlify("Other functions")) self.out.extend(sorted(others)) return @parse_args def do_invoke(self, args): self.out = [] self.listup_pyobject() self.print_output() return @register_command class GefAvailableCommandListCommand(GenericCommand, BufferingOutput): """Display a list of commands available for the current architecture and gdb execution mode.""" _cmdline_ = "gef avail-comm-list" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-s", "--sort", action="store_true", help="sort by command name.") parser.add_argument("-a", "--only-available", action="store_true", help="show only available commands.") parser.add_argument("-u", "--only-unavailable", action="store_true", help="show only unavailable commands.") parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def check_require_arch_set(self, decorators): for line in decorators: if "@require_arch_set" in line: return current_arch is None return False def check_include_mode(self, decorators): for line in decorators: if "@only_if_specific_gdb_mode" in line: if is_pin(): return '"pin"' in line if is_qemu_system(): return '"qemu-system"' in line if is_qemu_user(): return '"qemu-user"' in line if is_vmware(): return '"vmware"' in line if is_qiling(): return '"qiling"' in line if is_rr(): return '"rr"' in line if is_wine(): return '"wine"' in line if is_kgdb(): return '"kgdb"' in line return False return True def check_exclude_mode(self, decorators): for line in decorators: if "@exclude_specific_gdb_mode" in line: if is_pin(): return '"pin"' in line if is_qemu_system(): return '"qemu-system"' in line if is_qemu_user(): return '"qemu-user"' in line if is_vmware(): return '"vmware"' in line if is_qiling(): return '"qiling"' in line if is_rr(): return '"rr"' in line if is_wine(): return '"wine"' in line if is_kgdb(): return '"kgdb"' in line return False return False def get_arch_name(self): s = GefUtil.get_source(only_if_specific_arch).replace("\n", "") r = re.search(r"dic = (\{.*\})", s) dic = eval(r.group(1)) for arch, func in dic.items(): if func(): return '"{:s}"'.format(arch) return None def check_include_arch(self, decorators, arch_name): for line in decorators: if "@only_if_specific_arch" in line: return str(arch_name) in line return True def check_exclude_arch(self, decorators, arch_name): for line in decorators: if "@exclude_specific_arch" in line: return str(arch_name) in line return False def check_load_package(self, decorators, dec_name, import_name): for line in decorators: if dec_name in line: try: readline = sys.modules.get("readline", None) sys.modules["readline"] = None __import__(import_name) sys.modules["readline"] = readline except ImportError: return False return True return True def add_out(self, cmdline, avail, msg=""): if self.args.only_available and not avail: return if self.args.only_unavailable and avail: return if avail: self.out.append("{:<34s}: {:s}".format( cmdline, Color.colorify("Available", "bold green"), )) else: self.out.append("{:<34s}: {:s} ({:s})".format( cmdline, Color.colorify("Unavailable", "bold red"), msg, )) return def listup_avail_comms(self): arch_name = self.get_arch_name() for cmdline, instance in __gef_command_instances__.items(): s = GefUtil.get_source(instance.do_invoke) decorators = [line for line in s.splitlines() if line.lstrip().startswith("@")] if self.check_require_arch_set(decorators): self.add_out(cmdline, False, "current_arch is None") continue if not self.check_include_arch(decorators, arch_name): self.add_out(cmdline, False, "Unsupported arch") continue if self.check_exclude_arch(decorators, arch_name): self.add_out(cmdline, False, "Unsupported arch") continue if not self.check_include_mode(decorators): self.add_out(cmdline, False, "Unsupported gdb mode") continue if self.check_exclude_mode(decorators): self.add_out(cmdline, False, "Unsupported gdb mode") continue if not self.check_load_package(decorators, "@ModuleLoader.load_capstone", "capstone"): self.add_out(cmdline, False, "capstone package is unavailable") continue if not self.check_load_package(decorators, "@ModuleLoader.load_unicorn", "unicorn"): self.add_out(cmdline, False, "unicorn package is unavailable") continue if not self.check_load_package(decorators, "@ModuleLoader.load_keystone", "keystone"): self.add_out(cmdline, False, "keystone-engine package is unavailable") continue if not self.check_load_package(decorators, "@ModuleLoader.load_ropper", "ropper"): self.add_out(cmdline, False, "ropper package is unavailable") continue if not self.check_load_package(decorators, "@ModuleLoader.load_binwalk", "binwalk"): self.add_out(cmdline, False, "binwalk package is unavailable") continue if not self.check_load_package(decorators, "@ModuleLoader.load_crccheck", "crccheck"): self.add_out(cmdline, False, "crccheck package is unavailable") continue if not self.check_load_package(decorators, "@ModuleLoader.load_codext", "codext"): self.add_out(cmdline, False, "codext package is unavailable") continue if not self.check_load_package(decorators, "@ModuleLoader.load_angr", "angr"): self.add_out(cmdline, False, "angr package is unavailable") continue self.add_out(cmdline, True) return @parse_args @only_if_gdb_running def do_invoke(self, args): self.out = [] self.listup_avail_comms() if args.sort: self.out = sorted(self.out) self.print_output(check_terminal_size=True) return @register_command class GefSetArchCommand(GenericCommand): """Set a specific architecture to gef.""" _cmdline_ = "gef set-arch" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) group = parser.add_mutually_exclusive_group(required=True) group.add_argument("arch", metavar="ARCH", nargs="?", help="target architecture.") group.add_argument("-l", "--list", action="store_true", help="show supported architecture words.") _syntax_ = parser.format_help() def arch_listup(self): fmt = "{:12s} {:s}" legend = ["Arch", "Available names (Case insensitive)"] gef_print(GefUtil.make_legend(fmt.format(*legend))) queue = Architecture.__subclasses__() while queue: cls = queue.pop(0) queue = cls.__subclasses__() + queue arch = Color.boldify("{:12s}".format(cls.__name__)) words = ", ".join(filter(lambda x: isinstance(x, str), cls.load_condition)) gef_print("{:s} {:s}".format(arch, words)) return @parse_args def do_invoke(self, args): if args.list: self.arch_listup() return try: set_arch(args.arch) info("set_arch({:s}) is successfully".format(args.arch)) Cache.reset_gef_caches(all=True) except OSError: err("set_arch({:s}) is failed".format(args.arch)) return @register_command class GefStatusCommand(GenericCommand): """Display current gef status.""" _cmdline_ = "gef status" _category_ = "99. GEF Maintenance Command" _aliases_ = ["arch-info"] parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): gef_print(titlify("GDB/ELF settings")) show_arch = gdb.execute("show architecture", to_string=True).rstrip() gef_print("{:30s} -> {:s}".format("show architecture", show_arch)) if is_64bit(): bit_str = "64-bit" else: bit_str = "32-bit" if Endian.is_big_endian(): endian_str = "big" else: endian_str = "little" gef_print("{:30s} -> {:s}".format("bit", bit_str)) gef_print("{:30s} -> {:s}".format("endian", endian_str)) gef_print(titlify("GDB mode")) gef_print("{:30s} -> {!s}".format("is_normal_run()", is_normal_run())) gef_print("{:30s} -> {!s}".format("is_attach()", is_attach())) gef_print("{:30s} -> {!s}".format("is_remote_debug()", is_remote_debug())) gef_print("{:30s} -> {!s}".format("is_container_attach()", is_container_attach())) gef_print("{:30s} -> {!s}".format("is_qemu_system()", is_qemu_system())) gef_print("{:30s} -> {!s}".format("is_qemu_user()", is_qemu_user())) gef_print("{:30s} -> {!s}".format("is_pin()", is_pin())) gef_print("{:30s} -> {!s}".format("is_over_serial()", is_over_serial())) kgdb_forced = " (forced)" if Config.get_gef_setting("gef.kgdb_force") is True else "" gef_print("{:30s} -> {!s}{:s}".format("is_kgdb()", is_kgdb(), kgdb_forced)) gef_print("{:30s} -> {!s}".format("is_kdb()", is_kdb())) gef_print("{:30s} -> {!s}".format("is_qiling()", is_qiling())) gef_print("{:30s} -> {!s}".format("is_vmware()", is_vmware())) gef_print("{:30s} -> {!s}".format("is_in_kernel()", is_in_kernel())) gef_print("{:30s} -> {!s}".format("is_in_secure()", is_in_secure())) gef_print("{:30s} -> {!s}".format("is_rr()", is_rr())) gef_print("{:30s} -> {!s}".format("is_wine()", is_wine())) gef_print(titlify("Others")) gef_print("{:30s} -> {!s}".format("is_alive()", is_alive())) gef_print("{:30s} -> {!s}".format("is_kvm_enabled()", is_kvm_enabled())) gef_print("{:30s} -> {!s}".format("is_smp_enabled()", is_smp_enabled())) gef_print("{:30s} -> {!s}".format("is_support_secure_world()", is_support_secure_world())) gef_print("{:30s} -> {!s}".format("is_supported_physmode()", is_supported_physmode())) if is_supported_physmode(): gef_print("{:30s} -> {!s}".format("get_current_mmu_mode()", QemuMonitor.get_current_mmu_mode())) gef_print(titlify("GEF architecture information")) if current_arch is None: gef_print("{:30s} -> None".format("current_arch")) gef_print("{:30s} -> {!s}".format("ptrsize", AddressUtil.ptr_width())) return gef_print("{:30s} -> {!s}".format("current_arch.arch", current_arch.arch)) gef_print("{:30s} -> {!s}".format("current_arch.mode", current_arch.mode)) if is_arm32() or is_arm32_cortex_m(): gef_print("{:30s} -> {!s}".format("current_arch.is_cortex_m()", current_arch.is_cortex_m())) gef_print("{:30s} -> {!s}".format("current_arch.ptrsize", current_arch.ptrsize)) if current_arch.instruction_length is None: inst_len = "variable length" else: inst_len = str(current_arch.instruction_length) gef_print("{:30s} -> {!s}".format("instruction length", inst_len)) if current_arch.return_register is None: ret_regs = "different for each system call" else: ret_regs = str(current_arch.return_register) gef_print("{:30s} -> {!s}".format("return register", ret_regs)) fparams = ", ".join(current_arch.function_parameters) if len(current_arch.function_parameters) == 1: fparams += " (passing via stack)" gef_print("{:30s} -> {!s}".format("function parameters", fparams)) gef_print("{:30s} -> {!s}".format("syscall register", current_arch.syscall_register)) if current_arch.syscall_parameters is None: sparams = "different for each system call" else: sparams = ", ".join(current_arch.syscall_parameters) gef_print("{:30s} -> {!s}".format("syscall parameters", sparams)) if is_x86() or is_arm32() or is_arm64(): gef_print("{:30s} -> {!s}".format("32bit-emulated (compat mode)", is_emulated32())) gef_print("{:30s} -> {!s}".format("Has a call/jump delay slot", current_arch.has_delay_slot)) gef_print("{:30s} -> {!s}".format("Has a syscall delay slot", current_arch.has_syscall_delay_slot)) gef_print("{:30s} -> {!s}".format("Has a ret delay slot", current_arch.has_ret_delay_slot)) gef_print("{:30s} -> {!s}".format("Stack grow down", current_arch.stack_grow_down)) gef_print("{:30s} -> {!s}".format("Thread Local Storage support", current_arch.tls_supported)) gef_print("{:30s} -> {!s}".format("keystone support", current_arch.keystone_support)) gef_print("{:30s} -> {!s}".format("capstone support", current_arch.capstone_support)) gef_print("{:30s} -> {!s}".format("unicorn support", current_arch.unicorn_support)) return @register_command class GefVersionCommand(GenericCommand): """Display GEF version info.""" _cmdline_ = "gef version" _category_ = "99. GEF Maintenance Command" _aliases_ = ["version"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("--compact", action="store_true", help="show compact style.") _syntax_ = parser.format_help() def os_version(self): try: lsb_release_command = GefUtil.which("lsb_release") res = GefUtil.gef_execute_external([lsb_release_command, "-d"], as_list=True) for line in res: if line.startswith("Description:"): return line.split(":")[1].strip() except FileNotFoundError: pass if os.path.exists("/etc/issue.net"): content = open("/etc/issue.net").read().strip() if content: return content if os.path.exists("/etc/issue"): content = open("/etc/issue").read().strip() content = content.replace(" \\n \\l", "") if content: return content if os.path.exists("/etc/os-release"): content = open("/etc/os-release").read() for line in content.splitlines(): r = re.search(r'PRETTY_NAME="(.+)"', line) if r: return r.group(1) return "Not found" def kernel_version_from_uname(self): try: uname_command = GefUtil.which("uname") res = GefUtil.gef_execute_external([uname_command, "-a"], as_list=True) return res[0] except FileNotFoundError: return "Not found" def kernel_version_from_proc(self): try: return open("/proc/version").read().strip() except FileNotFoundError: return "Not found" def system_libc_version(self): res = GefUtil.gef_execute_external(["cat", "/proc/self/maps"], as_list=True) libc_targets = ("libc-2.", "libc.so.6", "libuClibc-") for line in res: if not any(kw in line for kw in libc_targets): continue path = line.split()[-1] if not os.path.exists(path): continue data = open(path, "rb").read() pos = re.search(b"(GNU C Library|uClibc-ng release) [\x20-\x7e]*", data) if pos: return String.bytes2str(pos.group(0)) return "Not found" def qemu_system_version(self): return gdb.execute("monitor info version", to_string=True).strip() def qemu_user_version(self): pid = Pid.get_pid() try: res = GefUtil.gef_execute_external(["/proc/{:d}/exe".format(pid), "--version"], as_list=True) return res[0].strip() except (IndexError, FileNotFoundError): return "Not recognized" def gef_version(self): gef_hash = hashlib.sha1(open(GEF_FILEPATH, "rb").read()).hexdigest() dt = datetime.datetime.fromtimestamp(os.stat(GEF_FILEPATH).st_mtime) return "Last modified: {} SHA1: {}".format(dt.strftime("%Y-%m-%d %H:%M:%S"), gef_hash) def gdb_version(self): try: return gdb.VERSION # GDB >= 8.1 (or earlier?) except AttributeError: return gdb.execute("show version", to_string=True).split("\n")[0] def python_version(self): return sys.version.replace("\n", " ") def capstone_version(self): @ModuleLoader.load_capstone def _capstone_version(): capstone = sys.modules["capstone"] return ".".join(map(str, capstone.cs_version())) try: return _capstone_version() except (KeyError, ImportWarning): return "Not found" def keystone_version(self): @ModuleLoader.load_keystone def _keystone_version(): keystone = sys.modules["keystone"] return ".".join(map(str, keystone.ks_version())) try: return _keystone_version() except (KeyError, ImportWarning): return "Not found" def unicorn_version(self): @ModuleLoader.load_unicorn def _unicorn_version(): unicorn = sys.modules["unicorn"] return unicorn.__version__ try: return _unicorn_version() except (KeyError, ImportWarning): return "Not found" def ropper_version(self): @ModuleLoader.load_ropper def _ropper_version(): ropper = sys.modules["ropper"] return ".".join(map(str, ropper.VERSION)) try: return _ropper_version() except (KeyError, ImportWarning, AttributeError): return "Not found" def angr_version(self): @ModuleLoader.load_angr def _angr_version(): angr = sys.modules["angr"] return angr.__version__ try: return _angr_version() except (KeyError, ImportWarning, AttributeError): return "Not found" def gcc_version(self): try: gcc_command = GefUtil.which("gcc") except FileNotFoundError: return "Not found" res = GefUtil.gef_execute_external([gcc_command, "--version"], as_list=True) return res[0] def readelf_version(self): try: readelf_command = GefUtil.which(Config.get_gef_setting("gef.readelf_command")) except FileNotFoundError: return "Not found" res = GefUtil.gef_execute_external([readelf_command, "-v"], as_list=True) return res[0] def objdump_version(self): try: objdump_command = GefUtil.which(Config.get_gef_setting("gef.objdump_command")) except FileNotFoundError: return "Not found" res = GefUtil.gef_execute_external([objdump_command, "-v"], as_list=True) return res[0] def seccomp_tools_version(self): try: seccomp_tools_command = GefUtil.which("seccomp-tools") except FileNotFoundError: return "Not found" res = GefUtil.gef_execute_external([seccomp_tools_command, "--version"], as_list=True) return res[0] def ceccomp_version(self): try: ceccomp_command = GefUtil.which("ceccomp") except FileNotFoundError: return "Not found" res = GefUtil.gef_execute_external([ceccomp_command, "version"], as_list=True) return res[0] def one_gadget_version(self): try: one_gadget_command = GefUtil.which("one_gadget") except FileNotFoundError: return "Not found" res = GefUtil.gef_execute_external([one_gadget_command, "--version"], as_list=True) return res[0] def rp_version(self): try: rp_lin_command = GefUtil.which("rp-lin") except FileNotFoundError: return "Not found" res = GefUtil.gef_execute_external([rp_lin_command, "--version", "--file", "a"], as_list=True) if "You are currently using " in res[0]: return res[0].replace("You are currently using ", "") return "Not found" def show_compact_info(self): gef_print("gdb: {:s}".format(self.gdb_version())) gef_print("python: {:s}".format(self.python_version())) gef_print("OS: {:s}".format(self.os_version())) gef_print("kernel: {:s}".format(self.kernel_version_from_uname())) if is_qemu_system(): gef_print("qemu: {:s}".format(self.qemu_system_version())) return def show_full_info(self): gef_print(titlify("versions")) gef_print("OS: {:s}".format(self.os_version())) gef_print("kernel (uname -a): {:s}".format(self.kernel_version_from_uname())) gef_print("kernel (/proc/version): {:s}".format(self.kernel_version_from_proc())) gef_print("System libc: {:s}".format(self.system_libc_version())) if is_qemu_system(): gef_print("qemu: {:s}".format(self.qemu_system_version())) if is_qemu_user(): gef_print("qemu: {:s}".format(self.qemu_user_version())) gef_print("GEF: {:s}".format(self.gef_version())) gef_print("gdb: {:s}".format(self.gdb_version())) gef_print("python: {:s}".format(self.python_version())) gef_print("capstone: {:s}".format(self.capstone_version())) gef_print("keystone: {:s}".format(self.keystone_version())) gef_print("unicorn: {:s}".format(self.unicorn_version())) gef_print("ropper: {:s}".format(self.ropper_version())) gef_print("angr: {:s}".format(self.angr_version())) gef_print("gcc: {:s}".format(self.gcc_version())) gef_print("readelf: {:s}".format(self.readelf_version())) gef_print("objdump: {:s}".format(self.objdump_version())) gef_print("seccomp-tools: {:s}".format(self.seccomp_tools_version())) gef_print("ceccomp: {:s}".format(self.ceccomp_version())) gef_print("one_gadget: {:s}".format(self.one_gadget_version())) gef_print("rp: {:s}".format(self.rp_version())) gef_print(titlify("gdb build config")) gdb.execute("show configuration") return @parse_args def do_invoke(self, args): if args.compact: self.show_compact_info() else: self.show_full_info() return @register_command class GefCheckUpdateCommand(GenericCommand): """Check for gef updates.""" _cmdline_ = "gef check-update" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) _syntax_ = parser.format_help() @parse_args def do_invoke(self, args): gef_remote = "https://raw.githubusercontent.com/bata24/gef/dev/gef.py" gef_remote_data = http_get(gef_remote) if gef_remote_data is None: err("[-] Failed to get remote gef") return hash_gef_local = hashlib.sha512(open(GEF_FILEPATH, "rb").read()).digest() hash_gef_remote = hashlib.sha512(gef_remote_data).digest() if hash_gef_local == hash_gef_remote: info("No update") else: info("Update found, try `python3 {:s} --upgrade`".format(GEF_FILEPATH)) return @register_command class GefTmuxSetupCommand(GenericCommand): """Setup a comfortable tmux environment.""" _cmdline_ = "gef tmux-setup" _category_ = "99. GEF Maintenance Command" _aliases_ = ["tmux-setup"] parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-r", "--reset", action="store_true", help="reset all panes.") _syntax_ = parser.format_help() _note_ = [ "- `screen` is no longer supported.", "", "- `tmux` settings are predefined and cannot be customized in this command.", "- If you want to customize it, edit `tmux_setup.py` and run `source /path/to/tmux_setup.py`.", "- It can be found in https://github.com/bata24/gef/blob/dev/dev/tmux/tmux_setup.py.", "", "- There is experimental support for `zellij` using a similar script.", "- Try starting `zellij-wrapper.py` in your shell (before starting `zellij` and `gdb`).", "- It can be found in https://github.com/bata24/gef/blob/dev/dev/zellij/zellij-wrapper.py.", ] _note_ = "\n".join(_note_) @staticmethod def get_redirect_configs(): configs = [ "context.redirect", "context_args.redirect", "context_code.redirect", "context_extra.redirect", "context_legend.redirect", "context_mem_access.redirect", "context_mem_watch.redirect", "context_regs.redirect", "context_source.redirect", "context_stack.redirect", "context_threads.redirect", "context_trace.redirect", ] return configs @staticmethod def get_tty_gef_used(): """Return a set of TTYs currently used by GEF for tmux redirection.""" tty_gef_used = [Config.get_gef_setting(c) for c in GefTmuxSetupCommand.get_redirect_configs()] tty_gef_used = [x for x in tty_gef_used if x] # filter "" return set(tty_gef_used) @staticmethod def reset_panes(): """Reset tmux panes used by GEF, killing relevant panes and clearing related configurations.""" # list panes tmux = GefUtil.which("tmux") res = subprocess.check_output([ tmux, "list-panes", "-F#{pane_active}:#{pane_id}:#{pane_tty}" ]).decode("utf-8").strip() # kill panes tty_gef_used = GefTmuxSetupCommand.get_tty_gef_used() for line in res.splitlines(): pane_active, pane_id, pane_tty = line.split(":") if pane_active == "1": continue if pane_tty not in tty_gef_used: continue subprocess.run([tmux, "kill-pane", "-t", pane_id]) # reset config for config in GefTmuxSetupCommand.get_redirect_configs(): gdb.execute('gef config {:s} ""'.format(config)) # remove destructor import atexit try: atexit.unregister(GefTmuxSetupCommand.reset_panes) except Exception: pass return def tmux_setup(self): """Prepare the tmux environment by vertically splitting and redirect context output.""" # reset previous settings tmux = GefUtil.which("tmux") if self.get_tty_gef_used(): warn("Since it is already split, discard previous screen") GefTmuxSetupCommand.reset_panes() # split ok("tmux session found, splitting window...") pane_id, pane_tty = subprocess.check_output([ tmux, "splitw", "-h", "-F#{pane_id}:#{pane_tty}", "-P", ]).decode("utf-8").strip().split(":") # add destructor import atexit atexit.register(GefTmuxSetupCommand.reset_panes) # clear the screen and let it wait for input forever gdb.execute(f"!'{tmux}' send-keys -t {pane_id} 'clear ; cat' C-m") gdb.execute(f"!'{tmux}' select-pane -L") ok(f"Setting `context.redirect` to '{pane_tty}'...") gdb.execute(f"gef config context.redirect {pane_tty}") Cache.reset_gef_caches(all=True) return @parse_args def do_invoke(self, args): try: GefUtil.which("tmux") except FileNotFoundError as e: err("{}".format(e)) return if args.reset: GefTmuxSetupCommand.reset_panes() return if os.getenv("TMUX"): self.tmux_setup() return warn("Not in a tmux session") return class GefAlias(gdb.Command): """Simple aliasing wrapper because GDB doesn't do what it should.""" _category_ = "99. GEF Maintenance Command" def __init__(self, alias, command, force_repeat=None, pre_defined=False): p = command.split() if not p: return # initialize self._alias_ = alias self._command_ = command if force_repeat is None: self._repeat_ = False else: self._repeat_ = force_repeat self._pre_defined_ = pre_defined # default alias settings of GEF self.__doc__ = "Alias for '{:s}'".format(Color.greenify(command)) # Inherit settings from the aliased command instance = __gef_command_instances__.get(command, None) if instance: # repeat settings if force_repeat is None: self._repeat_ = instance._repeat_ # doc self.__doc__ += ": {:s}".format(instance.__doc__) # complete settings if hasattr(instance, "complete"): self.complete = instance.complete # define aliased command if hasattr(instance, "complete"): # Aliased commands support only user completion. super().__init__(alias, gdb.COMMAND_NONE) else: super().__init__(alias, gdb.COMMAND_NONE, gdb.COMPLETE_NONE) # add or overwrite global __gef_alias_instances__ __gef_alias_instances__[alias] = self return def invoke(self, args, from_tty): # noqa if not self._repeat_: self.dont_repeat() gdb.execute("{} {}".format(self._command_, args), from_tty=from_tty) return @register_command class AliasesCommand(GenericCommand): """The base command to add, remove or list aliases.""" _cmdline_ = "aliases" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) if (sys.version_info.major, sys.version_info.minor) >= (3, 7): subparsers = parser.add_subparsers(title="command", required=True) else: subparsers = parser.add_subparsers(title="command") subparsers.add_parser("add") subparsers.add_parser("rm") subparsers.add_parser("ls") _syntax_ = parser.format_help() def __init__(self, *args, **kwargs): prefix = kwargs.get("prefix", True) super().__init__(prefix=prefix) return @parse_args def do_invoke(self, args): self.usage() return @register_command class AliasesAddCommand(AliasesCommand): """Add the command alias.""" _cmdline_ = "aliases add" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("alias", metavar="ALIAS", help="the name of new alias.") parser.add_argument("command", metavar="COMMAND", nargs="+", help="the command of new alias.") parser.add_argument("-r", "--repeat", action="store_true", help="enforce repeat feature.") _syntax_ = parser.format_help() _example_ = [ "{0:s} scope telescope", ] _example_ = "\n".join(_example_).format(_cmdline_) def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): if args.alias in __gef_command_instances__: err("Not allowed due to circular references") return command = " ".join(args.command) GefAlias(args.alias, command, force_repeat=args.repeat) gef_print("{:s} = {:s}".format(args.alias, command)) return @register_command class AliasesRmCommand(AliasesCommand): """Remove the command alias.""" _cmdline_ = "aliases rm" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("alias", metavar="ALIAS", help="the name of alias to be deleted.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): global __gef_alias_instances__ if args.alias in __gef_alias_instances__: del __gef_alias_instances__[args.alias] else: err("Could not find {:s} in aliases".format(args.alias)) return @register_command class AliasesListCommand(AliasesCommand, BufferingOutput): """List the command alias.""" _cmdline_ = "aliases ls" _category_ = "99. GEF Maintenance Command" parser = argparse.ArgumentParser(prog=_cmdline_) parser.add_argument("-n", "--no-pager", action="store_true", help="do not use the pager.") _syntax_ = parser.format_help() def __init__(self): super().__init__(prefix=False) return @parse_args def do_invoke(self, args): width = max(len(x) for x in __gef_alias_instances__.keys()) self.out = [] self.out.append(titlify("Pre-defined aliases")) for _, a in sorted(__gef_alias_instances__.items(), key=lambda x:x[0]): if a._pre_defined_: self.out.append("{:{:d}s} -> {:s}".format(a._alias_, width, a._command_)) self.out.append(titlify("User defined aliases")) for _, a in sorted(__gef_alias_instances__.items(), key=lambda x:x[0]): if not a._pre_defined_: self.out.append("{:{:d}s} -> {:s}".format(a._alias_, width, a._command_)) self.print_output(check_terminal_size=True) return class GefUtil: """A collection of utility functions that are related to GEF basic features.""" @staticmethod @Cache.cache_until_next def cached_lookup_type(_type): """Look up a GDB type by name and strip typedefs, returning None on failure.""" try: return gdb.lookup_type(_type).strip_typedefs() except RuntimeError: return None @staticmethod def get_tqdm(use_tqdm=True): """Return the tqdm progress bar if available and enabled; otherwise returns a passthrough function.""" tqdm = lambda x, leave=None, total=None, desc=None: x # noqa: F841 if not use_tqdm: return tqdm try: from tqdm import tqdm except ImportError: pass return tqdm __gef_convenience_vars_index__ = 0 # $_gef1, $_gef2, ... @staticmethod def gef_convenience(value): """Define a new convenience value.""" var_name = "$_gef{:d}".format(GefUtil.__gef_convenience_vars_index__) GefUtil.__gef_convenience_vars_index__ += 1 gdb.execute('set {:s} = "{:s}"'.format(var_name, value)) return var_name class ArgparseExitProxyException(Exception): pass @staticmethod def get_terminal_size(redirect=""): """Return the current terminal size.""" if redirect and os.getenv("TMUX"): res = subprocess.check_output([ GefUtil.which("tmux"), "list-panes", "-F#{pane_tty}:#{pane_height}:#{pane_width}", ]).decode("utf-8").strip() for line in res.splitlines(): tty, height, width = line.split(":") if tty == redirect: return int(height), int(width) try: tty_columns, tty_rows = os.get_terminal_size() return tty_rows, tty_columns except OSError: return 600, 100 @staticmethod def get_source(function): """Return the source of function.""" import inspect s = inspect.getsource(function) return s.rstrip() @staticmethod @Cache.cache_this_session def log2(x): import math return int(math.log2(x)) @staticmethod def fromhex_ignore_invalid(value, to_str=False): """Convert a hex string to bytes or string, ignoring invalid characters.""" sanitized_value = "" for c in value.lower(): if c in "0123456789abcdef": sanitized_value += c if len(sanitized_value) % 2 != 0: err("Hex value length is odd") return None if to_str: values = ["\\x" + hh for hh in slicer(sanitized_value, 2)] values = "".join(values) else: values = bytes.fromhex(sanitized_value) return values @staticmethod @Cache.cache_this_session def which(program): """Locate a command on the filesystem.""" def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) if not program: raise FileNotFoundError("Missing program name") if os.path.split(program)[0]: # check dirname if is_exe(program): return program else: # search from PATH env_path_default = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" env_path = os.getenv("PATH", env_path_default) env_path = env_path.split(os.pathsep) if hasattr(Gef, "GEF_VENV_BIN_PATH"): env_path.insert(0, Gef.GEF_VENV_BIN_PATH) if "/usr/local/bin" not in env_path: env_path.insert(0, "/usr/local/bin") # for rp-lin, vmlinux-to-elf for path in env_path: exe_file = os.path.join(path.strip('"'), program) if is_exe(exe_file): return exe_file raise FileNotFoundError("Missing file `{:s}`".format(program)) @staticmethod def show_last_exception(): """Display the last Python exception.""" def _show_code_line(fname, idx): fname = os.path.expanduser(os.path.expandvars(fname)) __data = open(fname, "r").read().splitlines() return __data[idx - 1] if idx < len(__data) else "" gef_print("") exc_type, exc_value, exc_traceback = sys.exc_info() HORIZONTAL_LINE = "-" gef_print(" Exception raised ".center(80, HORIZONTAL_LINE)) gef_print("{}: {}".format(Color.colorify(exc_type.__name__, "bold red underline"), exc_value)) gef_print(" Detailed stacktrace ".center(80, HORIZONTAL_LINE)) for fs in traceback.extract_tb(exc_traceback)[::-1]: filename, lineno, method, code = fs if not code or not code.strip(): code = _show_code_line(filename, lineno) filename_c = Color.yellowify(filename) method_c = Color.greenify(method) gef_print('File "{}", line {:d}, in {}()'.format(filename_c, lineno, method_c)) gef_print(" -> {}".format(code)) gef_print(" Last 10 GDB commands ".center(80, HORIZONTAL_LINE)) gdb.execute("show commands") gef_print(" Runtime environment ".center(80, HORIZONTAL_LINE)) gdb.execute("gef version --compact") gef_print(HORIZONTAL_LINE * 80) gef_print("") return @staticmethod def gef_execute_external(command, as_list=False, *args, **kwargs): """Execute an external command and return the result.""" env = os.environ.copy() env["LANG"] = "C" res = subprocess.check_output( command, stderr=subprocess.STDOUT, env=env, shell=kwargs.get("shell", False), ) if as_list: return [String.bytes2str(x) for x in res.splitlines()] return String.bytes2str(res) @staticmethod def walk(directory): """Return all file path excluding directories and symbolic links.""" for root, _dirs, files in os.walk(directory): for f in sorted(files): path = os.path.join(root, f) if os.path.islink(path): continue yield path return @staticmethod def now_str(): """Return formatted time.""" dt = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") return dt @staticmethod def mkstemp(prefix="", dir=None, suffix=None, dt=None): """Create a temporary file.""" if dt is None: dt = GefUtil.now_str() s = [] if prefix: s.append(prefix) if dt: s.append(dt) real_prefix = "-".join(s) if real_prefix: real_prefix += "-" if dir is None: dir = GEF_TEMP_DIR return tempfile.mkstemp(dir=dir, prefix=real_prefix, suffix=suffix) @staticmethod def rmdir(directory, verbose=False, keep_root=False): """Recursively delete the target directory.""" for root, dirs, files in os.walk(directory, topdown=False): for file in files: file_path = os.path.join(root, file) if verbose: info("Removed: {:s}".format(file_path)) os.remove(file_path) for dir in dirs: dir_path = os.path.join(root, dir) if verbose: info("Removed: {:s}".format(dir_path)) os.rmdir(dir_path) if not keep_root: os.rmdir(directory) return @staticmethod def make_legend(msg): """Apply color settings and generate legend string.""" color = Config.get_gef_setting("theme.table_heading") return Color.colorify(msg.rstrip(), color) @staticmethod def get_size_str(size, enable_color=True): if 0 <= size < 1024: return "{:5.1f} B".format(size) elif 1024 <= size < 1024 ** 2: return "{:5.1f} KB".format(size / 1024) elif 1024 ** 2 <= size < 100 * (1024 ** 2): # 1MB~100MB return "{:5.1f} MB".format(size / 1024 / 1024) elif 100 * (1024 ** 2) <= size < 1024 ** 3: # 100MB~1GB if enable_color: return Color.colorify("{:5.1f} MB".format(size / 1024 / 1024), "bold yellow") else: return "{:5.1f} MB".format(size / 1024 / 1024) elif 1024 ** 3 <= size: if enable_color: return Color.colorify("{:5.1f} GB".format(size / 1024 / 1024 / 1024), "bold red") else: return "{:5.1f} GB".format(size / 1024 / 1024 / 1024) return "???" class Gef: """A collection of utility functions that are related to GEF start up.""" missing_commands = {} @staticmethod def list_current_commands(): command_list = [] res = gdb.execute("help all", to_string=True) for line in res.splitlines(): line = line.strip() if " -- " not in line: continue commands = line.split(" -- ")[0] commands = commands.split(", ") command_list.extend(commands) return command_list @staticmethod def load_commands(): """Load all the commands and functions defined by GEF into GDB.""" global __gef_command_instances__ DEBUG_PERF_TIME = False DEBUG_CHECK_COMMAND_CONFLICT = False if DEBUG_CHECK_COMMAND_CONFLICT: loaded_commands = Gef.list_current_commands() FIRST_TIME = "gef reload" not in loaded_commands # skip check when `gef reload` nb_missing = 0 time_elapsed = [] for cmd_class in __gef_commands__: try: if DEBUG_PERF_TIME: start_time_real = time.perf_counter() start_time_proc = time.process_time() if DEBUG_CHECK_COMMAND_CONFLICT and FIRST_TIME: if cmd_class._cmdline_ in loaded_commands: warn("{:s} is already loaded".format(Color.boldify(cmd_class._cmdline_))) instance = cmd_class() # command loading is here __gef_command_instances__[cmd_class._cmdline_] = instance if DEBUG_PERF_TIME: end_time_real = time.perf_counter() end_time_proc = time.process_time() time_elapsed.append(( cmd_class._cmdline_, end_time_real - start_time_real, end_time_proc - start_time_proc, )) if hasattr(cmd_class._aliases_, "__iter__"): if isinstance(cmd_class._aliases_, str): cmd_class_aliases = [cmd_class._aliases_] elif isinstance(cmd_class._aliases_, list): cmd_class_aliases = cmd_class._aliases_ else: cmd_class_aliases = [] for alias in cmd_class_aliases: if DEBUG_CHECK_COMMAND_CONFLICT and FIRST_TIME: if alias in loaded_commands: warn("{:s} is already loaded".format(Color.boldify(alias))) GefAlias(alias, cmd_class._cmdline_, pre_defined=True) except Exception as reason: Gef.missing_commands[cmd_class._cmdline_] = reason nb_missing += 1 if DEBUG_PERF_TIME: gef_print(titlify("Top 10 commands that took the longest to load")) for cmdline, real, cpu in sorted(time_elapsed, key=lambda x: x[1], reverse=True)[:10]: gef_print("{:30s} Real:{:.10f} s, CPU:{:.10f} s".format(cmdline, real, cpu)) gef_print(titlify("")) # print message gef_print("{:s} is ready, type '{:s}' to start, '{:s}' to configure".format( Color.greenify("GEF"), Color.colorify("gef", "underline yellow"), Color.colorify("gef config", "underline magenta") )) ver = "{:d}.{:d}".format(sys.version_info.major, sys.version_info.minor) gef_print("Loaded {:s} commands (+{:s} aliases) for GDB {:s} using Python engine {:s}".format( Color.colorify(len(__gef_command_instances__), "bold green"), Color.colorify(len(__gef_alias_instances__), "bold green"), Color.colorify(gdb.VERSION, "bold yellow"), Color.colorify(ver, "bold red") )) if nb_missing: warn("{:s} command{} could not be loaded, run `{:s}` to know why.".format( Color.colorify(nb_missing, "bold red"), "s" if nb_missing > 1 else "", Color.colorify("gef missing", "underline magenta") )) return @staticmethod def gef_prompt(_current_prompt): """GEF custom prompt function.""" if Config.get_gef_setting("gef.readline_compat") is True: return "gef> " if Color.disable_color(): return "gef> " if is_alive(): return "\001\033[1;32m\002gef> \001\033[0m\002" return "\001\033[1;31m\002gef> \001\033[0m\002" @staticmethod def fix_venv(): """Detect if you are in a venv environment and adjust GEF settings.""" def fast_path(): """If you installed it with the latest installer, there should be a gev.venv.conf file. Interpreting this file will speed up the process.""" gef_venv_conf = os.path.join(os.path.dirname(GEF_FILEPATH), "gef.venv.conf") if not os.path.exists(gef_venv_conf): return False content = open(gef_venv_conf, "rb").read().decode() for line in content.splitlines(): if line.startswith("GEF_VENV_SYS_PATH="): Gef.GEF_VENV_SYS_PATH = line[len("GEF_VENV_SYS_PATH="):] to_add = [] for path in Gef.GEF_VENV_SYS_PATH.split(":"): if path and path not in sys.path: to_add.append(path) sys.path = to_add + sys.path continue if line.startswith("GEF_VENV_BIN_PATH="): Gef.GEF_VENV_BIN_PATH = line[len("GEF_VENV_BIN_PATH="):] # used by GefUtil.which() continue if line.startswith("GEF_VENV_GEM_HOME="): Gef.GEF_VENV_GEM_HOME = line[len("GEF_VENV_GEM_HOME="):] os.environ["GEM_HOME"] = Gef.GEF_VENV_GEM_HOME continue if hasattr(Gef, "GEF_VENV_SYS_PATH"): return True return False def create_skip_config(): # If .venv-gef is in the default location, it is likely that the user simply forgot to activate the venv. # Therefore, for convenience, skip-venv-check is not created. default_venv = os.path.join(os.path.dirname(GEF_FILEPATH), ".venv-gef") if os.path.exists(default_venv): return skip_config = os.path.join(GEF_TEMP_DIR, "skip-venv-check") open(skip_config, "w").close() return def slow_path(): """For those who used the old installer or installed manually. It launches python via shell and collects and imports its configuration.""" # venv check is very slow, so skip if unneeded skip_config = os.path.join(GEF_TEMP_DIR, "skip-venv-check") if os.path.exists(skip_config): return # GEF supports pyenv, venv, uv, etc. # To achieve this, you need to run python outside of gdb. # Modify sys.path based on the results of the execution. # check python3 command to get prefix try: pythonbin = GefUtil.which("python3") except FileNotFoundError: create_skip_config() return # check prefix cmds = [pythonbin, "-c", "import os,sys;print(sys.prefix)"] PREFIX = subprocess.check_output(cmds).decode("utf-8").strip() if PREFIX == sys.base_prefix: create_skip_config() return # add path cmds = [pythonbin, "-c", "import os,sys;print(os.linesep.join(sys.path).strip())"] SITE_PACKAGES_DIRS = subprocess.check_output(cmds).decode("utf-8").split() to_add = [] for path in SITE_PACKAGES_DIRS: if path not in sys.path: to_add.append(path) sys.path = to_add + sys.path return fast_path() or slow_path() return @staticmethod def main(): # check gdb version GDB_VERSION = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups())) GDB_MIN_VERSION = (9, 2) # ubuntu 20.04 if GDB_VERSION < GDB_MIN_VERSION: err("GDB version ({:s}) is too old (<{:d}.{:d}). Try upgrading it.".format( gdb.VERSION, GDB_MIN_VERSION[0], GDB_MIN_VERSION[1], )) return # create tmp dir if not os.path.exists(GEF_TEMP_DIR): os.mkdir(GEF_TEMP_DIR) # 0o755 # GEF runs with root privileges, but it may attach to a normal privileges program. # If you want to execute a command that involves stdout redirection for that program, # you will need write permission to /tmp/gef. os.chmod(GEF_TEMP_DIR, 0o777) # check tmp dir access permission # This check takes into account the cases where GEF is run with root privileges and with normal user privileges. if not os.access(GEF_TEMP_DIR, os.W_OK|os.R_OK|os.X_OK): err("Permission denied to {:s}. Please delete it first.".format(GEF_TEMP_DIR)) return # When using a python virtual environment (pyenv, venv, etc.), GDB still loads # the system-installed python, so GEF doesn't load site-packages dir from environment. # In order to fix it, from the shell we run the python3 binary, # take and parse its path, add the path to the current python process. Gef.fix_venv() # setup prompt gdb.prompt_hook = Gef.gef_prompt # noqa # common config gdb.execute("set confirm off") gdb.execute("set verbose off") gdb.execute("set pagination off") gdb.execute("set output-radix 0x10") # gdb history gdb.execute("set history save on") gdb.execute("set history size 1000") gdb.execute("set history filename ~/.gdb_history") # print gdb.execute("set print elements 0") # remove element count limit gdb.execute("set print pretty on") gdb.execute("set print array on") # use multi-line when print gdb.execute("set print array-indexes on") # add index when print gdb.execute("set print asm-demangle on") # demangle gdb.execute("set print object on") gdb.execute("set print vtbl on") try: # this will raise a gdb.error unless we're on x86 gdb.execute("set disassembly-flavor intel") except gdb.error: # we can safely ignore this pass # SIGALRM will simply display a message, but gdb won't forward the signal to the process gdb.execute("handle SIGALRM print nopass") # SIGSEGV/SIGTERM/SIG32(for thread creation) gdb.execute("handle SIGSEGV print nopass") gdb.execute("handle SIGTERM print nopass") gdb.execute("handle SIG32 nostop") # stops at first instruction of functions without debug info when stepping gdb.execute("set step-mode on") # frame gdb.execute("set backtrace past-main on") gdb.execute("set print frame-arguments all") # load all commands that has @register_command decorator. Gef.load_commands() # load the saved settings gdb.execute("gef restore") # follow mode if Config.get_gef_setting("gef.follow_child"): gdb.execute("set follow-fork-mode child") # index file gdb.execute("save gdb-index {:s}".format(GEF_TEMP_DIR)) # don't use {!r} # gdb events configuration EventHooking.gef_on_continue_hook(EventHandler.continue_handler) EventHooking.gef_on_stop_hook(EventHandler.hook_stop_handler) EventHooking.gef_on_new_hook(EventHandler.new_objfile_handler) EventHooking.gef_on_exit_hook(EventHandler.exit_handler) EventHooking.gef_on_memchanged_hook(EventHandler.memchanged_handler) EventHooking.gef_on_regchanged_hook(EventHandler.regchanged_handler) if gdb.current_progspace().filename is not None: # if here, we are sourcing gef from a gdb session already attached # we must force a call to the new_objfile handler EventHandler.new_objfile_handler(None) # python-interactive hexon() # If GEF is loaded after gdb is connected if is_alive(): if current_arch is None: set_arch(get_arch()) return if __name__ == "__main__": Gef.main() ================================================ FILE: install-minimal.sh ================================================ #!/bin/sh -ex echo "[+] Initialize" GDBINIT_PATH="/root/.gdbinit" GEF_DIR="/root/.gef" GEF_PATH="${GEF_DIR}/gef.py" echo "[+] User check" if [ "$(id -u)" != "0" ]; then echo "[-] Detected non-root user." echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Check if another gef is installed" if [ -e "${GEF_PATH}" ]; then echo "[-] ${GEF_PATH} already exists. Please delete or rename." echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Create .gef directory" if [ ! -e "${GEF_DIR}" ]; then mkdir -p "${GEF_DIR}" fi echo "[+] Download gef" wget -q https://raw.githubusercontent.com/bata24/gef/dev/gef.py -O "${GEF_PATH}" if [ ! -s "${GEF_PATH}" ]; then echo "[-] Downloading ${GEF_PATH} failed." rm -f "${GEF_PATH}" echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Setup gef" STARTUP_COMMAND="python sys.path.insert(0, \"${GEF_DIR}\"); from gef import *; Gef.main()" if [ ! -e "${GDBINIT_PATH}" ] || [ -z "$(grep "from gef import" "${GDBINIT_PATH}")" ]; then echo "${STARTUP_COMMAND}" >> "${GDBINIT_PATH}" fi echo "[+] INSTALLATION SUCCESSFUL" exit 0 ================================================ FILE: install-no-uv.sh ================================================ #!/bin/sh -ex echo "[+] Initialize" GDBINIT_PATH="/root/.gdbinit" GEF_DIR="/root/.gef" GEF_PATH="${GEF_DIR}/gef.py" echo "[+] User check" if [ "$(id -u)" != "0" ]; then echo "[-] Detected non-root user." echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Check if another gef is installed" if [ -e "${GEF_PATH}" ]; then echo "[-] ${GEF_PATH} already exists. Please delete or rename." echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Create .gef directory" if [ ! -e "${GEF_DIR}" ]; then mkdir -p "${GEF_DIR}" fi echo "[+] apt" apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata apt-get install -y gdb-multiarch wget unzip apt-get install -y binutils python3-pip ruby-dev git file colordiff imagemagick # Since installing bpftool fails inside a container, it is excluded. if [ ! -f /.dockerenv ]; then apt-get install -y bpftool fi # Installing binwalk requires a large number of packages and takes a significant amount of time, # so please enable it only when necessary. #apt-get install -y binwalk echo "[+] pip3" pip3 install setuptools crccheck unicorn capstone ropper keystone-engine tqdm magika codext angr pillow pyzbar # The GEF installer installs `seccomp-tools` if neither `ceccomp` nor `seccomp-tools` is found. # I recomend `ceccomp`, but its build is not simple. Install it manually if needed. echo "[+] Install seccomp-tools" if [ -z "$(command -v seccomp-tools)" ] && [ -z "$(command -v ceccomp)" ]; then gem install seccomp-tools fi echo "[+] Install one_gadget" if [ -z "$(command -v one_gadget)" ]; then gem install one_gadget fi echo "[+] Install rp++" if [ "$(uname -m)" = "x86_64" ]; then if [ -z "$(command -v rp-lin)" ] && [ ! -e /usr/local/bin/rp-lin ]; then wget -q https://github.com/0vercl0k/rp/releases/download/v2.1.5/rp-lin-clang.zip -P /tmp unzip /tmp/rp-lin-clang.zip -d /usr/local/bin/ rm /tmp/rp-lin-clang.zip fi fi echo "[+] Download gef" wget -q https://raw.githubusercontent.com/bata24/gef/dev/gef.py -O "${GEF_PATH}" if [ ! -s "${GEF_PATH}" ]; then echo "[-] Downloading ${GEF_PATH} failed." rm -f "${GEF_PATH}" echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Setup gef" STARTUP_COMMAND="python sys.path.insert(0, \"${GEF_DIR}\"); from gef import *; Gef.main()" if [ ! -e "${GDBINIT_PATH}" ] || [ -z "$(grep "from gef import" "${GDBINIT_PATH}")" ]; then echo "${STARTUP_COMMAND}" >> "${GDBINIT_PATH}" fi echo "[+] INSTALLATION SUCCESSFUL" exit 0 ================================================ FILE: install-uv.sh ================================================ #!/bin/sh -ex echo "[+] Initialize" GDBINIT_PATH="/root/.gdbinit" GEF_DIR="/root/.gef" GEF_PATH="${GEF_DIR}/gef.py" GEF_VENV_CONF_PATH="${GEF_DIR}/gef.venv.conf" GEF_VENV_PATH="${GEF_DIR}/.venv-gef" GEF_VENV_BIN_PATH="${GEF_VENV_PATH}/bin" echo "[+] User check" if [ "$(id -u)" != "0" ]; then echo "[-] Detected non-root user." echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Check if another gef is installed" if [ -e "${GEF_PATH}" ]; then echo "[-] ${GEF_PATH} already exists. Please delete or rename." echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Create .gef directory" if [ ! -e "${GEF_DIR}" ]; then mkdir -p "${GEF_DIR}" fi echo "[+] apt" apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata apt-get install -y gdb-multiarch wget unzip apt-get install -y binutils python3-dev gcc make ruby-dev git file colordiff imagemagick # Since installing bpftool fails inside a container, it is excluded. if [ ! -f /.dockerenv ]; then apt-get install -y bpftool fi # Installing binwalk requires a large number of packages and takes a significant amount of time, # so please enable it only when necessary. #apt-get install -y binwalk echo "[+] Install uv" if [ -z "$(command -v uv)" ]; then wget -qO- https://astral.sh/uv/install.sh | sh . $HOME/.local/bin/env fi echo "[+] Setup venv" if [ ! -e "${GEF_VENV_PATH}" ]; then uv venv "${GEF_VENV_PATH}" fi . "${GEF_VENV_PATH}/bin/activate" echo "[+] pip3" uv pip install setuptools crccheck unicorn capstone ropper keystone-engine tqdm magika codext angr pillow pyzbar # The GEF installer installs `seccomp-tools` if neither `ceccomp` nor `seccomp-tools` is found. # I recomend `ceccomp`, but its build is not simple. Install it manually if needed. echo "[+] Install seccomp-tools" if [ -z "$(command -v seccomp-tools)" ] && [ -z "$(command -v ceccomp)" ]; then gem install -i "${GEF_VENV_PATH}" seccomp-tools fi echo "[+] Install one_gadget" if [ -z "$(command -v one_gadget)" ]; then gem install -i "${GEF_VENV_PATH}" one_gadget fi echo "[+] Install rp++" if [ "$(uname -m)" = "x86_64" ]; then if [ -z "$(command -v rp-lin)" ]; then wget -q https://github.com/0vercl0k/rp/releases/download/v2.1.5/rp-lin-clang.zip -P /tmp unzip /tmp/rp-lin-clang.zip -d "${GEF_VENV_BIN_PATH}" rm /tmp/rp-lin-clang.zip fi fi echo "[+] Download gef" wget -q https://raw.githubusercontent.com/bata24/gef/dev/gef.py -O "${GEF_PATH}" if [ ! -s "${GEF_PATH}" ]; then echo "[-] Downloading ${GEF_PATH} failed." rm -f "${GEF_PATH}" echo "[-] INSTALLATION FAILED" exit 1 fi echo "[+] Setup gef" STARTUP_COMMAND="python sys.path.insert(0, \"${GEF_DIR}\"); from gef import *; Gef.main()" if [ ! -e "${GDBINIT_PATH}" ] || [ -z "$(grep "from gef import" "${GDBINIT_PATH}")" ]; then echo "${STARTUP_COMMAND}" >> "${GDBINIT_PATH}" fi echo "[+] Setup venv path hint file" GEF_VENV_SYS_PATH=$(python3 -c 'import sys,subprocess;a=subprocess.getoutput("gdb-multiarch -q -nx -ex \"pi sys.path\" -ex q");print(":".join(set(sys.path)-set(eval(a))-set([""])))') echo "GEF_VENV_GEM_HOME=${GEF_VENV_PATH}" >> ${GEF_VENV_CONF_PATH} echo "GEF_VENV_SYS_PATH=${GEF_VENV_SYS_PATH}" >> ${GEF_VENV_CONF_PATH} echo "GEF_VENV_BIN_PATH=${GEF_VENV_BIN_PATH}" >> ${GEF_VENV_CONF_PATH} echo "[+] INSTALLATION SUCCESSFUL" exit 0