From 1e56f32b3c83426628041680fd11910f0c9882ef Mon Sep 17 00:00:00 2001 From: Matthew Francis-Landau Date: Fri, 8 Jul 2016 19:58:16 -0700 Subject: [PATCH] starting a simple compiler interface which wraps asmjit --- .gitmodules | 3 + deps/asmjit | 1 + deps/fabricate/fabricate.py | 8 ++- make | 25 +++++++-- src/asm_macros.S | 6 +- src/asm_snippets.S | 3 - src/constants.h | 2 + src/jit_internal.h | 3 + src/simple_compiler.cc | 108 ++++++++++++++++++++++++++++++++++++ src/simple_compiler.h | 91 ++++++++++++++++++++++++++++++ src/tracer.cc | 101 ++++++++++++++++++--------------- src/tracer.h | 5 +- 12 files changed, 295 insertions(+), 61 deletions(-) create mode 160000 deps/asmjit create mode 100644 src/constants.h create mode 100644 src/simple_compiler.cc create mode 100644 src/simple_compiler.h diff --git a/.gitmodules b/.gitmodules index 26101bb..16f81e4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "deps/udis86"] path = deps/udis86 url = https://github.com/vmt/udis86.git +[submodule "deps/asmjit"] + path = deps/asmjit + url = https://github.com/kobalicek/asmjit.git diff --git a/deps/asmjit b/deps/asmjit new file mode 160000 index 0000000..c908c3d --- /dev/null +++ b/deps/asmjit @@ -0,0 +1 @@ +Subproject commit c908c3db8dc41e6958db5db04431bb95f63ddb03 diff --git a/deps/fabricate/fabricate.py b/deps/fabricate/fabricate.py index e1db73e..ce2caf8 100644 --- a/deps/fabricate/fabricate.py +++ b/deps/fabricate/fabricate.py @@ -140,8 +140,12 @@ def relative_to_abs_paths(args): ret.append(' '.join(s)) return ret -def Shell(args, **kwargs): - return shell(*shlex.split(args), **kwargs) +def Shell(args, silent=False, **kwargs): + #if silent: + return shell(*shlex.split(args), silent=silent, **kwargs) + # import ipdb; ipdb.set_trace(); + # pwd = os.path.abspath('.') + # return run('sh', '-c', 'cd {} && {}'.format(pwd, args)) def shell(*args, **kwargs): r""" Run a command: program name is given in first arg and command line diff --git a/make b/make index fcaf3c8..ca2a0ed 100755 --- a/make +++ b/make @@ -21,6 +21,7 @@ CXX_FLAGS = ( '-ggdb ' '-O0 ' '-I ./deps/udis86 ' + '-I ./deps/asmjit/src ' ) CXX_FLAGS_UNIT = ( '-I ./deps/catch/ ' @@ -36,6 +37,7 @@ LD_FLAGS = '' CXX='g++' CC='gcc' LD='g++' +RELEASE = False def build(): deps() @@ -43,11 +45,13 @@ def build(): link() def release(): - global CXX_FLAGS, LD_FLAGS + global CXX_FLAGS, LD_FLAGS, RELEASE + RELEASE = True CXX_FLAGS = CXX_FLAGS.replace('-O0', '-O2') CXX_FLAGS = CXX_FLAGS.replace('-ggdb', '') CXX_FLAGS += ' -DNDEBUG -fdata-sections -ffunction-sections -flto ' - LD_FLAGS += '-flto ' + LD_FLAGS += '-flto ' #-Wl,--gc-sections -Wl,--print-gc-sections ' + clean() build() Run('mkdir -p release') Run('cp build/libredmagic.so.1.0.0 release/') @@ -58,6 +62,7 @@ def release(): def clean(): autoclean() Shell('cd deps/udis86 && make clean', shell=True) + Shell('rm -rf build/asmjit') def run(): build() @@ -75,7 +80,9 @@ def link(): # **dict(globals(), **locals()) # )) udis_libs = ' '.join(glob.glob('deps/udis86/libudis86/.libs/*.o')) - Run('{LD} {LD_FLAGS} -shared -fPIC -Wl,-soname,libredmagic.so.1.0.0 -o build/libredmagic.so.1.0.0 {objs} {udis_libs} {LIBS}'.format( + # we are not using the compiler interface, just the assembler, would be nice if we could strip all the functions + asmjit_libs = ' '.join(filter(lambda x: 'compiler' not in x, glob.glob('build/asmjit/CMakeFiles/asmjit.dir/src/asmjit/*/*.o'))) + Run('{LD} {LD_FLAGS} -shared -fPIC -Wl,-soname,libredmagic.so.1.0.0 -o build/libredmagic.so.1.0.0 {objs} {udis_libs} {asmjit_libs} {LIBS}'.format( **dict(globals(), **locals()) )) after() @@ -145,10 +152,16 @@ def unit(): def deps(): # udis86 version 1.7.2 - if not os.path.isfile('deps/udis86/libudis86/.libs/libudis86.so') or not os.path.isfile('deps/udis86/libudis86/itab.h'): - Shell('cd deps/udis86 && ./autogen.sh && PYTHON=`which python2` ./configure && make', shell=True) - if not os.path.isfile('build'): + if not os.path.isdir('build'): Shell('mkdir -p build') + if not os.path.isfile('deps/udis86/libudis86/.libs/libudis86.so') or not os.path.isfile('deps/udis86/libudis86/itab.h'): + Shell('cd deps/udis86 && ./autogen.sh && PYTHON=`which python2` ./configure && make', shell=True) + if not os.path.isfile('build/asmjit/libasmjit.so'): + Shell('mkdir -p build/asmjit') + if RELEASE: + Shell('cd build/asmjit && cmake ../../deps/asmjit -DASMJIT_CFLAGS=\'-O2\' && make VERBOSE=1 && touch release', shell=True) + else: + Shell('cd build/asmjit && cmake ../../deps/asmjit -DASMJIT_CFLAGS=\'-ggdb\' && make VERBOSE=1 && touch debug', shell=True) after() diff --git a/src/asm_macros.S b/src/asm_macros.S index 8217fe7..4228ceb 100644 --- a/src/asm_macros.S +++ b/src/asm_macros.S @@ -6,11 +6,13 @@ * N.L represents a new line in the macro expansion */ +#include "constants.h" + //////////////////////////////////////////////////// .macro m_push_all_regs // should match sys/regs.h, sys/user.h // 216 bytes for the struct, and then offset by 512 from the current stack - sub $728, %rsp + sub $TRACE_STACK_OFFSET, %rsp movq %r15, 0(%rsp) movq %r14, 8(%rsp) @@ -97,7 +99,7 @@ //mov 208(%rsp), %gs movq 152(%rsp), %rsp - add $728, %rsp + add $TRACE_STACK_OFFSET, %rsp .endm diff --git a/src/asm_snippets.S b/src/asm_snippets.S index 388cd18..a909df6 100644 --- a/src/asm_snippets.S +++ b/src/asm_snippets.S @@ -191,8 +191,5 @@ red_asm_write_mem_to_reg_R15_start: red_asm_write_mem_to_reg_R15_end: - - - // we don't need executable stack .section .note.GNU-stack,"",%progbits diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 0000000..e7c20ca --- /dev/null +++ b/src/constants.h @@ -0,0 +1,2 @@ + +#define TRACE_STACK_OFFSET 728 /* hardcode offset from this base stack */ diff --git a/src/jit_internal.h b/src/jit_internal.h index 479dffb..00befa2 100644 --- a/src/jit_internal.h +++ b/src/jit_internal.h @@ -33,6 +33,7 @@ namespace redmagic { class Manager; class CodeBuffer; class Tracer; + class SimpleCompiler; typedef decltype(((struct user_regs_struct*)(NULL))->r15) register_t; typedef uint64_t mem_loc_t; // a memory location in the debugged program @@ -124,6 +125,8 @@ namespace redmagic { size_t buffer_consumed; bool owns_buffer; bool can_write_buffer; + + friend class SimpleCompiler; // struct rebind_jumps { // mem_loc_t buffer_offset; // // suppose that this could disappear so might not be best idea to deallcate these and reallocate? diff --git a/src/simple_compiler.cc b/src/simple_compiler.cc new file mode 100644 index 0000000..e18485a --- /dev/null +++ b/src/simple_compiler.cc @@ -0,0 +1,108 @@ +#include "simple_compiler.h" + +using namespace redmagic; +using namespace asmjit; + +SimpleCompiler::~SimpleCompiler() { + if(buffer) { + finalize(); + } +} + +CodeBuffer SimpleCompiler::finalize() { + assert(buffer); + // check that no one else used this buffer while this was running + assert(buffer->getRawBuffer() + buffer->getOffset() == buffer_cursor); + restore_registers(); + getOffset(); + getCodeSize(); + void *start = make(); + assert(start == (void*)buffer_cursor); + size_t len = runtime.getBaseAddress() - buffer_cursor; + buffer->setOffset(runtime.getBaseAddress() - buffer->getRawBuffer()); + + buffer = NULL; + CodeBuffer ret(buffer_cursor, len); + ret.can_write_buffer = true; + return ret; +} + +void SimpleCompiler::restore_registers() { + uint64_t restore = clobbered_registers; + int indx = 0; + while(restore) { + if(restore & 0x1) { + mov(get_register_from_id(indx), x86::ptr(x86::rsp, -TRACE_STACK_OFFSET + indx * 8 + move_stack_by)); + } + restore >>= 1; + indx++; + } + clobbered_registers = 0; +} + +void SimpleCompiler::protect_register(int id) { + if(clobbered_registers & (1 << id) == 0) { + mov(x86::ptr(x86::rsp, -TRACE_STACK_OFFSET + id * 8 + move_stack_by), get_register_from_id(id)); + clobbered_registers |= 1 << id; + } +} + +const asmjit::X86GpReg& SimpleCompiler::get_scratch_register() { + int indx = 0; + if(~regs_using & clobbered_registers) { + // if there is a clobbered register that we can use as scratch then favor that + while(indx <= RDI) { + if((~regs_using & clobbered_registers & (1 << indx)) == 0) { + regs_using |= 1 << indx; + return get_register(indx); + } + } + } + while(indx <= RDI) { + if((regs_using & (1 << indx)) == 0) { + protect_register(indx); + regs_using |= 1 << indx; + clobbered_registers |= 1 << indx; + return get_register_from_id(indx); + } + } + // did not find a register + assert(0); +} + +const asmjit::X86GpReg& SimpleCompiler::get_register(int id) { + assert((clobbered_registers & 1 << id) == 0); + regs_using |= 1 << id; + return get_register_from_id(id); +} + + +void SimpleCompiler::MemToRegister(mem_loc_t mem, int reg) { + auto r = get_register(reg); + mov(r, imm_u(mem)); + mov(r, x86::ptr(r)); +} + +void SimpleCompiler::RegisterToMem(int reg, mem_loc_t mem) { + auto r = get_register(reg); + auto scr = get_scratch_register(); + mov(scr, imm_u(mem)); + mov(x86::ptr(scr), r); +} + +void SimpleCompiler::SetRegister(int reg, register_t val) { + mov(get_register(reg), imm_u(val)); +} + +void SimpleCompiler::TestRegister(int reg, register_t val) { + auto r = get_register(reg); + Label success = newLabel(); + pushf(); + test(r, imm_u(val)); + je(success); + // TODO: make this use some label for a generated address + popf(); + jmp(imm_u(0xfafafafafafafafa)); + bind(success); + popf(); +} diff --git a/src/simple_compiler.h b/src/simple_compiler.h new file mode 100644 index 0000000..72cd33b --- /dev/null +++ b/src/simple_compiler.h @@ -0,0 +1,91 @@ +#ifndef REDMAGIC_ASMJIT_WRAP_H_ +#define REDMAGIC_ASMJIT_WRAP_H_ + +#include "jit_internal.h" +#include "constants.h" +#include + +namespace redmagic { + class SimpleCompiler final : public asmjit::X86Assembler { + public: + SimpleCompiler(CodeBuffer *buffer): + buffer(buffer), + runtime((void*)(buffer->getRawBuffer() + buffer->getOffset()), buffer->getSize() - buffer->getOffset()), + //assembler(&runtime), + buffer_cursor(buffer->getRawBuffer() + buffer->getOffset()), + asmjit::X86Assembler(&runtime) + { + } + + // trigger generating the code to the buffer; + ~SimpleCompiler(); + CodeBuffer finalize(); + + // asmjit::X86Assembler assembler; + + auto& get_register_from_id(int id) { + // take the sys struct register id and convert it to asmjit + using namespace asmjit::x86; + switch(id) { + case R15: return r15; + case R14: return r14; + case R13: return r13; + case R12: return r12; + case RBP: return rbp; + case RBX: return rbx; + case R11: return r11; + case R10: return r10; + case R9: return r9; + case R8: return r8; + case RAX: return rax; + case RCX: return rcx; + case RDX: return rdx; + case RSI: return rsi; + case RDI: return rdi; + //case RIP: return rip; + //case CS: return cs; + // case RSP: return rsp; + // case DS: return ds; + // case ES: return es; + // case FS: return fs; + // case GS: return gs; + } + assert(0); + } + + // stash the register + void protect_register(int id); + void restore_registers(); + void move_stack(int amount); + + // argument of which registers it should avoid when allocating a new scratch register + const asmjit::X86GpReg& get_scratch_register(); + // get the current value of the register + // should be called first since it will add to protection + const asmjit::X86GpReg& get_register(int id); + + void MemToRegister(mem_loc_t where, int reg); + void RegisterToMem(int reg, mem_loc_t where); + void SetRegister(int reg, register_t val); + + void TestRegister(int reg, register_t val); + + private: + CodeBuffer *buffer; + mem_loc_t buffer_cursor; + + // registers that we have clobbered and thus have to restore at the end + uint64_t clobbered_registers = 0; + // registers that our program is using for something + // so dont reallocate these + uint64_t regs_using = 0;; + + int32_t move_stack_by = 0; + + asmjit::StaticRuntime runtime; + + }; + +} + +#endif // REDMAGIC_ASMJIT_WRAP_H_ diff --git a/src/tracer.cc b/src/tracer.cc index e69c05c..1ac9221 100644 --- a/src/tracer.cc +++ b/src/tracer.cc @@ -1,6 +1,8 @@ #include "jit_internal.h" #include "tracer.h" +#include "simple_compiler.h" + #include #include @@ -169,56 +171,56 @@ struct group_register_instructions_s { CodeBuffer instruction; }; -#define LOAD_SET_REGISTER1(CNAME, RNAME, OFFSET) \ - ASM_BLOCK(set_reg_ ## CNAME) +// #define LOAD_SET_REGISTER1(CNAME, RNAME, OFFSET) \ +// ASM_BLOCK(set_reg_ ## CNAME) -#define LOAD_SET_REGISTER2(CNAME, RNAME, OFFSET) \ - { \ - CNAME , /* ref sys/regs.h */ \ - cb_asm_set_reg_ ## CNAME \ - } , +// #define LOAD_SET_REGISTER2(CNAME, RNAME, OFFSET) \ +// { \ +// CNAME , /* ref sys/regs.h */ \ +// cb_asm_set_reg_ ## CNAME \ +// } , -MAIN_REGISTERS(LOAD_SET_REGISTER1) +// MAIN_REGISTERS(LOAD_SET_REGISTER1) -static group_register_instructions_s set_register_instructions[] = { - MAIN_REGISTERS(LOAD_SET_REGISTER2) -}; +// static group_register_instructions_s set_register_instructions[] = { +// MAIN_REGISTERS(LOAD_SET_REGISTER2) +// }; -#define LOAD_TEST_REGISTER1(CNAME, RNAME, OFFSET) \ - ASM_BLOCK(test_reg_ ## CNAME) +// #define LOAD_TEST_REGISTER1(CNAME, RNAME, OFFSET) \ +// ASM_BLOCK(test_reg_ ## CNAME) -#define LOAD_TEST_REGISTER2(CNAME, RNAME, OFFSET) \ - { CNAME, cb_asm_test_reg_ ## CNAME } , +// #define LOAD_TEST_REGISTER2(CNAME, RNAME, OFFSET) \ +// { CNAME, cb_asm_test_reg_ ## CNAME } , -MAIN_REGISTERS(LOAD_TEST_REGISTER1) +// MAIN_REGISTERS(LOAD_TEST_REGISTER1) -static group_register_instructions_s test_register_instructions[] = { - MAIN_REGISTERS(LOAD_TEST_REGISTER2) -}; +// static group_register_instructions_s test_register_instructions[] = { +// MAIN_REGISTERS(LOAD_TEST_REGISTER2) +// }; -#define LOAD_WRITE_MEM_REGISTER1(CNAME, RNAME, OFFSET) \ - ASM_BLOCK(write_reg_to_addr_ ## CNAME) +// #define LOAD_WRITE_MEM_REGISTER1(CNAME, RNAME, OFFSET) \ +// ASM_BLOCK(write_reg_to_addr_ ## CNAME) -#define LOAD_WRITE_MEM_REGISTER2(CNAME, RNAME, OFFSET) \ - { CNAME, cb_asm_write_reg_to_addr_ ## CNAME } , +// #define LOAD_WRITE_MEM_REGISTER2(CNAME, RNAME, OFFSET) \ +// { CNAME, cb_asm_write_reg_to_addr_ ## CNAME } , -MAIN_REGISTERS(LOAD_WRITE_MEM_REGISTER1) +// MAIN_REGISTERS(LOAD_WRITE_MEM_REGISTER1) -static group_register_instructions_s write_register_mem_instruction[] = { - MAIN_REGISTERS(LOAD_WRITE_MEM_REGISTER2) -}; +// static group_register_instructions_s write_register_mem_instruction[] = { +// MAIN_REGISTERS(LOAD_WRITE_MEM_REGISTER2) +// }; -#define LOAD_READ_MEM_REGISTER1(CNAME, RNAME, OFFSET) \ - ASM_BLOCK(write_mem_to_reg_ ## CNAME) +// #define LOAD_READ_MEM_REGISTER1(CNAME, RNAME, OFFSET) \ +// ASM_BLOCK(write_mem_to_reg_ ## CNAME) -#define LOAD_READ_MEM_REGISTER2(CNAME, RNAME, OFFSET) \ - { CNAME, cb_asm_write_mem_to_reg_ ## CNAME } , +// #define LOAD_READ_MEM_REGISTER2(CNAME, RNAME, OFFSET) \ +// { CNAME, cb_asm_write_mem_to_reg_ ## CNAME } , -MAIN_REGISTERS(LOAD_READ_MEM_REGISTER1) +// MAIN_REGISTERS(LOAD_READ_MEM_REGISTER1) -static group_register_instructions_s read_mem_register_instruction[] = { - MAIN_REGISTERS(LOAD_READ_MEM_REGISTER2) -}; +// static group_register_instructions_s read_mem_register_instruction[] = { +// MAIN_REGISTERS(LOAD_READ_MEM_REGISTER2) +// }; ASM_BLOCK(pop_stack); ASM_BLOCK(push_stack); @@ -620,9 +622,14 @@ void Tracer::evaluate_instruction() { int ri = ud_register_to_sys(opr->base); assert(ri != -1); register_t rv = ((register_t*)regs_struct)[ri]; - assert(ri < sizeof(test_register_instructions) / sizeof(group_register_instructions_s)); - auto written = buffer->writeToEnd(test_register_instructions[ri].instruction); - written.replace_stump(0xfafafafafafafafa, rv); + // assert(ri < sizeof(test_register_instructions) / sizeof(group_register_instructions_s)); + // auto written = buffer->writeToEnd(test_register_instructions[ri].instruction); + // written.replace_stump(0xfafafafafafafafa, rv); + + SimpleCompiler compiler(buffer.get()); + compiler.TestRegister(ri, rv); + auto written = compiler.finalize(); + set_pc(rv); } else { @@ -727,10 +734,12 @@ void Tracer::replace_rip_instruction() { assert(val.is_ptr); assert(opr1->type == UD_OP_REG); int dest = ud_register_to_sys(opr1->base); - assert(dest < sizeof(set_register_instructions) / sizeof(group_register_instructions_s)); + // assert(dest < sizeof(set_register_instructions) / sizeof(group_register_instructions_s)); - auto written = buffer->writeToEnd(set_register_instructions[dest].instruction); - written.replace_stump(0xfafafafafafafafa, val.address); + // auto written = buffer->writeToEnd(set_register_instructions[dest].instruction); + // written.replace_stump(0xfafafafafafafafa, val.address); + SimpleCompiler compiler(buffer.get()); + compiler.SetRegister(dest, val.address); return; } assert(0); @@ -741,15 +750,15 @@ void Tracer::replace_rip_instruction() { const ud_operand_t *opr2 = ud_insn_opr(&disassm, 1); // source address assert(opr1 != NULL && opr2 != NULL); if(opr2->base == UD_R_RIP && opr2->index == UD_NONE) { - // then we are just reading some offset from this address + // // then we are just reading some offset from this address opr_value val = get_opr_value(opr2); assert(val.is_ptr); assert(opr1->type == UD_OP_REG); int dest = ud_register_to_sys(opr1->base); - assert(dest != -1 && dest < NUMBER_MAIN_REGISTERS); - auto written = buffer->writeToEnd(read_mem_register_instruction[dest].instruction); - written.replace_stump(0xfafafafafafafafa, val.address); - return; + + SimpleCompiler compiler(buffer.get()); + compiler.MemToRegister(val.address, dest); + } assert(0); } diff --git a/src/tracer.h b/src/tracer.h index 51899ff..4448bb8 100644 --- a/src/tracer.h +++ b/src/tracer.h @@ -2,12 +2,13 @@ #define REDMAGIC_TRACER_H_ #include "jit_internal.h" +#include "constants.h" + + namespace redmagic { struct jump_instruction_info; -#define TRACE_STACK_OFFSET 728 /* hardcode offset from this base stack */ - class Tracer { public: Tracer(std::shared_ptr buffer);