From ce2ba3c5463d27b24dcd7cf6bac7357942caf700 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Mon, 19 Feb 2024 19:03:03 -0500 Subject: [PATCH] hypercaller plugin --- panda/plugins/hypercaller/Makefile | 17 +++++ panda/plugins/hypercaller/README.md | 17 +++++ panda/plugins/hypercaller/hypercaller.cpp | 76 +++++++++++++++++++++ panda/plugins/hypercaller/hypercaller.h | 14 ++++ panda/python/core/create_panda_datatypes.py | 1 + panda/python/core/pandare/panda.py | 38 +++++++++++ 6 files changed, 163 insertions(+) create mode 100644 panda/plugins/hypercaller/Makefile create mode 100644 panda/plugins/hypercaller/README.md create mode 100644 panda/plugins/hypercaller/hypercaller.cpp create mode 100644 panda/plugins/hypercaller/hypercaller.h diff --git a/panda/plugins/hypercaller/Makefile b/panda/plugins/hypercaller/Makefile new file mode 100644 index 00000000000..897a6cccb53 --- /dev/null +++ b/panda/plugins/hypercaller/Makefile @@ -0,0 +1,17 @@ +# Don't forget to add your plugin to config.panda! + +# If you need custom CFLAGS or LIBS, set them up here +# CFLAGS+= +# LIBS+= + +# Example: this plugin has runtime symbol dependencies on plugin_x: +# LIBS+=-L$(PLUGIN_TARGET_DIR) -l:panda_plugin_x.so +# Also create a plugin_plugin.d file in this directory to ensure plugin_x +# gets compiled before this plugin, example contents: +# plugin-this_plugins_name : plugin-plugin_x +# or if you're using the extra plugins dir: +# extra-plugin-this_plugins_name : extra-plugin-plugin_x + +# The main rule for your plugin. List all object-file dependencies. +$(PLUGIN_TARGET_DIR)/panda_$(PLUGIN_NAME).so: \ + $(PLUGIN_OBJ_DIR)/$(PLUGIN_NAME).o diff --git a/panda/plugins/hypercaller/README.md b/panda/plugins/hypercaller/README.md new file mode 100644 index 00000000000..c6c37c55f34 --- /dev/null +++ b/panda/plugins/hypercaller/README.md @@ -0,0 +1,17 @@ +Plugin: NAME +=========== + +Summary +------- + +Arguments +--------- + +Dependencies +------------ + +APIs and Callbacks +------------------ + +Example +------- diff --git a/panda/plugins/hypercaller/hypercaller.cpp b/panda/plugins/hypercaller/hypercaller.cpp new file mode 100644 index 00000000000..5632b1dddb6 --- /dev/null +++ b/panda/plugins/hypercaller/hypercaller.cpp @@ -0,0 +1,76 @@ +/* PANDABEGINCOMMENT + * + * Authors: + * Luke Craig luke.craig@ll.mit.edu + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + * +PANDAENDCOMMENT */ +// This needs to be defined before anything is included in order to get +// the PRIx64 macro +#define __STDC_FORMAT_MACROS + +#include "panda/plugin.h" +#include + +// These need to be extern "C" so that the ABI is compatible with +// QEMU/PANDA, which is written in C +extern "C" { +bool init_plugin(void *); +void uninit_plugin(void *); +bool hypercall(CPUState *cpu); +#include "hypercaller.h" +} + +std::unordered_map hypercalls; + +void register_hypercall(target_ulong magic, hypercall_t hyp){ + if (hypercalls.find(magic) == hypercalls.end()){ + hypercalls[magic] = hyp; + }else{ + assert(false && "Hypercall already registered"); + } +} + +void unregister_hypercall(target_ulong magic){ + hypercalls.erase(magic); +} + +target_ulong get_magic(CPUState *cpu){ + target_ulong magic; + CPUArchState * env = (CPUArchState *)cpu->env_ptr; +#if defined(TARGET_ARM) + // r0 + magic =env->regs[0]; +#elif defined(TARGET_MIPS) + // a0 + magic =env->active_tc.gpr[4]; +#elif defined(TARGET_I386) + // eax + magic = env->regs[R_EAX]; +#elif defined(TARGET_PPC) + // r0 + magic = env->gpr[0]; +#else + #error "Unsupported target architecture" +#endif + return magic; +} + +bool guest_hypercall(CPUState *cpu) { + target_ulong magic = get_magic(cpu); + if (hypercalls.find(magic) != hypercalls.end()){ + hypercalls[magic](cpu); + return true; + } + return false; +} + +bool init_plugin(void *self) { + panda_cb pcb = { .guest_hypercall = guest_hypercall}; + panda_register_callback(self, PANDA_CB_GUEST_HYPERCALL, pcb); + return true; +} + +void uninit_plugin(void *self) {} \ No newline at end of file diff --git a/panda/plugins/hypercaller/hypercaller.h b/panda/plugins/hypercaller/hypercaller.h new file mode 100644 index 00000000000..adaa6ea34c3 --- /dev/null +++ b/panda/plugins/hypercaller/hypercaller.h @@ -0,0 +1,14 @@ +#ifndef __HYPERCALLER_H +#define __HYPERCALLER_H +// 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. + +typedef void (*hypercall_t)(CPUState *cpu); +void register_hypercall(target_ulong magic, hypercall_t); +void unregister_hypercall(target_ulong magic); + +// END_PYPANDA_NEEDS_THIS -- do not delete this comment! + +#endif \ No newline at end of file diff --git a/panda/python/core/create_panda_datatypes.py b/panda/python/core/create_panda_datatypes.py index a4e1dae587e..d34a1963889 100755 --- a/panda/python/core/create_panda_datatypes.py +++ b/panda/python/core/create_panda_datatypes.py @@ -381,6 +381,7 @@ def main(install=False,recompile=True): copy_ppp_header("%s/%s" % (PLUGINS_DIR+"/forcedexec", "forcedexec_ppp.h")) copy_ppp_header("%s/%s" % (PLUGINS_DIR+"/stringsearch", "stringsearch_ppp.h")) create_pypanda_header("%s/%s" % (PLUGINS_DIR+"/hooks2", "hooks2.h")) + create_pypanda_header("%s/%s" % (PLUGINS_DIR+"/hypercaller", "hypercaller.h")) copy_ppp_header("%s/%s" % (PLUGINS_DIR+"/proc_start_linux", "proc_start_linux_ppp.h")) create_pypanda_header("%s/%s" % (PLUGINS_DIR+"/proc_start_linux", "proc_start_linux.h")) diff --git a/panda/python/core/pandare/panda.py b/panda/python/core/pandare/panda.py index 3c775a49bd0..51f34062da5 100644 --- a/panda/python/core/pandare/panda.py +++ b/panda/python/core/pandare/panda.py @@ -242,6 +242,7 @@ def __init__(self, arch="i386", mem="128M", self.hook_list2 = {} self.mem_hooks = {} self.sr_hooks = [] + self.hypercalls = {} # Asid stuff self.current_asid_name = None @@ -3482,5 +3483,42 @@ def hook_virt_mem_write(self, start_address, end_address, on_before=True, on_aft Decorator to hook virtual memory writes with the mem_hooks plugin ''' return self._hook_mem(start_address,end_address,on_before,on_after, False, True, True, False, True) + + # HYPERCALLS + def hypercall(self, magic): + def decorator(fun): + hypercall_cb_type = self.ffi.callback("hypercall_t") + + def _run_and_catch(*args, **kwargs): # Run function but if it raises an exception, stop panda and raise it + if not hasattr(self, "exit_exception"): + try: + r = fun(*args, **kwargs) + return r + except Exception as e: + # exceptions wont work in our thread. Therefore we print it here and then throw it after the + # machine exits. + if self.catch_exceptions: + self.exit_exception = e + self.end_analysis() + else: + raise e + return None + + hook_cb_passed = hypercall_cb_type(_run_and_catch) + + self.plugins['hypercaller'].register_hypercall(magic, hook_cb_passed) + + def wrapper(*args, **kw): + _run_and_catch(args,kw) + self.hypercalls[wrapper] = [hook_cb_passed,magic] + return wrapper + return decorator + + def disable_hypercall(self, fn): + if fn in self.hypercalls: + self.plugins['hypercaller'].unregister_hypercall(self.hypercalls[fn][1]) + else: + breakpoint() + print("ERROR: Your hypercall was not in the hook list") # vim: expandtab:tabstop=4: