From ac171b2a0a6ff4f55ab337e0002fefcbdf70fd57 Mon Sep 17 00:00:00 2001 From: Andrew Fasano Date: Wed, 12 Oct 2022 15:17:07 -0400 Subject: [PATCH] contrib/plugins: Add virtual machine introspection VMI provides a generic interface for virtual machine introspection while vmi linux implements the backend for Linux guests --- contrib/plugins/Makefile | 38 + contrib/plugins/vmi/core.c | 40 + contrib/plugins/vmi/linux/.gitignore | 3 + contrib/plugins/vmi/linux/README.md | 70 + contrib/plugins/vmi/linux/default_profile.cpp | 68 + contrib/plugins/vmi/linux/default_profile.h | 19 + contrib/plugins/vmi/linux/endian_helpers.h | 23 + contrib/plugins/vmi/linux/kernel_profile.h | 12 + contrib/plugins/vmi/linux/kernelinfo.conf | 1294 +++++++++++++++++ contrib/plugins/vmi/linux/kernelinfo.h | 200 +++ contrib/plugins/vmi/linux/kernelinfo_read.c | 268 ++++ contrib/plugins/vmi/linux/vmi_linux.cpp | 617 ++++++++ contrib/plugins/vmi/linux/vmi_linux.h | 708 +++++++++ contrib/plugins/vmi/vmi.h | 15 + contrib/plugins/vmi/vmi_types.h | 186 +++ 15 files changed, 3561 insertions(+) create mode 100644 contrib/plugins/vmi/core.c create mode 100644 contrib/plugins/vmi/linux/.gitignore create mode 100644 contrib/plugins/vmi/linux/README.md create mode 100644 contrib/plugins/vmi/linux/default_profile.cpp create mode 100644 contrib/plugins/vmi/linux/default_profile.h create mode 100644 contrib/plugins/vmi/linux/endian_helpers.h create mode 100644 contrib/plugins/vmi/linux/kernel_profile.h create mode 100644 contrib/plugins/vmi/linux/kernelinfo.conf create mode 100644 contrib/plugins/vmi/linux/kernelinfo.h create mode 100644 contrib/plugins/vmi/linux/kernelinfo_read.c create mode 100644 contrib/plugins/vmi/linux/vmi_linux.cpp create mode 100644 contrib/plugins/vmi/linux/vmi_linux.h create mode 100644 contrib/plugins/vmi/vmi.h create mode 100644 contrib/plugins/vmi/vmi_types.h diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 827e025eff280..031223771c7b3 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -34,6 +34,8 @@ NAMES += cflow NAMES += stringsearch NAMES += syscalls NAMES += syscalls_logger +NAMES += vmi/core +NAMES += vmi/linux # Replace '/' with '_' in plugin names NAMES_UNDERSCORE := $(subst /,_,$(NAMES)) @@ -62,6 +64,36 @@ all: $(SONAMES) PLUGIN_OBJS := $(addsuffix .o,$(NAMES_UNDERSCORE)) +# Build VMI core and VMI Linux +VPATH += vmi vmi/linux +VMI_LINUX_SRCS := \ + vmi/linux/vmi_linux.cpp \ + vmi/linux/default_profile.cpp \ + vmi/linux/kernelinfo_read.c + +# Function to generate object file names from source file paths +define obj_from_src +$(subst /,_,$(basename $(1))).o +endef + +VMI_LINUX_OBJS := $(foreach src,$(VMI_LINUX_SRCS),$(call obj_from_src,$(src))) + +define compile_vmi_linux +$(call obj_from_src,$(1)): $(1) + $(call quiet-command, \ + $(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $$@ $$<, \ + BUILD, plugin $$@) +endef + +# Apply the compilation rules to all source files +$(foreach src,$(VMI_LINUX_SRCS),$(eval $(call compile_vmi_linux,$(src)))) + +# Define rule to build the shared library from multiple object files +libvmi_linux$(SO_SUFFIX): $(VMI_LINUX_OBJS) + $(call quiet-command, \ + $(CC) -shared -o $@ $^ $(LDLIBS), \ + LINK, plugin $@) + # Rule for creating shared libraries for individual plugins lib%$(SO_SUFFIX): %.o $(call quiet-command, \ @@ -92,6 +124,12 @@ endif $(CXX) $(CXXFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $<, \ BUILD, plugin $@) +# Special rule to build vmi_core.o +vmi_core.o: vmi/core.c + $(call quiet-command, \ + $(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $<, \ + BUILD, plugin $@) + clean distclean: rm -f *.o *$(SO_SUFFIX) *.d rm -Rf .libs diff --git a/contrib/plugins/vmi/core.c b/contrib/plugins/vmi/core.c new file mode 100644 index 0000000000000..7a403d0069799 --- /dev/null +++ b/contrib/plugins/vmi/core.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +QEMU_PLUGIN_EXPORT const char *qemu_plugin_name = "vmi"; +#include "vmi.h" + +static qemu_plugin_id_t self_id; +QEMU_PLUGIN_EXPORT VmiProc *get_current_process(void) { + VmiProc *p = NULL; + qemu_plugin_run_callback(self_id, "on_get_current_process", &p, NULL); + return p; +} + +QEMU_PLUGIN_EXPORT VmiProc *get_process(const VmiProcHandle *h) { + VmiProc *p = NULL; // output + struct get_process_data* evdata = (struct get_process_data*)malloc(sizeof(struct get_process_data)); + evdata->h = h; + evdata->p = &p; + + qemu_plugin_run_callback(self_id, "on_get_process", evdata, NULL); + return p; +} + +QEMU_PLUGIN_EXPORT VmiProcHandle *get_current_process_handle(void) { + VmiProcHandle *h = NULL; + qemu_plugin_run_callback(self_id, "on_get_current_process_handle", &h, NULL); + return h; +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, char **argv) { + self_id = id; + qemu_plugin_create_callback(id, "on_get_current_process"); + qemu_plugin_create_callback(id, "on_get_process"); + qemu_plugin_create_callback(id, "on_get_current_process_handle"); + return 0; +} diff --git a/contrib/plugins/vmi/linux/.gitignore b/contrib/plugins/vmi/linux/.gitignore new file mode 100644 index 0000000000000..08151c42fbdd3 --- /dev/null +++ b/contrib/plugins/vmi/linux/.gitignore @@ -0,0 +1,3 @@ +docs +*.sw.* +*~ diff --git a/contrib/plugins/vmi/linux/README.md b/contrib/plugins/vmi/linux/README.md new file mode 100644 index 0000000000000..45538880f302c --- /dev/null +++ b/contrib/plugins/vmi/linux/README.md @@ -0,0 +1,70 @@ +Plugin: VMI linux +=========== + +Summary +------- + +`vmi_linux` provides Linux introspection information and makes it available through the OSI interface. It does so by knowing about the offsets for various Linux kernel data structures and then providing algorithms that traverse these data structures in the guest virtual machine. + +Because the offsets of fields in Linux kernel data structures change frequently (and can even depend on the specific compilation flags used), `vmi_linux` uses a configuration file to specify the offsets of critical data structures. A portion of such a configuration file, which is in the [GLib key-value format](https://developer.gnome.org/glib/stable/glib-Key-value-file-parser.html) (similar to .ini files), is given below: + + [ubuntu:4.4.0-98-generic:32] + name = 4.4.0-98-generic|#121-Ubuntu SMP Tue Oct 10 14:23:20 UTC 2017|i686 + version.a = 4 + version.b = 4 + version.c = 90 + task.init_addr = 3249445504 + #task.init_addr = 0xC1AE9A80 + #task.per_cpu_offset_0 = 0x34B42000 + task.per_cpu_offset_0 = 884219904 + #task.current_task_addr = 0xC1C852A8 + task.current_task_addr = 3251131048 + task.size = 5824 + task.tasks_offset = 624 + task.pid_offset = 776 + + [... omitted ...] + + [debian:4.9.0-6-686-pae:32] + name = 4.9.0-6-686-pae|#1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02)|i686 + version.a = 4 + version.b = 9 + version.c = 88 + task.init_addr = 3245807232 + #task.init_addr = 0xC1771680 + #task.per_cpu_offset_0 = 0x36127000 + task.per_cpu_offset_0 = 907177984 + #task.current_task_addr = 0xC18C3208 + task.current_task_addr = 3247190536 + task.size = 5888 + task.tasks_offset = 708 + task.pid_offset = 864 + + [... omitted ...] + +Of course, generating this file by hand would be extremely painful. We refer interested readers to PANDA.re's infrastructure to generate these configurations: https://github.com/panda-re/panda/tree/dev/panda/plugins/osi_linux/utils + +Arguments +--------- + +* `conf`: string: The location of the configuration file that gives the required offsets for different versions of Linux. +* `group`: string, The name of a configuration group to use from the kernelinfo file (multiple configurations can be stored in a single `kernelinfo.conf`). + +Dependencies +------------ + +`vmi_linux` is an introspection provider for the `vmi` plugin. + +Example +------- + +Assuming you have a `kernelinfo.conf` in the current directory with a configuration named `my_kernel_info`, you can run the OSI test plugin on a Linux replay as follows: + +```bash + [qemu args] -plugin vmi_core -pluginvmi_linux,kconf_file=kernelinfo.conf,kconf_group=my_kernel_info +``` + +Background +--------- + +VMI Linux was originally developed for [PANDA.re](https://panda.re) and named `osi_linux` with OSI standing for Operating System Introspection, the name of PANDA's VMI system. diff --git a/contrib/plugins/vmi/linux/default_profile.cpp b/contrib/plugins/vmi/linux/default_profile.cpp new file mode 100644 index 0000000000000..4652bb091c0ef --- /dev/null +++ b/contrib/plugins/vmi/linux/default_profile.cpp @@ -0,0 +1,68 @@ +#include "vmi_linux.h" +#include "default_profile.h" +#include "../vmi_types.h" + +/** + * @brief Retrieves the task_struct address using per cpu information. + */ +target_ptr_t default_get_current_task_struct(void) +{ + struct_get_ret_t err; + target_ptr_t current_task_addr; + target_ptr_t ts; + current_task_addr = ki.task.current_task_addr; + err = struct_get(&ts, current_task_addr, ki.task.per_cpu_offset_0_addr); + //assert(err == struct_get_ret_t::SUCCESS && "failed to get current task struct"); + if (err != struct_get_ret_t::SUCCESS) { + // Callers need to check if we return NULL! + return 0; + } + + fixupendian(ts); + return ts; +} + +/** + * @brief Retrieves the address of the following task_struct in the process list. + */ +target_ptr_t default_get_task_struct_next(target_ptr_t task_struct) +{ + struct_get_ret_t err; + target_ptr_t tasks; + err = struct_get(&tasks, task_struct, ki.task.tasks_offset); + fixupendian(tasks); + assert(err == struct_get_ret_t::SUCCESS && "failed to get next task"); + return tasks-ki.task.tasks_offset; +} + +/** + * @brief Retrieves the thread group leader address from task_struct. + */ +target_ptr_t default_get_group_leader(target_ptr_t ts) +{ + struct_get_ret_t err; + target_ptr_t group_leader; + err = struct_get(&group_leader, ts, ki.task.group_leader_offset); + fixupendian(group_leader); + assert(err == struct_get_ret_t::SUCCESS && "failed to get group leader for task"); + return group_leader; +} + +/** + * @brief Retrieves the array of file structs from the files struct. + * The n-th element of the array corresponds to the n-th open fd. + */ +target_ptr_t default_get_file_fds(target_ptr_t files) +{ + struct_get_ret_t err; + target_ptr_t files_fds; + err = struct_get(&files_fds, files, {ki.fs.fdt_offset, ki.fs.fd_offset}); + if (err != struct_get_ret_t::SUCCESS) { + printf("Failed to retrieve file structs (error code: %d)", err); + return (target_ptr_t)NULL; + } + fixupendian(files_fds); + return files_fds; +} + +/* vim:set tabstop=4 softtabstop=4 expandtab: */ diff --git a/contrib/plugins/vmi/linux/default_profile.h b/contrib/plugins/vmi/linux/default_profile.h new file mode 100644 index 0000000000000..7db5d66999cd6 --- /dev/null +++ b/contrib/plugins/vmi/linux/default_profile.h @@ -0,0 +1,19 @@ +#pragma once + +#include "kernel_profile.h" + +target_ptr_t default_get_current_task_struct(); +target_ptr_t default_get_task_struct_next(target_ptr_t ts); +target_ptr_t default_get_group_leader(target_ptr_t ts); +target_ptr_t default_get_file_fds(target_ptr_t files); +bool can_read_current(); +void on_first_syscall(target_ulong pc, target_ulong callno); + +const KernelProfile DEFAULT_PROFILE = { + .get_current_task_struct = &default_get_current_task_struct, + .get_task_struct_next = &default_get_task_struct_next, + .get_group_leader = &default_get_group_leader, + .get_files_fds = &default_get_file_fds +}; + +/* vim:set tabstop=4 softtabstop=4 expandtab: */ diff --git a/contrib/plugins/vmi/linux/endian_helpers.h b/contrib/plugins/vmi/linux/endian_helpers.h new file mode 100644 index 0000000000000..39aac2b236170 --- /dev/null +++ b/contrib/plugins/vmi/linux/endian_helpers.h @@ -0,0 +1,23 @@ +// TODO: QEMU plugin model builds a signle plugin for all guests, +// we do not have a compile-time way to check if we need to swap endianness. +// Not sure if there's a way to check at runtime + +// VMI Linux works with a bunch of pointers which we need to +// flip if the guest/host endianness mismatch. +#if defined(TARGET_WORDS_BIGENDIAN) != defined(HOST_WORDS_BIGENDIAN) +// If guest and host endianness don't match: +// fixupendian will flip a dword in place +#define fixupendian(x) {x=bswap32((target_ptr_t)x);} +#define fixupendian64(x) {x=bswap64((uint64_t)x);} +// of flipbadendian will flip a dword +#define flipbadendian(x) bswap32((target_ptr_t)x) +#define flipbadendian64(x) bswap64((uint64_t)x) + + +#else +#define fixupendian(x) {} +#define fixupendian64(x) {} +#define flipbadendian(x) x +#define flipbadendian64(x) x +#endif + diff --git a/contrib/plugins/vmi/linux/kernel_profile.h b/contrib/plugins/vmi/linux/kernel_profile.h new file mode 100644 index 0000000000000..9a780b3004db1 --- /dev/null +++ b/contrib/plugins/vmi/linux/kernel_profile.h @@ -0,0 +1,12 @@ +#pragma once +#include "../vmi_types.h" + +struct KernelProfile +{ + target_ptr_t (*get_current_task_struct)(); + target_ptr_t (*get_task_struct_next)(target_ptr_t ts); + target_ptr_t (*get_group_leader)(target_ptr_t ts); + target_ptr_t (*get_files_fds)(target_ptr_t files); +}; + +/* vim:set tabstop=4 softtabstop=4 expandtab: */ diff --git a/contrib/plugins/vmi/linux/kernelinfo.conf b/contrib/plugins/vmi/linux/kernelinfo.conf new file mode 100644 index 0000000000000..5cfcab29d028d --- /dev/null +++ b/contrib/plugins/vmi/linux/kernelinfo.conf @@ -0,0 +1,1294 @@ +[ubuntu:5.4.0-42-generic:64] +name = 5.4.0-42-generic|#46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020|x86_64 Ubuntu Live DVD 18.04.5 +version.a = 5 +version.b = 4 +version.c = 44 +task.per_cpu_offsets_addr = 18446744071600072992 +task.per_cpu_offset_0_addr = 18446612687365341184 +task.current_task_addr = 93120 +task.init_addr = 18446744071601993600 +#task.per_cpu_offsets_addr = FFFFFFFF8243E920 +#task.per_cpu_offset_0_addr = 0xFFFF88813BA00000 +#task.current_task_addr = 0x00016BC0 +#task.init_addr = 0xFFFFFFFF82613780 +task.size = 9216 +task.tasks_offset = 1984 +task.pid_offset = 2240 +task.tgid_offset = 2244 +task.group_leader_offset = 2304 +task.thread_group_offset = 2416 +task.real_parent_offset = 2256 +task.parent_offset = 2264 +task.mm_offset = 2064 +task.stack_offset = 24 +task.real_cred_offset = 2656 +task.cred_offset = 2664 +task.comm_offset = 2680 +task.comm_size = 16 +task.files_offset = 2752 +task.start_time_offset = 2536 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 1032 +mm.mmap_offset = 0 +mm.pgd_offset = 80 +mm.arg_start_offset = 312 +mm.start_brk_offset = 288 +mm.brk_offset = 296 +mm.start_stack_offset = 304 +vma.size = 208 +vma.vm_mm_offset = 64 +vma.vm_start_offset = 0 +vma.vm_end_offset = 8 +vma.vm_next_offset = 16 +vma.vm_flags_offset = 80 +vma.vm_file_offset = 160 +fs.f_path_dentry_offset = 24 +fs.f_path_mnt_offset = 16 +fs.f_pos_offset = 104 +fs.fdt_offset = 32 +fs.fdtab_offset = 40 +fs.fd_offset = 8 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 32 +path.d_iname_offset = 56 +path.d_parent_offset = 24 +path.d_op_offset = 96 +path.d_dname_offset = 72 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -16 +path.mnt_mountpoint_offset = -8 + +[ubuntu:5.0.0-23-generic:64] +name = 5.0.0-23-generic|#24~18.04.1-Ubuntu SMP Mon Jul 29 16:12:28 UTC 2019|x86_64 Ubuntu Live DVD 18.04.3 +version.a = 5 +version.b = 0 +version.c = 15 +task.per_cpu_offsets_addr = 18446744071599519776 +task.per_cpu_offset_0_addr = 18446612687365341184 +task.current_task_addr = 89088 +task.init_addr = 18446744071602009920 +#task.per_cpu_offsets_addr = FFFFFFFF823B7820 +#task.per_cpu_offset_0_addr = 0xFFFF88813BA00000 +#task.current_task_addr = 0x00015C00 +#task.init_addr = 0xFFFFFFFF82617740 +task.size = 9152 +task.tasks_offset = 1960 +task.pid_offset = 2216 +task.tgid_offset = 2220 +task.group_leader_offset = 2280 +task.thread_group_offset = 2392 +task.real_parent_offset = 2232 +task.parent_offset = 2240 +task.mm_offset = 2040 +task.stack_offset = 24 +task.real_cred_offset = 2624 +task.cred_offset = 2632 +task.comm_offset = 2640 +task.comm_size = 16 +task.files_offset = 2712 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 1032 +mm.mmap_offset = 0 +mm.pgd_offset = 80 +mm.arg_start_offset = 304 +mm.start_brk_offset = 280 +mm.brk_offset = 288 +mm.start_stack_offset = 296 +vma.size = 208 +vma.vm_mm_offset = 64 +vma.vm_start_offset = 0 +vma.vm_end_offset = 8 +vma.vm_next_offset = 16 +vma.vm_flags_offset = 80 +vma.vm_file_offset = 160 +fs.f_path_dentry_offset = 24 +fs.f_path_mnt_offset = 16 +fs.f_pos_offset = 104 +fs.fdt_offset = 32 +fs.fdtab_offset = 40 +fs.fd_offset = 8 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 32 +path.d_iname_offset = 56 +path.d_parent_offset = 24 +path.d_op_offset = 96 +path.d_dname_offset = 72 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -16 +path.mnt_mountpoint_offset = -8 + +[ubuntu:5.3.0-28-generic:64] +name = 5.3.0-28-generic|#30~18.04.1-Ubuntu SMP Fri Jan 17 06:14:09 UTC 2020|x86_64 Ubuntu Live DVD 18.04.4 +version.a = 5 +version.b = 3 +version.c = 13 +task.per_cpu_offsets_addr = 18446744071599864096 +task.per_cpu_offset_0_addr = 18446612687365341184 +task.current_task_addr = 93120 +task.init_addr = 18446744071601993600 +#task.per_cpu_offsets_addr = FFFFFFFF8240B920 +#task.per_cpu_offset_0_addr = 0xFFFF88813BA00000 +#task.current_task_addr = 0x00016BC0 +#task.init_addr = 0xFFFFFFFF82613780 +task.size = 9152 +task.tasks_offset = 1984 +task.pid_offset = 2240 +task.tgid_offset = 2244 +task.group_leader_offset = 2304 +task.thread_group_offset = 2416 +task.real_parent_offset = 2256 +task.parent_offset = 2264 +task.mm_offset = 2064 +task.stack_offset = 24 +task.real_cred_offset = 2648 +task.cred_offset = 2656 +task.comm_offset = 2672 +task.comm_size = 16 +task.files_offset = 2744 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 1032 +mm.mmap_offset = 0 +mm.pgd_offset = 80 +mm.arg_start_offset = 304 +mm.start_brk_offset = 280 +mm.brk_offset = 288 +mm.start_stack_offset = 296 +vma.size = 208 +vma.vm_mm_offset = 64 +vma.vm_start_offset = 0 +vma.vm_end_offset = 8 +vma.vm_next_offset = 16 +vma.vm_flags_offset = 80 +vma.vm_file_offset = 160 +fs.f_path_dentry_offset = 24 +fs.f_path_mnt_offset = 16 +fs.f_pos_offset = 104 +fs.fdt_offset = 32 +fs.fdtab_offset = 40 +fs.fd_offset = 8 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 32 +path.d_iname_offset = 56 +path.d_parent_offset = 24 +path.d_op_offset = 96 +path.d_dname_offset = 72 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -16 +path.mnt_mountpoint_offset = -8 + +[ubuntu:4.4.0-98-generic:32] +name = 4.4.0-98-generic|#121-Ubuntu SMP Tue Oct 10 14:23:20 UTC 2017|i686 +version.a = 4 +version.b = 4 +version.c = 90 +task.per_cpu_offsets_addr = 0 +task.per_cpu_offset_0_addr = 884219904 +task.current_task_addr = 3251131048 +task.init_addr = 3249445504 +#task.per_cpu_offsets_addr = 0x00000000 +#task.per_cpu_offset_0_addr = 0x34B42000 +#task.current_task_addr = 0xC1C852A8 +#task.init_addr = 0xC1AE9A80 +task.size = 5824 +task.tasks_offset = 624 +task.pid_offset = 776 +task.tgid_offset = 780 +task.group_leader_offset = 812 +task.thread_group_offset = 868 +task.real_parent_offset = 788 +task.parent_offset = 792 +task.mm_offset = 664 +task.stack_offset = 4 +task.real_cred_offset = 1004 +task.cred_offset = 1008 +task.comm_offset = 1012 +task.comm_size = 16 +task.files_offset = 1052 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 460 +mm.mmap_offset = 0 +mm.pgd_offset = 32 +mm.arg_start_offset = 156 +mm.start_brk_offset = 144 +mm.brk_offset = 148 +mm.start_stack_offset = 152 +vma.size = 100 +vma.vm_mm_offset = 32 +vma.vm_start_offset = 0 +vma.vm_end_offset = 4 +vma.vm_next_offset = 8 +vma.vm_flags_offset = 44 +vma.vm_file_offset = 84 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 64 +fs.fdt_offset = 20 +fs.fdtab_offset = 24 +fs.fd_offset = 4 +qstr.size = 12 +qstr.name_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 +path.d_op_offset = 80 +path.d_dname_offset = 32 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -8 +path.mnt_mountpoint_offset = -4 + +[ubuntu:4.4.0-154-generic:64] +name = 4.4.0-154-generic|#181-Ubuntu SMP Tue Jun 25 05:29:03 UTC 2019|x86_64 +version.a = 4 +version.b = 4 +version.c = 179 +task.per_cpu_offsets_addr = 0 +task.per_cpu_offset_0_addr = 18446612134457507840 +task.current_task_addr = 64576 +task.init_addr = 18446744071593604352 +task.switch_task_hook_addr = 18446744071579563824 +#task.per_cpu_offsets_addr = 0x0000000000000000 +#task.per_cpu_offset_0_addr = 0xFFFF88007FC00000 +#task.current_task_addr = 0x0000FC40 +#task.init_addr = 0xFFFFFFFF81E13500 +task.size = 6848 +task.tasks_offset = 856 +task.pid_offset = 1104 +task.tgid_offset = 1108 +task.group_leader_offset = 1168 +task.thread_group_offset = 1280 +task.real_parent_offset = 1120 +task.parent_offset = 1128 +task.mm_offset = 936 +task.stack_offset = 8 +task.real_cred_offset = 1528 +task.cred_offset = 1536 +task.comm_offset = 1544 +task.comm_size = 16 +task.files_offset = 1608 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 976 +mm.mmap_offset = 0 +mm.pgd_offset = 64 +mm.arg_start_offset = 288 +mm.start_brk_offset = 264 +mm.brk_offset = 272 +mm.start_stack_offset = 280 +vma.size = 200 +vma.vm_mm_offset = 64 +vma.vm_start_offset = 0 +vma.vm_end_offset = 8 +vma.vm_next_offset = 16 +vma.vm_flags_offset = 80 +vma.vm_file_offset = 160 +fs.f_path_dentry_offset = 24 +fs.f_path_mnt_offset = 16 +fs.f_pos_offset = 112 +fs.fdt_offset = 32 +fs.fdtab_offset = 40 +fs.fd_offset = 8 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 32 +path.d_iname_offset = 56 +path.d_parent_offset = 24 +path.d_op_offset = 96 +path.d_dname_offset = 64 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -16 +path.mnt_mountpoint_offset = -8 + +[debian:3.2.0-4-686-pae:32] +name = 3.2.0-4-686-pae|#1 SMP Debian 3.2.51-1|i686 +version.a = 3 +version.b = 2 +version.c = 78 +task.per_cpu_offsets_addr = 0 +task.per_cpu_offset_0_addr = 111493120 +task.current_task_addr = 3242704652 +task.init_addr = 3242069984 +#task.per_cpu_offsets_addr = 0x00000000 +#task.per_cpu_offset_0_addr = 0x06A54000 +#task.current_task_addr = 0xC147BF0C +#task.init_addr = 0xC13E0FE0 +task.size = 1060 +task.tasks_offset = 212 +task.pid_offset = 292 +task.tgid_offset = 296 +task.group_leader_offset = 328 +task.thread_group_offset = 384 +task.real_parent_offset = 304 +task.parent_offset = 308 +task.mm_offset = 240 +task.stack_offset = 4 +task.real_cred_offset = 504 +task.cred_offset = 508 +task.comm_offset = 516 +task.comm_size = 16 +task.files_offset = 680 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 448 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 152 +mm.start_brk_offset = 140 +mm.brk_offset = 144 +mm.start_stack_offset = 148 +vma.size = 88 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 4 +vma.vm_end_offset = 8 +vma.vm_next_offset = 12 +vma.vm_flags_offset = 28 +vma.vm_file_offset = 80 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 40 +fs.fdt_offset = 4 +fs.fdtab_offset = 8 +fs.fd_offset = 4 +qstr.size = 12 +qstr.name_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 +path.d_op_offset = 80 +path.d_dname_offset = 28 +path.mnt_root_offset = 16 +path.mnt_parent_offset = 8 +path.mnt_mountpoint_offset = 12 + +[redhat:2.4.18-14:32] +name = 2.4.18-14|#1 Wed Sep 4 13:35:50 EDT 2002|i686 +version.a = 2 +version.b = 4 +version.c = 18 +task.init_addr = 3224657920 +#task.init_addr = 0xC0346000 +task.size = 1456 +task.next_task_offset = 80 +task.pid_offset = 120 +task.tgid_offset = 136 +task.thread_group_offset = 164 +task.leader_offset = 140 +task.p_opptr_offset = 144 +task.p_pptr_offset = 148 +task.mm_offset = 88 +task.comm_offset = 574 +task.comm_size = 16 +task.files_offset = 1364 +task.switch_task_hook_addr = 3222304656 +mm.size = 128 +mm.mmap_offset = 0 +mm.pgd_offset = 12 +mm.arg_start_offset = 76 +mm.start_brk_offset = 64 +mm.brk_offset = 68 +mm.start_stack_offset = 72 +vma.size = 68 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 4 +vma.vm_end_offset = 8 +vma.vm_next_offset = 12 +vma.vm_flags_offset = 20 +vma.vm_file_offset = 56 +fs.f_dentry_offset = 8 +fs.f_vfsmnt_offset = 12 +fs.f_pos_offset = 32 +fs.fd_offset = 16 +qstr.size = 12 +qstr.name_offset = 0 +path.d_name_offset = 60 +path.d_iname_offset = 96 +path.d_parent_offset = 12 +path.d_op_offset = 76 +path.d_name_offset = 60 +path.mnt_root_offset = 16 +path.mnt_parent_offset = 8 +path.mnt_mountpoint_offset = 12 + +[ubuntu:4.4.0-130-generic:32] +name = 4.4.0-130-generic|#156-Ubuntu SMP Thu Jun 14 08:51:45 UTC 2018|i686 +version.a = 4 +version.b = 4 +version.c = 134 +task.per_cpu_offsets_addr = 3250260736 +task.per_cpu_offset_0_addr = 501317632 +task.current_task_addr = 3251229352 +task.init_addr = 3249519232 +#task.per_cpu_offsets_addr = 0xC1BB0B00 +#task.per_cpu_offset_0_addr = 0x1DE18000 +#task.current_task_addr = 0xC1C9D2A8 +#task.init_addr = 0xC1AFBA80 +task.size = 5824 +task.tasks_offset = 632 +task.pid_offset = 784 +task.tgid_offset = 788 +task.group_leader_offset = 820 +task.thread_group_offset = 876 +task.real_parent_offset = 796 +task.parent_offset = 800 +task.mm_offset = 672 +task.stack_offset = 4 +task.real_cred_offset = 1012 +task.cred_offset = 1016 +task.comm_offset = 1020 +task.comm_size = 16 +task.files_offset = 1060 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 460 +mm.mmap_offset = 0 +mm.pgd_offset = 32 +mm.arg_start_offset = 156 +mm.start_brk_offset = 144 +mm.brk_offset = 148 +mm.start_stack_offset = 152 +vma.size = 100 +vma.vm_mm_offset = 32 +vma.vm_start_offset = 0 +vma.vm_end_offset = 4 +vma.vm_next_offset = 8 +vma.vm_flags_offset = 44 +vma.vm_file_offset = 84 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 64 +fs.fdt_offset = 20 +fs.fdtab_offset = 24 +fs.fd_offset = 4 +qstr.size = 12 +qstr.name_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 +path.d_op_offset = 80 +path.d_dname_offset = 32 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -8 +path.mnt_mountpoint_offset = -4 + +##################################################################### +# Profiles that may need to be updated in order to be consistent +# with the latest kernelinfo.h. +##################################################################### +[debian-3.2.63:32] +name = #1 Debian 3.2.63-2+deb7u1 i686 +version.a = 3 +version.b = 2 +version.c = 63 +task.size = 1000 +#task.init_addr = 0xC1397480 +task.init_addr = 3241768064 +task.tasks_offset = 204 +task.pid_offset = 248 +task.tgid_offset = 252 +task.group_leader_offset = 284 +task.thread_group_offset = 340 +task.real_parent_offset = 260 +task.parent_offset = 264 +task.mm_offset = 212 +task.stack_offset = 4 +task.real_cred_offset = 460 +task.cred_offset = 464 +task.comm_offset = 472 +task.comm_size = 16 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 144 +mm.start_brk_offset = 132 +mm.brk_offset = 136 +mm.start_stack_offset = 140 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 4 +vma.vm_end_offset = 8 +vma.vm_next_offset = 12 +vma.vm_file_offset = 76 +vma.vm_flags_offset = 24 +fs.f_dentry_offset = 12 +fs.f_path_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 + +[debian-3.2.65:32] +#[debian-3.2.54:32] +name = #1 SMP Debian 3.2.65-1+deb7u2 i686 +version.a = 3 +version.b = 2 +version.c = 65 +task.size = 1060 +#task.init_addr = 0xC13E2FE0 +task.init_addr = 3242078176 +task.tasks_offset = 212 +task.pid_offset = 292 +task.tgid_offset = 296 +task.group_leader_offset = 328 +task.thread_group_offset = 384 +task.real_parent_offset = 304 +task.parent_offset = 308 +task.mm_offset = 240 +task.stack_offset = 4 +task.real_cred_offset = 504 +task.cred_offset = 508 +task.comm_offset = 516 +task.comm_size = 16 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 152 +mm.start_brk_offset = 140 +mm.brk_offset = 144 +mm.start_stack_offset = 148 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 4 +vma.vm_end_offset = 8 +vma.vm_next_offset = 12 +vma.vm_flags_offset = 28 +vma.vm_file_offset = 80 +fs.f_dentry_offset = 12 +fs.f_path_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 + +# -os linux-64-ubuntu:4.15.0-72-generic +[ubuntu:4.15.0-72-generic:64] +name = 4.15.0-72-generic|#81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019|x86_64 +version.a = 4 +version.b = 15 +version.c = 18 +task.per_cpu_offsets_addr = 18446744071823869664 +task.per_cpu_offset_0_addr = 18446615399706394624 +task.current_task_addr = 89088 +task.init_addr = 18446744071826388096 +#task.per_cpu_offsets_addr = FFFFFFFF8F9AC6E0 +#task.per_cpu_offset_0_addr = 0xFFFF8AF8BFC00000 +#task.current_task_addr = 0x00015C00 +#task.init_addr = 0xFFFFFFFF8FC13480 +task.size = 9088 +task.tasks_offset = 1960 +task.pid_offset = 2216 +task.tgid_offset = 2220 +task.group_leader_offset = 2280 +task.thread_group_offset = 2392 +task.real_parent_offset = 2232 +task.parent_offset = 2240 +task.mm_offset = 2040 +task.stack_offset = 24 +task.real_cred_offset = 2624 +task.cred_offset = 2632 +task.comm_offset = 2640 +task.comm_size = 16 +task.files_offset = 2704 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 2056 +mm.mmap_offset = 0 +mm.pgd_offset = 80 +mm.arg_start_offset = 296 +mm.start_brk_offset = 272 +mm.brk_offset = 280 +mm.start_stack_offset = 288 +vma.size = 208 +vma.vm_mm_offset = 64 +vma.vm_start_offset = 0 +vma.vm_end_offset = 8 +vma.vm_next_offset = 16 +vma.vm_flags_offset = 80 +vma.vm_file_offset = 160 +fs.f_path_dentry_offset = 24 +fs.f_path_mnt_offset = 16 +fs.f_pos_offset = 104 +fs.fdt_offset = 32 +fs.fdtab_offset = 40 +fs.fd_offset = 8 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 32 +path.d_iname_offset = 56 +path.d_parent_offset = 24 +path.d_op_offset = 96 +path.d_dname_offset = 72 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -16 +path.mnt_mountpoint_offset = -8 + +[debian:3.2.0-4-amd64:64] +name = 3.2.0-4-amd64|#1 SMP Debian 3.2.51-1|x86_64 +version.a = 3 +version.b = 2 +version.c = 96 +task.per_cpu_offsets_addr = 18446744071585716928 +task.per_cpu_offset_0_addr = 18446612132444241920 +task.current_task_addr = 50944 +task.init_addr = 18446744071585189920 +#task.per_cpu_offsets_addr = FFFFFFFF8168DAC0 +#task.per_cpu_offset_0_addr = 0xFFFF880007C00000 +#task.current_task_addr = 0x0000C700 +#task.init_addr = 0xFFFFFFFF8160D020 +task.size = 1792 +task.tasks_offset = 368 +task.pid_offset = 484 +task.tgid_offset = 488 +task.group_leader_offset = 552 +task.thread_group_offset = 664 +task.real_parent_offset = 504 +task.parent_offset = 512 +task.mm_offset = 424 +task.stack_offset = 8 +task.real_cred_offset = 896 +task.cred_offset = 904 +task.comm_offset = 920 +task.comm_size = 16 +task.files_offset = 1152 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 920 +mm.mmap_offset = 0 +mm.pgd_offset = 72 +mm.arg_start_offset = 288 +mm.start_brk_offset = 264 +mm.brk_offset = 272 +mm.start_stack_offset = 280 +vma.size = 176 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 8 +vma.vm_end_offset = 16 +vma.vm_next_offset = 24 +vma.vm_flags_offset = 48 +vma.vm_file_offset = 152 +fs.f_path_dentry_offset = 24 +fs.f_path_mnt_offset = 16 +fs.f_pos_offset = 64 +fs.fdt_offset = 8 +fs.fdtab_offset = 16 +fs.fd_offset = 8 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 32 +path.d_iname_offset = 56 +path.d_parent_offset = 24 +path.d_op_offset = 96 +path.d_dname_offset = 56 +path.mnt_root_offset = 32 +path.mnt_parent_offset = 16 +path.mnt_mountpoint_offset = 24 + +[ubuntu:4.15.0-72-generic-noaslr-nokaslr:64] +name = 4.15.0-72-generic|#81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019|x86_64 +version.a = 4 +version.b = 15 +version.c = 18 +task.per_cpu_offsets_addr = 18446744071597377248 +task.per_cpu_offset_0_addr = 18446612683139579904 +task.current_task_addr = 89088 +task.init_addr = 18446744071599895680 +#task.per_cpu_offsets_addr = FFFFFFFF821AC6E0 +#task.per_cpu_offset_0_addr = 0xFFFF88803FC00000 +#task.current_task_addr = 0x00015C00 +#task.init_addr = 0xFFFFFFFF82413480 +task.switch_task_hook_addr = 18446744071579607008 +#task.switch_task_hook_addr = 0XFFFFFFFF810B9FDF +task.size = 9088 +task.tasks_offset = 1960 +task.pid_offset = 2216 +task.tgid_offset = 2220 +task.group_leader_offset = 2280 +task.thread_group_offset = 2392 +task.real_parent_offset = 2232 +task.parent_offset = 2240 +task.mm_offset = 2040 +task.stack_offset = 24 +task.real_cred_offset = 2624 +task.cred_offset = 2632 +task.comm_offset = 2640 +task.comm_size = 16 +task.files_offset = 2704 +task.start_time_offset = 2512 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 2056 +mm.mmap_offset = 0 +mm.pgd_offset = 80 +mm.arg_start_offset = 296 +mm.start_brk_offset = 272 +mm.brk_offset = 280 +mm.start_stack_offset = 288 +vma.size = 208 +vma.vm_mm_offset = 64 +vma.vm_start_offset = 0 +vma.vm_end_offset = 8 +vma.vm_next_offset = 16 +vma.vm_flags_offset = 80 +vma.vm_file_offset = 160 +fs.f_path_dentry_offset = 24 +fs.f_path_mnt_offset = 16 +fs.f_pos_offset = 104 +fs.fdt_offset = 32 +fs.fdtab_offset = 40 +fs.fd_offset = 8 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 32 +path.d_iname_offset = 56 +path.d_parent_offset = 24 +path.d_op_offset = 96 +path.d_dname_offset = 72 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -16 +path.mnt_mountpoint_offset = -8 + +[ubuntu:4.4.0-170-generic:32] +name = 4.4.0-170-generic|#199-Ubuntu SMP Thu Nov 14 01:44:41 UTC 2019|i386 +version.a = 4 +version.b = 4 +version.c = 0 +task.per_cpu_offsets_addr = 3250412288 +task.per_cpu_offset_0_addr = 0 +task.current_task_addr = 3249658496 +task.init_addr = 3249658496 +#task.per_cpu_offsets_addr = c1bd5b00 +#task.per_cpu_offset_0_addr = 0x0 +#task.current_task_addr = 0xc1b1da80 +#task.init_addr = 0xc1b1da80 +task.size = 5824 +task.tasks_offset = 632 +task.pid_offset = 788 +task.tgid_offset = 792 +task.group_leader_offset = 824 +task.thread_group_offset = 880 +task.real_parent_offset = 800 +task.parent_offset = 804 +task.mm_offset = 672 +task.stack_offset = 4 +task.real_cred_offset = 1016 +task.cred_offset = 1020 +task.comm_offset = 1024 +task.comm_size = 16 +task.files_offset = 1064 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 472 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 160 +mm.start_brk_offset = 148 +mm.brk_offset = 152 +mm.start_stack_offset = 156 +vma.size = 100 +vma.vm_mm_offset = 32 +vma.vm_start_offset = 0 +vma.vm_end_offset = 4 +vma.vm_next_offset = 8 +vma.vm_flags_offset = 44 +vma.vm_file_offset = 84 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 64 +fs.fdt_offset = 20 +fs.fdtab_offset = 24 +fs.fd_offset = 4 +qstr.size = 12 +qstr.name_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 +path.d_op_offset = 80 +path.d_dname_offset = 32 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -8 +path.mnt_mountpoint_offset = -4 + +[ubuntu:4.4.200-170-generic:32] +name = 4.4.0-170-generic|#199-Ubuntu SMP Thu Nov 14 01:44:41 UTC 2019|i686 +version.a = 4 +version.b = 4 +version.c = 200 +task.per_cpu_offsets_addr = 3250412288 +task.per_cpu_offset_0_addr = 879689728 +task.current_task_addr = 3251368616 +task.init_addr = 3249658496 +#task.per_cpu_offsets_addr = C1BD5B00 +#task.per_cpu_offset_0_addr = 0x346F0000 +#task.current_task_addr = 0xC1CBF2A8 +#task.init_addr = 0xC1B1DA80 +task.size = 5824 +task.tasks_offset = 632 +task.pid_offset = 788 +task.tgid_offset = 792 +task.group_leader_offset = 824 +task.thread_group_offset = 880 +task.real_parent_offset = 800 +task.parent_offset = 804 +task.mm_offset = 672 +task.stack_offset = 4 +task.real_cred_offset = 1016 +task.cred_offset = 1020 +task.comm_offset = 1024 +task.comm_size = 16 +task.files_offset = 1064 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 472 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 160 +mm.start_brk_offset = 148 +mm.brk_offset = 152 +mm.start_stack_offset = 156 +vma.size = 100 +vma.vm_mm_offset = 32 +vma.vm_start_offset = 0 +vma.vm_end_offset = 4 +vma.vm_next_offset = 8 +vma.vm_flags_offset = 44 +vma.vm_file_offset = 84 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 64 +fs.fdt_offset = 20 +fs.fdtab_offset = 24 +fs.fd_offset = 4 +qstr.size = 12 +qstr.name_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 +path.d_op_offset = 80 +path.d_dname_offset = 32 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -8 +path.mnt_mountpoint_offset = -4 + +[debian:3.2.0-4-versatile-arm:32] +name = 3.2.0-4-versatile|#1 Debian 3.2.51-1|armv5tejl +version.a = 3 +version.b = 2 +version.c = 96 +arch = arm +task.per_cpu_offsets_addr = 0 +task.per_cpu_offset_0_addr = 0 +task.current_task_addr = 3224974096 +task.init_addr = 3224974096 +task.switch_task_hook_addr = 3221300196 +#task.switch_task_hook_addr = c00123e4 +#task.per_cpu_offsets_addr = 00000000 +#task.per_cpu_offset_0_addr = 0x00000000 +#task.current_task_addr = 0xC0393310 +#task.init_addr = 0xC0393310 +task.size = 888 +task.tasks_offset = 216 +task.pid_offset = 260 +task.tgid_offset = 264 +task.group_leader_offset = 292 +task.thread_group_offset = 348 +task.real_parent_offset = 268 +task.parent_offset = 272 +task.mm_offset = 224 +task.stack_offset = 4 +task.real_cred_offset = 472 +task.cred_offset = 476 +task.comm_offset = 484 +task.comm_size = 16 +task.files_offset = 532 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 384 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 144 +mm.start_brk_offset = 132 +mm.brk_offset = 136 +mm.start_stack_offset = 140 +vma.size = 84 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 4 +vma.vm_end_offset = 8 +vma.vm_next_offset = 12 +vma.vm_flags_offset = 24 +vma.vm_file_offset = 76 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 32 +fs.fdt_offset = 4 +fs.fdtab_offset = 8 +fs.fd_offset = 4 +qstr.size = 12 +qstr.name_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 +path.d_op_offset = 80 +path.d_dname_offset = 28 +path.mnt_root_offset = 16 +path.mnt_parent_offset = 8 +path.mnt_mountpoint_offset = 12 + +[centos:2.6.32-279:32] +name = 2.6.32-279.el6.i686|#1 SMP Fri Jun 22 10:59:55 UTC 2012|i686 +version.a = 2 +version.b = 6 +version.c = 32 +task.per_cpu_offsets_addr = 3232381824 +task.per_cpu_offset_0_addr = 23998464 +task.current_task_addr = 3232887180 +task.init_addr = 3231983072 +task.switch_task_hook_addr = 3225701568 +#task.per_cpu_offsets_addr = C0AA3B80 +#task.per_cpu_offset_0_addr = 0x016E3000 +#task.current_task_addr = 0xC0B1F18C +#task.init_addr = 0xC0A425E0 +task.size = 1348 +task.tasks_offset = 480 +task.pid_offset = 540 +task.tgid_offset = 544 +task.group_leader_offset = 576 +task.thread_group_offset = 632 +task.real_parent_offset = 552 +task.parent_offset = 556 +task.mm_offset = 508 +task.stack_offset = 4 +task.real_cred_offset = 752 +task.cred_offset = 756 +task.comm_offset = 784 +task.comm_size = 16 +task.files_offset = 944 +task.start_time_offset = 688 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 448 +mm.mmap_offset = 0 +mm.pgd_offset = 40 +mm.arg_start_offset = 164 +mm.start_brk_offset = 152 +mm.brk_offset = 156 +mm.start_stack_offset = 160 +vma.size = 100 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 4 +vma.vm_end_offset = 8 +vma.vm_next_offset = 12 +vma.vm_flags_offset = 28 +vma.vm_file_offset = 80 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 36 +fs.fdt_offset = 4 +fs.fdtab_offset = 8 +fs.fd_offset = 4 +qstr.size = 12 +qstr.name_offset = 8 +path.d_name_offset = 32 +path.d_iname_offset = 92 +path.d_parent_offset = 28 +path.d_op_offset = 80 +path.d_dname_offset = 24 +path.mnt_root_offset = 16 +path.mnt_parent_offset = 8 +path.mnt_mountpoint_offset = 12 + +[centos:2.6.32-279:64] +name = 2.6.32-279.22.1.el6.x86_64|#1 SMP Wed Feb 6 03:10:46 UTC 2013|x86_64 +version.a = 2 +version.b = 6 +version.c = 32 +task.per_cpu_offsets_addr = 18446744071591395744 +task.per_cpu_offset_0_addr = 18446612132349870080 +task.current_task_addr = 52160 +task.init_addr = 18446744071589908512 +task.switch_task_hook_addr = 18446744071579201344 +#task.per_cpu_offsets_addr = FFFFFFFF81BF81A0 +#task.per_cpu_offset_0_addr = 0xFFFF880002200000 +#task.current_task_addr = 0x0000CBC0 +#task.init_addr = 0xFFFFFFFF81A8D020 +task.size = 2648 +task.tasks_offset = 1096 +task.pid_offset = 1192 +task.tgid_offset = 1196 +task.group_leader_offset = 1256 +task.thread_group_offset = 1368 +task.real_parent_offset = 1208 +task.parent_offset = 1216 +task.mm_offset = 1152 +task.stack_offset = 8 +task.real_cred_offset = 1600 +task.cred_offset = 1608 +task.comm_offset = 1656 +task.comm_size = 16 +task.files_offset = 1880 +task.start_time_offset = 1480 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 1384 +mm.mmap_offset = 0 +mm.pgd_offset = 80 +mm.arg_start_offset = 320 +mm.start_brk_offset = 296 +mm.brk_offset = 304 +mm.start_stack_offset = 312 +vma.size = 200 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 8 +vma.vm_end_offset = 16 +vma.vm_next_offset = 24 +vma.vm_flags_offset = 48 +vma.vm_file_offset = 152 +fs.f_path_dentry_offset = 24 +fs.f_path_mnt_offset = 16 +fs.f_pos_offset = 64 +fs.fdt_offset = 8 +fs.fdtab_offset = 16 +fs.fd_offset = 8 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 48 +path.d_iname_offset = 160 +path.d_parent_offset = 40 +path.d_op_offset = 136 +path.d_dname_offset = 48 +path.mnt_root_offset = 32 +path.mnt_parent_offset = 16 +path.mnt_mountpoint_offset = 24 + +[debian:3.2.0-4-4kc-malta:32] +name = 3.2.0-4-4kc-malta|#1 Debian 3.2.51-1|mips +version.a = 3 +version.b = 2 +version.c = 78 +#arch = mips +task.per_cpu_offsets_addr = 0 +task.per_cpu_offset_0_addr = 0 +task.current_task_addr = 2154330880 +task.init_addr = 2154330880 +task.switch_task_hook_addr = 2148684512 +#task.switch_task_hook_addr = 801252e0 +#task.per_cpu_offsets_addr = 0 +#task.per_cpu_offset_0_addr = 0 +#task.current_task_addr = 0x80687B00 +#task.init_addr = 0x80687B00 +task.size = 1304 +task.tasks_offset = 216 +task.pid_offset = 260 +task.tgid_offset = 264 +task.group_leader_offset = 292 +task.thread_group_offset = 348 +task.real_parent_offset = 268 +task.parent_offset = 272 +task.mm_offset = 224 +task.stack_offset = 4 +task.real_cred_offset = 472 +task.cred_offset = 476 +task.comm_offset = 484 +task.comm_size = 16 +task.files_offset = 916 +task.start_time_offset = 404 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 388 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 144 +mm.start_brk_offset = 132 +mm.brk_offset = 136 +mm.start_stack_offset = 140 +vma.size = 84 +vma.vm_mm_offset = 0 +vma.vm_start_offset = 4 +vma.vm_end_offset = 8 +vma.vm_next_offset = 12 +vma.vm_flags_offset = 24 +vma.vm_file_offset = 76 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 32 +fs.fdt_offset = 4 +fs.fdtab_offset = 8 +fs.fd_offset = 4 +qstr.size = 12 +qstr.name_offset = 8 +path.d_name_offset = 20 +path.d_iname_offset = 36 +path.d_parent_offset = 16 +path.d_op_offset = 80 +path.d_dname_offset = 28 +path.mnt_root_offset = 16 +path.mnt_parent_offset = 8 +path.mnt_mountpoint_offset = 12 + +[buildroot:5.10.7-4kc-malta:32] +name = 5.10.7|#2 SMP Tue Sep 28 17:32:41 EDT 2021|mips +version.a = 5 +version.b = 10 +version.c = 7 +#arch = mips +task.per_cpu_offsets_addr = 2155389928 +task.per_cpu_offset_0_addr = 0 +task.current_task_addr = 2155400320 +task.init_addr = 2155400320 +#task.per_cpu_offsets_addr = 8078a3e8 +#task.per_cpu_offset_0_addr = 0x0 +#task.current_task_addr = 0x8078cc80 +#task.init_addr = 0x8078cc80 +task.size = 1728 +task.tasks_offset = 552 +task.pid_offset = 704 +task.tgid_offset = 708 +task.group_leader_offset = 740 +task.thread_group_offset = 796 +task.real_parent_offset = 716 +task.parent_offset = 720 +task.mm_offset = 592 +task.stack_offset = 4 +task.real_cred_offset = 964 +task.cred_offset = 968 +task.comm_offset = 972 +task.comm_size = 16 +task.files_offset = 1008 +task.start_time_offset = 880 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 480 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 176 +mm.start_brk_offset = 164 +mm.brk_offset = 168 +mm.start_stack_offset = 172 +vma.size = 92 +vma.vm_mm_offset = 32 +vma.vm_start_offset = 0 +vma.vm_end_offset = 4 +vma.vm_next_offset = 8 +vma.vm_flags_offset = 40 +vma.vm_file_offset = 80 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 64 +fs.fdt_offset = 20 +fs.fdtab_offset = 24 +fs.fd_offset = 4 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 24 +path.d_iname_offset = 44 +path.d_parent_offset = 16 +path.d_op_offset = 88 +path.d_dname_offset = 36 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -8 +path.mnt_mountpoint_offset = -4 + +[buildroot:5.10.7-4kc-malta-el:32] +name = 5.10.7|#2 SMP Tue Sep 28 16:41:52 EDT 2021|mips +version.a = 5 +version.b = 10 +version.c = 7 +#arch = mips +task.per_cpu_offsets_addr = 2155340776 +task.per_cpu_offset_0_addr = 0 +task.current_task_addr = 2155351168 +task.init_addr = 2155351168 +#task.per_cpu_offsets_addr = 8077e3e8 +#task.per_cpu_offset_0_addr = 0x0 +#task.current_task_addr = 0x80780c80 +#task.init_addr = 0x80780c80 +task.size = 1728 +task.tasks_offset = 552 +task.pid_offset = 704 +task.tgid_offset = 708 +task.group_leader_offset = 740 +task.thread_group_offset = 796 +task.real_parent_offset = 716 +task.parent_offset = 720 +task.mm_offset = 592 +task.stack_offset = 4 +task.real_cred_offset = 964 +task.cred_offset = 968 +task.comm_offset = 972 +task.comm_size = 16 +task.files_offset = 1008 +task.start_time_offset = 880 +cred.uid_offset = 4 +cred.gid_offset = 8 +cred.euid_offset = 20 +cred.egid_offset = 24 +mm.size = 480 +mm.mmap_offset = 0 +mm.pgd_offset = 36 +mm.arg_start_offset = 176 +mm.start_brk_offset = 164 +mm.brk_offset = 168 +mm.start_stack_offset = 172 +vma.size = 92 +vma.vm_mm_offset = 32 +vma.vm_start_offset = 0 +vma.vm_end_offset = 4 +vma.vm_next_offset = 8 +vma.vm_flags_offset = 40 +vma.vm_file_offset = 80 +fs.f_path_dentry_offset = 12 +fs.f_path_mnt_offset = 8 +fs.f_pos_offset = 64 +fs.fdt_offset = 20 +fs.fdtab_offset = 24 +fs.fd_offset = 4 +qstr.size = 16 +qstr.name_offset = 8 +path.d_name_offset = 24 +path.d_iname_offset = 44 +path.d_parent_offset = 16 +path.d_op_offset = 88 +path.d_dname_offset = 36 +path.mnt_root_offset = 0 +path.mnt_parent_offset = -8 +path.mnt_mountpoint_offset = -4 diff --git a/contrib/plugins/vmi/linux/kernelinfo.h b/contrib/plugins/vmi/linux/kernelinfo.h new file mode 100644 index 0000000000000..7cfcb29228ef6 --- /dev/null +++ b/contrib/plugins/vmi/linux/kernelinfo.h @@ -0,0 +1,200 @@ +/*! + * @file kernelinfo.h + * @brief Kernel specific information used for Linux OSI. + * + * @author Manolis Stamatogiannakis + * @copyright This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ +#pragma once +#include + +/** + * @brief Macro used to declare structs are as packed (or not). + * + * `kernelinfo_read.c` uses a bitmap to determine which members of + * `struct kernelinfo` were not read from the kernel config file. Due to + * alignment padding by the compiler, the bitmap check will have false + * positives for the bytes of the padding. + * + * Declaring the structs as packed will eliminate the false positives. + * This may be useful when debugging osi_linux or adding support for new + * kernels. However, packed structs may be slower to access in practice. + * For this, we keep packing off by default and bear with the false + * positives. + */ +#if 0 +#define PACKED_STRUCT(x) struct __attribute__((__packed__)) x +#else +#define PACKED_STRUCT(x) struct x +#endif + +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) +#define PROFILE_KVER_EQ(ki, _va, _vb, _vc) (KERNEL_VERSION(ki.version.a, ki.version.b, ki.version.c) == KERNEL_VERSION(_va, _vb, _vc)) +#define PROFILE_KVER_NE(ki, _va, _vb, _vc) (KERNEL_VERSION(ki.version.a, ki.version.b, ki.version.c) != KERNEL_VERSION(_va, _vb, _vc)) +#define PROFILE_KVER_LT(ki, _va, _vb, _vc) (KERNEL_VERSION(ki.version.a, ki.version.b, ki.version.c) < KERNEL_VERSION(_va, _vb, _vc)) +#define PROFILE_KVER_GT(ki, _va, _vb, _vc) (KERNEL_VERSION(ki.version.a, ki.version.b, ki.version.c) > KERNEL_VERSION(_va, _vb, _vc)) +#define PROFILE_KVER_LE(ki, _va, _vb, _vc) (KERNEL_VERSION(ki.version.a, ki.version.b, ki.version.c) <= KERNEL_VERSION(_va, _vb, _vc)) +#define PROFILE_KVER_GE(ki, _va, _vb, _vc) (KERNEL_VERSION(ki.version.a, ki.version.b, ki.version.c) >= KERNEL_VERSION(_va, _vb, _vc)) +// BEGIN_PYPANDA_NEEDS_THIS -- do not delete this comment bc pypanda +// api autogen needs it. And don't put any compiler directives +// between this and END_PYPANDA_NEEDS_THIS except includes of other +// files in this directory that contain subsections like this one. + +/** + * @brief Kernel Version information + */ +PACKED_STRUCT(version) { + int a; + int b; + int c; +}; + +/** + * @brief Information and offsets related to `struct task_struct`. + */ +PACKED_STRUCT(task_info) { + uint64_t per_cpu_offsets_addr; + uint64_t per_cpu_offset_0_addr; + uint64_t switch_task_hook_addr; /**< Address to hook for task switch notifications. */ + uint64_t current_task_addr; + uint64_t init_addr; /**< Address of the `struct task_struct` of the init task. */ + size_t size; /**< Size of `struct task_struct`. */ + union { + int tasks_offset; /**< TODO: add documentation for the rest of the struct members */ + int next_task_offset; + }; + int pid_offset; + int tgid_offset; + int group_leader_offset; + int thread_group_offset; + union { + int real_parent_offset; + int p_opptr_offset; + }; + union { + int parent_offset; + int p_pptr_offset; + }; + int mm_offset; + int stack_offset; + int real_cred_offset; + int cred_offset; + int comm_offset; /**< Offset of the command name in `struct task_struct`. */ + size_t comm_size; /**< Size of the command name. */ + int files_offset; /**< Offset for open files information. */ + int start_time_offset; /** offset of start_time */ +}; + +/** + * @brief Information and offsets related to `struct cred`. + */ +PACKED_STRUCT(cred_info) { + int uid_offset; + int gid_offset; + int euid_offset; + int egid_offset; +}; + +/** + * @brief Information and offsets related to `struct mm_struct`. + */ +PACKED_STRUCT(mm_info) { + size_t size; /**< Size of `struct mm_struct`. */ + int mmap_offset; + int pgd_offset; + int arg_start_offset; + int start_brk_offset; + int brk_offset; + int start_stack_offset; +}; + +/** + * @brief Information and offsets related to `struct vm_area_struct`. + */ +PACKED_STRUCT(vma_info) { + size_t size; /**< Size of `struct vm_area_struct`. */ + int vm_mm_offset; + int vm_start_offset; + int vm_end_offset; + int vm_next_offset; + int vm_file_offset; + int vm_flags_offset; +}; + +/** + * @brief Filesystem information and offsets. + */ +PACKED_STRUCT(fs_info) { + union { + int f_path_dentry_offset; + int f_dentry_offset; + }; + union { + int f_path_mnt_offset; + int f_vfsmnt_offset; + }; + int f_pos_offset; + int fdt_offset; + int fdtab_offset; + int fd_offset; +}; + +/** + * @brief qstr information and offsets + */ +PACKED_STRUCT(qstr_info) { + size_t size; + size_t name_offset; +}; + +/** + * @brief Path related information and offsets. + */ +PACKED_STRUCT(path_info) { + int d_name_offset; + int d_iname_offset; + int d_parent_offset; + int d_op_offset; /**< Offset of the dentry ops table. */ + int d_dname_offset; /**< Offset of dynamic name function in dentry ops. */ + int mnt_root_offset; + int mnt_parent_offset; + int mnt_mountpoint_offset; +}; + +/** + * @brief Wrapper for the structure-specific structs. + */ +PACKED_STRUCT(kernelinfo) { + char *name; + struct version version; + struct task_info task; + struct cred_info cred; + struct mm_info mm; + struct vma_info vma; + struct fs_info fs; + struct qstr_info qstr; + struct path_info path; +}; + +// END_PYPANDA_NEEDS_THIS -- do not delete this comment! + +#if defined(__G_LIB_H__) || defined(DOXYGEN) +/*! + * \def DEFAULT_KERNELINFO_FILE + * Default name for the kernel info configuration file. + */ +#define DEFAULT_KERNELINFO_FILE "kernelinfo.conf" + +#ifdef __cplusplus +extern "C" { +#endif + +int read_kernelinfo(gchar const *file, gchar const *group, struct kernelinfo *ki); +void list_kernelinfo_groups(gchar const *file); +#ifdef __cplusplus +} +#endif +#endif + +/* vim:set tabstop=4 softtabstop=4 noexpandtab: */ diff --git a/contrib/plugins/vmi/linux/kernelinfo_read.c b/contrib/plugins/vmi/linux/kernelinfo_read.c new file mode 100644 index 0000000000000..ecb5ca35ba6f9 --- /dev/null +++ b/contrib/plugins/vmi/linux/kernelinfo_read.c @@ -0,0 +1,268 @@ +/*! + * @file kernelinfo_read.c + * + * @brief Reads kernel information (struct offsets and such) from key-value config files. + * @see https://developer.gnome.org/glib/stable/glib-Key-value-file-parser.html + * + * @author Manolis Stamatogiannakis + * @copyright This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ +#include +#include +#include +#include +#include +#include "kernelinfo.h" + +/*! + * @brief Wrapper for error counters. + */ +struct kernelinfo_errors { + int version; + int name; + int task; + int cred; + int mm; + int vma; + int fs; + int qstr; + int path; +}; + +/* make sure PANDA_MSG is defined somewhere */ +#if defined(PLUGIN_NAME) +#include "panda/debug.h" +#else +#define PANDA_MSG +#endif + +/*! + * @brief Wrapper for reading information from keyfile and handle errors. + */ +#define READ_INFO_X(key_file_get, ki, memb, gerr, errcount, errbmp)\ + ((ki)->memb) = key_file_get(keyfile, group_real, #memb, &gerr);\ + if (gerr != NULL) { errcount++; g_error_free(gerr); gerr = NULL; fprintf(stderr, "failed to read " #memb); }\ + else { memset(&(errbmp)->memb, 0xff, sizeof((errbmp)->memb)); } + +#define READ_INFO_INT(ki, memb, gerr, errcount, errbmp)\ + READ_INFO_X(g_key_file_get_integer, ki, memb, gerr, errcount, errbmp) + +#define READ_INFO_UINT64(ki, memb, gerr, errcount, errbmp)\ + READ_INFO_X(g_key_file_get_uint64, ki, memb, gerr, errcount, errbmp) + +#define READ_INFO_STRING(ki, memb, gerr, errcount, errbmp)\ + READ_INFO_X(g_key_file_get_string, ki, memb, gerr, errcount, errbmp) + + +#define OPTIONAL_READ_INFO_X(key_file_get, ki, memb, gerr, errcount, errbmp)\ + ((ki)->memb) = key_file_get(keyfile, group_real, #memb, &gerr);\ + if (gerr != NULL) { g_error_free(gerr); gerr = NULL; fprintf(stderr, "failed to read " #memb); ((ki)->memb) = (uint64_t)NULL;}\ + memset(&(errbmp)->memb, 0xff, sizeof((errbmp)->memb)); + +#define OPTIONAL_READ_INFO_INT(ki, memb, gerr, errcount, errbmp)\ + OPTIONAL_READ_INFO_X(g_key_file_get_integer, ki, memb, gerr, errcount, errbmp) + +#define OPTIONAL_READ_INFO_UINT64(ki, memb, gerr, errcount, errbmp)\ + OPTIONAL_READ_INFO_X(g_key_file_get_uint64, ki, memb, gerr, errcount, errbmp) + + +/*! Reads kernel information (struct offsets and such) from the specified file. + * + * Each file may contain several contain information for many different kernels + * in groups. A specific group can be chosen with \p group. + * + * \param file The name of the kernel information file. When `NULL` the default #DEFAULT_KERNELINFO_FILE is used. + * \param group The name of the group to use from the kernel information file. When `NULL`, the first group is used. + * \param ki A structure used to read the kernel information. + * \return 0 for success. -1 for failure. + */ +int read_kernelinfo(gchar const *file, gchar const *group, struct kernelinfo *ki) { + int rval = 0; /**< return value */ + GKeyFile *keyfile; + gchar *group_real = NULL; + + GError *gerr = NULL; /**< glib errors */ + struct kernelinfo_errors err = {0}; /**< error counters for kernelinfo */ + struct kernelinfo errbmp = {0}; /**< error bitmap for kernelinfo */ + + + /* open file */ + memset(ki, 0, sizeof(struct kernelinfo)); + keyfile = g_key_file_new(); + g_key_file_load_from_file (keyfile, (file != NULL ? file : DEFAULT_KERNELINFO_FILE), G_KEY_FILE_NONE, &gerr); + if (gerr != NULL) { rval = -1; goto end; } + + /* get group */ + if (group != NULL) group_real = g_strdup(group); + else group_real = g_key_file_get_start_group(keyfile); + if (!g_key_file_has_group(keyfile, group_real)) { rval = -1; goto end; } + + /* read kernel full name */ + READ_INFO_STRING(ki, name, gerr, err.name, &errbmp); + + /* read kernel version information */ + READ_INFO_INT(ki, version.a, gerr, err.version, &errbmp); + READ_INFO_INT(ki, version.b, gerr, err.version, &errbmp); + READ_INFO_INT(ki, version.c, gerr, err.version, &errbmp); + + /* read init task address */ + READ_INFO_UINT64(ki, task.init_addr, gerr, err.task, &errbmp); + + /* read task information */ + READ_INFO_INT(ki, task.size, gerr, err.task, &errbmp); + + if (KERNEL_VERSION(ki->version.a, ki->version.b, ki->version.c) > KERNEL_VERSION(2, 4, 254)) { + READ_INFO_INT(ki, task.tasks_offset, gerr, err.task, &errbmp); + + READ_INFO_UINT64(ki, task.per_cpu_offsets_addr, gerr, err.task, &errbmp); + READ_INFO_UINT64(ki, task.per_cpu_offset_0_addr, gerr, err.task, &errbmp); + READ_INFO_UINT64(ki, task.current_task_addr, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.group_leader_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.stack_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.real_cred_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.cred_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.real_parent_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.parent_offset, gerr, err.task, &errbmp); + + /* read cred information */ + READ_INFO_INT(ki, cred.uid_offset, gerr, err.cred, &errbmp); + READ_INFO_INT(ki, cred.gid_offset, gerr, err.cred, &errbmp); + READ_INFO_INT(ki, cred.euid_offset, gerr, err.cred, &errbmp); + READ_INFO_INT(ki, cred.egid_offset, gerr, err.cred, &errbmp); + + READ_INFO_INT(ki, fs.f_path_dentry_offset, gerr, err.fs, &errbmp); + READ_INFO_INT(ki, fs.f_path_mnt_offset, gerr, err.fs, &errbmp); + READ_INFO_INT(ki, fs.fdt_offset, gerr, err.fs, &errbmp); + READ_INFO_INT(ki, fs.fdtab_offset, gerr, err.fs, &errbmp); + READ_INFO_INT(ki, path.d_dname_offset, gerr, err.path, &errbmp); + } else if (KERNEL_VERSION(ki->version.a, ki->version.b, ki->version.c) >= KERNEL_VERSION(2, 4, 0)) { + READ_INFO_INT(ki, task.p_opptr_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.p_pptr_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.next_task_offset, gerr, err.task, &errbmp); + + READ_INFO_INT(ki, fs.f_dentry_offset, gerr, err.fs, &errbmp); + READ_INFO_INT(ki, fs.f_vfsmnt_offset, gerr, err.fs, &errbmp); + } + + OPTIONAL_READ_INFO_INT(ki, task.thread_group_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.pid_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.tgid_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.mm_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.comm_offset, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.comm_size, gerr, err.task, &errbmp); + READ_INFO_INT(ki, task.files_offset, gerr, err.task, &errbmp); + OPTIONAL_READ_INFO_INT(ki, task.start_time_offset, gerr, err.task, &errbmp); + OPTIONAL_READ_INFO_UINT64(ki, task.switch_task_hook_addr, gerr, err.task, &errbmp); + + /* read mm information */ + READ_INFO_INT(ki, mm.size, gerr, err.mm, &errbmp); + OPTIONAL_READ_INFO_INT(ki, mm.mmap_offset, gerr, err.mm, &errbmp); + READ_INFO_INT(ki, mm.pgd_offset, gerr, err.mm, &errbmp); + READ_INFO_INT(ki, mm.arg_start_offset, gerr, err.mm, &errbmp); + READ_INFO_INT(ki, mm.start_brk_offset, gerr, err.mm, &errbmp); + READ_INFO_INT(ki, mm.brk_offset, gerr, err.mm, &errbmp); + READ_INFO_INT(ki, mm.start_stack_offset, gerr, err.mm, &errbmp); + + /* read vma information */ + READ_INFO_INT(ki, vma.size, gerr, err.vma, &errbmp); + READ_INFO_INT(ki, vma.vm_mm_offset, gerr, err.vma, &errbmp); + READ_INFO_INT(ki, vma.vm_start_offset, gerr, err.vma, &errbmp); + READ_INFO_INT(ki, vma.vm_end_offset, gerr, err.vma, &errbmp); + OPTIONAL_READ_INFO_INT(ki, vma.vm_next_offset, gerr, err.vma, &errbmp); + READ_INFO_INT(ki, vma.vm_file_offset, gerr, err.vma, &errbmp); + READ_INFO_INT(ki, vma.vm_flags_offset, gerr, err.vma, &errbmp); + + /* read fs information */ + READ_INFO_INT(ki, fs.f_pos_offset, gerr, err.fs, &errbmp); + READ_INFO_INT(ki, fs.fd_offset, gerr, err.fs, &errbmp); + + /* read qstr information */ + READ_INFO_INT(ki, qstr.size, gerr, err.qstr, &errbmp); + READ_INFO_INT(ki, qstr.name_offset, gerr, err.qstr, &errbmp); + + /* read path information */ + READ_INFO_INT(ki, path.d_name_offset, gerr, err.path, &errbmp); + READ_INFO_INT(ki, path.d_iname_offset, gerr, err.path, &errbmp); + READ_INFO_INT(ki, path.d_parent_offset, gerr, err.path, &errbmp); + READ_INFO_INT(ki, path.d_op_offset, gerr, err.path, &errbmp); + READ_INFO_INT(ki, path.mnt_root_offset, gerr, err.path, &errbmp); + READ_INFO_INT(ki, path.mnt_parent_offset, gerr, err.path, &errbmp); + READ_INFO_INT(ki, path.mnt_mountpoint_offset, gerr, err.path, &errbmp); + + /* check number of errors */ + { + int nerrors = 0; + int *e = (int *)&err; + int *e_last = (int *)((uint8_t *)&err + sizeof(err)); + while (e < e_last) { + nerrors += *e; + e++; + } + if (nerrors > 0) { + fprintf(stderr, "%d errors reading from group %s", nerrors, group_real); + rval = -1; + } + } + + /* check the bitmap for values that were not read */ + { + int notread = 0; + uint8_t *b_first = (uint8_t *)&errbmp;; + uint8_t *b_last = (uint8_t *)&errbmp + sizeof(errbmp); + uint8_t *b = b_first; + while (b < b_last) { + bool doprint = false; + + if (*b != 0xff) { + notread++; + if (!(b+1 < b_last)) { + doprint = true; + } + } + else if (notread > 0) { + doprint = true; + } + + if (doprint) { + /* don't make errors critical - alignment padding bytes are never written */ + notread = 0; + /* rval = -1; */ + } + + /* debug */ + /* printf("%3td %x:%x\n", b-b_first, *b, ((uint8_t *)ki)[b-b_first]); */ + + b++; + } + } + +end: + g_key_file_free(keyfile); + g_free(group_real); + return rval; +} + +/*! + * @brief Print a list of valid groupnames in a kernelinfo file + */ +void list_kernelinfo_groups(gchar const *file) { + GKeyFile *keyfile; + gchar ** groups; + GError *gerr = NULL; + int idx = 0; + const char* fname = (file != NULL ? file : DEFAULT_KERNELINFO_FILE); + + keyfile = g_key_file_new(); + g_key_file_load_from_file (keyfile, fname, G_KEY_FILE_NONE, &gerr); + if (gerr != NULL) { + printf("\tError parsing %s\n", fname); + return; + } + + groups = g_key_file_get_groups(keyfile, NULL); + while (groups[idx] != NULL) { + printf("\t%s\n", groups[idx]); + idx++; + } +} diff --git a/contrib/plugins/vmi/linux/vmi_linux.cpp b/contrib/plugins/vmi/linux/vmi_linux.cpp new file mode 100644 index 0000000000000..dfb90ab43bfac --- /dev/null +++ b/contrib/plugins/vmi/linux/vmi_linux.cpp @@ -0,0 +1,617 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Other plugins +extern "C" { +#include +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +QEMU_PLUGIN_EXPORT const char *qemu_plugin_name = "vmi_linux"; +} + +#include "vmi_linux.h" +#include "default_profile.h" +#include "../../syscalls.h" +#include "../vmi.h" + +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define TARGET_PID_FMT "%u" + + +void on_first_syscall(gpointer evdata, gpointer udata); + +// Using these +void on_get_process(gpointer evdata, gpointer udata); +void on_get_current_process_handle(gpointer evdata, gpointer udata); +void on_get_current_process(gpointer evdata, gpointer udata); + +// Not yet using these +//void on_get_process_handles(GArray **out); +//void on_get_processes(GArray **out); +//void on_get_mappings(VmiProc *p, GArray **out); +//void on_get_current_thread(VmiThread *t); + +void init_per_cpu_offsets(); +struct kernelinfo ki; +struct KernelProfile const *kernel_profile; + +extern const char *qemu_file; +bool vmi_initialized; +static bool first_vmi_check = true; + + +/** + * Resolves a file struct and returns its full pathname. + */ +static char *get_file_name(target_ptr_t file_struct) { + char *name = NULL; + target_ptr_t file_dentry, file_mnt; + + // Read addresses for dentry, vfsmnt structs. + file_dentry = get_file_dentry(file_struct); + file_mnt = get_file_mnt(file_struct); + + if (unlikely(file_dentry == (target_ptr_t)NULL || file_mnt == (target_ptr_t)NULL)) { + fprintf(stderr, "failure resolving file struct %lx/%lx", file_dentry, file_mnt); + return NULL; + } + + char *s1, *s2; + s1 = read_vfsmount_name(file_mnt); + s2 = read_dentry_name(file_dentry); + name = g_strconcat(s1, s2, NULL); + g_free(s1); + g_free(s2); + + return name; +} + +static uint64_t get_file_pvmition(target_ptr_t file_struct) { + return get_file_pos(file_struct); +} + +static target_ptr_t get_file_struct_ptr( target_ptr_t task_struct, int fd) { + target_ptr_t files = get_files(task_struct); + target_ptr_t fds = kernel_profile->get_files_fds(files); + target_ptr_t fd_file_ptr, fd_file; + + // fds is a flat array with struct file pointers. + // Calculate the address of the nth pointer and read it. + fd_file_ptr = fds + fd*sizeof(target_ptr_t); + if (-1 == panda_virtual_memory_rw(fd_file_ptr, (uint8_t *)&fd_file, sizeof(target_ptr_t), 0)) { + return (target_ptr_t)NULL; + } + fixupendian(fd_file); + if (fd_file == (target_ptr_t)NULL) { + return (target_ptr_t)NULL; + } + return fd_file; +} + +/** + * Resolves a file struct and returns its full pathname. + */ +static char *get_fd_name( target_ptr_t task_struct, int fd) { + target_ptr_t fd_file = get_file_struct_ptr(task_struct, fd); + if (fd_file == (target_ptr_t)NULL) return NULL; + return get_file_name(fd_file); +} + +/** + * Retrieves the current offset of a file descriptor. + */ +static uint64_t get_fd_pos(target_ptr_t task_struct, int fd) { + target_ptr_t fd_file = get_file_struct_ptr(task_struct, fd); + if (fd_file == (target_ptr_t)NULL) return ((uint64_t) INVALID_FILE_POS); + return get_file_pvmition(fd_file); +} + +/** + * Fills an VmiProcHandle struct. + */ +static void fill_vmiprochandle(VmiProcHandle *h, + target_ptr_t task_addr) { + struct_get_ret_t err; + + // h->asid = taskd->mm->pgd (some kernel tasks are expected to return error) + err = struct_get(&h->asid, task_addr, {ki.task.mm_offset, ki.mm.pgd_offset}); + + if (err == struct_get_ret_t::SUCCESS) { + // Convert asid to physical to be able to compare it with the pgd register. + h->asid = qemu_plugin_virt_to_phys(h->asid); + h->taskd = kernel_profile->get_group_leader(task_addr); + } else { + h->asid = (target_ulong)NULL; + h->taskd = (target_ptr_t)NULL; + } +} + +/** + * Fills an VmiProc struct. Any existing contents are overwritten. + */ +void fill_vmiproc(VmiProc *p, target_ptr_t task_addr) { + struct_get_ret_t err; + memset(p, 0, sizeof(VmiProc)); + + // p->asid = taskd->mm->pgd (some kernel tasks are expected to return error) + err = struct_get(&p->asid, task_addr, {ki.task.mm_offset, ki.mm.pgd_offset}); + assert(err == struct_get_ret_t::SUCCESS); + + // p->ppid = taskd->real_parent->pid + err = struct_get( &p->ppid, task_addr, + {ki.task.real_parent_offset, ki.task.tgid_offset}); + assert(err == struct_get_ret_t::SUCCESS); + + // Convert asid to physical to be able to compare it with the pgd register. + p->asid = p->asid ? qemu_plugin_virt_to_phys(p->asid) : (target_ulong) NULL; + p->taskd = kernel_profile->get_group_leader(task_addr); + + p->name = get_name(task_addr, p->name); + p->pid = get_tgid(task_addr); + //p->ppid = get_real_parent_pid(task_addr); + p->pages = NULL; // VmiPage - TODO + + //if kernel version is < 3.17 + if(ki.version.a < 3 || (ki.version.a == 3 && ki.version.b < 17)) { + uint64_t tmp = get_start_time(task_addr); + + //if there's an endianness mismatch TODO PORT TO Q7 XXX + #if defined(TARGET_WORDS_BIGENDIAN) != defined(HOST_WORDS_BIGENDIAN) + //convert the most significant half into nanoseconds, then add the rest of the nanoseconds + p->create_time = (((tmp & 0xFFFFFFFF00000000) >> 32) * 1000000000) + (tmp & 0x00000000FFFFFFFF); + #else + //convert the least significant half into nanoseconds, then add the rest of the nanoseconds + p->create_time = ((tmp & 0x00000000FFFFFFFF) * 1000000000) + ((tmp & 0xFFFFFFFF00000000) >> 32); + #endif + + } else { + p->create_time = get_start_time(task_addr); + } +} + +/** + * Fills an VmiModule struct. + */ +static void fill_vmimodule(VmiModule *m, target_ptr_t vma_addr) { + target_ulong vma_start, vma_end; + target_ptr_t vma_vm_file; + target_ptr_t vma_dentry; + target_ptr_t mm_addr, start_brk, brk, start_stack; + + vma_start = get_vma_start(vma_addr); + vma_end = get_vma_end(vma_addr); + vma_vm_file = get_vma_vm_file(vma_addr); + + // Fill everything but m->name and m->file. + m->modd = vma_addr; + m->base = vma_start; + m->size = vma_end - vma_start; + + if (vma_vm_file != + (target_ptr_t)NULL) { // Memory area is mapped from a file. + vma_dentry = get_vma_dentry(vma_addr); + m->file = read_dentry_name(vma_dentry); + m->name = g_strrstr(m->file, "/"); + if (m->name != NULL) m->name = g_strdup(m->name + 1); + } else { // Other memory areas. + mm_addr = get_vma_vm_mm(vma_addr); + start_brk = get_mm_start_brk(mm_addr); + brk = get_mm_brk(mm_addr); + start_stack = get_mm_start_stack(mm_addr); + + m->file = NULL; + if (vma_start <= start_brk && vma_end >= brk) { + m->name = g_strdup("[heap]"); + } else if (vma_start <= start_stack && vma_end >= start_stack) { + m->name = g_strdup("[stack]"); + } else { + m->name = g_strdup("[???]"); + } + } +} + +/** + * Fills an VmiThread struct. Any existing contents are overwritten. + */ +void fill_vmithread(VmiThread *t, + target_ptr_t task_addr) { + memset(t, 0, sizeof(*t)); + t->tid = get_pid(task_addr); + t->pid = get_tgid(task_addr); +} + +/* ****************************************************************** + Initialization logic +****************************************************************** */ +/** + * When necessary, after the first syscall ensure we can read current task + */ + +void on_first_syscall(gpointer evdata, gpointer udata) { + assert(can_read_current() && "Couldn't find current task struct at first syscall"); + if (!vmi_initialized) { + qemu_plugin_outs("vmi_linux: initialization complete."); + } + vmi_initialized = true; + qemu_plugin_unreg_callback("syscalls", "on_all_sys_enter", on_first_syscall); +} + +/** + * Test to see if we can read the current task struct + */ +inline bool can_read_current() { + target_ptr_t ts = kernel_profile->get_current_task_struct(); + return 0x0 != ts; +} + +/** + * Check if we've successfully initialized vmi for the guest. + * Returns true if introspection is available. + * + * If introspection is unavailable at the first check, this will register a QPP-style + * callback with syscalls to try reinitializing at the first syscall. + * + * If that fails, then we raise an assertion because vmi has really failed. + */ +bool vmi_guest_is_ready(void** ret) { + + if (vmi_initialized) { // If vmi_initialized is set, the guest must be ready + return true; // or, if it isn't, the user wants an assertion error + } + + // If it's the very first time, try reading current, if we can't + // wait until first sycall and try again + if (first_vmi_check) { + first_vmi_check = false; + + init_per_cpu_offsets(); + + // Try to load current, if it works, return true + if (can_read_current()) { + // Disable on_first_syscall QPP callback because we're all set + qemu_plugin_unreg_callback("syscalls", "on_all_sys_enter", on_first_syscall); + qemu_plugin_outs("vmi_linux: initialization complete.\n"); + vmi_initialized = true; + return true; + } + + // We can't read the current task right now. This isn't a surprise, e.g., + // it could be happening because we're in boot. + // Wait until on_first_syscall runs, everything should work then + qemu_plugin_outs("vmi_linux: cannot find current task struct. Deferring vmi initialization until first syscall.\n"); + + qemu_plugin_reg_callback("syscalls", "on_all_sys_enter", on_first_syscall); + } + // Not yet initialized, just set the caller's result buffer to NULL + ret = NULL; + return false; +} + +/* ****************************************************************** +QPP Callbacks +****************************************************************** */ + +/** + * QPP callback to retrieve info about the currently running process. + */ +void on_get_current_process(gpointer evdata, gpointer udata) { + VmiProc **out = (VmiProc**)evdata; + if (!vmi_guest_is_ready((void**)out)) return; + + static target_ptr_t last_ts = 0x0; + static target_ptr_t cached_taskd = 0x0; + static target_ptr_t cached_asid = 0x0; + static char *cached_name = (char *)g_malloc0(ki.task.comm_size); + static target_ptr_t cached_pid = -1; + static target_ptr_t cached_ppid = -1; + static void *cached_comm_ptr = NULL; + static uint64_t cached_start_time = 0; + // VmiPage - TODO + + VmiProc *p = NULL; + target_ptr_t ts = kernel_profile->get_current_task_struct(); + if (0x0 != ts) { + p = (VmiProc *)g_malloc(sizeof(*p)); + if ((ts != last_ts) || (NULL == cached_comm_ptr) || + (0 != strncmp((char *)cached_comm_ptr, cached_name, + ki.task.comm_size))) { + last_ts = ts; + fill_vmiproc(p, ts); + + // update the cache + cached_taskd = p->taskd; + cached_asid = p->asid; + memset(cached_name, 0, ki.task.comm_size); + strncpy(cached_name, p->name, ki.task.comm_size); + cached_pid = p->pid; + cached_ppid = p->ppid; + cached_start_time = p->create_time; + //cached_comm_ptr = qemu_plugin_virt_to_host( + // ts + ki.task.comm_offset, ki.task.comm_size); + } else { + p->taskd = cached_taskd; + p->asid = cached_asid; + p->name = g_strdup(cached_name); + p->pid = cached_pid; + p->ppid = cached_ppid; + p->pages = NULL; + p->create_time = cached_start_time; + } + } + *out = p; +} + +/** + * QPP callback to the handle of the currently running process. + */ +void on_get_current_process_handle(gpointer evdata, gpointer udata) { + VmiProcHandle **out = (VmiProcHandle**)evdata; + if (!vmi_guest_is_ready((void**)out)) return; + + VmiProcHandle *p = NULL; + // Very first thing that happens. Woop + target_ptr_t ts = kernel_profile->get_current_task_struct(); + if (ts) { + p = (VmiProcHandle *)g_malloc(sizeof(VmiProcHandle)); + fill_vmiprochandle(p, ts); + } + *out = p; +} + +/** + * QPP callback to retrieve info about a running process using its + * handle. + */ +void on_get_process(gpointer evdata, gpointer udata) { + struct get_process_data *data = (struct get_process_data*)(evdata); + const VmiProcHandle *h = data->h; + VmiProc **out = data->p; + if (!vmi_guest_is_ready((void**)out)) return; + + VmiProc *p = NULL; + if (h != NULL && h->taskd != (target_ptr_t)NULL) { + p = (VmiProc *)g_malloc(sizeof(VmiProc)); + fill_vmiproc(p, h->taskd); + } + *out = p; +} + +/** + * @brief QPP callback to retrieve VmiModules from the running OS. + * + * Current implementation returns all the memory areas mapped by the + * process and the files they were mapped from. Libraries that have + * many mappings will appear multiple times. + * + * @todo Remove duplicates from results. + */ +void on_get_mappings(VmiProc *p, GArray **out) { + if (!vmi_guest_is_ready((void**)out)) return; + + VmiModule m; + target_ptr_t vma_first, vma_current; + + // Read the module info for the process. + vma_first = vma_current = get_vma_first(p->taskd); + if (vma_current == (target_ptr_t)NULL) goto error0; + + if (*out == NULL) { + *out = g_array_sized_new(false, false, sizeof(VmiModule), 128); + g_array_set_clear_func(*out, (GDestroyNotify)free_vmimodule_contents); + } + + do { + memset(&m, 0, sizeof(VmiModule)); + fill_vmimodule(&m, vma_current); + g_array_append_val(*out, m); + vma_current = get_vma_next(vma_current); + } while(vma_current != (target_ptr_t)NULL && vma_current != vma_first); + + return; + +error0: + if(*out != NULL) { + g_array_free(*out, true); + } + *out = NULL; + return; +} + +/** + * QPP callback to retrieve the process pid from a handle. + */ +void on_get_process_pid(const VmiProcHandle *h, target_pid_t *pid) { + if (!vmi_guest_is_ready((void**)pid)) return; + + if (h->taskd == 0 || h->taskd == (target_ptr_t)-1) { + *pid = (target_pid_t)-1; + } else { + *pid = get_tgid(h->taskd); + } +} + +/** + * QPP callback to retrieve the process parent pid from a handle. + */ +void on_get_process_ppid(const VmiProcHandle *h, target_pid_t *ppid) { + struct_get_ret_t err; + if (!vmi_guest_is_ready((void**)ppid)) return; + + if (h->taskd == (target_ptr_t)-1) { + *ppid = (target_pid_t)-1; + } else { + // ppid = taskd->real_parent->pid + err = struct_get(ppid, h->taskd, + {ki.task.real_parent_offset, ki.task.pid_offset}); + if (err != struct_get_ret_t::SUCCESS) { + *ppid = (target_pid_t)-1; + } + } +} + +/* ****************************************************************** + vmi_linux extra API +****************************************************************** */ + +char *vmi_linux_fd_to_filename(VmiProc *p, int fd) { + char *filename = NULL; + target_ptr_t ts_current; + //const char *err = NULL; + + if (p == NULL) { + //err = "Null VmiProc argument"; + goto end; + } + + ts_current = p->taskd; + if (ts_current == 0) { + //err = "can't get task"; + goto end; + } + + filename = get_fd_name(ts_current, fd); + if (unlikely(filename == NULL)) { + //err = "can't get filename"; + goto end; + } + + filename = g_strchug(filename); + if (unlikely(g_strcmp0(filename, "") == 0)) { + //err = "filename is empty"; + g_free(filename); + filename = NULL; + goto end; + } + +end: + return filename; +} + + +target_ptr_t ext_get_file_dentry(target_ptr_t file_struct) { + return get_file_dentry(file_struct); +} + +target_ptr_t ext_get_file_struct_ptr(target_ptr_t task_struct, int fd) { + return get_file_struct_ptr(task_struct, fd); +} + + +unsigned long long vmi_linux_fd_to_pos(VmiProc *p, int fd) { + target_ptr_t ts_current = 0; + ts_current = p->taskd; + if (ts_current == 0) return INVALID_FILE_POS; + return get_fd_pos(ts_current, fd); +} + +/* ****************************************************************** + Plugin Initialization/Cleanup +****************************************************************** */ +/** + * Updates any per-cpu offsets we need for introspection. + * This allows kernel profiles to be independent of boot-time configuration. + * If ki.task.per_cpu_offsets_addr is set to 0, the values of the per-cpu + * offsets in the profile will not be updated. + * + * Currently the only per-cpu offset we use in vmi_linux is + * ki.task.per_cpu_offset_0_addr. + */ +void init_per_cpu_offsets() { + // old kernel - no per-cpu offsets to update + if (PROFILE_KVER_LE(ki, 2, 4, 254)) { + return; + } + + // skip update because there's no per_cpu_offsets_addr + if (ki.task.per_cpu_offsets_addr == 0) { + return; + } + + // skip update because of failure to read from per_cpu_offsets_addr + target_ptr_t per_cpu_offset_0_addr; + auto r = struct_get(&per_cpu_offset_0_addr, ki.task.per_cpu_offsets_addr, + 0*(sizeof(target_ptr_t))); // final argument is 0 but like this to avoid compiler warning + if (r != struct_get_ret_t::SUCCESS) { + fprintf(stderr, "Unable to update value of ki.task.per_cpu_offset_0_addr.\n"); + assert(false); + return; + } + + ki.task.per_cpu_offset_0_addr = per_cpu_offset_0_addr; +} + +/** + * After guest has restored snapshot, reset so we can redo + * initialization + */ +void restore_after_snapshot(qemu_plugin_id_t id, unsigned int cpu_index) { + qemu_plugin_outs("vmi_linux: Snapshot loaded. Re-initializing\n"); + + // By setting these, we'll redo our init logic which determines + // if vmi is ready at the first time it's used, otherwise + // it runs at the first syscall (and asserts if it fails) + vmi_initialized = false; + first_vmi_check = true; + qemu_plugin_reg_callback("syscalls", "on_all_sys_enter", on_first_syscall); +} + + +/** + * Initializes plugin. + */ +extern "C" QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, char **argv) { + + gchar* kconf_file = NULL; + gchar* kconf_group = NULL; + vmi_initialized = false; + + //parse the arguments + for (int i = 0; i < argc; i++) { + g_autofree gchar** tokens = g_strsplit(argv[i], "=", 2); + + if (g_strcmp0(tokens[0], "conf") == 0) { + kconf_file = g_strdup(tokens[1]); + } else if (g_strcmp0(tokens[0], "group") == 0) { + kconf_group = g_strdup(tokens[1]); + } + } + + if (!kconf_file || !kconf_group) { + fprintf(stderr, "vmi_linux is missing arguments\n"); + fprintf(stderr, "USAGE: -plugin libvmi_linux.so,conf=kernel_info.conf,group=group_name\n"); + return 1; + } + + // Load kernel offsets from the configuration file + if (read_kernelinfo(kconf_file, kconf_group, &ki) != 0) { + fprintf(stderr, "vmi_linux: Failed to read group %s from %s.\n", kconf_group, kconf_file); + return 1; + } + + if (PROFILE_KVER_LE(ki, 2, 4, 254)) { + //kernel_profile = &KERNEL24X_PROFILE; + fprintf(stderr, "vmi_linux: Old kernel detected. Not currently supported\n"); + return 1; + } else { + kernel_profile = &DEFAULT_PROFILE; + } + // After a snapshot is loaded, we'll need to re-initialize + qemu_plugin_register_vcpu_loadvm_cb(id, restore_after_snapshot); + + qemu_plugin_reg_callback("vmi", "on_get_process", on_get_process); + qemu_plugin_reg_callback("vmi", "on_get_current_process_handle", on_get_current_process_handle); + qemu_plugin_reg_callback("vmi", "on_get_current_process", on_get_current_process); + + + return 0; +} + +/* vim:set tabstop=4 softtabstop=4 expandtab: */ diff --git a/contrib/plugins/vmi/linux/vmi_linux.h b/contrib/plugins/vmi/linux/vmi_linux.h new file mode 100644 index 0000000000000..892ce3ff68ecd --- /dev/null +++ b/contrib/plugins/vmi/linux/vmi_linux.h @@ -0,0 +1,708 @@ +/*! + * This file is heavily based on PANDA.re's vmi Linux plugin's vmi_linux.h file. + * The offset getter macros have been based off the code from + * linux_vmi plugin and TEMU's read_linux. + * + * @author Andrew Fasano + * @author Manolis Stamatogiannakis + * @copyright This work is licensed under the terms of the GNU GPL, version 2. + */ + +#pragma once +#if defined(__cplusplus) +#include +#include +#include +#endif +#include "../vmi_types.h" + +extern "C" { +#include "kernelinfo.h" +} +#include "kernel_profile.h" +#include "endian_helpers.h" +#include +#include +extern "C" { +#include +} + +#define INVALID_FILE_POS (-1) + +extern struct kernelinfo ki; +extern struct KernelProfile const *kernel_profile; + +#if defined(__cplusplus) +typedef enum : int8_t { + ERROR_DEREF = -10, + ERROR_MEMORY, + SUCCESS = 0 +} struct_get_ret_t; + +/** + * Stupid panda shim + * Reads memory from a given virtual address into a provided buffer. + * + * @param gva The virtual address to read from. + * @param buf A pointer to the buffer where the data will be stored. + * @param length The number of bytes to read. + * @param is_write A flag indicating if the operation is a write (not implemented). + * @return 0 on success, or -1 on failure. + */ +static int panda_virtual_memory_rw(uint64_t gva, void* buf, size_t length, bool is_write) { + assert(is_write == 0); // Not yet implemented + + if (length == 0) { + return 0; + } + + // Create a GByteArray with preallocated size to avoid reallocations + GByteArray *data = g_byte_array_sized_new(length); + g_byte_array_set_size(data, length); + + // Read memory from the virtual address into the GByteArray + bool success = qemu_plugin_read_memory_vaddr(gva, data, length); + + if (!success) { + g_byte_array_free(data, TRUE); + return -1; // Indicate failure + } + + // Copy the data from the GByteArray into the provided buffer + memcpy(buf, data->data, length); + + // Free the GByteArray + g_byte_array_free(data, TRUE); + + return 0; // Indicate success +} + + +// End shims + +/** + * @brief Template function for reading a struct member given a pointer + * to the struct and the offset of the member. + * This is a proper C++ replacement for the preprocessor hack macro of + * IMPLEMENT_OFFSET_GET. + */ +template +struct_get_ret_t struct_get(T *v, target_ptr_t ptr, off_t offset) { + if (ptr == (target_ptr_t)NULL) { + printf("WARNING: struct get of NULLPTR\n"); + memset((uint8_t *)v, 0, sizeof(T)); + return struct_get_ret_t::ERROR_DEREF; + } + + switch(panda_virtual_memory_rw(ptr+offset, (void *)v, sizeof(T), 0)) { + case -1: + memset((uint8_t *)v, 0, sizeof(T)); + return struct_get_ret_t::ERROR_MEMORY; + break; + default: + return struct_get_ret_t::SUCCESS; + break; + } +} + +/** + * @brief Template function for reading a nested struct member given a + * pointer to the top level struct and a series of offsets. + * This is a proper C++ replacement for the preprocessor hack macro of + * IMPLEMENT_OFFSET_GET*. + */ +template +struct_get_ret_t struct_get(T *v, target_ptr_t ptr, std::initializer_list offsets) { + // read all but last item as pointers + // After each pointer read, flip endianness as necessary + auto it = offsets.begin(); + auto o = *it; + while (true) { + it++; + if (it == offsets.end()) break; + auto r = struct_get(&ptr, ptr, o); + if (r != struct_get_ret_t::SUCCESS) { + memset((uint8_t *)v, 0, sizeof(T)); + return r; + } + o = *it; + // We just read a pointer so we may need to fix its endianness + if (sizeof(T) == 4) fixupendian(ptr); // XXX wrong for 64-bit guests + } + + // last item is read using the size of the type of v + // this isn't a pointer so there's no need to fix its endianness + auto ret = struct_get(v, ptr, o); // deref ptr into v, result in ret + fixupendian(*v); + return ret; +} +#endif + +/** + * @brief IMPLEMENT_OFFSET_GET is a macro for generating uniform + * inlines for retrieving data based on a location+offset. + * + * @deprecated Directly returning a value complicates error handling + * and doesn't work for arrays or simple structs. + * Use IMPLEMENT_OFFSET_GETN instead. + */ +#define IMPLEMENT_OFFSET_GET(_name, _paramName, _retType, _offset, _errorRetValue) \ +static inline _retType _name(target_ptr_t _paramName) { \ + _retType _t; \ + if (-1 == panda_virtual_memory_rw(_paramName + _offset, &_t, sizeof(_retType), 0)) { \ + return (_errorRetValue); \ + } \ + return (flipbadendian(_t)); \ +} + +/** + * @brief IMPLEMENT_OPTIONAL_OFFSET_GET is a macro for generating uniform + * inlines for retrieving data based on a location+offset as above, but + * it returns 0 if the underlying offset was not read in the first place + * and was optional. + * + * @deprecated Directly returning a value complicates error handling + * and doesn't work for arrays or simple structs. + * Use IMPLEMENT_OFFSET_GETN instead. + */ +#define IMPLEMENT_OPTIONAL_OFFSET_GET(_name, _paramName, _retType, _offset, _errorRetValue) \ +static inline _retType _name(target_ptr_t _paramName) { \ + _retType _t; \ + if (_offset == NULL)\ + return 0; \ + if (-1 == panda_virtual_memory_rw(_paramName + _offset, &_t, sizeof(_retType), 0)) { \ + return (_errorRetValue); \ + } \ + return (flipbadendian(_t)); \ +} + + + + +/** + * @brief IMPLEMENT_OFFSET_GET2L is a macro for generating uniform + * inlines for retrieving data based on a *(location+offset1) + offset2. + * + * @deprecated Directly returning a value complicates error handling + * and doesn't work for arrays or simple structs. + * Use IMPLEMENT_OFFSET_GET2LN instead. + */ +#define IMPLEMENT_OFFSET_GET2L(_name, _paramName, _retType1, _offset1, _retType2, _offset2, _errorRetValue) \ +static inline _retType2 _name(target_ptr_t _paramName) { \ + _retType1 _t1; \ + _retType2 _t2; \ + if (-1 == panda_virtual_memory_rw(_paramName + _offset1, &_t1, sizeof(_retType1), 0)) { \ + return (_errorRetValue); \ + } \ + if (-1 == panda_virtual_memory_rw(flipbadendian(_t1) + _offset2, &_t2, sizeof(_retType2), 0)) { \ + return (_errorRetValue); \ + } \ + return (flipbadendian(_t2)); \ +} + +#define OG_AUTvmiZE 0 +#define OG_SUCCESS 0 +#define OG_ERROR_MEMORY -1 +#define OG_ERROR_DEREF -2 +#define OG_printf(...) +//#define OG_printf(...) printf(__VA_ARGS__) + +/** + * @brief IMPLEMENT_OFFSET_GETN is a macro for generating uniform + * inlines for retrieving data based on a location+offset. + * It provides better error handling than IMPLEMENT_OFFSET_GET and is not + * limited to retrieving only primitive types. + */ +#define IMPLEMENT_OFFSET_GETN(_funcName, _paramName, _retType, _retName, _retSize, _offset) \ +static inline int _funcName(target_ptr_t _paramName, _retType* _retName) { \ + size_t ret_size = ((_retSize) == OG_AUTvmiZE) ? sizeof(_retType) : (_retSize); \ + OG_printf(#_funcName ":1:%lx:%d\n", _paramName, _offset); \ + OG_printf(#_funcName ":2:%lx:%zu\n", _paramName + _offset, ret_size); \ + if (-1 == panda_virtual_memory_rw(_paramName + _offset, _retName, ret_size, 0)) { \ + return OG_ERROR_MEMORY; \ + } \ + OG_printf(#_funcName ":3:ok\n"); \ + return OG_SUCCESS; \ +} + +/** + * @brief IMPLEMENT_OFFSET_GET2LN is an improved macro for generating uniform + * inlines for retrieving data based on a *(location+offset1) + offset2. + * It provides better error handling than IMPLEMENT_OFFSET_GET2L and is not + * limited to retrieving only primitive types. + */ +#define IMPLEMENT_OFFSET_GET2LN(_funcName, _paramName, _retType, _retName, _retSize, _offset1, _offset2) \ +static inline int _funcName(target_ptr_t _paramName, _retType* _retName) { \ + target_ptr_t _p1; \ + size_t ret_size = ((_retSize) == OG_AUTvmiZE) ? sizeof(_retType) : (_retSize); \ + OG_printf(#_funcName ":1:%lx:%d\n", _paramName, _offset1); \ + OG_printf(#_funcName ":2:%lx:%zu\n", _paramName + _offset1, sizeof(target_ptr_t)); \ + if (-1 == panda_virtual_memory_rw(_paramName + _offset1, &_p1, sizeof(target_ptr_t), 0)) { \ + return OG_ERROR_MEMORY; \ + } \ + OG_printf(#_funcName ":3:%lx:%d\n", _p1, _offset2); \ + if (_p1 == (target_ptr_t)NULL) { \ + return OG_ERROR_DEREF; \ + } \ + OG_printf(#_funcName ":4:%lx:%zu\n", _p1 + _offset2, ret_size); \ + if (-1 == panda_virtual_memory_rw(_p1 + _offset2, _retName, ret_size, 0)) { \ + return OG_ERROR_MEMORY; \ + } \ + OG_printf(#_funcName ":5:ok\n"); \ + return OG_SUCCESS; \ +} + + + +/* ****************************************************************** + Offset getters are defined below. Only the getters used by the + plugin have been defined. See kernelinfo.conf to see what additional + getters can be added. +****************************************************************** */ + +/** + * @brief Retrieves the thread group address from task_struct. + * If the thread group address points back to itself, then the task_struct + * corresponds to a process. + */ +IMPLEMENT_OFFSET_GET(get_thread_group, task_struct, target_ptr_t, ki.task.thread_group_offset, 0) + +/** + * @brief Retrieves the pid from a task_struct. + */ +IMPLEMENT_OFFSET_GET(get_pid, task_struct, int, ki.task.pid_offset, 0) + +/** + * @brief Retrieves the tgid from a task_struct. + */ +IMPLEMENT_OFFSET_GET(get_tgid, task_struct, int, ki.task.tgid_offset, 0) + +/** + * @brief Retrieves the start_time from a task_struct. + */ +IMPLEMENT_OPTIONAL_OFFSET_GET(get_start_time, task_struct, uint64_t, ki.task.start_time_offset, 0) + + +/** + * @brief Retrieves the address of the mm_struct from a task_struct. + */ +IMPLEMENT_OFFSET_GET(get_mm_start_brk, mm_struct, target_ptr_t, ki.mm.start_brk_offset, 0) + +/** + * @brief Retrieves the address of the mm_struct from a task_struct. + */ +IMPLEMENT_OFFSET_GET(get_mm_brk, mm_struct, target_ptr_t, ki.mm.brk_offset, 0) + +/** + * @brief Retrieves the address of the mm_struct from a task_struct. + */ +IMPLEMENT_OFFSET_GET(get_mm_start_stack, mm_struct, target_ptr_t, ki.mm.start_stack_offset, 0) + +/** + * @brief Retrieves the address of the first vm_area_struct of the task. + */ +IMPLEMENT_OFFSET_GET2L(get_vma_first, task_struct, target_ptr_t, ki.task.mm_offset, target_ptr_t, ki.mm.mmap_offset, 0) + +/** + * @brief Retrieves the address of the following vm_area_struct. + * This is used to iterate the mmap list. + */ +IMPLEMENT_OFFSET_GET(get_vma_next, vma_struct, target_ptr_t, ki.vma.vm_next_offset, 0) + +/** + * @brief Retrieves the of the mm_struct where this vm_area_struct belongs to. + */ +IMPLEMENT_OFFSET_GET(get_vma_vm_mm, vma_struct, target_ptr_t, ki.vma.vm_mm_offset, 0) + +/** + * @todo Retrieves the address of the following vm_area_struct. + */ +IMPLEMENT_OFFSET_GET(get_vma_start, vma_struct, target_ulong, ki.vma.vm_start_offset, 0) + +/** + * @todo Retrieves the address of the following vm_area_struct. + */ +IMPLEMENT_OFFSET_GET(get_vma_end, vma_struct, target_ulong, ki.vma.vm_end_offset, 0) + +/** + * @todo Retrieves the address of the following vm_area_struct. + */ +IMPLEMENT_OFFSET_GET(get_vma_flags, vma_struct, target_ulong, ki.vma.vm_flags_offset, 0) + +/** + * @brief Retrieves the address of the mm_struct from a task_struct. + */ +IMPLEMENT_OFFSET_GET(get_vma_vm_file, vma_struct, target_ptr_t, ki.vma.vm_file_offset, 0) + +/** + * @brief Retrieves the dentry associated with a vma_struct. + * + * XXX: Convert uses of this to the single level getter of f_path_dentry_offset. + * Operating on file structs vs vma structs, will help to share code between + * mm resolution and fd resolution. + */ +IMPLEMENT_OFFSET_GET2L(get_vma_dentry, vma_struct, target_ptr_t, ki.vma.vm_file_offset, target_ptr_t, ki.fs.f_path_dentry_offset, 0) + +/** + * @brief Retrieves the vfsmount dentry associated with a vma_struct. + * + * XXX: Reading the vfsmount dentry is required to get the full pathname of files not located in the root fs. + * This hasn't been implemented yet... + */ +IMPLEMENT_OFFSET_GET2L(get_vma_vfsmount_dentry, vma_struct, target_ptr_t, ki.vma.vm_file_offset, target_ptr_t, ki.fs.f_path_dentry_offset, 0) + +/** + * @brief Retrieves the address of the files struct associated with a task_struct. + */ +IMPLEMENT_OFFSET_GET(get_files, task_struct, target_ptr_t, ki.task.files_offset, 0) + +/** + * @brief Retrieves the dentry struct associated with a file struct. + */ +IMPLEMENT_OFFSET_GET(get_file_dentry, file_struct, target_ptr_t, ki.fs.f_path_dentry_offset, 0) + +/** + * @brief Retrieves the vfsmount struct associated with a file struct. + */ +IMPLEMENT_OFFSET_GET(get_file_mnt, file_struct, target_ptr_t, ki.fs.f_path_mnt_offset, 0) + +IMPLEMENT_OFFSET_GET(get_file_pos, file_struct, target_ptr_t, ki.fs.f_pos_offset, 0) + +/** + * @brief Retrieves the mnt_parent vfsmount struct associated with a vfsmount struct. + */ +IMPLEMENT_OFFSET_GETN(get_vfsmount_parent, vfsmount, target_ptr_t, vfsmount_parent, OG_AUTvmiZE, ki.path.mnt_parent_offset) + +/** + * @brief Retrieves the dentry struct associated with a vfsmount struct. + */ +IMPLEMENT_OFFSET_GETN(get_vfsmount_dentry, vfsmount, target_ptr_t, vfsmount_dentry, OG_AUTvmiZE, ki.path.mnt_mountpoint_offset) + +/** + * @brief Retrieves the mnt_root dentry struct associated with a vfsmount struct. + */ +IMPLEMENT_OFFSET_GETN(get_vfsmount_root_dentry, vfsmount, target_ptr_t, root_dentry, OG_AUTvmiZE, ki.path.mnt_root_offset) + +/** + * @brief Retrieves the qstr for a dentry. + */ +IMPLEMENT_OFFSET_GETN(get_dentry_name, dentry, uint8_t, dname_qstr, ki.qstr.size*sizeof(uint8_t), ki.path.d_name_offset) + +/** + * @brief Retrieves the dynamic name function for a dentry. + */ +IMPLEMENT_OFFSET_GET2LN(get_dentry_dname, dentry, target_ptr_t, dname_funcp, OG_AUTvmiZE, ki.path.d_op_offset, ki.path.d_dname_offset) + +/** + * @brief Retrieves the parent of a dentry. + */ +IMPLEMENT_OFFSET_GETN(get_dentry_parent, dentry, target_ptr_t, dentry_parent, OG_AUTvmiZE, ki.path.d_parent_offset) + +/* ****************************************************************** + Slightly more complex inlines that can't be implemented as simple + offset getters. +****************************************************************** */ +/** + * @brief Retrieves the n-th file struct from an fd file array. (pp 479) + */ +static inline target_ptr_t get_fd_file(target_ptr_t fd_file_array, int n) { + target_ptr_t fd_file, fd_file_ptr; + // Compute address of the pointer to the file struct of the n-th fd. + fd_file_ptr = fd_file_array+n*sizeof(target_ptr_t); + + // Read address of the file struct. + if (-1 == panda_virtual_memory_rw(fd_file_ptr, &fd_file, sizeof(target_ptr_t), 0)) { + return (target_ptr_t)NULL; + } + fixupendian(fd_file_ptr); + return fd_file_ptr; +} + +/** + * @brief Retrieves the name of the file associated with a dentry struct. + * + * The function traverses all the path components it meets until it + * reaches a mount point. + * + * @note We can always use dentry.d_name->name and ignore dentry.d_iname. + * When the latter is used, the former will be set to point to it. + */ +static inline char *read_dentry_name(target_ptr_t dentry) { + char *name = NULL; + + // current path component + char *pcomp = NULL; + uint32_t pcomp_length = 0; + uint32_t pcomp_capacity = 0; + + // all path components read so far + char **pcomps = NULL; + uint32_t pcomps_idx = 0; + uint32_t pcomps_capacity = 0; + + // for reversing pcomps + char **pcomps_start, **pcomps_end; + + target_ptr_t current_dentry_parent = dentry; + target_ptr_t current_dentry = (target_ptr_t)NULL; + uint8_t *d_name = (uint8_t *)g_malloc(ki.qstr.size * sizeof(uint8_t)); + while (current_dentry_parent != current_dentry) { + int og_err1, og_err2; + current_dentry = current_dentry_parent; + //printf("1#%lx\n", (uintptr_t)(current_dentry + ki.path.d_name_offset)); + + // read dentry d_parent and d_name + memset(d_name, 0, ki.qstr.size * sizeof(uint8_t)); + og_err1 = get_dentry_name(current_dentry, d_name); + og_err2 = get_dentry_parent(current_dentry, ¤t_dentry_parent); + + // Note we don't fix endian of dentry name because it's a large(r than 4) + // byte buffer. Instead we'll fix it just before use (in guest_addr) + fixupendian(current_dentry_parent); + + //HEXDUMP(d_name, ki.path.qstr_size, current_dentry + ki.path.d_name_offset); + if (OG_SUCCESS != og_err1 || OG_SUCCESS != og_err2) { + break; + } + + // read d_dname function pointer - indicates a dynamic name + target_ptr_t d_dname; + og_err1 = get_dentry_dname(current_dentry, &d_dname); + if (OG_SUCCESS != og_err1) { + // static name + d_dname = (target_ptr_t)NULL; + } + + // read component + pcomp_length = *(uint32_t *)(d_name + sizeof(uint32_t)); + fixupendian(pcomp_length); + if (pcomp_length == (uint32_t)-1) { // Not sure why this happens, but it does + printf("Warning: vmi_linux Unhandled pcomp value, ignoring\n"); + break; + } + pcomp_length += 1; // space for string terminator + + if (pcomp_capacity < pcomp_length) { + pcomp_capacity = pcomp_length + 16; + pcomp = (char *)g_realloc(pcomp, pcomp_capacity * sizeof(char)); + if (pcomp == NULL) { + printf("Warning: vmi_linux pcomp g_realloc failed\n"); + break; + } + } + + target_ptr_t guest_addr = *(target_ptr_t *)(d_name + ki.qstr.name_offset); + fixupendian(guest_addr); + og_err1 = panda_virtual_memory_rw(guest_addr, (void *)pcomp, pcomp_length*sizeof(char), 0); + + // I think this aims to be a re-implementation of the Linux kernel function + // __dentry_path but the logic seems pretty different. + //printf("2#%lx\n", (uintptr_t)*(target_ptr_t *)(d_name + 2*sizeof(uint32_t))); + //printf("3#%s\n", pcomp); + if (-1 == og_err1) { + break; + } + + // use the empty string for "/" components (mountpoints?) + if (pcomp[0] == '/' && pcomp[1] == '\0') { + pcomp[0] = '\0'; + } + + // copy component + if (pcomps_idx + 1 >= pcomps_capacity) { // +1 accounts for the terminating NULL + pcomps_capacity += 16; + pcomps = (char **)g_realloc(pcomps, pcomps_capacity * sizeof(char *)); + } + if (d_dname == (target_ptr_t)NULL) { + // static name + pcomps[pcomps_idx++] = g_strdup(pcomp); + } + else { + // XXX: full reconstruction of dynamic names in not currently supported + pcomps[pcomps_idx++] = g_strdup(pcomp); + } + } + + // reverse components order and join them + g_free(d_name); + g_free(pcomp); + if (pcomps != NULL) { + pcomps_start = pcomps; + pcomps_end = &pcomps[pcomps_idx - 1]; + while (pcomps_start < pcomps_end) { + pcomp = *pcomps_start; + *pcomps_start = *pcomps_end; + *pcomps_end = pcomp; + pcomps_start++; + pcomps_end--; + } + pcomps[pcomps_idx] = NULL; // NULL terminate vector + name = g_strjoinv("/", pcomps); + g_strfreev(pcomps); + } + +#if defined(vmi_LINUX_FDNDEBUG) + if (name == NULL) { + LOG_WARN("Error reading d_entry."); + } +#endif + return name; +} + +/** + * @brief Retrieves the name of the file associated with a dentry struct. + * + * The function traverses all the mount points to the root mount. + */ +static inline char *read_vfsmount_name(target_ptr_t vfsmount) { + char *name = NULL; + + // current path component + char *pcomp = NULL; + + // all path components read so far + char **pcomps = NULL; + uint32_t pcomps_idx = 0; + uint32_t pcomps_capacity = 0; + + target_ptr_t current_vfsmount_parent = vfsmount; + target_ptr_t current_vfsmount = (target_ptr_t)NULL; + while(current_vfsmount != current_vfsmount_parent) { + int og_err0, og_err1; + target_ptr_t current_vfsmount_dentry; + //int og_err2; + //target_ptr_t root_dentry; + current_vfsmount = current_vfsmount_parent; + + // retrieve vfsmount members + og_err0 = get_vfsmount_dentry(current_vfsmount, ¤t_vfsmount_dentry); + og_err1 = get_vfsmount_parent(current_vfsmount, ¤t_vfsmount_parent); + fixupendian(current_vfsmount_dentry); + fixupendian(current_vfsmount_parent); + + //printf("###D:%d:%lx:%lx\n", og_err0, current_vfsmount, current_vfsmount_dentry); + //printf("###R:%d:%lx:%lx\n", og_err2, current_vfsmount, root_dentry); + //og_err2 = get_vfsmount_root_dentry(current_vfsmount, &root_dentry); + //printf("###P:%d:%lx:%lx\n", og_err1, current_vfsmount, current_vfsmount_parent); + + // check whether we should break out + if (OG_SUCCESS != og_err0 || OG_SUCCESS != og_err1) { + break; + } + if (current_vfsmount_dentry == (target_ptr_t)NULL) { + break; + } + + // read and copy component + pcomp = read_dentry_name(current_vfsmount_dentry); + //printf("###S:%s\n", pcomp); + + // this may hapen it seems + if (pcomp == NULL) { + continue; + } + + if (pcomps_idx + 1 >= pcomps_capacity) { // +1 accounts for the terminating NULL + pcomps_capacity += 16; + pcomps = (char **)g_realloc(pcomps, pcomps_capacity * sizeof(char *)); + } + pcomps[pcomps_idx++] = pcomp; + } + + // reverse components order and join them + if (pcomps != NULL) { + char **pcomps_start = pcomps; + char **pcomps_end = &pcomps[pcomps_idx - 1]; + while (pcomps_start < pcomps_end) { + pcomp = *pcomps_start; + *pcomps_start = *pcomps_end; + *pcomps_end = pcomp; + pcomps_start++; + pcomps_end--; + } + pcomps[pcomps_idx] = NULL; // NULL terminate vector + name = g_strjoinv("", pcomps); // slashes are included in pcomps + g_strfreev(pcomps); + } + + //printf("###F:%s\n", name); + return name; +} + +/** + * @brief Retrieves the command name from a task_struct. + * + * @note task.comm is a fixed length array. + * This means that we don't have to account for the terminating '\0'. + */ +static inline char *get_name(target_ptr_t task_struct, char *name) { + if (name == NULL) { name = (char *)g_malloc0(ki.task.comm_size * sizeof(char)); } + else { name = (char *)g_realloc(name, ki.task.comm_size * sizeof(char)); } + if (-1 == panda_virtual_memory_rw(task_struct + ki.task.comm_offset, (void *)name, ki.task.comm_size * sizeof(char), 0)) { + strncpy(name, "N/A", ki.task.comm_size*sizeof(char)); + } + return name; +} + +void fill_vmiproc(VmiProc *p, target_ptr_t task_addr); +void fill_vmithread(VmiThread *t, target_ptr_t task_addr); + +#if defined(__cplusplus) +/** + * @brief Template function for extracting data for all running processes. + * This can be used to quickly implement extraction of partial process + * information without having to rewrite the process list traversal + * code. + * + * @note The ascii pictogram in kernel_structs.html roughly explains how the + * process list traversal works. However, it may be inacurrate for some corner + * cases. E.g. it doesn't explain why some inifnite loop cases manifest. + * Avoiding these infinite loops was mostly a trial+error process. + */ +template +void get_process_info(GArray **out, + void (*fill_element)(ET *, target_ptr_t), + void (*free_element_contents)(ET *)) { + ET element; + target_ptr_t ts_first, ts_current; + + if (*out == NULL) { + // g_array_sized_new() args: zero_term, clear, element_sz, reserved_sz + *out = g_array_sized_new(false, false, sizeof(ET), 128); + g_array_set_clear_func(*out, (GDestroyNotify)free_element_contents); + } + + // Start process enumeration (roughly) from the current task. This is the default. + ts_first = kernel_profile->get_current_task_struct(); + + // To avoid infinite loops, we need to actually start traversal from the next + // process after the thread group leader of the current task. + ts_first = kernel_profile->get_group_leader(ts_first); + ts_first = kernel_profile->get_task_struct_next(ts_first); + + ts_current = ts_first; + + if (ts_first == (target_ptr_t)NULL) goto error; + + do { + memset(&element, 0, sizeof(ET)); + fill_element(&element, ts_current); + g_array_append_val(*out, element); + ts_current = kernel_profile->get_task_struct_next(ts_current); + } while(ts_current != (target_ptr_t)NULL && ts_current != ts_first); + + // memory read error + if (ts_current == (target_ptr_t)NULL) goto error; + + return; + +error: + if(*out != NULL) { + g_array_free(*out, true); + } + *out = NULL; + return; +} +#endif + +/* vim:set tabstop=4 softtabstop=4 expandtab: */ diff --git a/contrib/plugins/vmi/vmi.h b/contrib/plugins/vmi/vmi.h new file mode 100644 index 0000000000000..ffb5264f770e7 --- /dev/null +++ b/contrib/plugins/vmi/vmi.h @@ -0,0 +1,15 @@ +#ifndef VMI_H +#define VMI_H + +#include "vmi_types.h" + +QPP_FUN_PROTOTYPE(vmi, VmiProc*, get_current_process, void); +QPP_FUN_PROTOTYPE(vmi, VmiProc*, get_process, const VmiProcHandle*); +QPP_FUN_PROTOTYPE(vmi, VmiProcHandle*, get_current_process_handle, void); + +struct get_process_data { + VmiProc **p; + const VmiProcHandle *h; +}; + +#endif \ No newline at end of file diff --git a/contrib/plugins/vmi/vmi_types.h b/contrib/plugins/vmi/vmi_types.h new file mode 100644 index 0000000000000..0b4ac7fe9e011 --- /dev/null +++ b/contrib/plugins/vmi/vmi_types.h @@ -0,0 +1,186 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef __cplusplus +} +#endif + +// XXX are these right? +typedef uint64_t target_ulong; +typedef uint64_t target_ptr_t; +typedef uint32_t target_pid_t; + +/** + * Minimal handle for a process. Contains a unique identifier \p asid + * and a task descriptor pointer \p taskd that can be used to retrieve the full + * details of the process. + */ +typedef struct vmi_proc_handle_struct { + target_ptr_t taskd; + target_ptr_t asid; +} VmiProcHandle; + +/** + * Minimal information about a process thread. + * Address space and open resources are shared between threads + * of the same process. This information is stored in VmiProc. + */ +typedef struct vmi_thread_struct { + target_pid_t pid; + target_pid_t tid; +} VmiThread; + +/** + * @brief Represents a page in the address space of a process. + * + * @note This has not been implemented/used so far. + */ +typedef struct vmi_page_struct { + target_ptr_t start; + target_ptr_t len; +} VmiPage; + + +/** + * Represents information about a guest OS module (kernel module + * or shared library). + */ +typedef struct vmi_module_struct { + target_ptr_t modd; + target_ptr_t base; + target_ptr_t size; + char *file; + char *name; +} VmiModule; + +/** + * Detailed information for a process. + */ +typedef struct vmi_proc_struct { + target_ptr_t taskd; + target_ptr_t asid; + target_pid_t pid; + target_pid_t ppid; + char *name; + VmiPage *pages; + uint64_t create_time; +} VmiProc; + +/* ****************************************************************** + * Helper functions for freeing/copying vmi structs. + ******************************************************************* */ + +/** + * Frees an VmiProcHandle struct and its contents. + * To be used for freeing standalone VmiProcHandle structs. + */ +static inline void free_vmiprochandle(VmiProcHandle *h) { + g_free(h); +} + +/** + * Frees an VmiThread struct and its contents. + * To be used for freeing standalone VmiThread structs. + */ +static inline void free_vmithread(VmiThread *t) { + g_free(t); +} + +/** + * Frees an VmiPage struct and its contents. + * To be used for freeing standalone VmiPage structs. + */ +static inline void free_vmipage(VmiPage *p) { + g_free(p); +} + +/** + * Frees the contents of an VmiModule struct. + * Meant to be passed to g_array_set_clear_func. + */ +static inline void free_vmimodule_contents(VmiModule *m) { + if (m == NULL) return; + g_free(m->file); + g_free(m->name); +} + +/** + * Frees an VmiModule struct and its contents. + * To be used for freeing standalone VmiModule structs. + */ +static inline void free_vmimodule(VmiModule *m) { + free_vmimodule_contents(m); + g_free(m); +} + +/** + * Frees the contents of an VmiProc struct. + * Meant to be passed to g_array_set_clear_func. + */ +static inline void free_vmiproc_contents(VmiProc *p) { + if (p == NULL) return; + g_free(p->name); + g_free(p->pages); +} + +/** + * Frees an VmiProc struct and its contents. + * To be used for freeing standalone VmiProc structs. + */ +static inline void free_vmiproc(VmiProc *p) { + free_vmiproc_contents(p); + g_free(p); +} + +/** + * Copies an VmiProcHandle struct. + * Returns a pointer to the destination location. + */ +static inline VmiProcHandle *copy_vmiprochandle(VmiProcHandle *from, VmiProcHandle *to) { + if (from == NULL) return NULL; + if (to == NULL) { + to = (VmiProcHandle *)g_malloc0(sizeof(VmiProc)); + } + memcpy(to, from, sizeof(VmiProc)); + return to; +} + +/** + * Copies an VmiProc struct. + * Returns a pointer to the destination location. + */ +static inline VmiProc *copy_vmiproc(VmiProc *from, VmiProc *to) { + if (from == NULL) return NULL; + if (to == NULL) { + to = (VmiProc *)g_malloc0(sizeof(VmiProc)); + } else { + free_vmiproc_contents(to); + } + memcpy(to, from, sizeof(VmiProc)); + to->name = g_strdup(from->name); + to->pages = NULL; // VmiPage - TODO + return to; +} + +/** + * Copies an VmiModule struct. + * Returns a pointer to the destination location. + */ +static inline VmiModule *copy_vmimod(VmiModule *from, VmiModule *to) { + if (from == NULL) return NULL; + if (to == NULL) { + to = (VmiModule *)g_malloc0(sizeof(VmiModule)); + } else { + free_vmimodule_contents(to); + } + memcpy(to, from, sizeof(VmiModule)); + to->name = g_strdup(from->name); + to->file = g_strdup(from->file); + return to; +}