This transformation translates a function F into a new function F' consisting of a sequence of intermediate code instructions such that, when F' is executed, F will be dynamically compiled to machine code. I.e., this is an example of runtime code generation, or just-in-time compilation, or dynamic unpacking.

For example, the first few lines of a jitted function may look like this:

int fac(int x ) { 
   ...
    p3 = jit_init();
    jit_enable_optimization(p3, 3L);
    label4 = jit_get_label(p3);
    jit_add_prolog(p3, & _3_obf_int_binary_I___foo, 0);
    localSize6 = jit_allocai(p3, 8L);
    jit_add_op(p3, JIT_DECL_ARG, ((0 << 4) | (2 << 2)) | 2, 0, 4, 0L, 0L, 0);
    jit_add_op(p3, JIT_DECL_ARG, ((0 << 4) | (2 << 2)) | 2, 0, 4, 0L, 0L, 0);
    jit_add_op(p3, JIT_ADD | 2, ((2 << 4) | (1 << 2)) | 3, 0 | ((0 << 1) | (1 << 4)), 0 | ((2 << 1) | (0 << 4)), (jit_value )(localSize6 + 4), 0L, 0);
    jit_add_op(p3, JIT_GETARG, ((0 << 4) | (2 << 2)) | 3, 0 | ((0 << 1) | (2 << 4)), 1L, 0L, 0L, 0);
    jit_add_op(p3, JIT_ST | 1, ((0 << 4) | (1 << 2)) | 1, 0 | ((0 << 1) | (1 << 4)), 0 | ((0 << 1) | (2 << 4)), 0L, 4, 0);
    jit_add_op(p3, JIT_ADD | 2, ((2 << 4) | (1 << 2)) | 3, 0 | ((0 << 1) | (3 << 4)), 0 | ((2 << 1) | (0 << 4)), (jit_value )(localSize6 + 0), 0L, 0);
    jit_add_op(p3, JIT_GETARG, ((0 << 4) | (2 << 2)) | 3, 0 | ((0 << 1) | (4 << 4)), 0L, 0L, 0L, 0);
    jit_add_op(p3, JIT_ST | 1, ((0 << 4) | (1 << 2)) | 1, 0 | ((0 << 1) | (3 << 4)), 0 | ((0 << 1) | (4 << 4)), 0L, 4, 0);
   ...
   jit_generate_code(p6);
   ...
   result58 = (*fac_foo)(x);
   return (result58);
}
 

Diversity

Intermediate code operators (the JIT_MOV, JIT_EQ, etc. in the example above) are randomized every time Tigress is invoked. Thus, the statement
op11 = jit_add_op(p6, JIT_JMP | 2, ((0 << 4) | (0 << 2)) | 2, 0, 0L, 0L, 0L);
may be compiled into
op11 = jit_add_op(p6, 42, 2, 0, 0L, 0L, 0L);
where "42" will be a different literal for every invocation.

There is no diversity in the dynamically generated code at this point; i.e., every time the program is run, the same code is generated.  

Usage

In order to invoke this feature you must add one of the following directives to the top of your C file (adjusting the path depending on which version of Tigress you have installed):
#include "PATH_TO_TIGRESS/bin/tigress-unstable/jitter-i386.c"
#include "PATH_TO_TIGRESS/bin/tigress-unstable/jitter-amd64.c"
#include "PATH_TO_TIGRESS/bin/tigress-unstable/jitter-sparc.c"
or, if you're using clang (not supported by gcc), run Tigress with
tigress -include $TIGRESS_HOME/jitter-amd64.c --Transform=Jit ...
or, if you're on Darwin,
tigress -include $TIGRESS_HOME/apple.h -include $TIGRESS_HOME/jitter-amd64.c --Transform=Jit ...

If you set the option --JitFrequency=n for n>0, the function will be jitted every n:th time it is called. This means that every n:th time the function is called, its address trace (but not its instruction trace) will change. With --JitFrequency=0, the function will only be jitted the first time it is called.

To disrupt dynamic taint analysis, the option --JitImplicitFlow inserts implicit flow between where the function gets generated, and where it is invoked:

int fac(int x ) { 
  ...
  fac_foo1 = IMPLICIT_FLOW(fac_foo);
  result58 = (*fac_foo1)(x);
  return (result58);
}

As usual, this transformation needs to be combined with others to break up the predictable static structure. The Split and Virtualize transformations are particularly appropriate.  

Options

OptionArgumentsDescription
--Transform Jit Turn a function into a sequence of instructions that dynamically builds up the function at runtime.
--JitEncoding hard, soft How the jitted instructions are encoded. Default=hard.
  • hard = The jitted instructions are encoded as code.
  • soft = The jitted instructions are encoded as data (not implemented).
--JitFrequency INTSPEC How often to jit the code at runtime. 0=only the first time; n>0=Every n:th time the function is called. Default=0.
--JitOptimizeBinary INTSPEC Optimize the jitted binary code. 1=omit frame pointer, 2=omit unused assignments, 4=merge ADDs and MULs. Default=1|4=5.
--JitImplicitFlowHandle BOOLSPEC Insert implicit flow to the generated function handle. Default=false.
--JitImplicitFlow S-Expression The type of implicit flow to insert. See --AntiTaintAnalysisImplicitFlow for a description. Default=none.
--JitObfuscateHandle BOOLSPEC Add an opaque predicate to the generated function handle. Default=false.
--JitObfuscateArguments BOOLSPEC Add bogus arguments and opaque predicates to the jit_add_op function calls. Default=false.
--JitDumpOpcodes INTSPEC Print the jitter's bytecode. OR the numeric arguments together, or 0 for no dumping. Default=0.
  • 0x01 = JIT_DEBUG_OPS
  • 0x02 = JIT_DEBUG_CODE
  • 0x04 = JIT_DEBUG_COMBINED
  • 0x08 = JIT_DEBUG_COMPILABLE
  • 0x100 = JIT_DEBUG_LOADS
  • 0x200 = JIT_DEBUG_ASSOC
  • 0x400 = JIT_DEBUG_LIVENESS
--JitTrace INTSPEC Insert runtime tracing of instructions. Set to 1 to turn it on. Default=0.
--JitTraceExec BOOLSPEC Annotate each instruction, showing from where it was generated, and the results of execution. Default=false.
--JitDumpTree BOOLSPEC Print the tree representation of the function, prior to generating the jitting code." Default=false.
--JitAnnotateTree BOOLSPEC Annotate the generated code with the corresponding intermediate tree code instructions." Default=false.
--JitDumpIntermediate BOOLSPEC Print the generated intermediate code at translation time." Default=false.
--JitDumpBinary BOOLSPEC Print the generated machine code. Requires 'objdump' to be installed. Default=false.
--JitRandomizeBlocks BOOLSPEC Randomize the order of basic blocks Default=true.
 

Issues

 

Permanent Issues

These are issues with jitted code that will probably never be resolved:  

Acknowledgments

This feature of Tigress is based on the MyJit library by Petr Krajča. We are indebted to Petr for his hard work modifying MyJit to fit our needs.