diff options
| -rw-r--r-- | miasm2/arch/x86/sem.py | 10 | ||||
| -rw-r--r-- | miasm2/jitter/arch/JitCore_x86.c | 8 | ||||
| -rw-r--r-- | miasm2/jitter/codegen.py | 6 | ||||
| -rw-r--r-- | miasm2/jitter/csts.py | 1 | ||||
| -rw-r--r-- | miasm2/jitter/jitcore_python.py | 23 | ||||
| -rw-r--r-- | miasm2/os_dep/win_32_structs.py | 35 | ||||
| -rw-r--r-- | miasm2/os_dep/win_api_x86_32_seh.py | 149 | ||||
| -rw-r--r-- | test/arch/x86/unit/mn_seh.py | 106 | ||||
| -rw-r--r-- | test/test_all.py | 10 |
9 files changed, 253 insertions, 95 deletions
diff --git a/miasm2/arch/x86/sem.py b/miasm2/arch/x86/sem.py index cdc98fba..565e63c5 100644 --- a/miasm2/arch/x86/sem.py +++ b/miasm2/arch/x86/sem.py @@ -2850,7 +2850,7 @@ def cmovns(ir, instr, arg1, arg2): def icebp(ir, instr): e = [] e.append(m2_expr.ExprAff(exception_flags, - m2_expr.ExprInt32(EXCEPT_PRIV_INSN))) + m2_expr.ExprInt32(EXCEPT_SOFT_BP))) return e, [] # XXX @@ -2874,6 +2874,13 @@ def l_sysenter(ir, instr): m2_expr.ExprInt32(EXCEPT_PRIV_INSN))) return e, [] + +def l_syscall(ir, instr): + e = [] + e.append(m2_expr.ExprAff(exception_flags, + m2_expr.ExprInt32(EXCEPT_PRIV_INSN))) + return e, [] + # XXX @@ -4260,6 +4267,7 @@ mnemo_func = {'mov': mov, 'out': l_out, "sysenter": l_sysenter, + "syscall": l_syscall, "cmpxchg": cmpxchg, "cmpxchg8b": cmpxchg8b, "lds": lds, diff --git a/miasm2/jitter/arch/JitCore_x86.c b/miasm2/jitter/arch/JitCore_x86.c index 0b788071..94729b90 100644 --- a/miasm2/jitter/arch/JitCore_x86.c +++ b/miasm2/jitter/arch/JitCore_x86.c @@ -57,6 +57,8 @@ reg_dict gpreg_dict[] = { {.name = "RAX", .offset = offsetof(vm_cpu_t, RAX)}, {.name = "tsc1", .offset = offsetof(vm_cpu_t, tsc1)}, {.name = "tsc2", .offset = offsetof(vm_cpu_t, tsc2)}, + {.name = "exception_flags", .offset = offsetof(vm_cpu_t, exception_flags)}, + {.name = "interrupt_num", .offset = offsetof(vm_cpu_t, interrupt_num)}, }; @@ -521,6 +523,9 @@ getset_reg_u64(MM7); getset_reg_u32(tsc1); getset_reg_u32(tsc2); +getset_reg_u32(exception_flags); +getset_reg_u32(interrupt_num); + PyObject* get_gpreg_offset_all(void) { @@ -674,6 +679,9 @@ static PyGetSetDef JitCpu_getseters[] = { {"tsc1", (getter)JitCpu_get_tsc1, (setter)JitCpu_set_tsc1, "tsc1", NULL}, {"tsc2", (getter)JitCpu_get_tsc2, (setter)JitCpu_set_tsc2, "tsc2", NULL}, + {"exception_flags", (getter)JitCpu_get_exception_flags, (setter)JitCpu_set_exception_flags, "exception_flags", NULL}, + {"interrupt_num", (getter)JitCpu_get_interrupt_num, (setter)JitCpu_set_interrupt_num, "interrupt_num", NULL}, + {NULL} /* Sentinel */ }; diff --git a/miasm2/jitter/codegen.py b/miasm2/jitter/codegen.py index 7630a2ef..c5f28b9f 100644 --- a/miasm2/jitter/codegen.py +++ b/miasm2/jitter/codegen.py @@ -307,14 +307,10 @@ class CGen(object): return ("%s" % dst2index[label], "0") - elif (isinstance(expr, m2_expr.ExprId) or - isinstance(expr, m2_expr.ExprMem) or - isinstance(expr, m2_expr.ExprSlice)): + else: dst2index[expr] = -1 return ("-1", self.id_to_c(expr)) - else: - raise RuntimeError("Unsupported IRDst type %s" % expr) def gen_assignblk_dst(self, dst): dst2index = {} diff --git a/miasm2/jitter/csts.py b/miasm2/jitter/csts.py index 7af2435f..95cd34a8 100644 --- a/miasm2/jitter/csts.py +++ b/miasm2/jitter/csts.py @@ -4,6 +4,7 @@ # VM Mngr Exceptions EXCEPT_DO_NOT_UPDATE_PC = 1 << 25 +EXCEPT_NUM_UPDT_EIP = (1<<11) EXCEPT_CODE_AUTOMOD = (1 << 0) EXCEPT_SOFT_BP = (1 << 1) diff --git a/miasm2/jitter/jitcore_python.py b/miasm2/jitter/jitcore_python.py index ae72b307..87259f71 100644 --- a/miasm2/jitter/jitcore_python.py +++ b/miasm2/jitter/jitcore_python.py @@ -1,7 +1,7 @@ import miasm2.jitter.jitcore as jitcore import miasm2.expression.expression as m2_expr import miasm2.jitter.csts as csts -from miasm2.expression.simplifications import expr_simp +from miasm2.expression.simplifications import ExpressionSimplifier from miasm2.jitter.emulatedsymbexec import EmulatedSymbExec @@ -17,8 +17,11 @@ class JitCore_Python(jitcore.JitCore): super(JitCore_Python, self).__init__(ir_arch, bs) self.ir_arch = ir_arch - # CPU & VM (None for now) will be set by the "jitted" Python function - self.symbexec = EmulatedSymbExec(None, None, self.ir_arch, {}) + # CPU & VM (None for now) will be set later + expr_simp = ExpressionSimplifier() + expr_simp.enable_passes(ExpressionSimplifier.PASS_COMMONS) + self.symbexec = EmulatedSymbExec(None, None, self.ir_arch, {}, + sb_expr_simp=expr_simp) self.symbexec.enable_emulated_simplifications() def set_cpu_vm(self, cpu, vm): @@ -49,6 +52,7 @@ class JitCore_Python(jitcore.JitCore): # Get exec engine exec_engine = self.symbexec + expr_simp = exec_engine.expr_simp # For each irbloc inside irblocs while True: @@ -87,17 +91,18 @@ class JitCore_Python(jitcore.JitCore): if self.log_mn: print "%08x %s" % (line.offset, line) - # Check for memory exception - if (vmmngr.get_exception() != 0): + # Check for exception + if (vmmngr.get_exception() != 0 or + cpu.get_exception() != 0): exec_engine.update_cpu_from_engine() return line.offset # Eval current instruction (in IR) exec_engine.eval_ir(ir) - - # Check for memory exception which do not update PC - if (vmmngr.get_exception() & csts.EXCEPT_DO_NOT_UPDATE_PC != 0): - exec_engine.update_cpu_from_engine() + # Check for exceptions which do not update PC + exec_engine.update_cpu_from_engine() + if (vmmngr.get_exception() & csts.EXCEPT_DO_NOT_UPDATE_PC != 0 or + cpu.get_exception() > csts.EXCEPT_NUM_UPDT_EIP): return line.offset vmmngr.check_invalid_code_blocs() diff --git a/miasm2/os_dep/win_32_structs.py b/miasm2/os_dep/win_32_structs.py index 993fc79c..e76eb0a9 100644 --- a/miasm2/os_dep/win_32_structs.py +++ b/miasm2/os_dep/win_32_structs.py @@ -114,6 +114,39 @@ class PEB(MemStruct): ] +class EXCEPTION_REGISTRATION_RECORD(MemStruct): + """ + +0x00 Next : struct _EXCEPTION_REGISTRATION_RECORD * + +0x04 Handler : Ptr32 Void + """ + + fields = [ + ("Next", Ptr("<I", Self())), + ("Handler", Ptr("<I", Void())), + ] + + +class EXCEPTION_RECORD(MemStruct): + """ + DWORD ExceptionCode; + DWORD ExceptionFlags; + struct _EXCEPTION_RECORD *ExceptionRecord; + PVOID ExceptionAddress; + DWORD NumberParameters; + ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; + """ + EXCEPTION_MAXIMUM_PARAMETERS = 15 + + fields = [ + ("ExceptionCode", Num("<I")), + ("ExceptionFlags", Num("<I")), + ("ExceptionRecord", Ptr("<I", Self())), + ("ExceptionAddress", Ptr("<I", Void())), + ("NumberParameters", Num("<I")), + ("ExceptionInformation", Ptr("<I", Void())), + ] + + class NT_TIB(MemStruct): """ @@ -128,7 +161,7 @@ class NT_TIB(MemStruct): """ fields = [ - ("ExceptionList", Ptr("<I", Void())), + ("ExceptionList", Ptr("<I", EXCEPTION_REGISTRATION_RECORD)), ("StackBase", Ptr("<I", Void())), ("StackLimit", Ptr("<I", Void())), ("SubSystemTib", Ptr("<I", Void())), diff --git a/miasm2/os_dep/win_api_x86_32_seh.py b/miasm2/os_dep/win_api_x86_32_seh.py index da8df7d7..cfd83729 100644 --- a/miasm2/os_dep/win_api_x86_32_seh.py +++ b/miasm2/os_dep/win_api_x86_32_seh.py @@ -29,7 +29,8 @@ from miasm2.core.utils import pck32, upck32 import miasm2.arch.x86.regs as x86_regs from miasm2.os_dep.win_32_structs import LdrDataEntry, ListEntry, \ - TEB, NT_TIB, PEB, PEB_LDR_DATA, ContextException + TEB, NT_TIB, PEB, PEB_LDR_DATA, ContextException, \ + EXCEPTION_REGISTRATION_RECORD, EXCEPTION_RECORD # Constants Windows EXCEPTION_BREAKPOINT = 0x80000003 @@ -539,15 +540,14 @@ def ctxt2regs(jitter, ctxt_ptr): jitter.cpu.SS = ctxt.ss -def fake_seh_handler(jitter, except_code): +def fake_seh_handler(jitter, except_code, previous_seh=None): """ Create an exception context @jitter: jitter instance @except_code: x86 exception code + @previous_seh: (optional) last SEH address when multiple SEH are used """ - global seh_count - regs = jitter.cpu.get_gpreg() log.warning('Exception at %x %r', jitter.cpu.EIP, seh_count) seh_count += 1 @@ -560,59 +560,49 @@ def fake_seh_handler(jitter, except_code): # Save a CONTEXT regs2ctxt(jitter, context_address) + jitter.cpu.ESP = new_ESP # Get current seh (fs:[0]) - seh_ptr = upck32(jitter.vm.get_mem(tib_address, 4)) - - # Retrieve seh fields - old_seh, eh, safe_place = struct.unpack( - 'III', jitter.vm.get_mem(seh_ptr, 0xc)) + tib = NT_TIB(jitter.vm, tib_address) + seh = tib.ExceptionList.deref + if previous_seh: + # Recursive SEH + while seh.get_addr() != previous_seh: + seh = seh.Next.deref + seh = seh.Next.deref - log.info('seh_ptr %x { old_seh %x eh %x safe_place %x} ctx_addr %x', - seh_ptr, old_seh, eh, safe_place, context_address) + log.info('seh_ptr %x { old_seh %r eh %r} ctx_addr %x', + seh.get_addr(), seh.Next, seh.Handler, context_address) - jitter.cpu.ESP = new_ESP # Write exception_record - - """ - #http://msdn.microsoft.com/en-us/library/aa363082(v=vs.85).aspx - - typedef struct _EXCEPTION_RECORD { - DWORD ExceptionCode; - DWORD ExceptionFlags; - struct _EXCEPTION_RECORD *ExceptionRecord; - PVOID ExceptionAddress; - DWORD NumberParameters; - ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; - } EXCEPTION_RECORD, *PEXCEPTION_RECORD; - """ - - jitter.vm.set_mem(exception_record_address, - pck32(except_code) + pck32(0) + pck32(0) + - pck32(jitter.cpu.EIP) + pck32(0)) + except_record = EXCEPTION_RECORD(jitter.vm, exception_record_address) + except_record.memset("\x00") + except_record.ExceptionCode = except_code + except_record.ExceptionAddress = jitter.cpu.EIP # Prepare the stack jitter.push_uint32_t(context_address) # Context - jitter.push_uint32_t(seh_ptr) # SEH - jitter.push_uint32_t(exception_record_address) # ExceptRecords + jitter.push_uint32_t(seh.get_addr()) # SEH + jitter.push_uint32_t(except_record.get_addr()) # ExceptRecords jitter.push_uint32_t(return_from_exception) # Ret address # Set fake new current seh for exception log.info("Fake seh ad %x", fake_seh_address) - jitter.vm.set_mem(fake_seh_address, pck32(seh_ptr) + pck32( - 0xaaaaaaaa) + pck32(0xaaaaaabb) + pck32(0xaaaaaacc)) - jitter.vm.set_mem(tib_address, pck32(fake_seh_address)) - + fake_seh = EXCEPTION_REGISTRATION_RECORD(jitter.vm, fake_seh_address) + fake_seh.Next.val = tib.ExceptionList.val + fake_seh.Handler = 0xaaaaaaaa + tib.ExceptionList.val = fake_seh.get_addr() dump_seh(jitter) - log.info('Jumping at %x', eh) + # Remove exceptions jitter.vm.set_exception(0) jitter.cpu.set_exception(0) # XXX set ebx to nul? jitter.cpu.EBX = 0 - return eh + log.info('Jumping at %r', seh.Handler) + return seh.Handler.val def dump_seh(jitter): @@ -620,24 +610,18 @@ def dump_seh(jitter): Walk and dump the SEH entries @jitter: jitter instance """ - log.info('Dump_seh. Tib_address: %x', tib_address) - cur_seh_ptr = upck32(jitter.vm.get_mem(tib_address, 4)) - indent = 1 + cur_seh_ptr = NT_TIB(jitter.vm, tib_address).ExceptionList loop = 0 - while True: + while cur_seh_ptr and jitter.vm.is_mapped(cur_seh_ptr.val, + len(cur_seh_ptr)): if loop > MAX_SEH: log.warn("Too many seh, quit") return - if not jitter.vm.is_mapped(cur_seh_ptr, 8): - break - prev_seh, eh = struct.unpack('II', jitter.vm.get_mem(cur_seh_ptr, 8)) - log.info('\t' * indent + 'seh_ptr: %x { prev_seh: %x eh %x }', - cur_seh_ptr, prev_seh, eh) - if prev_seh == 0: - break - cur_seh_ptr = prev_seh - indent += 1 + err = cur_seh_ptr.deref + log.info('\t' * (loop + 1) + 'seh_ptr: %x { prev_seh: %r eh %r }', + err.get_addr(), err.Next, err.Handler) + cur_seh_ptr = err.Next loop += 1 @@ -647,11 +631,8 @@ def set_win_fs_0(jitter, fs=4): @jitter: jitter instance @fs: segment selector value """ - - regs = jitter.cpu.get_gpreg() - regs['FS'] = 0x4 - jitter.cpu.set_gpreg(regs) - jitter.cpu.set_segm_base(regs['FS'], FS_0_AD) + jitter.cpu.FS = fs + jitter.cpu.set_segm_base(fs, FS_0_AD) segm_to_do = set([x86_regs.FS]) return segm_to_do @@ -660,34 +641,48 @@ def return_from_seh(jitter): """Handle the return from an exception handler @jitter: jitter instance""" - # Get current context + # Get object addresses + seh_address = upck32(jitter.vm.get_mem(jitter.cpu.ESP + 0x4, 4)) context_address = upck32(jitter.vm.get_mem(jitter.cpu.ESP + 0x8, 4)) - log.info('Context address: %x', context_address) - jitter.cpu.ESP = upck32(jitter.vm.get_mem(context_address + 0xc4, 4)) - log.info('New esp: %x', jitter.cpu.ESP) - - # Rebuild SEH - old_seh = upck32(jitter.vm.get_mem(tib_address, 4)) - new_seh = upck32(jitter.vm.get_mem(old_seh, 4)) - log.info('Old seh: %x New seh: %x', old_seh, new_seh) - jitter.vm.set_mem(tib_address, pck32(new_seh)) + # Get registers changes + log.info('Context address: %x', context_address) + status = jitter.cpu.EAX + ctxt2regs(jitter, context_address) + + # Rebuild SEH (remove fake SEH) + tib = NT_TIB(jitter.vm, tib_address) + seh = tib.ExceptionList.deref + log.info('Old seh: %x New seh: %x', seh.get_addr(), seh.Next.val) + tib.ExceptionList.val = seh.Next.val dump_seh(jitter) - if jitter.cpu.EAX == 0x0: + # Handle returned values + if status == 0x0: # ExceptionContinueExecution - ctxt_ptr = context_address - log.info('Seh continues Context: %x', ctxt_ptr) - - # Get registers changes - # ctxt_str = jitter.vm.get_mem(ctxt_ptr, 0x2cc) - ctxt2regs(jitter, ctxt_ptr) + log.info('SEH continue') jitter.pc = jitter.cpu.EIP log.info('Context::Eip: %x', jitter.pc) - elif jitter.cpu.EAX == -1: - raise NotImplementedError("-> seh try to go to the next handler") - - elif jitter.cpu.EAX == 1: + elif status == 1: # ExceptionContinueSearch - raise NotImplementedError("-> seh, gameover") + log.info("Delegate to the next SEH handler") + # exception_base_address: context_address - 0xfc + # -> exception_record_address: exception_base_address + 0xe8 + exception_record = EXCEPTION_RECORD(jitter.vm, + context_address - 0xfc + 0xe8) + + pc = fake_seh_handler(jitter, exception_record.ExceptionCode, + seh_address) + jitter.pc = pc + + else: + # https://msdn.microsoft.com/en-us/library/aa260344%28v=vs.60%29.aspx + # But the type _EXCEPTION_DISPOSITION may take 2 others values: + # - ExceptionNestedException = 2 + # - ExceptionCollidedUnwind = 3 + raise ValueError("Valid values are ExceptionContinueExecution and " + "ExceptionContinueSearch") + + # Jitter's breakpoint compliant + return True diff --git a/test/arch/x86/unit/mn_seh.py b/test/arch/x86/unit/mn_seh.py new file mode 100644 index 00000000..cc8b5cc2 --- /dev/null +++ b/test/arch/x86/unit/mn_seh.py @@ -0,0 +1,106 @@ +#! /usr/bin/env python +import sys + +from miasm2.os_dep.win_api_x86_32_seh import fake_seh_handler, build_teb, \ + set_win_fs_0, return_from_exception, EXCEPTION_PRIV_INSTRUCTION, \ + return_from_seh, FS_0_AD, DEFAULT_SEH +from miasm2.os_dep.win_32_structs import ContextException + +from asm_test import Asm_Test_32 + +from pdb import pm + +class Test_SEH(Asm_Test_32): + """SEH Handling""" + + @staticmethod + def deal_exception_priv(jitter): + print 'Exception Priv', hex(jitter.cpu.ESP) + pc = fake_seh_handler(jitter, EXCEPTION_PRIV_INSTRUCTION) + jitter.pc = pc + jitter.cpu.EIP = pc + return True + + def init_machine(self): + super(Test_SEH, self).init_machine() + build_teb(self.myjit, FS_0_AD) + set_win_fs_0(self.myjit) + self.myjit.add_exception_handler((1 << 17), + Test_SEH.deal_exception_priv) + self.myjit.add_breakpoint(return_from_exception, return_from_seh) + + +class Test_SEH_simple(Test_SEH): + TXT = ''' + main: + XOR EAX, EAX + XOR EDX, EDX + + PUSH handler + PUSH DWORD PTR FS:[EDX] + MOV DWORD PTR FS:[EDX], ESP + + STI + + MOV EBX, DWORD PTR [ESP] + MOV DWORD PTR FS:[EDX], EBX + ADD ESP, 0x8 + + RET + + handler: + MOV ECX, DWORD PTR [ESP+0xC] + INC DWORD PTR [ECX+0x%08x] + MOV DWORD PTR [ECX+0x%08x], 0xcafebabe + XOR EAX, EAX + RET + ''' % (ContextException.get_offset("eip"), + ContextException.get_offset("eax")) + + def check(self): + assert(self.myjit.cpu.EAX == 0xcafebabe) + assert(self.myjit.cpu.EBX == DEFAULT_SEH) + + +class Test_SEH_double(Test_SEH_simple): + TXT = ''' + main: + XOR EAX, EAX + XOR EDX, EDX + + PUSH handler1 + PUSH DWORD PTR FS:[EDX] + MOV DWORD PTR FS:[EDX], ESP + + PUSH handler2 + PUSH DWORD PTR FS:[EDX] + MOV DWORD PTR FS:[EDX], ESP + + STI + + MOV EBX, DWORD PTR [ESP] + MOV DWORD PTR FS:[EDX], EBX + ADD ESP, 0x8 + + MOV EBX, DWORD PTR [ESP] + MOV DWORD PTR FS:[EDX], EBX + ADD ESP, 0x8 + + RET + + handler1: + MOV EAX, 0x1 + RET + + handler2: + MOV ECX, DWORD PTR [ESP+0xC] + INC DWORD PTR [ECX+0x%08x] + MOV DWORD PTR [ECX+0x%08x], 0xcafebabe + XOR EAX, EAX + RET + ''' % (ContextException.get_offset("eip"), + ContextException.get_offset("eax")) + + +if __name__ == "__main__": + [test(*sys.argv[1:])() for test in [Test_SEH_simple, Test_SEH_double]] diff --git a/test/test_all.py b/test/test_all.py index c3e3c1fb..7b878c89 100644 --- a/test/test_all.py +++ b/test/test_all.py @@ -48,12 +48,15 @@ testset += RegressionTest(["x86/arch.py"], base_dir="arch", class ArchUnitTest(RegressionTest): """Test against arch unit regression tests""" - jitter_engines = ["tcc", "llvm", "gcc"] + jitter_engines = ["tcc", "llvm", "gcc", "python"] def __init__(self, script, jitter ,*args, **kwargs): super(ArchUnitTest, self).__init__([script, jitter], *args, **kwargs) - +# script -> blacklisted jitter +blacklist = { + "x86/unit/mn_float.py": ["python"], +} for script in ["x86/sem.py", "x86/unit/mn_strings.py", "x86/unit/mn_float.py", @@ -71,6 +74,7 @@ for script in ["x86/sem.py", "x86/unit/mn_pextr.py", "x86/unit/mn_pmovmskb.py", "x86/unit/mn_pushpop.py", + "x86/unit/mn_seh.py", "arm/arch.py", "arm/sem.py", "aarch64/unit/mn_ubfm.py", @@ -82,6 +86,8 @@ for script in ["x86/sem.py", "mips32/unit/mn_bcc.py", ]: for jitter in ArchUnitTest.jitter_engines: + if jitter in blacklist.get(script, []): + continue tags = [TAGS[jitter]] if jitter in TAGS else [] testset += ArchUnitTest(script, jitter, base_dir="arch", tags=tags) |