--- /dev/null
+Tamas K Lengyel, tamas.lengyel@intel.com
--- /dev/null
+Copyright (c) 2020 Intel Corporation
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+Installation Instructions
+*************************
+
+ Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software
+Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+ Briefly, the shell command './configure && make && make install'
+should configure, build, and install this package. The following
+more-detailed instructions are generic; see the 'README' file for
+instructions specific to this package. Some packages provide this
+'INSTALL' file but do not implement all of the features documented
+below. The lack of an optional feature in a given package is not
+necessarily a bug. More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+ The 'configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a 'Makefile' in each directory of the package.
+It may also create one or more '.h' files containing system-dependent
+definitions. Finally, it creates a shell script 'config.status' that
+you can run in the future to recreate the current configuration, and a
+file 'config.log' containing compiler output (useful mainly for
+debugging 'configure').
+
+ It can also use an optional file (typically called 'config.cache' and
+enabled with '--cache-file=config.cache' or simply '-C') that saves the
+results of its tests to speed up reconfiguring. Caching is disabled by
+default to prevent problems with accidental use of stale cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how 'configure' could check whether to do them, and mail
+diffs or instructions to the address given in the 'README' so they can
+be considered for the next release. If you are using the cache, and at
+some point 'config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file 'configure.ac' (or 'configure.in') is used to create
+'configure' by a program called 'autoconf'. You need 'configure.ac' if
+you want to change it or regenerate 'configure' using a newer version of
+'autoconf'.
+
+ The simplest way to compile this package is:
+
+ 1. 'cd' to the directory containing the package's source code and type
+ './configure' to configure the package for your system.
+
+ Running 'configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type 'make' to compile the package.
+
+ 3. Optionally, type 'make check' to run any self-tests that come with
+ the package, generally using the just-built uninstalled binaries.
+
+ 4. Type 'make install' to install the programs and any data files and
+ documentation. When installing into a prefix owned by root, it is
+ recommended that the package be configured and built as a regular
+ user, and only the 'make install' phase executed with root
+ privileges.
+
+ 5. Optionally, type 'make installcheck' to repeat any self-tests, but
+ this time using the binaries in their final installed location.
+ This target does not install anything. Running this target as a
+ regular user, particularly if the prior 'make install' required
+ root privileges, verifies that the installation completed
+ correctly.
+
+ 6. You can remove the program binaries and object files from the
+ source code directory by typing 'make clean'. To also remove the
+ files that 'configure' created (so you can compile the package for
+ a different kind of computer), type 'make distclean'. There is
+ also a 'make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 7. Often, you can also type 'make uninstall' to remove the installed
+ files again. In practice, not all packages have tested that
+ uninstallation works correctly, even though it is required by the
+ GNU Coding Standards.
+
+ 8. Some packages, particularly those that use Automake, provide 'make
+ distcheck', which can by used by developers to test that all other
+ targets like 'make install' and 'make uninstall' work correctly.
+ This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the 'configure' script does not know about. Run './configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give 'configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here is
+an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU 'make'. 'cd' to the
+directory where you want the object files and executables to go and run
+the 'configure' script. 'configure' automatically checks for the source
+code in the directory that 'configure' is in and in '..'. This is known
+as a "VPATH" build.
+
+ With a non-GNU 'make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use 'make distclean' before
+reconfiguring for another architecture.
+
+ On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple '-arch' options to the
+compiler but only a single '-arch' option to the preprocessor. Like
+this:
+
+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CPP="gcc -E" CXXCPP="g++ -E"
+
+ This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the 'lipo' tool if you have problems.
+
+Installation Names
+==================
+
+ By default, 'make install' installs the package's commands under
+'/usr/local/bin', include files under '/usr/local/include', etc. You
+can specify an installation prefix other than '/usr/local' by giving
+'configure' the option '--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like '--bindir=DIR' to specify different values for particular
+kinds of files. Run 'configure --help' for a list of the directories
+you can set and what kinds of files go in them. In general, the default
+for these options is expressed in terms of '${prefix}', so that
+specifying just '--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+ The most portable way to affect installation locations is to pass the
+correct locations to 'configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+'make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+ The first method involves providing an override variable for each
+affected directory. For example, 'make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+'${prefix}'. Any directories that were specified during 'configure',
+but not in terms of '${prefix}', must each be overridden at install time
+for the entire installation to be relocated. The approach of makefile
+variable overrides for each directory variable is required by the GNU
+Coding Standards, and ideally causes no recompilation. However, some
+platforms have known limitations with the semantics of shared libraries
+that end up requiring recompilation when using this method, particularly
+noticeable in packages that use GNU Libtool.
+
+ The second method involves providing the 'DESTDIR' variable. For
+example, 'make install DESTDIR=/alternate/directory' will prepend
+'/alternate/directory' before all installation names. The approach of
+'DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters. On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of '${prefix}'
+at 'configure' time.
+
+Optional Features
+=================
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving 'configure' the
+option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
+
+ Some packages pay attention to '--enable-FEATURE' options to
+'configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to '--with-PACKAGE' options, where PACKAGE
+is something like 'gnu-as' or 'x' (for the X Window System). The
+'README' should mention any '--enable-' and '--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, 'configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the 'configure' options '--x-includes=DIR' and
+'--x-libraries=DIR' to specify their locations.
+
+ Some packages offer the ability to configure how verbose the
+execution of 'make' will be. For these packages, running './configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with 'make V=1'; while running './configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with 'make V=0'.
+
+Particular systems
+==================
+
+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC
+is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+ HP-UX 'make' updates targets which have the same time stamps as their
+prerequisites, which makes it generally unusable when shipped generated
+files such as 'configure' are involved. Use GNU 'make' instead.
+
+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its '<wchar.h>' header file. The option '-nodtk' can be used as a
+workaround. If GNU CC is not installed, it is therefore recommended to
+try
+
+ ./configure CC="cc"
+
+and if that doesn't work, try
+
+ ./configure CC="cc -nodtk"
+
+ On Solaris, don't put '/usr/ucb' early in your 'PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in '/usr/bin'. So, if you need '/usr/ucb'
+in your 'PATH', put it _after_ '/usr/bin'.
+
+ On Haiku, software installed for all users goes in '/boot/common',
+not '/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+ There may be some features 'configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, 'configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+'--build=TYPE' option. TYPE can either be a short name for the system
+type, such as 'sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file 'config.sub' for the possible values of each field. If
+'config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option '--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with '--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for 'configure' scripts to share,
+you can create a site shell script called 'config.site' that gives
+default values for variables like 'CC', 'cache_file', and 'prefix'.
+'configure' looks for 'PREFIX/share/config.site' if it exists, then
+'PREFIX/etc/config.site' if it exists. Or, you can set the
+'CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all 'configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to 'configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the 'configure' command line, using 'VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified 'gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
+Autoconf limitation. Until the limitation is lifted, you can use this
+workaround:
+
+ CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+'configure' Invocation
+======================
+
+ 'configure' recognizes the following options to control how it
+operates.
+
+'--help'
+'-h'
+ Print a summary of all of the options to 'configure', and exit.
+
+'--help=short'
+'--help=recursive'
+ Print a summary of the options unique to this package's
+ 'configure', and exit. The 'short' variant lists options used only
+ in the top level, while the 'recursive' variant lists options also
+ present in any nested packages.
+
+'--version'
+'-V'
+ Print the version of Autoconf used to generate the 'configure'
+ script, and exit.
+
+'--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally 'config.cache'. FILE defaults to '/dev/null' to
+ disable caching.
+
+'--config-cache'
+'-C'
+ Alias for '--cache-file=config.cache'.
+
+'--quiet'
+'--silent'
+'-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to '/dev/null' (any error
+ messages will still be shown).
+
+'--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ 'configure' can determine that directory automatically.
+
+'--prefix=DIR'
+ Use DIR as the installation prefix. *note Installation Names:: for
+ more details, including other options available for fine-tuning the
+ installation locations.
+
+'--no-create'
+'-n'
+ Run the configure checks, but stop before creating any output
+ files.
+
+'configure' also accepts some other, not widely useful, options. Run
+'configure --help' for more details.
--- /dev/null
+AC_INIT([shredder], [1.0])
+AM_INIT_AUTOMAKE([1.14 subdir-objects])
+
+AC_CONFIG_SRCDIR(src/main.c)
+AC_CONFIG_HEADERS(config.h)
+
+AC_CANONICAL_HOST
+AC_PROG_CC
+AM_SANITY_CHECK
+
+PKG_CHECK_MODULES([GLIB], [glib-2.0],[],[AC_MSG_ERROR(GLib not found. Install missing package and re-run)])
+PKG_CHECK_MODULES([VMI], [libvmi],[],[AC_MSG_ERROR(LibVMI not found. Install missing package and re-run)])
+PKG_CHECK_MODULES([CAPSTONE], [capstone],[],[AC_MSG_ERROR(Capstone not found. Install missing package and re-run)])
+AC_CHECK_HEADERS([xenctrl.h], [], [AC_ERROR([libxc headers are not usable. Install missing package and re-run])])
+AC_CHECK_HEADERS([libxl.h], [], [AC_ERROR([libxl headers are not usable. Install missing package and re-run])])
+AC_CHECK_LIB(xenctrl, xc_interface_open,[],[AC_MSG_ERROR(libxc not found. Install missing package and re-run)])
+
+AC_CONFIG_FILES(Makefile)
+AC_OUTPUT
--- /dev/null
+#include "private.h"
+
+static uint8_t bp = 0xCC;
+
+static void track_coverage(code_tracer_t *code_tracer, uint64_t cur_loc, uint64_t va)
+{
+ cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
+ cur_loc &= CODE_TRACER_MAP_SIZE - 1;
+ addr_t coverage_point = cur_loc ^ code_tracer->prev_loc;
+
+ code_tracer->map[coverage_point]++;
+ code_tracer->prev_loc = cur_loc >> 1;
+
+ if ( code_tracer->map[coverage_point] == 1 )
+ {
+ printf("\t\tNew code hit at 0x%lx (prev_loc: 0x%lx)\n", va, code_tracer->prev_loc);
+
+ code_tracer->points++;
+ code_tracer->counter = 0; // reset cf counter to allow exploration of the new path
+
+ if ( code_tracer->cb )
+ code_tracer->cb(code_tracer);
+ }
+}
+
+static bool is_cf(unsigned int id)
+{
+ switch ( id )
+ {
+ case X86_INS_JA:
+ case X86_INS_JBE:
+ case X86_INS_JB:
+ case X86_INS_JCXZ:
+ case X86_INS_JECXZ:
+ case X86_INS_JE:
+ case X86_INS_JGE:
+ case X86_INS_JG:
+ case X86_INS_JLE:
+ case X86_INS_JL:
+ case X86_INS_JMP:
+ case X86_INS_LJMP:
+ case X86_INS_JNE:
+ case X86_INS_JNO:
+ case X86_INS_JNP:
+ case X86_INS_JNS:
+ case X86_INS_JO:
+ case X86_INS_JP:
+ case X86_INS_JRCXZ:
+ case X86_INS_JS:
+ case X86_INS_CALL:
+ case X86_INS_RET:
+ case X86_INS_INT3:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static addr_t next_cf_insn(vmi_instance_t vmi, addr_t cr3, addr_t start, bool *int3)
+{
+ cs_insn *insn;
+
+ size_t count;
+ size_t read;
+
+ unsigned char buff[CODE_TRACER_BUFFER_SIZE] = { 0 };
+ addr_t next_cf = 0;
+
+ access_context_t ctx = {
+ .translate_mechanism = VMI_TM_PROCESS_DTB,
+ .dtb = cr3,
+ .addr = start
+ };
+
+ if ( VMI_FAILURE == vmi_read(vmi, &ctx, CODE_TRACER_BUFFER_SIZE, buff, &read) )
+ {
+ //printf("Failed to grab memory from 0x%lx\n", start);
+ return 0;
+ }
+
+ count = cs_disasm(cs_handle, buff, read, start, 0, &insn);
+ if ( count ) {
+ size_t j;
+ for ( j=0; j<count; j++) {
+ //printf("Next instruction @ 0x%lx: %s!\n", insn[j].address, insn[j].mnemonic);
+
+ if ( is_cf(insn[j].id) )
+ {
+ next_cf = insn[j].address;
+
+ *int3 = (insn[j].id == X86_INS_INT3);
+
+ //printf("\tFound next control flow instruction @ 0x%lx: %s!\n", next_cf, insn[j].mnemonic);
+ break;
+ }
+ }
+ cs_free(insn, count);
+ }
+
+ return next_cf;
+}
+
+static event_response_t singlestep_cb(vmi_instance_t vmi, vmi_event_t *event)
+{
+ addr_t pa = (event->ss_event.gfn << 12) + event->ss_event.offset;
+ code_tracer_t *code_tracer = (code_tracer_t *)event->data;
+
+ track_coverage(code_tracer, pa, event->x86_regs->rip);
+
+ access_context_t ctx = {
+ .translate_mechanism = VMI_TM_PROCESS_DTB,
+ .dtb = event->x86_regs->cr3,
+ };
+
+ bool int3;
+ code_tracer->next_cf = next_cf_insn(vmi, event->x86_regs->cr3, event->x86_regs->rip, &int3);
+ vmi_pagetable_lookup(vmi, event->x86_regs->cr3, code_tracer->next_cf, &code_tracer->next_cf_pa);
+
+ if ( int3 || !code_tracer->next_cf || code_tracer->next_cf == event->interrupt_event.gla )
+ {
+ vmi_pause_vm(vmi);
+ *code_tracer->stop = true;
+ return VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP;
+ }
+
+ ctx.addr = code_tracer->next_cf;
+ vmi_read_8(vmi, &ctx, &code_tracer->backup);
+ vmi_write_8(vmi, &ctx, &bp);
+
+ return VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP;
+}
+
+static event_response_t int3_cb(vmi_instance_t vmi, vmi_event_t *event)
+{
+ addr_t pa = (event->interrupt_event.gfn << 12) + event->interrupt_event.offset;
+ code_tracer_t *code_tracer = (code_tracer_t *)event->data;
+
+ code_tracer->counter++;
+ track_coverage(code_tracer, pa, event->x86_regs->rip);
+
+ event->interrupt_event.reinject = 0;
+
+ if ( event->interrupt_event.gla != code_tracer->next_cf )
+ {
+ event->interrupt_event.reinject = 1;
+ vmi_pause_vm(vmi);
+ *code_tracer->stop = true;
+ return 0;
+ }
+
+ if ( code_tracer->counter >= CODE_TRACER_CF_LIMIT )
+ {
+ printf("[CODE_TRACER] Stopping after exceeding CF limit\n");
+ vmi_pause_vm(vmi);
+ *code_tracer->stop = true;
+ return 0;
+ }
+
+ access_context_t ctx = {
+ .translate_mechanism = VMI_TM_PROCESS_DTB,
+ .dtb = event->x86_regs->cr3,
+ };
+
+ ctx.addr = event->interrupt_event.gla;
+ vmi_write_8(vmi, &ctx, &code_tracer->backup);
+
+ return VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP;
+}
+
+code_tracer_t *code_tracer_setup(vmi_t *vmi, uint8_t *map, bool *stop)
+{
+ code_tracer_t *code_tracer = g_try_malloc0(sizeof(code_tracer_t));
+ if ( !code_tracer )
+ return NULL;
+
+ code_tracer->vmi = vmi;
+ code_tracer->map = map;
+ code_tracer->stop = stop;
+
+ if ( !enable_callback(vmi, BREAKPOINT, int3_cb, code_tracer) )
+ goto err;
+ if ( !enable_callback(vmi, SINGLESTEP, singlestep_cb, code_tracer) )
+ goto err;
+
+ return code_tracer;
+
+err:
+ g_free(code_tracer);
+ disable_callback(vmi, BREAKPOINT, int3_cb);
+ disable_callback(vmi, SINGLESTEP, singlestep_cb);
+ return NULL;
+}
+
+bool code_tracer_start(code_tracer_t *code_tracer, addr_t pagetable, addr_t start)
+{
+ vmi_instance_t vmi = code_tracer->vmi->libvmi;
+ bool int3 = false;
+
+ code_tracer->prev_loc = 0;
+ code_tracer->counter = 0;
+
+ if ( !(code_tracer->next_cf = next_cf_insn(vmi, pagetable, start, &int3)) || int3 )
+ {
+ printf("[CODE_TRACER] Skipping memory location because no more CF left\n");
+ return false;
+ }
+
+ if ( VMI_FAILURE == vmi_pagetable_lookup(vmi, pagetable, code_tracer->next_cf, &code_tracer->next_cf_pa) )
+ return false;
+ if ( VMI_FAILURE == vmi_read_8_pa(vmi, code_tracer->next_cf_pa, &code_tracer->backup) )
+ return false;
+ if ( VMI_FAILURE == vmi_write_8_pa(vmi, code_tracer->next_cf_pa, &bp) )
+ return false;
+
+ return true;
+}
+
+void code_tracer_close(code_tracer_t *code_tracer)
+{
+ if ( !code_tracer )
+ return;
+
+ disable_callback(code_tracer->vmi, BREAKPOINT, int3_cb);
+ disable_callback(code_tracer->vmi, SINGLESTEP, singlestep_cb);
+
+ if ( code_tracer->next_cf_pa )
+ vmi_write_8_pa(code_tracer->vmi->libvmi, code_tracer->next_cf_pa, &code_tracer->backup);
+
+ g_free(code_tracer);
+}
--- /dev/null
+#ifndef CODE_TRACER_H
+#define CODE_TRACER_H
+
+#define CODE_TRACER_BUFFER_SIZE 128ul
+#define CODE_TRACER_MAP_SIZE (1ul << 16) //64kb
+#define CODE_TRACER_CF_LIMIT 20ul
+
+typedef struct code_tracer code_tracer_t;
+typedef void (*code_tracer_callback_t)(code_tracer_t *code_tracer);
+
+typedef struct code_tracer {
+ // private
+ vmi_t *vmi;
+ code_tracer_callback_t cb; // optional callback to issue when a new path is triggered
+
+ bool *stop;
+
+ addr_t prev_loc;
+ addr_t next_cf;
+ addr_t next_cf_pa;
+ addr_t points;
+
+ uint8_t backup;
+
+ // public
+ uint8_t *map;
+
+ unsigned long counter;
+ int error;
+} code_tracer_t;
+
+code_tracer_t *code_tracer_setup(vmi_t *vmi, uint8_t *map, bool *stop);
+bool code_tracer_start(code_tracer_t *code_tracer, addr_t pagetable, addr_t start);
+void code_tracer_close(code_tracer_t *code_tracer);
+
+#endif
--- /dev/null
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "forkvm.h"
+
+extern xc_interface *xc;
+
+bool fork_vm(uint32_t domid, uint32_t vcpus, uint32_t *forkdomid)
+{
+ int rc;
+ struct xen_domctl_createdomain create = {0};
+ create.flags |= XEN_DOMCTL_CDF_hvm;
+ create.flags |= XEN_DOMCTL_CDF_hap;
+ create.flags |= XEN_DOMCTL_CDF_oos_off;
+ create.arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
+ create.ssidref = 11; // SECINITSID_DOMU
+ create.max_vcpus = vcpus;
+ create.max_evtchn_port = 1023;
+ create.max_grant_frames = LIBXL_MAX_GRANT_FRAMES_DEFAULT;
+ create.max_maptrack_frames = LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT;
+
+ if ( (rc = xc_domain_create(xc, forkdomid, &create)) )
+ return false;
+
+ if ( (rc = xc_memshr_fork(xc, domid, *forkdomid, true, true)) )
+ {
+ xc_domain_destroy(xc, *forkdomid);
+ return false;
+ }
+
+ return true;
+}
--- /dev/null
+#ifndef FORKVM_H
+#define FORKVM_H
+
+#define XC_WANT_COMPAT_EVTCHN_API 1
+#define XC_WANT_COMPAT_MAP_FOREIGN_API 1
+#include <xenctrl.h>
+#define LIBXL_API_VERSION 0x041300
+#include <libxl.h>
+
+bool fork_vm(uint32_t domid, uint32_t vcpus, uint32_t *fork_domid);
+
+#endif
--- /dev/null
+#include "private.h"
+
+static const char* exit_reason(int interrupt)
+{
+ if ( interrupt < 0 )
+ return "signal";
+ if ( interrupt == 0 )
+ return "timeout";
+
+ return "deadend";
+}
+
+static bool fuzz_iterate(vmi_t *vmi, uint32_t fork_domid, uint64_t pa, uint64_t *mem, bool *stop)
+{
+ if ( mem )
+ {
+ //uint8_t test[9] = "notbeef";
+ uint8_t test[9] = { [8] = '\0' };
+ memcpy(&test[0], mem, 8);
+ //printf("\t Starting test with '%s'\n ", test);
+
+ uint8_t *v = (uint8_t*)mem;
+ printf("\t Starting fuzz iteration with: %x%x %x%x %x%x %x%x",
+ v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+
+ if ( g_str_is_ascii((const gchar*)test) )
+ printf(": %s", test);
+ printf("\n");
+
+ if ( VMI_FAILURE == vmi_write_64_pa(vmi->libvmi, pa, (uint64_t*)mem) )
+ return false;
+ }
+
+ loop_exit_condition_t exit_cond = loop_vmi(vmi, 50, stop);
+
+ xc_dominfo_t info = { 0 };
+ xc_domain_getinfo(xc, fork_domid, 1, &info);
+
+ vmi_pagecache_flush(vmi->libvmi);
+
+ GTimer *timer = g_timer_new();
+ g_timer_start(timer);
+ xc_memshr_fork_reset(xc, fork_domid);
+ g_timer_stop(timer);
+
+ printf("\t - VM Fork memory used: %lu kbyte\n", info.nr_pages * 4096 / 1024);
+ printf("\t - Reset time: %f\n", g_timer_elapsed(timer, NULL));
+ printf("\t - Exit condition: %s\n", exit_cond_str[exit_cond]);
+ printf("\t -------------------\n");
+
+ g_timer_destroy(timer);
+ return true;
+}
+
+uint64_t create_fork_and_fuzz(uint64_t domid, uint32_t vcpus, uint8_t *map, GHashTable *values, addr_t pagetable, addr_t rip, addr_t pa, addr_t va)
+{
+ bool stop = false, fork_success;
+ uint32_t fork_domid = 0;
+ vmi_t *vmi = NULL;
+ code_tracer_t *code_tracer = NULL;
+
+ guint size = g_hash_table_size(values);
+
+ GTimer *timer = g_timer_new();
+ g_timer_start(timer);
+
+ fork_success = fork_vm(domid, vcpus, &fork_domid);
+
+ g_timer_stop(timer);
+ gdouble time = g_timer_elapsed(timer, NULL);
+ g_timer_destroy(timer);
+
+ if ( !fork_success )
+ goto done;
+
+ printf("[FUZZ] Fork: %u. Creation time: %f. Memory @ 0x%lx -> 0x%lx. CF: 0x%lx. Values: %u\n", fork_domid, time, va, pa, rip, size);
+
+ vmi = setup_vmi(NULL, fork_domid, NULL, true, false);
+ if ( !vmi )
+ goto done;
+
+ code_tracer = code_tracer_setup(vmi, map, &stop);
+ if ( !code_tracer )
+ goto done;
+
+ /* Record the baseline execution first without touching anything */
+ if ( !size )
+ {
+ printf("[FUZZ] Starting baseline execution to setup coverage map\n");
+
+ if ( code_tracer_start(code_tracer, pagetable, rip) )
+ fuzz_iterate(vmi, fork_domid, 0, NULL, &stop);
+
+ goto done;
+ }
+
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init (&iter, values);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ if ( !code_tracer_start(code_tracer, pagetable, rip) )
+ break;
+ if ( !fuzz_iterate(vmi, fork_domid, pa, key, &stop) )
+ break;
+ }
+
+done:
+ code_tracer_close(code_tracer);
+ close_vmi(vmi);
+
+ if ( fork_domid )
+ xc_domain_destroy(xc, fork_domid);
+
+ return 0;
+}
--- /dev/null
+#ifndef FUZZ_H
+#define FUZZ_H
+
+uint64_t create_fork_and_fuzz(uint64_t domid, uint32_t vcpus, uint8_t *map, GHashTable *values, addr_t cr3, addr_t rip, addr_t pa, addr_t va);
+
+#endif
--- /dev/null
+#include <stdio.h>
+
+#include "private.h"
+
+os_t os;
+bool manual, refresh;
+
+int interrupted;
+xc_interface *xc;
+csh cs_handle;
+int vcpus;
+
+static inline void usage(void)
+{
+ fprintf(stderr,
+ "-d/--domain VM name\n"
+ "-j/--json-path Path to VM's kernel json config file\n"
+ "-f/--fuzz Turn on memory fuzzing (off by default)\n"
+ "-m/--manual Perform manual memory fuzz\n"
+ );
+}
+
+int main(int argc, char** argv)
+{
+ int ret = -1, c, long_index = 0;
+ const struct option long_opts[] =
+ {
+ {"domain", required_argument, NULL, 'd'},
+ {"json-path", required_argument, NULL, 'j'},
+ {"fuzz", optional_argument, NULL, 'f'},
+ {"manual", optional_argument, NULL, 'm'},
+ {NULL, 0, NULL, 0}
+ };
+ const char* opts = "d:j:fm";
+
+ const char* domain = NULL, *json_path = NULL;
+ bool fuzz = false;
+ manual = false;
+
+ while ((c = getopt_long (argc, argv, opts, long_opts, &long_index)) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ domain = optarg;
+ break;
+ case 'j':
+ json_path = optarg;
+ break;
+ case 'f':
+ fuzz = true;
+ break;
+ case 'm':
+ manual = true;
+ break;
+ default:
+ break;
+ };
+ }
+
+ if ( !domain || !json_path )
+ {
+ usage();
+ return ret;
+ }
+
+ if ( !(xc = xc_interface_open(0,0,0)) )
+ goto done;
+
+ if ( cs_open(CS_ARCH_X86, CS_MODE_64, &cs_handle) )
+ goto done;
+
+ shredder(domain, json_path, fuzz, manual);
+ ret = 0;
+
+done:
+ cs_close(&cs_handle);
+ xc_interface_close(xc);
+
+ return ret;
+}
--- /dev/null
+#include "private.h"
+
+void mem_tracer_print_stats(mem_tracer_t *mem_tracer)
+{
+ GList *offsets = g_hash_table_get_keys(mem_tracer->pages);
+ offsets = g_list_sort(offsets, gint64_compare);
+ unsigned long r_total = 0, rw_total = 0;
+
+ GList *offset = offsets;
+ while ( offset )
+ {
+ addr_t offset_value = *(addr_t*)offset->data;
+ offset = offset->next;
+
+ page_access_log_t *page = g_hash_table_lookup(mem_tracer->pages, &offset_value);
+
+ printf("[0x%lx %03lx] R: %5lu, R/W: %5lu\n", offset_value >> 12, offset_value & 0xfff, page->r_count, page->rw_count);
+
+ page->log = g_list_reverse(page->log);
+
+ GList *loop = page->log;
+ while ( loop )
+ {
+ uint64_t *mem = (uint64_t*)loop->data;
+
+ uint8_t *before = (uint8_t*)&mem[0];
+ uint8_t *after = (uint8_t*)&mem[1];
+
+ uint8_t test[9] = { [8] = '\0' };
+ memcpy(&test[0], after, 8);
+
+ printf("\t%02x%02x %02x%02x %02x%02x %02x%02x\n", before[0], before[1], before[2], before[3], before[4], before[5], before[6], before[7]);
+ printf("\t%02x%02x %02x%02x %02x%02x %02x%02x", after[0], after[1], after[2], after[3], after[4], after[5], after[6], after[7]);
+
+
+ if ( g_str_is_ascii((const char*)&test) )
+ printf(" -> '%s'\n", test);
+
+ printf("\n\n");
+
+ g_free(loop->data);
+ loop = loop->next;
+ }
+
+ g_list_free(page->log);
+
+ r_total += page->r_count;
+ rw_total += page->rw_count;
+ }
+
+ g_list_free(offsets);
+
+ printf("--------------------------\n");
+ printf("Total R: %5lu Total R/W: %5lu\n", r_total, rw_total);
+ printf("--------------------------\n\n");
+
+ printf("Unique memaccess values: %u\n", g_hash_table_size(mem_tracer->values));
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, mem_tracer->values);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ uint8_t test[9] = { [8] = '\0' };
+ memcpy(&test[0], key, 8);
+
+ uint8_t *v = (uint8_t*)key;
+ printf("\t[%3d] %02x%02x %02x%02x %02x%02x %02x%02x", GPOINTER_TO_UINT(value), v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+
+ if ( g_str_is_ascii((const char*)&test) )
+ printf(" -> '%s'", test);
+
+ printf("\n");
+ }
+}
+
+static event_response_t mem_cb(vmi_instance_t vmi, vmi_event_t *event)
+{
+ mem_tracer_t *mem_tracer = event->data;
+ addr_t pa = (event->mem_event.gfn << 12) + event->mem_event.offset;
+ page_access_log_t *page;
+
+ if ( !(page = g_hash_table_lookup(mem_tracer->pages, &pa)) )
+ {
+ if ( !(page = g_try_malloc0(sizeof(page_access_log_t))) )
+ {
+ mem_tracer->error = -ENOMEM;
+ goto done;
+ }
+
+ page->pa = pa;
+ g_hash_table_insert(mem_tracer->pages, &page->pa, page);
+ }
+
+ mem_tracer->reset_pages = g_slist_prepend(mem_tracer->reset_pages, page);
+
+ if ( event->mem_event.out_access & VMI_MEMACCESS_W )
+ page->rw_count++;
+ else if ( event->mem_event.out_access & VMI_MEMACCESS_R )
+ page->r_count++;
+
+ uint64_t *mem = g_try_malloc0(2*sizeof(uint64_t));
+ if ( !mem )
+ {
+ mem_tracer->error = -ENOMEM;
+ goto done;
+ }
+
+ vmi_read_64_pa(vmi, page->pa, mem);
+ page->log = g_list_prepend(page->log, mem);
+
+ if ( mem_tracer->cb )
+ {
+ mem_tracer->event = event;
+ mem_tracer->cb(mem_tracer, page, event->mem_event.gla);
+ }
+
+ vmi_set_mem_event(vmi, pa >> 12, VMI_MEMACCESS_N, 0);
+
+done:
+ return VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP;
+}
+
+static event_response_t singlestep_cb(vmi_instance_t vmi, vmi_event_t *event)
+{
+ mem_tracer_t *mem_tracer = event->data;
+ GSList *loop = mem_tracer->reset_pages;
+ while (loop)
+ {
+ page_access_log_t *page = loop->data;
+
+ uint64_t *mem = page->log->data;
+ vmi_read_64_pa(vmi, page->pa, ++mem);
+
+ guint count = 0;
+ gpointer v = g_hash_table_lookup(mem_tracer->values, mem);
+ if ( v )
+ count = GPOINTER_TO_UINT(v);
+ count++;
+
+ g_hash_table_insert(mem_tracer->values, mem, GUINT_TO_POINTER(count));
+ vmi_set_mem_event(vmi, page->pa >> 12, VMI_MEMACCESS_RW, 0);
+ loop=loop->next;
+ }
+
+ g_slist_free(mem_tracer->reset_pages);
+ mem_tracer->reset_pages = NULL;
+
+ return VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP;
+}
+
+mem_tracer_t *mem_tracer_setup(vmi_t *vmi, mem_tracer_callback_t cb, void *data)
+{
+ mem_tracer_t *mem_tracer = g_try_malloc0(sizeof(mem_tracer_t));
+ if ( !mem_tracer )
+ return NULL;
+
+ mem_tracer->pages = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free);
+ if ( !mem_tracer->pages )
+ goto err;
+
+ mem_tracer->values = g_hash_table_new(g_int64_hash, g_int64_equal);
+ if ( !mem_tracer->values )
+ goto err;
+
+ mem_tracer->vmi = vmi;
+ mem_tracer->cb = cb;
+ mem_tracer->data = data;
+
+ if ( !enable_callback(vmi, MEMACCESS, mem_cb, mem_tracer) )
+ goto err;
+ if ( !enable_callback(vmi, SINGLESTEP, singlestep_cb, mem_tracer) )
+ goto err;
+
+ return mem_tracer;
+
+err:
+ mem_tracer_close(mem_tracer, false);
+ return NULL;
+}
+
+void mem_tracer_trap_user_pages(mem_tracer_t *mem_tracer, addr_t pagetable)
+{
+ /*
+ * Set memory permissions on process and start memory listener
+ */
+ vmi_instance_t vmi = mem_tracer->vmi->libvmi;
+ GSList *page_list = vmi_get_va_pages(vmi, pagetable);
+ printf("Got %u pages in target process' memory at 0x%lx\n", g_slist_length(page_list), pagetable);
+
+ GSList *loop = page_list;
+ while ( loop )
+ {
+ page_info_t *info = (page_info_t*)loop->data;
+ loop = loop->next;
+
+ if ( !USER_SUPERVISOR(info->x86_ia32e.pte_value) )
+ continue;
+
+ printf("%lx -> %lx\n", info->vaddr, info->paddr);
+
+ //if ( READ_WRITE(info->x86_ia32e.pte_value) )
+ {
+ vmi_set_mem_event(vmi, info->paddr >> 12, VMI_MEMACCESS_RW, 0);
+ }
+ }
+
+ g_slist_free_full(page_list, (GDestroyNotify)g_free);
+}
+
+void mem_tracer_close(mem_tracer_t *mem_tracer, bool remove_traps)
+{
+ if ( !mem_tracer )
+ return;
+
+ disable_callback(mem_tracer->vmi, MEMACCESS, mem_cb);
+ disable_callback(mem_tracer->vmi, SINGLESTEP, singlestep_cb);
+
+ if ( remove_traps && mem_tracer->pages )
+ {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, mem_tracer->pages);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ page_access_log_t *page = value;
+ vmi_set_mem_event(mem_tracer->vmi->libvmi, page->pa >> 12, VMI_MEMACCESS_N, 0);
+ }
+ }
+
+ if ( mem_tracer->pages )
+ g_hash_table_destroy(mem_tracer->pages);
+ if ( mem_tracer->values )
+ g_hash_table_destroy(mem_tracer->values);
+ g_free(mem_tracer);
+}
--- /dev/null
+#ifndef MEM_TRACER_H
+#define MEM_TRACER_H
+
+typedef struct page_access_log page_access_log_t;
+typedef struct mem_tracer mem_tracer_t;
+typedef void (*mem_tracer_callback_t)(mem_tracer_t *mem_tracer, page_access_log_t *page, addr_t va);
+
+typedef struct page_access_log {
+ addr_t pa;
+ unsigned long r_count;
+ unsigned long rw_count;
+ GList *log; // list of two-dim arrays, with pre/post access values
+} page_access_log_t;
+
+typedef struct mem_tracer {
+ // private
+ event_callback_t mem_cb;
+ GSList *reset_pages; // list of page_access_log_t
+
+ vmi_t *vmi;
+ mem_tracer_callback_t cb; // optional callback to issue when a page is accessed
+ vmi_event_t *event; // the currently active event
+ void *data;
+ bool *stop;
+
+ // public
+ GHashTable *pages; // table of page_access_log_t, key: pa
+ GHashTable *values; // table of access counts, key: value
+ int error;
+} mem_tracer_t;
+
+/*
+ * Setup the mem_tracer infrastructure, initialize and register events.
+ * Callback is optional, issued before a memory page is accessed
+ */
+mem_tracer_t *mem_tracer_setup(vmi_t *vmi, mem_tracer_callback_t cb, void* data);
+
+/*
+ * Change EPT access permissions on user pages in target pagetable
+ */
+void mem_tracer_trap_user_pages(mem_tracer_t *mem_tracer, addr_t pagetable);
+
+/*
+ * Print out collected memaccess values and location information
+ */
+void mem_tracer_print_stats(mem_tracer_t *mem_tracer);
+
+/*
+ * Free mem_tracer infrastructure, optionally reset EPT access permissions
+ */
+void mem_tracer_close(mem_tracer_t *mem_tracer, bool remove_traps);
+
+#endif
--- /dev/null
+#ifndef PRIVATE_H
+#define PRIVATE_H
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#define LIBVMI_EXTRA_GLIB
+#define LIBVMI_EXTRA_JSON
+#include <libvmi/libvmi.h>
+#include <libvmi/events.h>
+#include <libvmi/libvmi_extra.h>
+#include <libvmi/x86.h>
+#include <libvmi/slat.h>
+
+#include <glib.h>
+
+#define XC_WANT_COMPAT_EVTCHN_API 1
+#define XC_WANT_COMPAT_MAP_FOREIGN_API 1
+#include <xenctrl.h>
+#define LIBXL_API_VERSION 0x041300
+#include <libxl.h>
+
+#include <capstone.h>
+
+#include "signal.h"
+#include "vmi.h"
+#include "shredder.h"
+#include "fuzz.h"
+#include "mem_tracer.h"
+#include "code_tracer.h"
+#include "forkvm.h"
+
+extern os_t os;
+extern xc_interface *xc;
+extern csh cs_handle;
+extern int interrupted;
+extern bool manual, refresh;
+
+gint gint64_compare(gconstpointer ptr_a, gconstpointer ptr_b);
+
+#endif
--- /dev/null
+#include "private.h"
+
+typedef struct shredder {
+ GHashTable *processes;
+ vmi_pid_t target_pid;
+ addr_t target_cr3;
+ addr_t magic_string;
+
+ bool manual;
+ bool harness_bp;
+ bool stop;
+
+ vmi_t *vmi;
+ mem_tracer_t *mem_tracer;
+
+ uint8_t *map;
+} shredder_t;
+
+gint gint64_compare(gconstpointer ptr_a, gconstpointer ptr_b)
+{
+ gint64 a = *(gint64*)ptr_a;
+ gint64 b = *(gint64*)ptr_b;
+
+ if ( a > b )
+ return 1;
+ if ( a == b )
+ return 0;
+ return -1;
+}
+
+static void find_existing_processes(shredder_t *shredder)
+{
+ vmi_instance_t vmi = shredder->vmi->libvmi;
+ GHashTable *processes = shredder->processes;
+
+ addr_t current_list_entry = 0, next_list_entry = 0, start_entry = 0;
+ addr_t linkedlist_offset, name_offset, pid_offset;
+ bool looped = 0;
+
+ if ( VMI_OS_LINUX == os )
+ {
+ if ( VMI_FAILURE == vmi_get_kernel_struct_offset(vmi, "task_struct", "tasks", &linkedlist_offset) )
+ return;
+ if ( VMI_FAILURE == vmi_get_kernel_struct_offset(vmi, "task_struct", "comm", &name_offset) )
+ return;
+ if ( VMI_FAILURE == vmi_get_kernel_struct_offset(vmi, "task_struct", "pid", &pid_offset) )
+ return;
+ if ( VMI_FAILURE == vmi_translate_ksym2v(vmi, "init_task", &next_list_entry) )
+ return;
+
+ next_list_entry += linkedlist_offset;
+ start_entry = next_list_entry;
+ }
+
+ do {
+
+ if ( !next_list_entry )
+ return;
+
+ if ( next_list_entry == start_entry && looped )
+ return;
+
+ current_list_entry = next_list_entry;
+ looped = true;
+
+ addr_t process = current_list_entry - linkedlist_offset;
+
+ if (VMI_FAILURE == vmi_read_addr_va(vmi, current_list_entry, 0, &next_list_entry))
+ return;
+
+ uint16_t pid;
+ if ( VMI_FAILURE == vmi_read_16_va(vmi, process + pid_offset, 0, &pid) )
+ continue;
+
+ gint *_pid = g_try_malloc0(sizeof(gint));
+ *_pid = pid;
+
+ if ( !g_hash_table_insert(processes, _pid, GSIZE_TO_POINTER(1)) )
+ return;
+
+#if 0
+ char *name = vmi_read_str_va(vmi, process + name_offset, 0);
+ printf("Found process [%u] %s\n", pid, name);
+ free(name);
+#endif
+ } while ( 1 );
+}
+
+static event_response_t int3_cb(vmi_instance_t vmi, vmi_event_t *event)
+{
+ shredder_t *shredder = event->data;
+
+ gint pid;
+ event->interrupt_event.reinject = 1;
+
+ if ( VMI_FAILURE == vmi_dtb_to_pid(vmi, event->x86_regs->cr3, (vmi_pid_t*)&pid) )
+ {
+ printf("Can't find PID for CR3: 0x%lx\n", event->x86_regs->cr3);
+ return 0;
+ }
+
+ if ( !shredder->target_pid )
+ {
+ shredder->target_pid = pid;
+ shredder->target_cr3 = event->x86_regs->cr3;
+ }
+
+ printf("INT3 in target pid! [%u] CR3: 0x%lx. SLAT: %u\n", pid, event->x86_regs->cr3, event->slat_id);
+
+ event_response_t rsp = VMI_EVENT_RESPONSE_SET_REGISTERS;
+ event->interrupt_event.reinject = 0;
+ event->x86_regs->rip += event->interrupt_event.insn_length;
+
+ vmi_pause_vm(vmi);
+ shredder->stop = true;
+
+ if ( !shredder->harness_bp && shredder->manual )
+ {
+ printf("Enter magic string location: \n");
+ char word[256];
+ char *w = fgets(word, sizeof(word), stdin);
+ shredder->magic_string = strtoull(w, NULL, 0);
+ shredder->harness_bp = true;
+ }
+
+ return rsp;
+}
+
+void mem_tracer_cb(mem_tracer_t *mem_tracer, page_access_log_t *page, addr_t va)
+{
+ shredder_t *shredder = mem_tracer->data;
+
+ vmi_instance_t vmi = mem_tracer->vmi->libvmi;
+ vmi_event_t *event = mem_tracer->event;
+
+ if ( shredder->manual )
+ {
+ if ( shredder->magic_string != va )
+ return;
+
+ printf("Enter magic string: \n");
+ char word[256], magic_string[8];
+ char *w = fgets(word, sizeof(word), stdin);
+
+ memcpy(&magic_string, w, 8);
+
+ GHashTable *values = g_hash_table_new(g_int64_hash, g_int64_equal);
+ g_hash_table_insert(values, &magic_string, GUINT_TO_POINTER(1));
+ create_fork_and_fuzz(vmi_get_vmid(vmi), vmi_get_num_vcpus(vmi), shredder->map, values, event->x86_regs->cr3, event->x86_regs->rip, page->pa, va);
+ g_hash_table_destroy(values);
+ return;
+ }
+
+ create_fork_and_fuzz(vmi_get_vmid(vmi), vmi_get_num_vcpus(vmi), shredder->map, mem_tracer->values, event->x86_regs->cr3, event->x86_regs->rip, page->pa, va);
+ return;
+}
+
+bool shredder(const char *domain, const char* json_path, bool fuzz, bool manual)
+{
+ shredder_t shredder = {0};
+ vmi_t *vmi = NULL;
+ vmi_instance_t libvmi = NULL;
+
+ if ( !(shredder.map = g_try_malloc0(CODE_TRACER_MAP_SIZE)) )
+ goto exit;
+
+ if ( !(shredder.vmi = setup_vmi(domain, 0, json_path, true, true)) )
+ goto exit;
+
+ shredder.manual = manual;
+ vmi = shredder.vmi;
+ libvmi = vmi->libvmi;
+
+ setup_signals();
+ vmi_pause_vm(libvmi);
+
+ printf("Waiting for harness int3\n");
+
+ if ( !enable_callback(vmi, BREAKPOINT, int3_cb, &shredder) )
+ goto exit;
+
+ if ( loop_vmi(vmi, ~0, &shredder.stop) != STOP )
+ goto exit;
+
+ shredder.mem_tracer = mem_tracer_setup(vmi, fuzz ? mem_tracer_cb : NULL, &shredder);
+ if ( !shredder.mem_tracer )
+ goto exit;
+
+ mem_tracer_trap_user_pages(shredder.mem_tracer, shredder.target_cr3);
+
+ if ( loop_vmi(vmi, ~0, &shredder.stop) != STOP )
+ goto exit;
+
+ if ( !fuzz )
+ mem_tracer_print_stats(shredder.mem_tracer);
+
+ vmi_resume_vm(libvmi);
+
+exit:
+ if ( shredder.processes )
+ g_hash_table_destroy(shredder.processes);
+ if ( shredder.map )
+ g_free(shredder.map);
+
+ mem_tracer_close(shredder.mem_tracer, true);
+ disable_callback(vmi, BREAKPOINT, int3_cb);
+ close_vmi(vmi);
+ return true;
+}
--- /dev/null
+#ifndef SHREDDER_H
+#define SHREDDER_H
+
+bool shredder(const char *domain, const char *json_path, bool fuzz, bool manual);
+
+#endif
--- /dev/null
+#include "private.h"
+
+static struct sigaction act;
+
+static void close_handler(int sig)
+{
+ printf("Interrupted: %i\n", -sig);
+ interrupted = -sig;
+}
+
+void setup_signals(void)
+{
+ act.sa_handler = close_handler;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGALRM, &act, NULL);
+}
--- /dev/null
+#ifndef SIGNAL_H
+#define SIGNAL_H
+
+void setup_signals();
+
+#endif
--- /dev/null
+#include "private.h"
+
+static event_response_t mem_cb(vmi_instance_t libvmi, vmi_event_t *event)
+{
+ event_response_t rsp = 0;
+ vmi_t *vmi = event->data;
+ GSList *loop = vmi->event[MEMACCESS].cb;
+
+ while (loop)
+ {
+ vmi_callback_t *cb = loop->data;
+ event->data = cb->data;
+ rsp |= cb->cb(libvmi, event);
+ loop = loop->next;
+ }
+
+ if ( rsp & VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP )
+ {
+ /* If we already have singlestep on, don't disable it */
+ if ( vmi->singlestep )
+ rsp &= ~VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP;
+ else
+ vmi->singlestep = true;
+ }
+
+ event->data = vmi;
+ return rsp;
+}
+
+static event_response_t int3_cb(vmi_instance_t libvmi, vmi_event_t *event)
+{
+ event_response_t rsp = 0;
+ vmi_t *vmi = event->data;
+ GSList *loop = vmi->event[BREAKPOINT].cb;
+
+ while (loop)
+ {
+ vmi_callback_t *cb = loop->data;
+ event->data = cb->data;
+ rsp |= cb->cb(libvmi, event);
+ loop = loop->next;
+ }
+
+ if ( rsp & VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP )
+ {
+ /* If we already have singlestep on, don't disable it */
+ if ( vmi->singlestep )
+ rsp &= ~VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP;
+ else
+ vmi->singlestep = true;
+ }
+
+ event->data = vmi;
+ return rsp;
+}
+
+static event_response_t cr3_cb(vmi_instance_t libvmi, vmi_event_t *event)
+{
+ event_response_t rsp = 0;
+ vmi_t *vmi = event->data;
+ GSList *loop = vmi->event[CR3WRITE].cb;
+
+ while (loop)
+ {
+ vmi_callback_t *scb = loop->data;
+ event->data = scb->data;
+ rsp |= scb->cb(libvmi, event);
+ loop = loop->next;
+ }
+
+ event->data = vmi;
+ return rsp;
+}
+
+static event_response_t singlestep_cb(vmi_instance_t libvmi, vmi_event_t *event)
+{
+ event_response_t rsp = 0;
+ vmi_t *vmi = event->data;
+ GSList *loop = vmi->event[SINGLESTEP].cb;
+
+ while (loop)
+ {
+ vmi_callback_t *cb = loop->data;
+ event->data = cb->data;
+ rsp |= cb->cb(libvmi, event);
+ loop = loop->next;
+ }
+
+ if ( rsp & VMI_EVENT_RESPONSE_TOGGLE_SINGLESTEP )
+ vmi->singlestep = false;
+
+ event->data = vmi;
+ return rsp;
+}
+
+vmi_t *setup_vmi(const char* domain, uint64_t domid, const char* json_path, bool init_events, bool init_os)
+{
+ //fprintf(stderr, "Init vmi, events: %i domain %s domid %lu\n", init_events, domain, domid);
+ vmi_t *vmi = g_try_malloc0(sizeof(vmi_t));
+ if ( !vmi )
+ return NULL;
+
+ vmi_mode_t mode = (init_events ? VMI_INIT_EVENTS : 0) | (domain ? VMI_INIT_DOMAINNAME : VMI_INIT_DOMAINID);
+ const void *d = domain?:(void*)&domid;
+
+ if ( VMI_FAILURE == vmi_init(&vmi->libvmi, VMI_XEN, d, mode, NULL, NULL) )
+ goto err;
+
+ vmi_instance_t libvmi = vmi->libvmi;
+
+ if ( init_os )
+ {
+ os = vmi_init_os(libvmi, VMI_CONFIG_JSON_PATH, (void*)json_path, NULL);
+
+ if ( VMI_OS_UNKNOWN == os )
+ goto err;
+ }
+ else
+ {
+ if ( VMI_PM_UNKNOWN == vmi_init_paging(libvmi, os == VMI_OS_WINDOWS ? VMI_PM_INITFLAG_TRANSITION_PAGES : 0) )
+ goto err;
+ }
+
+ if ( init_events )
+ {
+ SETUP_MEM_EVENT(&vmi->event[MEMACCESS].handler, ~0ULL, VMI_MEMACCESS_RWX, mem_cb, true);
+ SETUP_SINGLESTEP_EVENT(&vmi->event[SINGLESTEP].handler, ~0U, singlestep_cb, false);
+ SETUP_REG_EVENT(&vmi->event[CR3WRITE].handler, CR3, VMI_REGACCESS_W, 0, cr3_cb);
+ SETUP_INTERRUPT_EVENT(&vmi->event[BREAKPOINT].handler, int3_cb);
+
+ vmi->event[MEMACCESS].handler.data = vmi;
+ vmi->event[SINGLESTEP].handler.data = vmi;
+ vmi->event[CR3WRITE].handler.data = vmi;
+ vmi->event[BREAKPOINT].handler.data = vmi;
+ }
+
+ return vmi;
+
+err:
+ close_vmi(vmi);
+ return NULL;
+}
+
+void close_vmi(vmi_t *vmi)
+{
+ if ( !vmi )
+ return;
+
+ int i;
+ for ( i = 0; i < __EVENT_TYPE_MAX; i++)
+ {
+ GSList *cb = vmi->event[i].cb;
+ if ( cb )
+ g_slist_free_full(cb, (GDestroyNotify)g_free);
+ }
+
+ vmi_destroy(vmi->libvmi);
+ g_free(vmi);
+}
+
+loop_exit_condition_t loop_vmi(vmi_t *vmi, unsigned int timeout, bool *stop)
+{
+ loop_exit_condition_t ret = ERROR;
+ if ( !vmi )
+ return ret;
+
+ bool _s;
+ bool *_stop = stop ?: &_s;
+ *_stop = false;
+
+ vmi_instance_t libvmi = vmi->libvmi;
+ vmi_resume_vm(libvmi);
+
+ while (!interrupted && timeout-- && !*_stop)
+ {
+ if ( vmi_events_listen(libvmi, 100) == VMI_FAILURE )
+ {
+ fprintf(stderr, "Error in vmi_events_listen!\n");
+ break;
+ }
+ }
+
+ if ( !stop )
+ vmi_pause_vm(libvmi);
+ vmi_events_listen(libvmi, 0);
+
+ if ( interrupted < 0 )
+ ret = SIGNAL;
+ else if ( !timeout )
+ ret = TIMEOUT;
+ else if ( stop )
+ ret = STOP;
+ else
+ ret = ERROR;
+
+ interrupted = 0;
+
+ return ret;
+}
+
+static bool toggle_event(vmi_t *vmi, enum event_types type)
+{
+ bool ret;
+ if ( vmi->event[type].enabled )
+ ret = VMI_SUCCESS == vmi_clear_event(vmi->libvmi, &vmi->event[type].handler, NULL);
+ else
+ ret = VMI_SUCCESS == vmi_register_event(vmi->libvmi, &vmi->event[type].handler);
+
+ if ( ret )
+ vmi->event[type].enabled = !vmi->event[type].enabled;
+
+ return ret;
+}
+
+bool enable_callback(vmi_t *vmi, enum event_types type, event_callback_t cb, void *data)
+{
+ if ( !vmi )
+ return false;
+
+ vmi_callback_t *scb = g_try_malloc0(sizeof(vmi_callback_t));
+ if ( !scb )
+ return false;
+
+ scb->cb = cb;
+ scb->data = data;
+
+ if ( !vmi->event[type].enabled && !toggle_event(vmi, type) )
+ {
+ g_free(scb);
+ return false;
+ }
+
+ vmi->event[type].cb = g_slist_prepend(vmi->event[type].cb, scb);
+ return true;
+}
+
+bool disable_callback(vmi_t *vmi, enum event_types type, event_callback_t cb)
+{
+ if ( !vmi )
+ return false;
+
+ if ( !vmi->event[type].enabled )
+ return true;
+
+ GSList *loop = vmi->event[type].cb;
+ while (loop)
+ {
+ vmi_callback_t *scb = loop->data;
+ if ( scb->cb == cb )
+ break;
+ loop = loop->next;
+ }
+
+ if ( loop )
+ vmi->event[type].cb = g_slist_remove(vmi->event[type].cb, loop->data);
+
+ if ( !vmi->event[type].cb )
+ return toggle_event(vmi, type);
+
+ return true;
+}
--- /dev/null
+#ifndef VMI_H
+#define VMI_H
+
+typedef enum event_types {
+ MEMACCESS,
+ SINGLESTEP,
+ BREAKPOINT,
+ CR3WRITE,
+ __EVENT_TYPE_MAX
+} event_types_t;
+
+typedef struct {
+ void *data;
+ event_callback_t cb;
+} vmi_callback_t;
+
+typedef struct {
+ vmi_instance_t libvmi;
+
+ struct {
+ bool enabled;
+ vmi_event_t handler;
+ GSList *cb; // list of vmi_callback_t
+ } event[__EVENT_TYPE_MAX];
+
+ bool singlestep;
+ int interrupted;
+ int error;
+} vmi_t;
+
+vmi_t *setup_vmi(const char* domain, uint64_t domid, const char* json_path, bool init_events, bool init_os);
+void close_vmi(vmi_t *vmi);
+
+typedef enum loop_exit_condition {
+ SIGNAL,
+ ERROR,
+ TIMEOUT,
+ STOP
+} loop_exit_condition_t;
+
+static const char *exit_cond_str[] = {
+ [SIGNAL] = "Signal",
+ [ERROR] = "Error",
+ [TIMEOUT] = "Timeout",
+ [STOP] = "Stop"
+};
+
+loop_exit_condition_t loop_vmi(vmi_t *vmi, unsigned int timeout, bool *stop);
+
+bool enable_callback(vmi_t *vmi, event_types_t type, event_callback_t cb, void *data);
+bool disable_callback(vmi_t *vmi, event_types_t type, event_callback_t cb);
+
+#endif