diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3f5a2cd..069a379 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -70,4 +70,5 @@ add_subdirectory(callstack) add_subdirectory(apicall_tracer) add_subdirectory(memory_regions) add_subdirectory(pmemdump) +add_subdirectory(logging_events) #add_subdirectory(volatility) diff --git a/plugins/apicall_tracer/apicall_tracer.cc b/plugins/apicall_tracer/apicall_tracer.cc index 0bd2f02..d780f81 100644 --- a/plugins/apicall_tracer/apicall_tracer.cc +++ b/plugins/apicall_tracer/apicall_tracer.cc @@ -547,10 +547,10 @@ void uninit_plugin(void* self) if (!g_initialized) { // create output file std::fstream results; - results.open(g_database_path, + results.open(g_database_path, std::fstream::in | std::fstream::out | std::fstream::trunc); results.close(); throw std::runtime_error( - "panda introspection never initialized. Corrupted recording?"); + "panda introspection never initialized. Corrupted recording?"); } } diff --git a/plugins/logging_events/CMakeLists.txt b/plugins/logging_events/CMakeLists.txt new file mode 100644 index 0000000..da7af4a --- /dev/null +++ b/plugins/logging_events/CMakeLists.txt @@ -0,0 +1,41 @@ +set(PANDA_PLUGIN_NAME "logging_events") +set(PLUGIN_TARGET "panda_${PANDA_PLUGIN_NAME}") + +# The logging events plugin requires linking against python +find_package(PythonLibs 3.8 REQUIRED) +if (NOT PYTHONLIBS_FOUND) + message(FATAL_ERROR "Could not find python libraries. Is python-dev installed?") +endif() +include_directories(${PYTHON_INCLUDE_DIRS}) + +# Set flags, build, and link the actual plugin +add_definitions(-DNEED_CPU_H) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11") +set(LINK_LIBS ${PYTHON_LIBRARIES} -lavro liboffset libiohal libosi) +set(SRC_FILES ${PANDA_PLUGIN_NAME}_new.cpp ../volatility/filter.cc) + + +set(LINK_LIBS_I386 ${LINK_LIBS} panda_ipanda-i386 panda_callstack-i386) +set(LINK_LIBS_X86_64 ${LINK_LIBS} panda_ipanda-x86_64 panda_callstack-x86_64) + +set(TARGET_DEPS_I386 panda_ipanda-i386) +set(TARGET_DEPS_X86_64 panda_ipanda-x86_64) + +add_custom_command(OUTPUT ${PANDA_PLUGIN_DIR_I386}/evtxglue.py + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/evtxglue.py ${PANDA_PLUGIN_DIR_I386}/evtxglue.py + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/evtxglue.py) +add_custom_command(OUTPUT ${PANDA_PLUGIN_DIR_X86_64}/evtxglue.py + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/evtxglue.py ${PANDA_PLUGIN_DIR_X86_64}/evtxglue.py + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/evtxglue.py) +add_custom_target(evtxglue-script ALL + DEPENDS ${PANDA_PLUGIN_DIR_I386}/evtxglue.py ${PANDA_PLUGIN_DIR_X86_64}/evtxglue.py) + +add_i386_plugin(${PLUGIN_TARGET} SRC_FILES LINK_LIBS_I386) +add_x86_64_plugin(${PLUGIN_TARGET} SRC_FILES LINK_LIBS_X86_64) + +add_dependencies(${PLUGIN_TARGET}-i386 ${TARGET_DEPS_I386}) +add_dependencies(${PLUGIN_TARGET}-x86_64 ${TARGET_DEPS_X86_64}) + +install(FILES ${PANDA_PLUGIN_DIR_I386}/evtxglue.py DESTINATION lib/panda/i386) +install(FILES ${PANDA_PLUGIN_DIR_X86_64}/evtxglue.py DESTINATION lib/panda/x86_64) \ No newline at end of file diff --git a/plugins/logging_events/Makefile b/plugins/logging_events/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/plugins/logging_events/evtxglue.py b/plugins/logging_events/evtxglue.py new file mode 100644 index 0000000..6faf92f --- /dev/null +++ b/plugins/logging_events/evtxglue.py @@ -0,0 +1,27 @@ +import evtxtract, json, traceback +from collections import defaultdict + +def run(imagename): + with open(imagename, 'rb') as f: + buf = f.read() + offsets = evtxtract.carvers.find_evtx_records(buf) + + analysis_results = defaultdict(list) + for offset in offsets: + try: + record = evtxtract.carvers.extract_record(buf, offset) + analysis_results[record.substitutions[14][1]].append(str(record)) + records.append(record) + except Exception: + continue + + json_str = json.dumps(analysis_results, indent=1) + return json_str + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Test analysis") + parser.add_argument("--image_name", default="../volatility/mymem.dd") + args = parser.parse_args() + print(run(args.image_name)) diff --git a/plugins/logging_events/log_lib-rr-nondet.log b/plugins/logging_events/log_lib-rr-nondet.log new file mode 100644 index 0000000..38c01ee Binary files /dev/null and b/plugins/logging_events/log_lib-rr-nondet.log differ diff --git a/plugins/logging_events/log_lib-rr-snp b/plugins/logging_events/log_lib-rr-snp new file mode 100644 index 0000000..5d1d36a Binary files /dev/null and b/plugins/logging_events/log_lib-rr-snp differ diff --git a/plugins/logging_events/logging_events.cpp b/plugins/logging_events/logging_events.cpp new file mode 100644 index 0000000..613e53f --- /dev/null +++ b/plugins/logging_events/logging_events.cpp @@ -0,0 +1,483 @@ +#define PLUGIN_MAIN +#define __STDC_FORMAT_MACROS +#define OSI_TEST_ON_ASID_CHANGED + +extern "C" { + #include + #include + #include +} + +#include "panda/plugin.h" +#include "panda/plugin_plugin.h" +#include "panda/common.h" +#include "ipanda/ipanda.h" +#include "ipanda/manager.h" +#include "ipanda/types.h" +#include "panda/plugins/syscalls2/syscalls2.h" +#include "panda/plugins/syscalls2/syscalls_ext_typedefs.h" +#include "callstack/callstack.h" +#include "callstack/prog_point.h" +#include "apicall_tracer/trace_filter.h" + + +extern "C" { + bool init_plugin(void*); + void uninit_plugin(void*); + // #include "osi/osi_types.h" + // #include "panda/plugins/osi/osi_ext.h" + // #include "panda/plugins/osi/os_intro.h" + #include "panda/plugins/osi_linux/default_profile.h" + #include "panda/plugins/osi_linux/kernel_profile.h" + #include "panda/plugins/dynamic_symbols/dynamic_symbols_int_fns.h" + #include "panda/plugins/hooks/hooks_int_fns.h" + #include "panda/plugins/hooks2/hooks2.h" +} + +typedef bool (*__can_read_current_t)(CPUState* cpu); +__can_read_current_t __can_read_current = NULL; + +typedef target_ptr_t (*__default_get_current_task_struct_t)(CPUState* cpu); +__default_get_current_task_struct_t __default_get_current_task_struct = NULL; + +// dynamic_symbols +typedef struct symbol (*__resolve_symbol_t)(CPUState* cpu, target_ulong asid, char* section_name, char* symbol); +__resolve_symbol_t __resolve_symbol = NULL; + +typedef struct symbol (*__hook_symbol_resolution_t)(struct hook_symbol_resolve* h); +__hook_symbol_resolution_t __hook_symbol_resolution = NULL; + +typedef struct symbol (*__get_best_matching_symbol_t)(CPUState* cpu, target_ulong address, target_ulong asid); +__get_best_matching_symbol_t __get_best_matching_symbol = NULL; + +// hooks +typedef void (*__enable_hooking_t)(); +__enable_hooking_t __enable_hooking = NULL; + +typedef void (*__add_hook_t)(struct hook* h); +__add_hook_t __add_hook = NULL; + +typedef void (*__add_symbol_hook_t)(struct symbol_hook* h); +__add_symbol_hook_t __add_symbol_hook = NULL; + +// hooks2 +typedef void (*__enable_hooks2_t)(int id); +__enable_hooks2_t __enable_hooks2 = NULL; + +typedef int (*__add_hooks2_t)(hooks2_func_t hook, + void *cb_data, + bool is_kernel, + const char *procname, + const char *libname, + target_ulong trace_start, + target_ulong trace_stop, + target_ulong range_begin, + target_ulong range_end); +__add_hooks2_t __add_hooks2 = NULL; + + +extern CurrentProcessOSI* g_current_osi = NULL; +static std::shared_ptr os_manager; + +static int id = 9; +static bool hook_registered = false; + +char g_program_name[] = "log_plugin"; +char g_module_name[] = "gluemod"; +char g_script_path[4096] = {0}; + +static PyObject* g_pfunc = NULL; +PyConfig config; + + +bool init_log_detect(CPUState* env) { + if (!init_ipanda(env, os_manager)) { + fprintf(stderr, "Could not initialize the introspection library.\n"); + return false; + } + return true; +} + + +bool context_switch_callback(CPUState* env, target_ulong old_asid, target_ulong new_asid) { + if (old_asid == new_asid) { + printf("ASID not changed\n"); + } else { + printf("ASID changed\n"); + } + return false; +} + +void syslog_syscall_hook(CPUState* env, target_ulong pc, int type, target_ulong bufp, int len) { + if (bufp) { + uint8_t read_buf[20]; + panda_virtual_memory_read(env, bufp, read_buf, len); + printf("syslog(%d, %s, %d)", type, (char*) read_buf, len); + } +} + +static const uint8_t _zero_block[1024] = {0}; +static void actually_dump_physical_memory(FILE* out, size_t len) +{ + hwaddr addr = 0; + uint8_t block[sizeof(_zero_block)]; + + if (!out) + return; + + while (len != 0) + { + size_t l = sizeof(block); + if (l > len) + l = len; + if (panda_physical_memory_read(addr, block, l) == MEMTX_OK) + fwrite(block, 1, l, out); + else + fwrite(_zero_block, 1, l, out); + addr += l; + len -= l; + } +} + +static void dump_memory(char* filename, char* register_filename, uint64_t pmem_len){ + FILE* out = fopen(filename, "wb"); + + if (pmem_len == 0){ + // dump all memory if not specified as arg + pmem_len = ram_size; + } + + actually_dump_physical_memory(out, pmem_len); + fclose(out); + if (register_filename) + { + if ((out = fopen(register_filename, "w")) != NULL) + { + CPUState* cpu; + CPU_FOREACH(cpu) + { + fprintf(out, "CPU#%d\n", cpu->cpu_index); + cpu_dump_state(cpu, out, fprintf, CPU_DUMP_FPU); + } + fclose(out); + } + } + + panda_replay_end(); +} + +void syslog_block_hook(CPUState* env, TranslationBlock* tb, struct hook* h) { + // if (panda_current_pc(env) != tb->pc) { + // return; + // } + + CPUX86State* regs = (CPUX86State*) env; + target_ulong msg_ptr = regs->regs[R_ESI]; // 2nd arg: message + target_ulong asid = panda_current_asid(env); + + CPUArchState *CASenv = (CPUArchState *)env->env_ptr; + target_ulong pc = 0x0; + target_ulong cs_base = 0x0; + uint32_t flags = 0x0; + cpu_get_tb_cpu_state(CASenv, &pc, &cs_base, &flags); + + uint8_t read_buf[20]; + int status = panda_virtual_memory_read(env, 0x4191d5, read_buf, sizeof(read_buf) - 1); + printf("Status: %d\n", status); + if (status == 0) { + read_buf[255] = '\0'; + printf("[HOOK] syslog(message=\"%s\")\n", read_buf); + } else { + printf("[HOOK] syslog(message=)\n"); + } + + printf("rr count: %lu\n", rr_get_guest_instr_count()); + printf("tb pc: 0x%lx\n", tb->pc); + printf("In kernel: %d\n", panda_in_kernel(env)); + // printf("pc: 0x%lx\n", panda_current_pc(env)); + printf("pc: 0x%lx\n", pc); + printf("guest pc: 0x%lx\n", env->panda_guest_pc); // probably from savevm state (begin record) + // dump_memory("mem2.ram", "mem2.regs.txt", 0); + + int num_bytes = 24; + uint8_t buf[num_bytes]; + panda_virtual_memory_read(env, tb->pc, buf, num_bytes); + printf("[DISASM] tb->pc = 0x%lx | bytes = ", tb->pc); + for (int i = 0; i < num_bytes; i++) { + printf("%02x ", buf[i]); + } + printf("\n"); + + // char* sym_str = "syslog"; + // struct symbol sym = __resolve_symbol(env, asid, NULL, sym_str); + // printf("syslog address: 0x%lx\n", sym.address); + // if (sym.address) { + // printf("[FORCE] __resolve_symbol resolved %s at 0x%lx %s 0x%lx\n", sym.name, sym.address, sym.section, sym.value); + // } else { + // printf("[FORCE] __resolve_symbol failed to resolve %s\n", sym_str); + // } + + struct { + const char* name; + const target_ulong* ptr; + } cpudata[] = { + {"RAX", ®s->regs[R_EAX]}, + {"RBX", ®s->regs[R_EBX]}, + {"RCX", ®s->regs[R_ECX]}, + {"RDX", ®s->regs[R_EDX]}, + {"R8", ®s->regs[8]}, + {"R9", ®s->regs[9]}, + {"RSI", ®s->regs[R_ESI]}, + {"RDI", ®s->regs[R_EDI]}, + {"RSP", ®s->regs[R_ESP]}, + {"RBP", ®s->regs[R_EBP]}, + }; + // for (size_t i = 0; i < sizeof(cpudata)/sizeof(cpudata[0]); ++i) + // { + // uint8_t buffer[256] = {0}; + // printf("[DEBUG] msg_ptr (%s) = %#18lx\n", cpudata[i].name, *cpudata[i].ptr); + // if (panda_virtual_memory_read(env, *cpudata[i].ptr, buffer, sizeof(buffer) - 1)) { + // buffer[255] = '\0'; + // printf("[HOOK] syslog(message=\"%s\")\n", buffer); + // } else { + // printf("[HOOK] syslog(message=)\n"); + // } + // } + // target_ulong start = regs->regs[R_ESP] - (8 * sizeof(uint64_t)); + // for (size_t i = 0; i < 16; ++i) + // { + // uint64_t stkdata = 0; + // target_ulong target = start + (i * sizeof(stkdata)); + // if (panda_virtual_memory_read(env, target, (uint8_t*)&stkdata, sizeof(stkdata))) + // { + // printf("[Stack Dump] %#18lx: %#18lx\n", target, stkdata); + // } + // else + // { + // printf("[Stack Dump] %#18lx: \n", target); + // } + // } + uint8_t buffer[256] = {0}; + printf("[DEBUG] msg_ptr = 0x%lx\n", msg_ptr); + status = panda_virtual_memory_read(env, msg_ptr, buffer, sizeof(buffer) - 1); + printf("Status: %d\n", status); + if (status == 0) { + buffer[255] = '\0'; + printf("[HOOK] syslog(message=\"%s\")\n", buffer); + } else { + printf("[HOOK] syslog(message=)\n"); + } +} + +// void on_syslog_resolved(struct hook_symbol_resolve* h, struct symbol sym, target_ulong asid) { +// printf("[RESOLVE] syslog resolved at 0x%lx (section: %s)\n", sym.address, sym.section); + +// // Install hooks2 instruction hook at the resolved address +// int hook_id = __add_hooks2( +// syslog_block_hook, +// NULL, +// false, +// NULL, +// NULL, +// 0, 0, +// sym.address, +// sym.address + 1 +// ); + +// if (hook_id >= 0) { +// printf("[HOOKS2] Hook installed with ID %d\n", hook_id); +// hook_registered = true; +// } else { +// printf("[ERROR] Failed to install hook via add_hooks2\n"); +// } +// } + +void register_hook(CPUState* env, TranslationBlock* tb) { + if (hook_registered) { + return; + } + struct symbol_hook h = {0}; + strncpy(h.name, "syslog", 256); + h.cb.start_block_exec = syslog_block_hook; + h.type = PANDA_CB_START_BLOCK_EXEC; + h.hook_offset = false; + strncpy(h.section, "libc-", 256); + __add_symbol_hook(&h); + + // target_ulong asid = panda_current_asid(env); + // struct symbol sym = __resolve_symbol(env, asid, NULL, (char*)"syslog"); + // printf("[RESOLVE] syslog resolved at address: 0x%lx (section: %s)\n", sym.address, sym.section); + // struct hook h = {0}; + // h.addr = sym.address; + // h.asid = asid; + // h.type = PANDA_CB_START_BLOCK_EXEC; + // h.cb.start_block_exec = syslog_block_hook; + // h.km = MODE_ANY; + // h.enabled = true; + // h.sym = sym; + // h.context = NULL; + // __add_hook(&h); + + hook_registered = true; + + // struct hook_symbol_resolve h = {0}; + // strncpy(h.name, "syslog", 256); + // h.hook_offset = true; + // h.enabled = true; + // h.cb = on_syslog_resolved; + // h.id = id; + + // __hook_symbol_resolution(&h); + char* sym_str = "syslog"; + // struct symbol sym = __resolve_symbol(env, asid, NULL, sym_str); + // printf("syslog address: 0x%lx\n", sym.address); + // if (sym.address) { + // printf("[FORCE] __resolve_symbol resolved %s at 0x%lx\n", sym.name, sym.address); + // } else { + // printf("[FORCE] __resolve_symbol failed to resolve %s\n", sym_str); + // } + + // struct symbol matching = __get_best_matching_symbol(env, 0x7f4a4715e1a0, asid); + // printf("[MATCHING NAME] %s\n", matching.name); + + // return false; +} + + +bool init_dynamic_symbols_api() { + void* dynamic_symbols = panda_get_plugin_by_name("dynamic_symbols"); + if (dynamic_symbols == NULL){ + panda_require("dynamic_symbols"); + dynamic_symbols = panda_get_plugin_by_name("dynamic_symbols"); + } + if (dynamic_symbols != NULL){ + __resolve_symbol = (__resolve_symbol_t) dlsym(dynamic_symbols, "resolve_symbol"); + __hook_symbol_resolution = (__hook_symbol_resolution_t) dlsym(dynamic_symbols, "hook_symbol_resolution"); + __get_best_matching_symbol = (__get_best_matching_symbol_t) dlsym(dynamic_symbols, "get_best_matching_symbol"); + if (__resolve_symbol == NULL || __hook_symbol_resolution == NULL || __get_best_matching_symbol == NULL) { + return false; + } + } else { + return false; + } + return true; +} + +bool init_hooks_api() { + void* hooks = panda_get_plugin_by_name("hooks"); + if (hooks == NULL){ + panda_require("hooks"); + hooks = panda_get_plugin_by_name("hooks"); + } + if (hooks != NULL){ + __enable_hooking = (__enable_hooking_t) dlsym(hooks, "enable_hooking"); + __add_hook = (__add_hook_t) dlsym(hooks, "add_hook"); + __add_symbol_hook = (__add_symbol_hook_t) dlsym(hooks, "add_symbol_hook"); + if (__enable_hooking == NULL || __add_hook == NULL || __add_symbol_hook == NULL) { + return false; + } + } else { + return false; + } + return true; +} + +bool init_hooks2_api() { + void* hooks2 = panda_get_plugin_by_name("hooks2"); + if (hooks2 == NULL){ + panda_require("hooks2"); + hooks2 = panda_get_plugin_by_name("hooks2"); + } + if (hooks2 != NULL){ + __enable_hooks2 = (__enable_hooks2_t) dlsym(hooks2, "enable_hooks2"); + __add_hooks2 = (__add_hooks2_t) dlsym(hooks2, "add_hooks2"); + if (__enable_hooks2 == NULL || __add_hooks2 == NULL) { + return false; + } + } else { + return false; + } + return true; +} + +bool init_osi_linux_api() { + void* osi_linux = panda_get_plugin_by_name("osi_linux"); + if (osi_linux == NULL) { + panda_require("osi_linux"); + osi_linux = panda_get_plugin_by_name("osi_linux"); + } + if (osi_linux != NULL){ + __can_read_current = (__can_read_current_t) dlsym(osi_linux, "can_read_current"); + __default_get_current_task_struct = (__default_get_current_task_struct_t) dlsym(osi_linux, "default_get_current_task_struct"); + if (__can_read_current == NULL || __default_get_current_task_struct == NULL) { + printf("can read current is null\n"); + return false; + } + } else { + printf("osi linux is null\n"); + return false; + } + return true; +} + +void register_panda_callbacks(void* self) { + panda_cb pcb; + + // pcb.after_loadvm = (reinterpret_cast(init_log_detect)); + // panda_register_callback(self, PANDA_CB_AFTER_LOADVM, pcb); + + // pcb.asid_changed = register_hook; + // panda_register_callback(self, PANDA_CB_ASID_CHANGED, pcb); + + pcb.before_block_exec = register_hook; + panda_register_callback(self, PANDA_CB_BEFORE_BLOCK_EXEC, pcb); + + // set_callstack_osi(g_current_osi); + // init_callstack_plugin(self, g_current_osi); + // register_callstack_callback("on_call", call_insn_callback); + + PPP_REG_CB("syscalls2", on_sys_syslog_enter, syslog_syscall_hook); +} + + +bool init_plugin(void* self) +{ + fflush(stdout); + printf("In init plugin\n"); + + panda_require("syscalls2"); + + panda_enable_precise_pc(); + + assert(init_dynamic_symbols_api()); + // assert(init_osi_linux_api()); + + assert(init_hooks_api()); + __enable_hooking(); + + // assert(init_hooks2_api()); + // __enable_hooks2(id); + + register_panda_callbacks(self); + + const char* profile = panda_os_name; + if (!profile) { + fprintf(stderr, + "[%s] Could not find os name. Please re-run with -os flag\n", + __FILE__); + return false; + } + + panda_arg_list* args = panda_get_args("logging_events"); + const char* log_path = strdup(panda_parse_string(args, "output", "logging.jsonl")); + fprintf(stdout, "Writing analysis results to %s\n", log_path); + panda_free_args(args); + + return true; +} + +void uninit_plugin(void* self) { + fprintf(stdout, "uninit"); + panda_arg_list* args = panda_get_args("logging_events"); + panda_free_args(args); +} diff --git a/plugins/logging_events/logging_events_new.cpp b/plugins/logging_events/logging_events_new.cpp new file mode 100644 index 0000000..871facf --- /dev/null +++ b/plugins/logging_events/logging_events_new.cpp @@ -0,0 +1,453 @@ +#define PLUGIN_MAIN +#define __STDC_FORMAT_MACROS +#define OSI_TEST_ON_ASID_CHANGED + +extern "C" { + #include + #include + #include +} +#include +#include +#include +#include + +#include "panda/plugin.h" +#include "panda/common.h" +#include "exec/cpu-defs.h" +#include "ipanda/panda_x86.h" + +#include "ipanda/ipanda.h" +#include "ipanda/manager.h" + +#include "osi/windows/wintrospection.h" + +#include "../volatility/filter.h" + +// Globals to be set by configs, eventually +char g_program_name[] = "logging_events_plugin"; +char g_module_name[] = "gluemod"; +char g_func_name[] = "run"; + +char g_script_path[4096] = {0}; + +char g_imagename[512] = "\0"; +char g_filter_path[512] = {0}; +const char g_script_name[] = "/evtxglue.py"; + +// Globals +std::shared_ptr os_manager; +std::shared_ptr g_os_manager; +struct WindowsProcess* g_current_process = nullptr; + +std::shared_ptr g_filter; +bool g_check_for_process = true; +bool g_targeted = true; + +static PyObject* g_pfunc = NULL; +PyConfig config; + +static double percent = -1; + +#define CHECK_OR_DIE(_obj, _emsg, _elabel) \ + do { \ + if (!_obj) { \ + if (PyErr_Occurred()) { \ + PyErr_Print(); \ + } \ + fprintf(stderr, _emsg); \ + goto _elabel; \ + } \ + } while (0) + +extern "C" { + bool init_plugin(void*); + void uninit_plugin(void*); +} + +int run_evtx_analysis(CPUState* env); +bool log_analysis_results(CPUState* env, const char* data); + +static const uint8_t _zero_block[1024] = {0}; +static void actually_dump_physical_memory(FILE* out, size_t len) +{ + hwaddr addr = 0; + uint8_t block[sizeof(_zero_block)]; + + if (!out) + return; + + while (len != 0) + { + size_t l = sizeof(block); + if (l > len) + l = len; + if (panda_physical_memory_read(addr, block, l) == MEMTX_OK) + fwrite(block, 1, l, out); + else + fwrite(_zero_block, 1, l, out); + addr += l; + len -= l; + } +} + +static void dump_memory(char* filename, char* register_filename, uint64_t pmem_len){ + FILE* out = fopen(filename, "wb"); + + if (pmem_len == 0){ + // dump all memory if not specified as arg + pmem_len = ram_size; + } + + actually_dump_physical_memory(out, pmem_len); + fclose(out); + if (register_filename) + { + if ((out = fopen(register_filename, "w")) != NULL) + { + CPUState* cpu; + CPU_FOREACH(cpu) + { + fprintf(out, "CPU#%d\n", cpu->cpu_index); + cpu_dump_state(cpu, out, fprintf, CPU_DUMP_FPU); + } + fclose(out); + } + } + + panda_replay_end(); +} + +/** + * Run the evtxtract analysis, passing the image name + * as a python string. Stores the results in the panda log or writes them + * to stderr + */ +int run_evtx_analysis(CPUState* env) +{ + dump_memory("mem.ram", "mem.regs.txt", 0); + PyObject* pimage_name = PyUnicode_FromString(g_imagename); + PyObject* pfilter_str = PyUnicode_FromString(g_filter_path); + + PyObject* pargs = PyTuple_New(1); + + // Mildly concerned about death-by-oom + if (!pimage_name) { + fprintf(stderr, "[%s] Failed to allocate args\n", __FILE__); + Py_XDECREF(pimage_name); + Py_XDECREF(pargs); + } + + // Add these strings to an argument object + PyTuple_SetItem(pargs, 0, pimage_name); + // PyTuple_SetItem(pargs, 1, pfilter_str); + + // Call run(imagename) + PyObject* pvalue = PyObject_CallObject(g_pfunc, pargs); + if (pvalue) { + // The function returned a value successfully + if (PyUnicode_Check(pvalue)) { + const char* json_str = PyUnicode_AsUTF8(pvalue); + fprintf(stdout, "%s\n", json_str); + if (log_analysis_results(env, json_str)) { + fprintf(stderr, "[%s] Failed to record result!\n", __FILE__); + } + } else { + fprintf(stderr, "[%s] Return value was not a string!\n", __FILE__); + } + } else { + // The function failed to return correctly + if (PyErr_Occurred()) { + PyErr_Print(); + } + fprintf(stderr, "[%s] Didn't receive response from analysis!\n", __FILE__); + } + + Py_XDECREF(pargs); // pargs handles components refs + Py_XDECREF(pvalue); + return 0; +} + +avro_schema_t g_schema = nullptr; +avro_file_writer_t g_db = nullptr; + +bool init_avro(const char* dbname) +{ + int status = 0; + + // Initialize the schema for a memstring + g_schema = avro_schema_record("logging_events", NULL); + avro_schema_record_field_append(g_schema, "rrindex", avro_schema_long()); + avro_schema_record_field_append(g_schema, "results", avro_schema_string()); + + remove(dbname); + + status = avro_file_writer_create_with_codec(dbname, g_schema, &g_db, "deflate", + 512 * 1024 * 1024); + if (status) { + fprintf(stderr, "[%s] Avro failed to open %s for writing\n", __FILE__, dbname); + fprintf(stderr, "[E] error message: %s\n", avro_strerror()); + return true; + } + fprintf(stdout, "Writing analysis results to %s\n", dbname); + return false; +} + +void teardown_avro() +{ + avro_file_writer_close(g_db); + avro_schema_decref(g_schema); +} + +bool log_analysis_results(CPUState* env, const char* data) +{ + avro_datum_t log_dt = avro_record(g_schema); + avro_datum_t rrindex_dt = avro_int64((int64_t)rr_get_guest_instr_count()); + avro_datum_t result_dt = avro_string(data); + + if (avro_record_set(log_dt, "rrindex", rrindex_dt)) { + fprintf(stderr, "Avro failed to add rrindex to record\n"); + return true; + } + if (avro_record_set(log_dt, "results", result_dt)) { + fprintf(stderr, "[E] Avro failed to build logging result: %s\n", + avro_strerror()); + return true; + } + + if (avro_file_writer_append(g_db, log_dt)) { + fprintf(stderr, "[E] Avro failed to write logging: %s\n", avro_strerror()); + return true; + } + avro_datum_decref(result_dt); + avro_datum_decref(rrindex_dt); + avro_datum_decref(log_dt); + return false; +} + +void before_block_exec(CPUState* env, TranslationBlock* tb) +{ + auto kosi = g_os_manager->get_kosi(); + + if (g_check_for_process) { + free_process(g_current_process); + + g_current_process = kosi_get_current_process(kosi); + g_targeted = g_filter->thread_check(process_get_pid(g_current_process), + process_get_asid(g_current_process)); + + g_check_for_process = false; + } + + if (!g_targeted) { + return; + } + + auto pid = process_get_pid(g_current_process); + auto asid = process_get_asid(g_current_process); + auto tid = kosi_get_current_tid(kosi); + + if (!g_filter->thread_check(pid, asid, tid)) { + return; + } + + run_evtx_analysis(env); + + // remove the thread now that we've handled it and make + // the next bb refresh state info + g_filter->remove_thread(pid, asid, tid); + g_check_for_process = true; + + return; +} + +bool check_for_process(CPUState* env, target_ulong oldval, target_ulong newval) +{ + g_check_for_process = true; + return 0; +} + +/** + * Set the path to the default python script, which should be + * in the same directory as the shared object + */ +void set_default_python_script() +{ + Dl_info dl_info; + dladdr((void*)set_default_python_script, &dl_info); + + if (dl_info.dli_sname == NULL) { + fprintf(stderr, "[%s] Failed to locate logging_events plugin shared object!\n", + __FILE__); + return; + } + + const char* lib_path = dl_info.dli_fname; + char* tmp_lib = strdup(lib_path); + char* dir_path = dirname(tmp_lib); + + strncpy(g_script_path, dir_path, sizeof(g_script_path) - 1); + strncat(g_script_path, g_script_name, sizeof(g_script_path) - 1); + free(tmp_lib); +} + +char* read_script(const char* fpath) +{ + fprintf(stdout, "Reading python script from %s\n", fpath); + FILE* fp = fopen(fpath, "r"); + if (fp == NULL) { + fprintf(stderr, "[E] Failed to open %s: %s\n", fpath, strerror(errno)); + return NULL; + } + + fseek(fp, 0, SEEK_END); + int len = ftell(fp); + + char* script = (char*)malloc(len + 1); + if (!script) { + fprintf(stderr, "[%s] Failed to allocate storage for python script of size %d\n", + __FILE__, len + 1); + return NULL; + } + + fseek(fp, 0, SEEK_SET); + int bytes_read = fread(script, 1, len, fp); + if (bytes_read != len) { + fprintf(stderr, "[%s] Failed to read entire python script (%d / %d)!\n", __FILE__, + bytes_read, len); + free(script); + return NULL; + } + + script[len] = '\0'; + return script; +} + +bool init_plugin(void* self) +{ + PyObject* pmodule = NULL; + PyObject* pcode = NULL; + const char* output_path = nullptr; + const char* filter_path = nullptr; + + panda_arg_list* log_args = panda_get_args("logging_events"); + output_path = panda_parse_string(log_args, "output", "logging_events.panda"); + + panda_arg_list* filter_args = panda_get_args("filter"); + filter_path = panda_parse_string(filter_args, "file", ""); + strncpy(g_filter_path, filter_path, sizeof(g_filter_path) - 1); + g_filter.reset(new InstrumentationFilter(g_filter_path)); + panda_free_args(filter_args); + + if (init_avro(output_path)) { + return false; + } + set_default_python_script(); + + // Read arguments + const char* profile_arg = panda_os_name; + if (!profile_arg) { + fprintf(stderr, "[%s] The -os flag is required\n", __FILE__); + return false; + } + + strncat(g_imagename, "mem.ram", sizeof(g_imagename) - 1); + + const char* python_script = panda_parse_string(log_args, "script", g_script_path); + + panda_free_args(log_args); + + panda_cb pcb; + pcb.asid_changed = check_for_process; + panda_register_callback(self, PANDA_CB_ASID_CHANGED, pcb); + pcb.before_block_exec = before_block_exec; + panda_register_callback(self, PANDA_CB_BEFORE_BLOCK_EXEC, pcb); + + // This hack can be avoided by working with PANDA + // to expose the python shared library + dlopen("libpython3.8.so", RTLD_LAZY | RTLD_GLOBAL); + + char* script_contents = read_script(python_script); + if (!script_contents) { + fprintf(stderr, "[%s] Failed to open python script!\n", __FILE__); + return false; + } + + const char* venv_path_cstr = std::getenv("VIRTUAL_ENV"); + std::string venv_path(venv_path_cstr); + std::string exec_path = venv_path + "/bin/python"; + std::wstring w_venv_path(venv_path.begin(), venv_path.end()); + std::wstring w_exec(exec_path.begin(), exec_path.end()); + + Py_SetProgramName((wchar_t*) g_program_name); + PyConfig_InitPythonConfig(&config); + PyConfig_SetString(&config, &config.executable, w_exec.c_str()); + Py_InitializeFromConfig(&config); + + // Load the program as a code object + pcode = Py_CompileString(script_contents, "evtxglue.py", Py_file_input); + CHECK_OR_DIE(pcode, "Failed to compile python program!\n", cleanup); + + // Load the code object into a module + // fprintf(stdout, "Module name: %s\n", g_module_name); + pmodule = PyImport_ExecCodeModule("gluemod", pcode); + CHECK_OR_DIE(pmodule, "Failed to load as module!\n", cleanup); + + // Extract the entry point of our new module + g_pfunc = PyObject_GetAttrString(pmodule, g_func_name); + CHECK_OR_DIE(g_pfunc, "Failed to find function!\n", cleanup); + + if (!PyCallable_Check(g_pfunc)) { + fprintf(stderr, "[%s] Object %s is not a callable!\n", __FILE__, g_func_name); + goto cleanup; + } + + fprintf(stdout, "Successfully initialized python routines.\n"); + + + if (script_contents) { + free(script_contents); + } + Py_XDECREF(pcode); + pcode = NULL; + Py_XDECREF(pmodule); + pmodule = NULL; + + if (!init_ipanda(self, os_manager)) { + fprintf(stderr, "Could not initialize the introspection library.\n"); + goto cleanup; + } + + // temporary -- forcing to be windows specific so i don't have to edit any more code + // in this plugin + g_os_manager = std::dynamic_pointer_cast(os_manager); + + return true; + +cleanup: + // If we can't load everything, explode + if (script_contents) { + free(script_contents); + } + Py_XDECREF(pcode); + pcode = NULL; + Py_XDECREF(pmodule); + pmodule = NULL; + Py_XDECREF(g_pfunc); + g_pfunc = NULL; + PyConfig_Clear(&config); + unlink("mem.ram"); + unlink("mem.regs.txt"); + return false; +} + +void uninit_plugin(void* self) +{ + Py_XDECREF(g_pfunc); + g_pfunc = NULL; + PyConfig_Clear(&config); + Py_Finalize(); + teardown_avro(); + unlink("mem.ram"); + unlink("mem.regs.txt"); +} \ No newline at end of file diff --git a/plugins/logging_events/logs-rr-nondet.log b/plugins/logging_events/logs-rr-nondet.log new file mode 100644 index 0000000..5b8de1b Binary files /dev/null and b/plugins/logging_events/logs-rr-nondet.log differ diff --git a/plugins/logging_events/logs-rr-snp b/plugins/logging_events/logs-rr-snp new file mode 100644 index 0000000..a631be6 Binary files /dev/null and b/plugins/logging_events/logs-rr-snp differ