From e547ec882a7bc694fb33acd93094c1b3a0a59436 Mon Sep 17 00:00:00 2001 From: Axel Tillequin Date: Mon, 2 Mar 2020 08:25:52 +0100 Subject: [PATCH] Merging 2.x branches tasks,emul,dbgr: * add Mach-O loader and system.osx skeleton * modify emulator module to match dbgr api (wip) * add server and basic client cmdcli ui * fix various errors/warnings --- README.rst | 8 + amoco/arch/arm/v7/asm.py | 10 +- amoco/arch/arm/v7/formats.py | 2 +- amoco/arch/arm/v7/spec_thumb.py | 24 + amoco/arch/arm/v7/spec_thumb2.py | 63 +- amoco/arch/arm/v7/utils.py | 3 +- amoco/arch/arm/v8/asm64.py | 6 +- amoco/arch/arm/v8/formats.py | 1 - amoco/arch/avr/asm.py | 15 +- amoco/arch/eBPF/spec_bpf.py | 2 +- amoco/arch/msp430/formats.py | 2 +- amoco/arch/pic/F46K22/asm.py | 2 +- amoco/arch/sparc/formats.py | 8 +- amoco/arch/superh/sh2/asm.py | 8 +- amoco/arch/superh/sh4/env.py | 2 +- amoco/arch/x64/asm.py | 11 +- amoco/arch/x64/formats.py | 2 +- amoco/arch/x86/asm.py | 7 - amoco/arch/x86/formats.py | 2 +- amoco/cas/expressions.py | 13 +- amoco/cas/mapper.py | 18 +- amoco/cas/smt.py | 2 +- amoco/config.py | 14 +- amoco/emu.py | 33 +- amoco/logger.py | 4 + amoco/sa/backward.py | 2 +- amoco/signals.py | 3 +- amoco/system/__init__.py | 11 +- amoco/system/baremetal/pic18.py | 3 +- amoco/system/core.py | 54 +- amoco/system/elf.py | 8 +- amoco/system/fs/ufs.py | 4 +- amoco/system/linux32/arm.py | 33 +- amoco/system/linux32/idt.py | 1 - amoco/system/linux64/aarch64.py | 1 + amoco/system/linux64/x64.py | 1 + amoco/system/macho.py | 1888 +++++++++++++++++ amoco/system/osx/__init__.py | 9 + amoco/system/osx/x64.py | 245 +++ amoco/system/pe.py | 2 +- amoco/system/structs.py | 145 +- amoco/system/vb6.py | 388 ---- amoco/system/vm/dwarf.py | 2 +- amoco/ui/cli.py | 79 + amoco/ui/srv.py | 97 +- setup.py | 2 +- tests/samples/x64/toc.osx/include/libtoc.h | 29 + tests/samples/x64/toc.osx/lib/libtoc.c | 11 + .../x64/toc.osx/lib/libtoc.dylib.mach-o | Bin 0 -> 8464 bytes tests/samples/x64/toc.osx/toc.c | 14 + tests/samples/x64/toc.osx/toc.mach-o | Bin 0 -> 12796 bytes tests/test_cas_mapper.py | 14 +- tests/test_system_macho.py | 16 + 53 files changed, 2789 insertions(+), 535 deletions(-) create mode 100644 amoco/system/macho.py create mode 100644 amoco/system/osx/__init__.py create mode 100644 amoco/system/osx/x64.py delete mode 100644 amoco/system/vb6.py create mode 100644 amoco/ui/cli.py create mode 100644 tests/samples/x64/toc.osx/include/libtoc.h create mode 100644 tests/samples/x64/toc.osx/lib/libtoc.c create mode 100755 tests/samples/x64/toc.osx/lib/libtoc.dylib.mach-o create mode 100644 tests/samples/x64/toc.osx/toc.c create mode 100755 tests/samples/x64/toc.osx/toc.mach-o create mode 100644 tests/test_system_macho.py diff --git a/README.rst b/README.rst index 2892fd6..d191269 100644 --- a/README.rst +++ b/README.rst @@ -78,6 +78,13 @@ Please see `LICENSE`_. Changelog ========= +- `v2.9.1`_ + + * add Mach-O loader and system.osx skeleton + * improve emulator module + * add server and basic client cmdcli ui + * fix various errors/warnings + - `v2.9.0`_ * add skeleton of emulator module (emul class) @@ -306,6 +313,7 @@ Changelog .. _ply: http://www.dabeaz.com/ply/ .. _sqlalchemy: http://www.sqlalchemy.org .. _LICENSE: https://github.com/bdcht/amoco/blob/release/LICENSE +.. _v2.9.1: https://github.com/bdcht/amoco/releases/tag/v2.9.1 .. _v2.9.0: https://github.com/bdcht/amoco/releases/tag/v2.9.0 .. _v2.6.3: https://github.com/bdcht/amoco/releases/tag/v2.6.3 .. _v2.6.2: https://github.com/bdcht/amoco/releases/tag/v2.6.2 diff --git a/amoco/arch/arm/v7/asm.py b/amoco/arch/arm/v7/asm.py index f535a99..c170198 100644 --- a/amoco/arch/arm/v7/asm.py +++ b/amoco/arch/arm/v7/asm.py @@ -116,7 +116,7 @@ def i_ADC(i,fmap): cond,dest,op1,op2 = __pre(i,fmap) result,cout,overflow = AddWithCarry(fmap(op1),fmap(op2),fmap(C)) fmap[dest] = stst(cond,result,fmap(dest)) - if dest is pc: + if dest == pc: __check_state(i,fmap) elif i.setflags: __setflags(fmap,cond,cout,result,overflow) @@ -125,7 +125,7 @@ def i_ADD(i,fmap): cond,dest,op1,op2 = __pre(i,fmap) result,cout,overflow = AddWithCarry(fmap(op1),fmap(op2)) fmap[dest] = stst(cond,result,fmap(dest)) - if dest is pc: + if dest == pc: __check_state(i,fmap) elif i.setflags: __setflags(fmap,cond,cout,result,overflow) @@ -303,7 +303,7 @@ def i_ROR(i,fmap): else: result,cout = ror(op1,op2), top(1) fmap[dest] = stst(cond,result,fmap(dest)) - if dest is pc: + if dest == pc: __check_state(i,fmap) if i.setflags: __setflags(fmap,cond,cout,result) @@ -472,7 +472,7 @@ def i_BFC(i,fmap): src = fmap(dest) result = composer([src[0:lsb],cst(0,size),src[lsb+size:src.size]]) fmap[dest] = stst(cond,result,fmap(dest)) - if dest is pc: + if dest == pc: raise InstructionError(i) def i_BFI(i,fmap): @@ -480,7 +480,7 @@ def i_BFI(i,fmap): src = fmap(src) result = composer([dest[0:lsb],src[lsb,lsb+size],dest[lsb+size:dest.size]]) fmap[dest] = stst(cond,result,fmap(dest)) - if dest is pc: + if dest == pc: raise InstructionError(i) def i_CLZ(i,fmap): diff --git a/amoco/arch/arm/v7/formats.py b/amoco/arch/arm/v7/formats.py index 6f709b5..235f0a1 100644 --- a/amoco/arch/arm/v7/formats.py +++ b/amoco/arch/arm/v7/formats.py @@ -26,7 +26,7 @@ def reglist(i,pos=-1): def deref(i,pos=-2): assert len(i.operands)>2 base,offset = i.operands[pos], i.operands[pos+1] - if base is pc: + if base == pc: if i.address is not None: base = i.address if internals['isetstate']==0: base = base+8 diff --git a/amoco/arch/arm/v7/spec_thumb.py b/amoco/arch/arm/v7/spec_thumb.py index e57727f..cd48036 100644 --- a/amoco/arch/arm/v7/spec_thumb.py +++ b/amoco/arch/arm/v7/spec_thumb.py @@ -82,6 +82,7 @@ def A_default(obj,DN,Rm,Rdn): @ispec("16[ 1010 1 Rd(3) imm8(8) ]", mnemonic="ADD") def A_default(obj,Rd,imm8): + obj.setflags = False obj.d = env.regs[Rd] obj.n = env.sp obj.imm32 = env.cst(imm8<<2,32) @@ -92,6 +93,7 @@ def A_default(obj,Rd,imm8): @ispec("16[ 1011 0000 0 imm7(7) ]", mnemonic="ADD") @ispec("16[ 1011 0000 1 imm7(7) ]", mnemonic="SUB") def A_default(obj,imm7): + obj.setflags = False obj.d = env.sp obj.n = env.sp obj.imm32 = env.cst(imm7<<2,32) @@ -101,6 +103,7 @@ def A_default(obj,imm7): @ispec("16[ 01000100 DM 1101 Rdm(3) ]", mnemonic="ADD") def A_default(obj,DM,Rdm): + obj.setflags = False obj.d = env.regs[(DM<<3)+Rdm] obj.n = env.sp obj.m = obj.d @@ -110,6 +113,7 @@ def A_default(obj,DM,Rdm): @ispec("16[ 01000100 1 Rm(4) 101 ]", mnemonic="ADD") def A_default(obj,Rm): + obj.setflags = False obj.d = env.sp obj.n = env.sp obj.m = env.regs[Rm] @@ -119,6 +123,7 @@ def A_default(obj,Rm): @ispec("16[ 1010 0 Rd(3) imm8(8) ]", mnemonic="ADR", add=True) def A_adr(obj,Rd,imm8): + obj.setflags = False obj.d = env.regs[Rd] obj.imm32 = env.cst(imm8<<2,32) obj.operands = [obj.d,obj.imm32] @@ -139,12 +144,14 @@ def A_default(obj,imm5,Rm,Rd): @ispec("16[ 1101 .cond(4) imm8(8) ]", mnemonic="B") def A_label(obj,imm8): + obj.setflags = False obj.imm32 = env.cst(imm8<<1,9).signextend(32) obj.operands = [obj.imm32] obj.type = type_control_flow @ispec("16[ 11100 imm11(11) ]", mnemonic="B") def A_label(obj,imm11): + obj.setflags = False obj.imm32 = env.cst(imm11<<1,12).signextend(32) obj.operands = [obj.imm32] obj.type = type_control_flow @@ -153,6 +160,7 @@ def A_label(obj,imm11): @ispec("16[ 1101 1110 imm8(8) ]", mnemonic="BKPT") @ispec("16[ 1101 1111 imm8(8) ]", mnemonic="SVC") def A_default(obj,imm8): + obj.setflags = False obj.imm32 = env.cst(imm8,32) obj.operands = [obj.imm32] obj.type = type_cpu_state @@ -161,6 +169,7 @@ def A_default(obj,imm8): @ispec("16[ 010001 11 0 Rm(4) 000 ]", mnemonic="BX") @ispec("16[ 010001 11 1 Rm(4) 000 ]", mnemonic="BLX") def A_default(obj,Rm): + obj.setflags = False obj.m = env.regs[Rm] if Rm==15 and obj.mnemonic=='BLX': raise InstructionError(obj) obj.operands = [obj.m] @@ -170,6 +179,7 @@ def A_default(obj,Rm): @ispec("16[ 1011 0 0 #i 1 #imm5(5) Rn(3) ]", mnemonic="CBZ") @ispec("16[ 1011 1 0 #i 1 #imm5(5) Rn(3) ]", mnemonic="CBNZ") def A_default(obj,i,imm5,Rn): + obj.setflags = False obj.n = env.regs[Rn] obj.imm32 = env.cst(int(i+imm5+'0',2),32) obj.operands = [obj.n, obj.imm32] @@ -180,6 +190,7 @@ def A_default(obj,i,imm5,Rn): @ispec("16[ 010000 1010 Rm(3) Rn(3) ]", mnemonic="CMP") @ispec("16[ 010000 1000 Rm(3) Rn(3) ]", mnemonic="TST") def A_default(obj,Rm,Rn): + obj.setflags = False obj.n = env.regs[Rn] obj.m = env.regs[Rm] obj.operands = [obj.n, obj.m] @@ -189,6 +200,7 @@ def A_default(obj,Rm,Rn): @ispec("16[ 001 01 Rn(3) imm8(8) ]", mnemonic="CMP") def A_default(obj,Rn,imm8): + obj.setflags = False obj.n = env.regs[Rn] obj.imm32 = env.cst(imm8,32) obj.operands = [obj.n, obj.imm32] @@ -198,6 +210,7 @@ def A_default(obj,Rn,imm8): @ispec("16[ 010001 01 N Rm(4) Rn(3) ]", mnemonic="CMP") def A_default(obj,N,Rm,Rn): + obj.setflags = False obj.n = env.regs[(N<<3)+Rn] obj.m = env.regs[Rm] obj.operands = [obj.n, obj.m] @@ -207,11 +220,13 @@ def A_default(obj,N,Rm,Rn): @ispec("16[ 1011 1111 .firstcond(4) .mask(4) ]", mnemonic="IT") def A_default(obj): + obj.setflags = False obj.type = type_cpu_state obj.cond = env.CONDITION_AL @ispec("16[ 1100 1 Rn(3) ~register_list(8) ]", mnemonic="LDM") def A_reglist(obj,Rn,register_list): + obj.setflags = False obj.n = env.regs[Rn] obj.registers = [env.regs[i] for i,r in enumerate(register_list) if r==1] if len(obj.registers)<1: raise InstructionError(obj) @@ -222,6 +237,7 @@ def A_reglist(obj,Rn,register_list): @ispec("16[ 1100 0 Rn(3) ~register_list(8) ]", mnemonic="STM") def A_reglist(obj,Rn,register_list): + obj.setflags = False obj.n = env.regs[Rn] obj.registers = [env.regs[i] for i,r in enumerate(register_list) if r==1] obj.wback = True @@ -236,6 +252,7 @@ def A_reglist(obj,Rn,register_list): @ispec("16[ 100 0 0 imm5(5) Rn(3) Rt(3) ]", mnemonic="STRH",_s=1) @ispec("16[ 011 0 0 imm5(5) Rn(3) Rt(3) ]", mnemonic="STR", _s=2) def A_deref(obj,imm5,Rn,Rt,_s): + obj.setflags = False obj.n = env.regs[Rn] obj.t = env.regs[Rt] obj.imm32 = env.cst(imm5<<_s,32) @@ -249,6 +266,7 @@ def A_deref(obj,imm5,Rn,Rt,_s): @ispec("16[ 1001 1 Rt(3) imm8(8) ]", mnemonic="LDR") @ispec("16[ 1001 0 Rt(3) imm8(8) ]", mnemonic="STR") def A_deref(obj,Rt,imm8): + obj.setflags = False obj.n = env.sp obj.t = env.regs[Rt] obj.imm32 = env.cst(imm8<<2,32) @@ -261,6 +279,7 @@ def A_deref(obj,Rt,imm8): @ispec("16[ 01001 Rt(3) imm8(8) ]", mnemonic="LDR") def A_deref(obj,Rt,imm8): + obj.setflags = False obj.n = env.pc obj.t = env.regs[Rt] obj.imm32 = env.cst(imm8<<2,32) @@ -280,6 +299,7 @@ def A_deref(obj,Rt,imm8): @ispec("16[ 0101 010 Rm(3) Rn(3) Rt(3) ]", mnemonic="STRB") @ispec("16[ 0101 001 Rm(3) Rn(3) Rt(3) ]", mnemonic="STRH") def A_deref(obj,Rm,Rn,Rt): + obj.setflags = False obj.n = env.regs[Rn] obj.t = env.regs[Rt] obj.m = env.regs[Rm] @@ -349,11 +369,13 @@ def A_default(obj,Rm,Rd): @ispec("16[ 1011 1111 0011 0000 ]", mnemonic="WFI") @ispec("16[ 1011 1111 0001 0000 ]", mnemonic="YIELD") def A_default(obj): + obj.setflags = False obj.type = type_cpu_state obj.cond = env.CONDITION_AL @ispec("16[ 1011 1 10 #P #register_list(8) ]", mnemonic="POP") def A_reglist(obj,P,register_list): + obj.setflags = False obj.registers = [env.regs[i] for i,r in enumerate(register_list[::-1]+'0'*7+P) if r=='1'] obj.operands = [obj.registers] obj.type = type_data_processing @@ -362,6 +384,7 @@ def A_reglist(obj,P,register_list): @ispec("16[ 1011 0 10 #M #register_list(8) ]", mnemonic="PUSH") def A_reglist(obj,M,register_list): + obj.setflags = False obj.registers = [env.regs[i] for i,r in enumerate(register_list[::-1]+'0'*6+M+'0') if r=='1'] obj.operands = [obj.registers] obj.type = type_data_processing @@ -379,6 +402,7 @@ def A_default(obj,Rn,Rd): @ispec("16[ 1011 0110 010 1 E 000 ]", mnemonic="SETEND") def instr_SETEND(obj,E): + obj.setflags = False obj.set_bigend = (E==1) obj.operands = [obj.set_bigend] obj.type = type_cpu_state diff --git a/amoco/arch/arm/v7/spec_thumb2.py b/amoco/arch/arm/v7/spec_thumb2.py index 6a99513..0ccae84 100644 --- a/amoco/arch/arm/v7/spec_thumb2.py +++ b/amoco/arch/arm/v7/spec_thumb2.py @@ -23,37 +23,52 @@ @ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 0 0010 S Rn(4) ]", mnemonic="ORR") @ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 0 0011 S Rn(4) ]", mnemonic="ORN") @ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 0 0100 S Rn(4) ]", mnemonic="EOR") -@ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 0 1000 S Rn(4) ]", mnemonic="ADD") @ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 0 1010 S Rn(4) ]", mnemonic="ADC") @ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 0 1011 S Rn(4) ]", mnemonic="SBC") -@ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 0 1101 S Rn(4) ]", mnemonic="SUB") @ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 0 1110 S Rn(4) ]", mnemonic="RSB") def A_default(obj,i,S,Rn,imm3,Rd,imm8): - obj.setflags = (S==1) - obj.n = env.regs[Rn] - obj.d = env.regs[Rd] - if Rn==13 and obj.mnemonic in ('ADD','SUB'): - if Rn==15 and S==0: - raise InstructionError(obj) - elif (BadReg(Rd) or Rn==15): - raise InstructionError(obj) - obj.imm32 = ThumbExpandImm(i+imm3+imm8) - obj.operands = [obj.d,obj.n,obj.imm32] - obj.type = type_data_processing - obj.cond = env.CONDITION_AL + obj.setflags = (S==1) + obj.n = env.regs[Rn] + obj.d = env.regs[Rd] + if (BadReg(Rd) or Rn==15): + raise InstructionError(obj) + obj.imm32 = ThumbExpandImm(i+imm3+imm8) + obj.operands = [obj.d,obj.n,obj.imm32] + obj.type = type_data_processing + obj.cond = env.CONDITION_AL + +@ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 1 0000 1 Rn(4) ]", mnemonic="ADD") +@ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 1 0101 1 Rn(4) ]", mnemonic="SUB") +def A_default(obj,i,Rn,imm3,Rd,imm8): + obj.setflags = True + obj.n = env.regs[Rn] + obj.d = env.regs[Rd] + if (Rd==15) or (Rn==13): + raise InstructionError(obj) + obj.imm32 = ThumbExpandImm(i+imm3+imm8) + obj.operands = [obj.d,obj.n,obj.imm32] + if (Rd==13) or (Rn==15): + obj.type = type_unpredictable + else: + obj.type = type_data_processing + obj.cond = env.CONDITION_AL @ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 1 0000 0 Rn(4) ]", mnemonic="ADD") @ispec("32[ 0 #imm3(3) Rd(4) #imm8(8) 11110 #i 1 0101 0 Rn(4) ]", mnemonic="SUB") def A_default(obj,i,Rn,imm3,Rd,imm8): - obj.setflags = False - obj.n = env.regs[Rn] - obj.d = env.regs[Rd] - if Rd==15 : raise InstructionError(obj) - # note: i, imm3, imm8 are provided as "01..." strings - obj.imm32 = cst(int(i+imm3+imm8,2),32) - obj.operands = [obj.d,obj.n,obj.imm32] - obj.type = type_data_processing - obj.cond = env.CONDITION_AL + obj.setflags = False + if BadReg(Rn) : raise InstructionError(obj) + obj.n = env.regs[Rn] + obj.d = env.regs[Rd] + # TODO: manual says its a ZeroExtend here, but need to double check with gdb + # cause its looks weird... + obj.imm32 = cst(int(i+imm3+imm8,2),32) + obj.operands = [obj.d,obj.n,obj.imm32] + if BadReg(Rd): + obj.type = type_unpredictable + else: + obj.type = type_data_processing + obj.cond = env.CONDITION_AL @ispec("32[ 0 imm3(3) Rd(4) imm2(2) stype(2) Rm(4) 11101 01 0000 S Rn(4) ]", mnemonic="AND") @ispec("32[ 0 imm3(3) Rd(4) imm2(2) stype(2) Rm(4) 11101 01 0001 S Rn(4) ]", mnemonic="BIC") @@ -454,7 +469,7 @@ def A_default(obj,S,Rd,Rm): obj.m = env.regs[Rm] obj.operands = [obj.d, obj.m] obj.type = type_data_processing - if obj.mnemonic=="MOV" and (obj.d is env.pc): + if obj.mnemonic=="MOV" and (obj.d == env.pc): obj.type = type_control_flow if obj.mnemonic=="RRX": if BadReg(Rd) or BadReg(Rm): raise InstructionError(obj) diff --git a/amoco/arch/arm/v7/utils.py b/amoco/arch/arm/v7/utils.py index b40c8a3..da82908 100644 --- a/amoco/arch/arm/v7/utils.py +++ b/amoco/arch/arm/v7/utils.py @@ -74,7 +74,8 @@ def Shift_C(x, stype, shift, carry_in): if stype==0: return LSL_C(x,shift) elif stype==1: return LSR_C(x,shift) elif stype==2: return ASR_C(x,shift) - elif stype==3: return RRX_C(x,carry_in) if shift==0 else ROR_C(x,shift) + elif stype==3: return ROR_C(x,shift) + elif stype==4: return RRX_C(x,carry_in) # reg is an instance of reg expression, shift is an integer or reg. def DecodeShift(stype, reg, shift): diff --git a/amoco/arch/arm/v8/asm64.py b/amoco/arch/arm/v8/asm64.py index 0b0692d..e966a01 100644 --- a/amoco/arch/arm/v8/asm64.py +++ b/amoco/arch/arm/v8/asm64.py @@ -268,7 +268,7 @@ def i_ERET(i,fmap): def i_EXTR(i,fmap): fmap[pc] = fmap[pc]+i.length dst, op1, op2, lsb = i.operands - concat = composer(fmap(op2),fmap(op1)) + concat = composer([fmap(op2),fmap(op1)]) result = concat[lsb:lsb+i.datasize] fmap[dst] = result @@ -318,9 +318,9 @@ def i_STLR(i,fmap): if i.pair: if not i.excl: raise InstructionError(i) if internals['endianstate']==0: - data = composer(i.t,i.t2) + data = composer([i.t,i.t2]) else: - data = composer(i.t2,i.t) + data = composer([i.t2,i.t]) else: data = i.t if i.excl: diff --git a/amoco/arch/arm/v8/formats.py b/amoco/arch/arm/v8/formats.py index f5a9ea8..5035263 100644 --- a/amoco/arch/arm/v8/formats.py +++ b/amoco/arch/arm/v8/formats.py @@ -85,7 +85,6 @@ def alias_AND(i): return m.ljust(12) + ', '.join(r) def alias_BFM(i): - m = mnemo(i) r = regs(i) if i.imms0: cond = fmap.conds.pop() @@ -30,6 +30,9 @@ def pcnpc(ifmap): f(i,fmap) return pcnpc +def __nopc(f): + return f.__closure__[0].cell_contents + # flags for arithmetic operations: def __setflags__A(i,fmap,a,b,x,neg=False): fmap[zf] = (x==0) @@ -113,7 +116,7 @@ def i_ADIW(i,fmap): def i_ADC(i,fmap): dst,src = i.operands _c=fmap[cf] - i_ADD(i,fmap) + __nopc(i_ADD)(i,fmap) a=fmap(dst) b=tst(_c,cst(1,a.size),cst(0,a.size)) x=a+b @@ -218,7 +221,7 @@ def i_CPC(i,fmap): a=fmap(dst) b=fmap(src) _c=fmap[cf] - i_CP(i,fmap) + __nopc(i_CP)(i,fmap) a=fmap(a-b) b=tst(_c,cst(1,a.size),cst(0,a.size)) x=a-b @@ -227,10 +230,8 @@ def i_CPC(i,fmap): @__pc def i_SBC(i,fmap): dst,src = i.operands - a=fmap(dst) - b=fmap(src) _c=fmap[cf] - i_SUB(i,fmap) + __nopc(i_SUB)(i,fmap) a=fmap(dst) b=tst(_c,cst(1,a.size),cst(0,a.size)) x=a-b diff --git a/amoco/arch/eBPF/spec_bpf.py b/amoco/arch/eBPF/spec_bpf.py index 5df1518..dcfddc7 100644 --- a/amoco/arch/eBPF/spec_bpf.py +++ b/amoco/arch/eBPF/spec_bpf.py @@ -65,7 +65,7 @@ def bpf_ret_(obj,s,jt,jf,k): # BPF_MISC (0x7) instructions: @ispec("64>[ 111 s 0000 {00} jt(8) jf(8) ~k(32) ]", mnemonic="mov") def bpf_mov_(obj,s,jt,jf,k): - dst,src = env.X,env.A if s==0 else env.A,env.X + dst,src = (env.X,env.A) if s==0 else (env.A,env.X) obj.operands = [dst,src] obj.type = type_data_processing diff --git a/amoco/arch/msp430/formats.py b/amoco/arch/msp430/formats.py index d9edcdf..9a88404 100644 --- a/amoco/arch/msp430/formats.py +++ b/amoco/arch/msp430/formats.py @@ -17,7 +17,7 @@ def jump(i): l = mn(m+s) adr = i.operands[0].value if i.address is None: - l.append((Token.Constant,'.%+'%adr)) + l.append((Token.Constant,'.+%d'%adr)) else: l.append((Token.Address,'*%s'%(i.address+adr+2))) return l diff --git a/amoco/arch/pic/F46K22/asm.py b/amoco/arch/pic/F46K22/asm.py index 1f70a65..58d2d8d 100644 --- a/amoco/arch/pic/F46K22/asm.py +++ b/amoco/arch/pic/F46K22/asm.py @@ -570,7 +570,7 @@ def i_SUBWFB(i,fmap): fmap[zf] = (res==0) _post_(i,fmap) -def i_SUBWFB(i,fmap): +def i_SWAPF(i,fmap): fmap[pc] = fmap(pc)+i.length _pre_(i,fmap) src = i.src diff --git a/amoco/arch/sparc/formats.py b/amoco/arch/sparc/formats.py index 3842e3a..360b1d9 100644 --- a/amoco/arch/sparc/formats.py +++ b/amoco/arch/sparc/formats.py @@ -19,10 +19,10 @@ def address(a): l = reg_or_imm(a.l) op = a.op.symbol r = reg_or_imm(a.r) - return '{0}{1}{2}'.format(l,op,r) + return l+[(Token.Literal,op)]+r def deref(a): - return [(Token.Memory,'[%s]%s'%(address(a.base+a.disp),a.seg))] + return [(Token.Memory,'[')]+address(a.base+a.disp)+[(Token.Memory,']%s'%a.seg)] def mnemo_icc(i): s = i.mnemonic @@ -170,10 +170,10 @@ def label(i): format_st = [mnemo, lambda i: TokenListJoin(', ', regn(i,0)+deref(i.operands[1]))] format_logic = [mnemo_icc, lambda i: TokenListJoin(', ', regn(i,0)+reg_or_imm(i.operands[1],'%#x')+regn(i,2))] format_sethi = [mnemo, lambda i: TokenListJoin(', ', reg_or_imm(i.operands[0])+regn(i,1))] -format_arith = [mnemo_icc, lambda i: TokenListJoin('n ', regn(i,0)+reg_or_imm(i.operands[1],'%d')+regn(i,2))] +format_arith = [mnemo_icc, lambda i: TokenListJoin(', ', regn(i,0)+reg_or_imm(i.operands[1],'%d')+regn(i,2))] format_xb = [mnemo_cond, label] format_call = [mnemo, lambda i: TokenListJoin(', ', label(i)+[(Token.Constant,'0')])] -format_jmpl = [mnemo, lambda i: TokenListJoin(', ', address(i.operands[0]), regn(i,1))] +format_jmpl = [mnemo, lambda i: address(i.operands[0])+ [(Token.Literal,', ')]+regn(i,1)] format_addr = [mnemo, lambda i: address(i.operands[0])] format_t = [lambda i: [(Token.Mnemonic, CONDT[i.cond])]+reg_or_imm(i.operands[0])] format_rd = format_regs diff --git a/amoco/arch/superh/sh2/asm.py b/amoco/arch/superh/sh2/asm.py index 01129dc..e7ba5cb 100644 --- a/amoco/arch/superh/sh2/asm.py +++ b/amoco/arch/superh/sh2/asm.py @@ -153,17 +153,17 @@ def i_SWAP(ins,fmap): sz = ins.misc['sz'] if sz==0: b0,b1 = src[0:8],src[8:16] - fmap[dst] = fmap(composer(b1,b0,src[16:32])) + fmap[dst] = fmap(composer([b1,b0,src[16:32]])) if sz==1: w0,w1 = src[0:16],src[16:32] - fmap[dst] = fmap(composer(w1,w0)) + fmap[dst] = fmap(composer([w1,w0])) @__pc def i_XTRCT(ins,fmap): src,dst = ins.operands p1 = dst[16:32] p2 = src[0:16] - fmap[dst] = fmap(composer(p1,p2)) + fmap[dst] = fmap(composer([p1,p2])) @__pc def i_ADD(ins,fmap): @@ -327,7 +327,7 @@ def i_DIV1(ins,fmap): tmp0 = rn rn_11 = rn-fmap(Rm) tmp1 = rn_11>tmp0 - newq1_1 = tst(q, tmp1, tmp1=bit0) + newq1_1 = tst(q, tmp1, tmp1==bit0) rn1 = tst(fmap(M),rn_11,rn_10) newq1 = tst(fmap(M),newq1_1,newq1_0) # div1 step result: diff --git a/amoco/arch/superh/sh4/env.py b/amoco/arch/superh/sh4/env.py index e3c30e4..81c2b45 100644 --- a/amoco/arch/superh/sh4/env.py +++ b/amoco/arch/superh/sh4/env.py @@ -67,7 +67,7 @@ D = slc(PTEL,2,1,'PTEL.D') C = slc(PTEL,3,1,'PTEL.C') SZ0 = slc(PTEL,4,1,'PTEL.SZ0') -PR = slc(PTEL,5,2,'PTEL.PR') +PR_ = slc(PTEL,5,2,'PTEL.PR') SZ1 = slc(PTEL,7,1,'PTEL.SZ1') V = slc(PTEL,8,1,'PTEL.V') PPN = slc(PTEL,10,19,'PTEL.PPN') diff --git a/amoco/arch/x64/asm.py b/amoco/arch/x64/asm.py index 62e5b47..69f789f 100644 --- a/amoco/arch/x64/asm.py +++ b/amoco/arch/x64/asm.py @@ -90,15 +90,6 @@ def i_RET(i,fmap): def i_HLT(i,fmap): fmap[rip] = top(64) -def i_XLATB(i,fmap): - fmap[rip] = fmap[rip]+i.length - _table = bx if i.misc['opdsz']==16 else ebx - REX = i.misc['REX'] - W = 0 - if REX: W=REX[0] - if W==1: _table = rbx - fmap[al] = fmap(mem(_table+al.zeroextend(_table.size),8)) - #------------------------------------------------------------------------------ def _ins_(i,fmap,l): counter = cx if i.misc['adrsz'] else rcx @@ -356,7 +347,7 @@ def i_IN(i,fmap): fmap[rip] = fmap[rip]+i.length op1 = i.operands[0] op2 = fmap(i.operands[1]) - x = ext('IN%s'%op2,op1.size).call(fmap) + x = ext('IN%s'%op2,size=op1.size).call(fmap) op1,x = _r32_zx64(op1,x) fmap[op1] = x diff --git a/amoco/arch/x64/formats.py b/amoco/arch/x64/formats.py index c96f1ff..d04a35d 100644 --- a/amoco/arch/x64/formats.py +++ b/amoco/arch/x64/formats.py @@ -123,7 +123,7 @@ def opers_att(i): elif op._is_reg: s.append((Token.Register,'%{}'.format(op))) else: - raise(ValueError,op) + raise ValueError(op) s.append((Token.Literal,', ')) if len(s)>0: s.pop() return s diff --git a/amoco/arch/x86/asm.py b/amoco/arch/x86/asm.py index 0e499a0..9efe258 100644 --- a/amoco/arch/x86/asm.py +++ b/amoco/arch/x86/asm.py @@ -1076,10 +1076,6 @@ def i_SFENCE(i,fmap): logger.verbose('%s semantic is not defined'%i.mnemonic) fmap[eip] = fmap[eip]+i.length -def i_MWAIT(i,fmap): - logger.verbose('%s semantic is not defined'%i.mnemonic) - fmap[eip] = fmap[eip]+i.length - def i_LGDT(i,fmap): logger.verbose('%s semantic is not defined'%i.mnemonic) fmap[eip] = fmap[eip]+i.length @@ -1267,9 +1263,6 @@ def i_XGETBV(i,fmap): def i_XSETBV(i,fmap): logger.verbose('%s semantic is not defined'%i.mnemonic) fmap[eip] = fmap[eip]+i.length -def i_XSETBV(i,fmap): - logger.verbose('%s semantic is not defined'%i.mnemonic) - fmap[eip] = fmap[eip]+i.length def i_FXSAVE(i,fmap): logger.verbose('%s semantic is not defined'%i.mnemonic) fmap[eip] = fmap[eip]+i.length diff --git a/amoco/arch/x86/formats.py b/amoco/arch/x86/formats.py index 568e23c..a942f8e 100644 --- a/amoco/arch/x86/formats.py +++ b/amoco/arch/x86/formats.py @@ -120,7 +120,7 @@ def opers_att(i): elif op._is_reg: s.append((Token.Register,'%{}'.format(op))) else: - raise(ValueError,op) + raise ValueError(op) s.append((Token.Literal,', ')) if len(s)>0: s.pop() return s diff --git a/amoco/cas/expressions.py b/amoco/cas/expressions.py index da8f13d..7695baf 100644 --- a/amoco/cas/expressions.py +++ b/amoco/cas/expressions.py @@ -1640,11 +1640,19 @@ def eqn1_helpers(e,**kargs): return OP_CONDT[notop](e.r.l,e.r.r) return e +def get_lsb_msb(v): + msb = v.bit_length()-1 + lsb = (v & -v).bit_length()-1 + return (lsb,msb) + +def ismask(v): + i1,i2 = get_lsb_msb(v) + return ((1<<(i2+1))-1) ^ ((1<threshold: e.r = top(e.r.size) @@ -1811,7 +1819,8 @@ def toks(self,**kargs): t.append((render.Token.Literal,u']')) return t - def simplify(self,widening=False): + def simplify(self,**kargs): + widening = kargs.get('widening',False) l = [] for e in self.l: ee = e.simplify() diff --git a/amoco/cas/mapper.py b/amoco/cas/mapper.py index ef6cdb3..ff00924 100644 --- a/amoco/cas/mapper.py +++ b/amoco/cas/mapper.py @@ -253,9 +253,21 @@ def __setitem__(self,k,v): self.__map[loc] = r def update(self,instr): - "update the current mapper with the provided instruction" + "opportunistic update of the self mapper with instruction" instr(self) + def safe_update(self,instr): + "update of the self mapper with instruction *only* if no exception occurs" + try: + m = mapper() + instr(m) + mm = self>>m + except Exception as e: + logger.error("instruction @ %s raises exception %s"%(instr.address,e)) + raise e + else: + self.update(instr) + def __call__(self,x): """evaluation of expression x in this map: note the difference between a mapper[mem(p)] and mapper(mem(p)): @@ -304,8 +316,8 @@ def rcompose(self,m): mm.conds.append(cc) for loc,v in self: if loc._is_ptr: - loc = mm(loc) - mm[loc] = mm(v) + loc = m(loc) + mm[loc] = m(v) return mm def __lshift__(self,m): diff --git a/amoco/cas/smt.py b/amoco/cas/smt.py index ee777bb..19bb1ea 100644 --- a/amoco/cas/smt.py +++ b/amoco/cas/smt.py @@ -27,7 +27,7 @@ except ImportError: logger.info('z3 package not found => solve() method is not implemented') class solver(object): - def __init__(self,eqns=None): + def __init__(self,eqns=None,tactics=None,timeout=None): raise NotImplementedError has_solver = False else: diff --git a/amoco/config.py b/amoco/config.py index 7ab7715..8552e6e 100644 --- a/amoco/config.py +++ b/amoco/config.py @@ -82,9 +82,16 @@ class Log(Configurable): class UI(Configurable): "configurable parameters related to User Interface(s)" - formatter = Unicode('Null',config=True) - graphics = Unicode('term',config=True) - console = Unicode('python',config=True) + formatter = Unicode('Null',config=True) + graphics = Unicode('term',config=True) + console = Unicode('python',config=True) + completekey = Unicode('tab',config=True) + cli = Unicode('cmdcli',config=True) + +class Server(Configurable): + "configurable parameters related to the Server mode" + wbsz = Integer(0x1000,config=True) + timeout = Integer(600,config=True) class Arch(Configurable): assemble = Bool(False,config=True) @@ -129,6 +136,7 @@ def __init__(self,f=None): self.Log = Log(config=c) self.Cas = Cas(config=c) self.System = System(config=c) + self.Server = Server(config=c) self.src = c def __str__(self): diff --git a/amoco/emu.py b/amoco/emu.py index 974ba4c..ad7e5e9 100644 --- a/amoco/emu.py +++ b/amoco/emu.py @@ -32,21 +32,48 @@ def __init__(self,task): self.cpu = task.cpu self.pc = task.cpu.PC() self.psz = self.pc.size + self.hooks = [] + self.handlers = {} if task.OS is not None: self.abi = task.OS.abi else: self.abi = None def stepi(self): - addr = self.task.state(self.pc) + addr = self.task.getx(self.pc) i = self.task.read_instruction(addr) if i is not None: - self.task.state.update(i) + self.task.state.safe_update(i) else: raise DecodeError(addr) return i def iterate(self): + lasti = None while True: - yield self.stepi() + if not self.checkstate(lasti): + break + try: + lasti = self.stepi() + yield lasti + except MemoryError as e: + lasti = None + if not self.exception_handler(e): + break + + def exception_handler(self,e): + te = type(e) + logger.debug('exception %s received'%te) + if te in self.handlers: + return self.handlers[te](self,e) + raise(e) + + def checkstate(self,prev=None): + res = True + for f in self.hooks: + res &= f(self,prev) + if not res: break + return res + + diff --git a/amoco/logger.py b/amoco/logger.py index 3bace91..82f4039 100644 --- a/amoco/logger.py +++ b/amoco/logger.py @@ -146,4 +146,8 @@ def set_log_file(filename): for l in Log.loggers.values(): l.addHandler(logfile) +def flush_all(): + for l in Log.loggers.values(): + l.handlers[0].flush() + Log.loggers = {} diff --git a/amoco/sa/backward.py b/amoco/sa/backward.py index 909ba3b..739a639 100644 --- a/amoco/sa/backward.py +++ b/amoco/sa/backward.py @@ -70,7 +70,7 @@ def get_targets(self,node,parent): fsym = n.data.misc['callers'][0].data.misc['to'].ref except (IndexError,TypeError,AttributeError): fsym = 'f' - func = code.func(n.c,name="%s:%s"%(fsym,n.name)) + func = code.func(n.c) logger.verbose("function %s created"%func) if mpc._is_mem and len(mpc.mods)>0: pol = '(assume_no_aliasing)' if self.policy['frame-aliasing']==False else '' diff --git a/amoco/signals.py b/amoco/signals.py index 79a1093..d6844b9 100644 --- a/amoco/signals.py +++ b/amoco/signals.py @@ -55,7 +55,8 @@ def __init__(self,obj): logger.debug('ref to regular function %s'%name) elif hasattr(obj,'__qualname__'): qn = obj.__qualname__.split('.') - assert qn.pop(-1)==name + assert qn[-1]==name + qn.pop() while len(qn)>0: ctx = getattr(ctx,qn.pop(0)) assert inspect.isclass(ctx) diff --git a/amoco/system/__init__.py b/amoco/system/__init__.py index 1b9dd55..84e80e2 100644 --- a/amoco/system/__init__.py +++ b/amoco/system/__init__.py @@ -20,6 +20,7 @@ def load_program(f,cpu=None): from . import linux64 from . import win32 from . import win64 + from . import osx from . import baremetal p = core.read_program(f) @@ -28,13 +29,19 @@ def load_program(f,cpu=None): try: x = Loaders[p.Ehdr.e_machine](p) except KeyError: - logger.error(u'ELF machine type not supported:\n%s'%p.Ehdr) + core.logger.error(u'ELF machine type not supported:\n%s'%p.Ehdr) x = None elif p.is_PE: try: x = Loaders[p.NT.Machine](p) except KeyError: - logger.error(u'PE machine type not supported:\n%s'%p.NT) + core.logger.error(u'PE machine type not supported:\n%s'%p.NT) + x = None + elif p.is_MachO: + try: + x = Loaders[p.header.cputype](p) + except KeyError: + core.logger.error(u'Mach-O machine type not supported:\n%s'%p.header.cputype) x = None else: x = Loaders['raw'](p,cpu) diff --git a/amoco/system/baremetal/pic18.py b/amoco/system/baremetal/pic18.py index a69dbcd..c1e71fe 100644 --- a/amoco/system/baremetal/pic18.py +++ b/amoco/system/baremetal/pic18.py @@ -9,7 +9,7 @@ import amoco.arch.pic.cpu_pic18f46k22 as cpu -class PIC18(CoreExec): +class PIC18(object): def __init__(self,p): self.cpu = cpu @@ -50,7 +50,6 @@ def initenv(self): def codehelper(self,**kargs): if 'seq' in kargs: return self.seqhelper(kargs['seq']) if 'block' in kargs: return self.blockhelper(kargs['block']) - if 'func' in kargs: return self.funchelper(kargs['func']) # seqhelper provides arch-dependent information to amoco.main classes def seqhelper(self,seq): diff --git a/amoco/system/core.py b/amoco/system/core.py index 923dd71..a72b978 100644 --- a/amoco/system/core.py +++ b/amoco/system/core.py @@ -15,13 +15,18 @@ """ from io import BytesIO - +from amoco.arch.core import Bits from amoco.system.memory import * from amoco.logger import Log logger = Log(__name__) logger.debug('loading module') +try: + IntType = (int,long) +except NameError: + IntType = (int,) + #------------------------------------------------------------------------------ class CoreExec(object): @@ -70,21 +75,25 @@ def read_instruction(self,vaddr,**kargs): logger.error('no cpu imported') raise ValueError maxlen = self.cpu.disassemble.maxlen + if isinstance(vaddr,IntType): + addr = self.cpu.cst(vaddr,self.cpu.PC().size) + else: + addr = vaddr try: istr = self.state.mmap.read(vaddr,maxlen) except MemoryError as e: - logger.verbose("vaddr %s is not mapped"%vaddr) + logger.verbose("vaddr %s is not mapped"%addr) raise MemoryError(e) else: if len(istr)<=0 or not isinstance(istr[0],bytes): - logger.verbose("failed to read instruction at %s"%vaddr) + logger.verbose("failed to read instruction at %s"%addr) return None i = self.cpu.disassemble(istr[0],**kargs) if i is None: - logger.warning("disassemble failed at vaddr %s"%vaddr) + logger.warning("disassemble failed at vaddr %s"%addr) return None else: - if i.address is None: i.address = vaddr + if i.address is None: i.address = addr xsz = i.misc['xsz'] or 0 if xsz>0: xdata = self.state.mmap.read(vaddr+i.length,xsz) @@ -100,12 +109,12 @@ def getx(self,loc,size=8,sign=False): addr = self.cpu.cst(loc,psz) x = self.cpu.mem(addr,size,endian=endian) else: - raise ValueError('provided location should be a register name (str) or memory address (int)') + x = loc r = self.state(x) r.sf = sign - return r.value() if r._is_cst else r + return r.value if r._is_cst else r - def setx(self,loc,val,size=8): + def setx(self,loc,val,size=0): if isinstance(loc,str): x = getattr(self.cpu,loc) size = x.size @@ -115,10 +124,15 @@ def setx(self,loc,val,size=8): x = self.cpu.mem(self.cpu.cst(addr,psz),size,endian=endian) else: x = loc + size = x.size if isinstance(val,bytes): - if x._is_reg: raise ValueError('register location should be set to an integer value') - x.size = len(val) - self.state._Mem_write(x.a,val) + if x._is_mem: + x.size = len(val) if size==0 else size + self.state._Mem_write(x.a,val) + else: + endian = self.cpu.get_data_endian() + v = self.cpu.cst(Bits(val[0:x.size:endian],bitorder=1).int(),x.size*8) + self.state[x] = v elif isinstance(val,IntType): self.state[x] = self.cpu.cst(val,size) else: @@ -161,6 +175,7 @@ def __call__(self,f): class BinFormat(object): is_ELF = False is_PE = False + is_MachO = False basemap = None symtab = None strtab = None @@ -285,15 +300,14 @@ def softspace(self): #------------------------------------------------------------------------------ def read_program(filename): ''' - Identifies the program header (ELF/PE) and returns an ELF, PE or DataIO - instance. + Identifies the program header and returns an ELF, PE, Mach-O or DataIO. Args: filename (str): the program to read. Returns: - an instance of currently supported program format (ELF, PE) - + an instance of currently supported program format + (ELF, PE, Mach-O, HEX, SREC) ''' try: @@ -323,6 +337,16 @@ def read_program(filename): f.seek(0) logger.debug('PEError raised for %s'%f.name) + try: + from amoco.system import macho + # open file as a Mach-O object: + p = macho.MachO(f) + logger.info("Mach-O format detected") + return p + except macho.MachOError: + f.seek(0) + logger.debug('MachOError raised for %s'%f.name) + try: from amoco.system import utils # open file as a HEX object: diff --git a/amoco/system/elf.py b/amoco/system/elf.py index 507b3d7..6c0cee1 100644 --- a/amoco/system/elf.py +++ b/amoco/system/elf.py @@ -156,9 +156,13 @@ def readcode(self,target,size=None): data = c[offset:] return data,0,base+offset - def getfileoffset(self,addr): + def getfileoffset(self,target): s,offset,base = self.getinfo(target) - return S.p_offset+offset + if s!=None: + result = s.p_offset+offset + else: + result = None + return result def readsegment(self,S): self.__file.seek(S.p_offset) diff --git a/amoco/system/fs/ufs.py b/amoco/system/fs/ufs.py index 1237c35..0ead7fc 100644 --- a/amoco/system/fs/ufs.py +++ b/amoco/system/fs/ufs.py @@ -594,8 +594,8 @@ def getindir(pos,data,bsz,order): # third-indirect blocks: if sz>1)<<1 + self.state[x] = v + # LIBC HOOKS DEFINED HERE : diff --git a/amoco/system/linux32/idt.py b/amoco/system/linux32/idt.py index 986d1c7..f2775f5 100644 --- a/amoco/system/linux32/idt.py +++ b/amoco/system/linux32/idt.py @@ -228,7 +228,6 @@ 217: "pivot_root", 218: "mincore", 219: "madvise", -219: "madvise1", 220: "getdents64", 221: "fcntl64", 224: "gettid", diff --git a/amoco/system/linux64/aarch64.py b/amoco/system/linux64/aarch64.py index e9ac2ad..c9bbefc 100644 --- a/amoco/system/linux64/aarch64.py +++ b/amoco/system/linux64/aarch64.py @@ -164,6 +164,7 @@ def __init__(self,conf=None): self.ASLR = conf.aslr self.NX = conf.nx self.tasks = [] + self.abi = None @classmethod def loader(cls,bprm,conf=None): diff --git a/amoco/system/linux64/x64.py b/amoco/system/linux64/x64.py index f901168..4a0b67c 100644 --- a/amoco/system/linux64/x64.py +++ b/amoco/system/linux64/x64.py @@ -74,6 +74,7 @@ def __init__(self,conf=None): self.ASLR = conf.aslr self.NX = conf.nx self.tasks = [] + self.abi = None @classmethod def loader(cls,bprm,conf=None): diff --git a/amoco/system/macho.py b/amoco/system/macho.py new file mode 100644 index 0000000..36d33b5 --- /dev/null +++ b/amoco/system/macho.py @@ -0,0 +1,1888 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2020 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +""" +system/macho.py +================ + +The system macho module implements the Mach-O executable format parser. +""" +from amoco.logger import Log +logger = Log(__name__) +logger.debug('loading module') + + +# our exception handler: +class MachOError(Exception): + def __init__(self,message): + self.message = message + def __str__(self): + return str(self.message) + +#------------------------------------------------------------------------------ +from amoco.system.core import BinFormat,DataIO +from amoco.system.utils import read_leb128,read_uleb128 + +class MachO(BinFormat): + is_MachO = True + @property + def entrypoints(self): + if self.__entry: + return [self.__entry] + for c in self.cmds: + if c.cmd in (LC_THREAD, LC_UNIXTHREAD): + try: + self.__entry = c.entrypoint + except AttributeError: + pass + if c.cmd == LC_MAIN: + base = self.base_address() + # remplacement for thread commands + self.__entry = base+c.entryoff + break + return [self.__entry] + + @property + def filename(self): + return self.__file.name + + def __init__(self,f): + self.__file = f + self.__entry = None + self._is_fat = False + try: + self.header = struct_mach_header(f) + except: + raise MachOError('not a Mach-O header') + if self.header.magic == MH_MAGIC_64: + self.header = struct_mach_header_64(f) + elif self.header.magic == FAT_CIGAM: + self.header = struct_fat_header(f) + self._is_fat = True + else: + if not self.header.magic == MH_MAGIC: + raise MachOError('not a Mach-O header') + offset = len(self.header) + if self._is_fat: + self.archs = [] + for i in range(self.header.nfat_arch): + a = struct_fat_arch(f,offset) + offset += len(a) + self.archs.append(a) + self.read_fat_arch(a) + else: + self.cmds = self.read_commands(offset) + for c in self.cmds: + if c.cmd in (LC_THREAD,LC_UNIXTHREAD): + c.getstate(self.header.cputype) + elif c.cmd==LC_SYMTAB: + self.symtab = self.__read_symtab(c) + elif c.cmd==LC_DYSYMTAB: + self.dysymtab = self.__read_dysymtab(c) + elif c.cmd in (LC_DYLD_INFO, LC_DYLD_INFO_ONLY): + self.dyld_info = self.__read_dyld_info(c) + elif c.cmd == LC_FUNCTION_STARTS: + self.function_starts = self.__read_funcstarts(c) + self.la_symbol_ptr = self.__la_bindings() + self.nl_symbol_ptr = self.__nl_bindings() + + def read_fat_arch(self,a): + self.__f.seek(a.offset) + data = self.__f.read(a.size) + a.bin = MachO(DataIO(data)) + + def read_commands(self,offset): + cmds = [] + lcsize = 0 + f = self.__file + f.seek(0) + while lcsize0: n+=1 + return s.ljust(n*pagesize,b'\0') + + def readsection(self,s): + pass + + def __read_table(self,off,elt,count,sz=0): + tab = [] + if count>0: + self.__file.seek(off) + if sz==0: + sz = elt.size() + for n in range(count): + data = self.__file.read(sz) + tab.append(elt(data)) + return tab + + def __read_symtab(self,s): + if self.header.magic == MH_MAGIC_64: + el = struct_nlist64 + elif self.header.magic == MH_MAGIC: + el = struct_nlist + else: + raise NotImplementedError + dylibs = [] + for c in self.cmds: + if c.cmd == LC_LOAD_DYLIB: + self.dynamic = True + dylibs.append(c) + symtab = self.__read_table(s.symoff,el,s.nsyms) + strtab = self.__read_strtab(s) + for x in symtab: + sta = x.n_strx + sto = strtab.index(b'\0',sta) + x.strx = strtab[sta:sto] + if self.header.flags & MH_TWOLEVEL: + i = (x.n_desc&0xff00)>>8 + if i>0: + x.strx = b'%s::%s'%(dylibs[i-1].dylib.name,x.strx) + return symtab + + def __read_strtab(self,s): + self.__file.seek(s.stroff) + data = self.__file.read(s.strsize) + return data + + def __read_dyld_info(self,cmd): + t = type('container',(object,),{}) + #rebase opcodes (raw): + self.__file.seek(cmd.rebase_off) + t.rebase = self.__file.read(cmd.rebase_size) + #bind opcodes (as a list of binding records): + self.__file.seek(cmd.bind_off) + rawbind = self.__file.read(cmd.bind_size) + t.bind = self.__read_bind_opcodes(rawbind) + #weak_bind opcodes (as a list of binding records): + self.__file.seek(cmd.weak_bind_off) + weak_bind = self.__file.read(cmd.weak_bind_size) + t.weak_bind = self.__read_bind_opcodes(weak_bind) + #lazy_bind opcodes (as a list of binding records): + #see source code: the record type is POINTER by default... + self.__file.seek(cmd.lazy_bind_off) + lazy_bind = self.__file.read(cmd.lazy_bind_size) + t.lazy_bind = self.__read_bind_opcodes(lazy_bind,BIND_TYPE_POINTER) + #exports (raw): + self.__file.seek(cmd.export_off) + t.export = self.__file.read(cmd.export_size) + return t + + # see dyld source: ImageLoaderMachOCompressed.cpp. + def __read_bind_opcodes(self,raw,default_type=0): + r = record(0,0,0,default_type,0,0) + L = [] + cur = 0 + l = 8 if self.header.magic==MH_MAGIC_64 else 4 + while curcur: r.symbol = raw[cur:nulchar] + cur = nulchar+1 + elif op == BIND_OPCODE_SET_TYPE_IMM: + r.type = im + elif op == BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + r.seg_index = im + val,cnt = read_uleb128(raw[cur:]) + r.seg_offset = val + cur += cnt + elif op == BIND_OPCODE_DO_BIND: + L.append(r.as_list()) + r.seg_offset += l + elif op == BIND_OPCODE_ADD_ADDR_ULEB: + val,cnt = read_uleb128(raw[cur:]) + r.seg_offset += val + cur += cnt + elif op == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + L.append(r.as_list()) + val,cnt = read_uleb128(raw[cur:]) + r.seg_offset += (l + val) + cur += cnt + elif op == BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + L.append(r.as_list()) + r.seg_offset += (im*l + l) + cur += cnt + elif op == BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count,cnt = read_uleb128(raw[cur:]) + skip,cnt2 = read_uleb128(raw[cur+cnt:]) + for i in range(count): + L.append(r.as_list()) + r.seg_offset += skip + l + cur += cnt+cnt2 + else: + raise NotImplementedError + return L + + def __read_dysymtab(self,cmd): + t = type('container',(object,),{}) + # read table of contents: + t.toc = self.__read_table(cmd.tocoff, + struct_dylib_table_of_contents, + cmd.ntoc) + # read module table: + if self.header.magic==MH_MAGIC_64: + elt = struct_dylib_module_64 + elif self.header.magic==MH_MAGIC: + elt = struct_dylib_module + else: + raise MachOError('module table error') + t.modtab = self.__read_table(cmd.modtaboff, + elt, + cmd.nmodtab) + t.extrefsym = self.__read_table(cmd.extrefsymoff, + struct_dylib_reference, + cmd.nextrefsyms) + t.indirectsym = self.__read_table(cmd.indirectsymoff, + struct_indirect_entry, + cmd.nindirectsyms) + t.extrel = self.__read_table(cmd.extreloff, + struct_relocation_info, + cmd.nextrel) + t.locrel = self.__read_table(cmd.locreloff, + struct_relocation_info, + cmd.nlocrel) + return t + + def __la_bindings(self): + segs = [c for c in self.cmds if c.cmd in (LC_SEGMENT,LC_SEGMENT_64)] + libs = [c for c in self.cmds if c.cmd == LC_LOAD_DYLIB] + D = {} + if hasattr(self,'dyld_info'): + for b in self.dyld_info.lazy_bind: + r = record(*b) + addr = segs[r.seg_index].vmaddr + r.seg_offset + r.addend + name = b"%s::%s"%(libs[r.lib_ordinal-1].dylib.name,r.symbol) + D[addr] = name + return D + + def __nl_bindings(self): + segs = [c for c in self.cmds if c.cmd in (LC_SEGMENT,LC_SEGMENT_64)] + libs = [c for c in self.cmds if c.cmd == LC_LOAD_DYLIB] + D = {} + if hasattr(self,'dyld_info'): + for b in self.dyld_info.bind: + r = record(*b) + addr = segs[r.seg_index].vmaddr + r.seg_offset + r.addend + name = b"%s::%s"%(libs[r.lib_ordinal-1].dylib.name,r.symbol) + D[addr] = name + return D + + def base_address(self): + fbaseaddr = None + for c in self.cmds: + if c.cmd in (LC_SEGMENT,LC_SEGMENT_64): + if c.fileoffset==0 and c.filesize>0: + fbaseaddr = c.vmaddr + return fbaseaddr + + # source: https://opensource.apple.com/source/ld64/ld64-127.2/src/other/dyldinfo.cpp + def __read_funcstarts(self,fs): + fbaseaddr = self.base_address() + addr = fbaseaddr + F = [] + if fs is not None: + self.__file.seek(fs.dataoff) + data = self.__file.read(fs.datasize) + p = 0 + while p magic +I :> nfat_arch +""") +class struct_fat_header(StructFormatter): + alt = 'mh' + def __init__(self,data=None): + self.name_formatter('magic') + if data: + self.unpack(data) + +@StructDefine(""" +I :> cputype +I :> cpusubtype +I :> offset +I :> size +I :> align +""") +class struct_fat_arch(StructFormatter): + alt = 'mh' + def __init__(self,data=None): + self.name_formatter('cputype') + self.func_formatter(cpusubtype=self.subtype_fmt) + if data: + self.unpack(data) + def subtype_fmt(self,k,v,cls): + alt2 = Consts.All['mh.cputype'].get(self.cputype&0xff,'') + cls2 = "%s.%s"%(self.alt,alt2) + return token_name_fmt(k,v&0xff,cls2) + +@StructDefine(""" +I : magic +i : cputype +i : cpusubtype +I : filetype +I : ncmds +I : sizeofcmds +I : flags +""") +class struct_mach_header(StructFormatter): + alt = 'mh' + def __init__(self,data=None): + self.name_formatter('magic') + self.name_formatter('cputype','filetype') + self.func_formatter(cpusubtype=self.subtype_fmt) + self.flag_formatter('flags') + if data: + self.unpack(data) + def subtype_fmt(self,k,v,cls): + alt2 = Consts.All['mh.cputype'].get(self.cputype&0xff,'') + cls2 = "%s.%s"%(self.alt,alt2) + return token_name_fmt(k,v&0xff,cls2) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : magic +I : cputype +I : cpusubtype +I : filetype +I : ncmds +I : sizeofcmds +I : flags +I : reserved +""") +class struct_mach_header_64(StructFormatter): + alt = 'mh' + def __init__(self,data=None): + self.name_formatter('magic') + self.name_formatter('cputype','filetype') + self.func_formatter(cpusubtype=self.subtype_fmt) + self.flag_formatter('flags') + if data: + self.unpack(data) + def subtype_fmt(self,k,v,cls): + alt2 = Consts.All['mh.cputype'].get(self.cputype&0xff,'') + cls2 = "%s.%s"%(self.alt,alt2) + return token_name_fmt(k,v&0xff,cls2) + +with Consts('mh.magic'): + MH_MAGIC = 0xfeedface + MH_CIGAM = 0xcefaedfe + MH_MAGIC_64 = 0xfeedfacf + MH_CIGAM_64 = 0xcffaedfe + FAT_MAGIC = 0xcafebabe + FAT_CIGAM = 0xbebafeca + FAT_MAGIC_64 = 0xcafebabf + FAT_CIGAM_64 = 0xbfbafeca + +with Consts('mh.cputype'): + ANY = -1 + VAX = 1 + MC680x0 = 6 + X86 = 7 + X86_64 = 16777223 + MC98000 = 10 + HPPA = 11 + ARM = 12 + MC88000 = 13 + SPARC = 14 + I860 = 15 + POWERPC = 18 + POWERPC64 = 16777234 + +with Consts('mh.X86.cpusubtype'): + CPU_SUBTYPE_386 = 3 + CPU_SUBTYPE_486 = 4 + CPU_SUBTYPE_486SX = 0x84 + CPU_SUBTYPE_586 = 5 + CPU_SUBTYPE_X86_64_H = 8 + CPU_SUBTYPE_PENTPRO = 0x16 + CPU_SUBTYPE_PENTII_M3 = 0x36 + CPU_SUBTYPE_PENTII_M5 = 0x56 + CPU_SUBTYPE_CELERON = 0x67 + CPU_SUBTYPE_CELERON_MOBILE = 0x77 + CPU_SUBTYPE_PENTIUM_3 = 0x08 + CPU_SUBTYPE_PENTIUM_3_M = 0x18 + CPU_SUBTYPE_PENTIUM_3_XEON = 0x28 + CPU_SUBTYPE_PENTIUM_M = 0x09 + CPU_SUBTYPE_PENTIUM_4 = 0x0a + CPU_SUBTYPE_PENTIUM_4_M = 0x1a + CPU_SUBTYPE_ITANIUM = 0x0b + CPU_SUBTYPE_ITANIUM_2 = 0x1b + CPU_SUBTYPE_XEON = 0x0c + CPU_SUBTYPE_XEON_MP = 0x1c + +with Consts('mh.ARM.cpusubtype'): + CPU_SUBTYPE_ARM_ALL = 0 + CPU_SUBTYPE_ARM_V4T = 5 + CPU_SUBTYPE_ARM_V6 = 6 + CPU_SUBTYPE_ARM_V5 = 7 + CPU_SUBTYPE_ARM_XSCALE = 8 + CPU_SUBTYPE_ARM_V7 = 9 + CPU_SUBTYPE_ARM_V7S = 11 + CPU_SUBTYPE_ARM_V7K = 12 + CPU_SUBTYPE_ARM_V6M = 14 + CPU_SUBTYPE_ARM_V7M = 15 + CPU_SUBTYPE_ARM_V7EM = 16 + +with Consts('mh.POWERPC.cpusubtype'): + CPU_SUBTYPE_POWERPC_ALL = 0 + CPU_SUBTYPE_POWERPC_601 = 1 + CPU_SUBTYPE_POWERPC_602 = 2 + CPU_SUBTYPE_POWERPC_603 = 3 + CPU_SUBTYPE_POWERPC_603e = 4 + CPU_SUBTYPE_POWERPC_603ev = 5 + CPU_SUBTYPE_POWERPC_604 = 6 + CPU_SUBTYPE_POWERPC_604e = 7 + CPU_SUBTYPE_POWERPC_620 = 8 + CPU_SUBTYPE_POWERPC_750 = 9 + CPU_SUBTYPE_POWERPC_7400 = 10 + CPU_SUBTYPE_POWERPC_7450 = 11 + CPU_SUBTYPE_POWERPC_970 = 100 + +with Consts('mh.filetype'): + MH_OBJECT = 0x1 # relocatable object file + MH_EXECUTE = 0x2 # executable binary + MH_FVMLIB = 0x3 + MH_CORE = 0x4 # core dump + MH_PRELOAD = 0x5 + MH_DYLIB = 0x6 # dynamic library + MH_DYLINKER = 0x7 # dynamic linker + MH_BUNDLE = 0x8 # Plug-ins + MH_DYLIB_STUB = 0x9 + MH_DSYM = 0xa # symbol file & debug info + MH_KEXT_BUNDLE = 0xb # kernel extension + +with Consts('mh.flags'): + MH_NOUNDEFS = 0x1 + MH_INCRLINK = 0x2 + MH_DYLDLINK = 0x4 + MH_BINDATLOAD = 0x8 + MH_PREBOUND = 0x10 + MH_SPLIT_SEGS = 0x20 + MH_LAZY_INIT = 0x40 + MH_TWOLEVEL = 0x80 + MH_FORCE_FLAT = 0x100 + MH_NOMULTIDEFS = 0x200 + MH_NOFIXPREBINDING = 0x400 + MH_PREBINDABLE = 0x800 + MH_ALLMODSBOUND = 0x1000 + MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000 + MH_CANONICAL = 0x4000 + MH_WEAK_DEFINES = 0x8000 + MH_BINDS_TO_WEAK = 0x10000 + MH_ALLOW_STACK_EXECUTION = 0x20000 + MH_ROOT_SAFE = 0x40000 + MH_SETUID_SAFE = 0x80000 + MH_NO_REEXPORTED_DYLIBS = 0x100000 + MH_PIE = 0x200000 + MH_DEAD_STRIPPABLE_DYLIB = 0x400000 + MH_HAS_TLV_DESCRIPTORS = 0x800000 + MH_NO_HEAP_EXECUTION = 0x1000000 + +#------------------------------------------------------------------------------ +def token_cmd_fmt(k,val,cls=None): + s = [] + if val&LC_REQ_DYLD: + s.append(highlight([(Token.Name,'LC_REQ_DYLD')])) + s.append(token_name_fmt(k,val,'lc')) + return '+'.join(s) + +@StructDefine(""" +I : cmd +I : cmdsize +""") +class struct_load_command(StructFormatter): + alt = 'lc' + def __init__(self,data=None,offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + +with Consts('lc.cmd'): + LC_REQ_DYLD = 0x80000000 + LC_SEGMENT = 0x1 + LC_SYMTAB = 0x2 + LC_SYMSEG = 0x3 + LC_THREAD = 0x4 + LC_UNIXTHREAD = 0x5 + LC_LOADFVMLIB = 0x6 + LC_IDFVMLIB = 0x7 + LC_IDENT = 0x8 + LC_FVMFILE = 0x9 + LC_PREPAGE = 0xa + LC_DYSYMTAB = 0xb + LC_LOAD_DYLIB = 0xc # load dynamic lib + LC_ID_DYLIB = 0xd # id of a dynamic lib + LC_LOAD_DYLINKER = 0xe + LC_ID_DYLINKER = 0xf + LC_PREBOUND_DYLIB = 0x10 + LC_ROUTINES = 0x11 + LC_SUB_FRAMEWORK = 0x12 + LC_SUB_UMBRELLA = 0x13 + LC_SUB_CLIENT = 0x14 + LC_SUB_LIBRARY = 0x15 + LC_TWOLEVEL_HINTS = 0x16 + LC_PREBIND_CKSUM = 0x17 + LC_LOAD_WEAK_DYLIB = (0x18|LC_REQ_DYLD) + LC_SEGMENT_64 = 0x19 + LC_ROUTINES_64 = 0x1a + LC_UUID = 0x1b + LC_RPATH = (0x1c|LC_REQ_DYLD) + LC_CODE_SIGNATURE = 0x1d + LC_SEGMENT_SPLIT_INFO = 0x1e + LC_REEXPORT_DYLIB = (0x1f|LC_REQ_DYLD) + LC_LAZY_LOAD_DYLIB = 0x20 # defered load dynamic lib + LC_ENCRYPTION_INFO = 0x21 + LC_DYLD_INFO = 0x22 + LC_DYLD_INFO_ONLY = (0x22|LC_REQ_DYLD) + LC_LOAD_UPWARD_DYLIB = (0x23|LC_REQ_DYLD) + LC_VERSION_MIN_MACOSX = 0x24 + LC_VERSION_MIN_IPHONEOS = 0x25 + LC_FUNCTION_STARTS = 0x26 # compressed table of funcs addr + LC_DYLD_ENVIRONMENT = 0x27 + LC_MAIN = (0x28|LC_REQ_DYLD) + LC_DATA_IN_CODE = 0x29 + LC_SOURCE_VERSION = 0x2A + LC_DYLIB_CODE_SIGN_DRS = 0x2B + LC_ENCRYPTION_INFO_64 = 0x2C + LC_LINKER_OPTION = 0x2D + LC_LINKER_OPTIMIZATION_HINT = 0x2E + LC_VERSION_MIN_TVOS = 0x2F + LC_VERSION_MIN_WATCHOS = 0x30 + LC_NOTE = 0x31 + LC_BUILD_VERSION = 0x32 + +#------------------------------------------------------------------------------ + +class MachoFormatter(StructFormatter): + fkeys = defaultdict(default_formatter) + def pack(self,data=None): + res = StructFormatter.pack(self,data) + return res.ljust(self.cmdsize,b'\0') + +@StructDefine(""" +I : cmd +I : cmdsize +s*16 : segname +I : vmaddr +I : vmsize +I : fileoffset +I : filesize +i : maxprot +i : initprot +I : nsects +I : flags +""") +class struct_segment_command(MachoFormatter): + alt = 'sg' + def __init__(self,data=None,offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.func_formatter(segname=self.segname_fmt) + self.flag_formatter('flags') + self.address_formatter('vmaddr','vmsize') + self.address_formatter('fileoffset','filesize') + if data: + self.unpack(data,offset) + offset += len(self) + self.sections = [] + for i in range(self.nsects): + s = struct_section(data,offset) + offset += len(s) + self.sections.append(s) + def segname_fmt(self,k,x,cls=None): + return highlight([(Token.String,str(x.strip(b'\0')))]) + +#------------------------------------------------------------------------------ + +@StructDefine(""" +I : cmd +I : cmdsize +s*16 : segname +Q : vmaddr +Q : vmsize +Q : fileoffset +Q : filesize +i : maxprot +i : initprot +I : nsects +I : flags +""") +class struct_segment_command_64(MachoFormatter): + alt = 'sg' + def __init__(self,data=None,offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.func_formatter(segname=self.segname_fmt) + self.flag_formatter('flags') + self.address_formatter('vmaddr','vmsize') + self.address_formatter('fileoffset','filesize') + if data: + self.unpack(data,offset) + offset += len(self) + self.sections = [] + for i in range(self.nsects): + s = struct_section_64(data,offset) + offset += len(s) + self.sections.append(s) + def segname_fmt(self,k,x,cls=None): + return highlight([(Token.String,str(x.strip(b'\0')))]) + +#------------------------------------------------------------------------------ +with Consts('sg.flags'): + SG_HIGHVM = 0x1 + SG_FVMLIB = 0x2 + SG_NORELOC = 0x4 + SG_PROTECTED_VERSION_1 = 0x8 + +@StructDefine(""" +B : type +B*3 : attr +""") +class SFLG(MachoFormatter): + alt = 'sflg' + def __init__(self,data=None,offset=0): + self.name_formatter('type') + self.func_formatter(attr=self.attr_fmt) + if data: + self.unpack(data,offset) + + @staticmethod + def attr_fmt(k,val,cls=None): + v = val[0]|(val[1]<<8)|(val[2]<<16) + return token_flag_fmt(k,v,cls) + +with Consts('sflg.type'): + S_REGULAR = 0x0 + S_ZEROFILL = 0x1 + S_CSTRING_LITERALS = 0x2 + S_4BYTE_LITERALS = 0x3 + S_8BYTE_LITERALS = 0x4 + S_LITERAL_POINTERS = 0x5 + S_NON_LAZY_SYMBOL_POINTERS = 0x6 + S_LAZY_SYMBOL_POINTERS = 0x7 + S_SYMBOL_STUBS = 0x8 + S_MOD_INIT_FUNC_POINTERS = 0x9 + S_MOD_TERM_FUNC_POINTERS = 0xa + S_COALESCED = 0xb + S_GB_ZEROFILL = 0xc + S_INTERPOSING = 0xd + S_16BYTE_LITERALS = 0xe + S_DTRACE_DOF = 0xf + S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10 + S_THREAD_LOCAL_REGULAR = 0x11 + S_THREAD_LOCAL_ZEROFILL = 0x12 + S_THREAD_LOCAL_VARIABLES = 0x13 + S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14 + S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15 + +with Consts('sflg.attr'): + SECTION_ATTRIBUTES_USR = 0xff000000 + S_ATTR_PURE_INSTRUCTIONS = 0x80000000 + S_ATTR_NO_TOC = 0x40000000 + S_ATTR_STRIP_STATIC_SYMS = 0x20000000 + S_ATTR_NO_DEAD_STRIP = 0x10000000 + S_ATTR_LIVE_SUPPORT = 0x08000000 + S_ATTR_SELF_MODIFYING_CODE = 0x04000000 + S_ATTR_DEBUG = 0x02000000 + SECTION_ATTRIBUTES_SYS = 0x00ffff00 + S_ATTR_SOME_INSTRUCTIONS = 0x00000400 + S_ATTR_EXT_RELOC = 0x00000200 + S_ATTR_LOC_RELOC = 0x00000100 + +@StructDefine(""" +s*16 : sectname +s*16 : segname +I : addr +I : size_ +I : offset +I : align +I : reloff +I : nreloc +SFLG : flags +I : reserved1 +I : reserved2 +""") +class struct_section(MachoFormatter): + alt = 's' + def __init__(self,data=None,offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.address_formatter('addr') + self.address_formatter('offset') + self.address_formatter('reloff') + if data: + self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +s*16 : sectname +s*16 : segname +Q : addr +Q : size_ +I : offset +I : align +I : reloff +I : nreloc +SFLG : flags +I : reserved1 +I : reserved2 +I : reserved3 +""") +class struct_section_64(MachoFormatter): + alt = 's' + def __init__(self,data=None,offset=0): + self.func_formatter(cmd=token_cmd_fmt,offset=0) + self.address_formatter('addr') + self.address_formatter('offset') + self.address_formatter('reloff') + if data: + self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@UnionDefine(""" +I : offset +P : header_addr +""") +class lc_str(MachoFormatter): + def __init__(self,data=None,offset=0): + if data: + self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : offset +I : minor_version +I : header_addr +""") +class struct_fvmlib(MachoFormatter): + def __init__(self,data=None,offset=0): + if data: + self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +struct_fvmlib : fvmlib +""") +class struct_fvmlib_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self.fvmlib.offset + i = data.find(b'\0',sta) + if i!=-1: + self.fvmlib.name = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : offset +I : timestamp +I : current_version +I : compatibility_version +""") +class struct_dylib(MachoFormatter): + def __init__(self,data="",offset=0): + if data: + self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +struct_dylib : dylib +""") +class struct_dylib_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self.dylib.offset + i = data.find(b'\0',sta) + if i!=-1: + self.dylib.name = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +""") +class struct_sub_framework_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self._v.offset + i = data.find(b'\0',sta) + if i!=-1: + self.umbrella = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +""") +class struct_sub_client_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self._v.offset + i = data.find(b'\0',sta) + if i!=-1: + self.client = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +""") +class struct_sub_umbrella_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self._v.offset + i = data.find(b'\0',sta) + if i!=-1: + self.sub_umbrella = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +""") +class struct_sub_library_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self._v.offset + i = data.find(b'\0',sta) + if i!=-1: + self.sub_library = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +I : nmodules +I : linked_modules +""") +class struct_prebound_dylib_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self._v.offset + i = data.find(b'\0',sta) + if i!=-1: + self.name = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +""") +class struct_dylinker_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self._v.offset + i = data.find(b'\0',sta) + if i!=-1: + self.name = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : flavor +I : count +""") +class struct_thread_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.name_formatter('flavor') + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + offset += 16 + self.data = data[offset:offset+self.cmdsize-16] + + def getstate(self,cputype): + if cputype in (X86, X86_64): + cls2 = 'lc.x86' + self.name_formatter('flavor') + if self.flavor == x86_THREAD_STATE32: + self.state = struct_x86_thread_state32(self.data) + self.entrypoint = self.state['eip'] + elif self.flavor == x86_THREAD_STATE64: + self.state = struct_x86_thread_state64(self.data) + self.entrypoint = self.state['rip'] + elif cputype in (ARM, ): + cls2 = 'lc.arm' + self.name_formatter('flavor') + if self.flavor == ARM_THREAD_STATE32: + self.state = struct_x86_thread_state32(self.data) + elif self.flavor == ARM_THREAD_STATE64: + self.state = struct_x86_thread_state64(self.data) + self.entrypoint = self.state['pc'] + return self.state + +with Consts('lc.x86.flavor'): + x86_THREAD_STATE32 = 1 + x86_FLOAT_STATE32 = 2 + x86_EXCEPTION_STATE32 = 3 + x86_THREAD_STATE64 = 4 + x86_FLOAT_STATE64 = 5 + x86_EXCEPTION_STATE64 = 6 + x86_THREAD_STATE = 7 + x86_FLOAT_STATE = 8 + x86_EXCEPTION_STATE = 9 + x86_DEBUG_STATE32 = 10 + x86_DEBUG_STATE64 = 11 + x86_DEBUG_STATE = 12 + THREAD_STATE_NONE = 13 + x86_AVX_STATE32 = 16 + x86_AVX_STATE64 = (x86_AVX_STATE32 + 1) + x86_AVX_STATE = (x86_AVX_STATE32 + 2) + x86_AVX512_STATE32 = 19 + x86_AVX512_STATE64 = (x86_AVX512_STATE32 + 1) + x86_AVX512_STATE = (x86_AVX512_STATE32 + 2) + +with Consts('lc.arm.flavor'): + ARM_THREAD_STATE = 1 + ARM_VFP_STATE = 2 + ARM_EXCEPTION_STATE = 3 + ARM_DEBUG_STATE = 4 + THREAD_STATE_NONE = 5 + ARM_THREAD_STATE64 = 6 + ARM_EXCEPTION_STATE64 = 7 + ARM_THREAD_STATE32 = 9 + ARM_DEBUG_STATE32 = 14 + ARM_DEBUG_STATE64 = 15 + ARM_NEON_STATE = 16 + ARM_NEON_STATE64 = 17 + ARM_CPMU_STATE64 = 18 + ARM_SAVED_STATE32 = 20 + ARM_SAVED_STATE64 = 21 + ARM_NEON_SAVED_STATE32= 20 + ARM_NEON_SAVED_STATE64= 21 + +@StructDefine(""" +I: eax; +I: ebx; +I: ecx; +I: edx; +I: edi; +I: esi; +I: ebp; +I: esp; +I: e8; +I: e9; +I: e10; +I: e11; +I: e12; +I: e13; +I: e14; +I: e15; +I: eip; +I: eflags; +I: cs; +I: fs; +I: gs; +""") +class struct_x86_thread_state32(MachoFormatter): + def __init__(self,data="",offset=0): + for f in self.fields: + self.address_formatter(f.name) + if data: self.unpack(data,offset) + +@StructDefine(""" +Q: rax; +Q: rbx; +Q: rcx; +Q: rdx; +Q: rdi; +Q: rsi; +Q: rbp; +Q: rsp; +Q: r8; +Q: r9; +Q: r10; +Q: r11; +Q: r12; +Q: r13; +Q: r14; +Q: r15; +Q: rip; +Q: rflags; +Q: cs; +Q: fs; +Q: gs; +""") +class struct_x86_thread_state64(MachoFormatter): + def __init__(self,data="",offset=0): + for f in self.fields: + self.address_formatter(f.name) + if data: self.unpack(data,offset) + +@StructDefine(""" +I*13: r +I : sp +I : lr +I : pc +I : cpsr +""") +class struct_arm_thread_state32(MachoFormatter): + def __init__(self,data="",offset=0): + for f in self.fields: + self.address_formatter(f.name) + if data: self.unpack(data,offset) + +@StructDefine(""" +Q*29: x +Q : fp +Q : lr +Q : sp +Q : pc +I : cpsr +I : pad +""") +class struct_arm_thread_state64(MachoFormatter): + def __init__(self,data="",offset=0): + for f in self.fields: + self.address_formatter(f.name) + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : init_address +I : init_module +I : reserved1 +I : reserved2 +I : reserved3 +I : reserved4 +I : reserved5 +I : reserved6 +""") +class struct_routines_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +Q : init_address +Q : init_module +Q : reserved1 +Q : reserved2 +Q : reserved3 +Q : reserved4 +Q : reserved5 +Q : reserved6 +""") +class struct_routines_command_64(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : symoff +I : nsyms +I : stroff +I : strsize +""") +class struct_symtab_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.address_formatter('symoff') + self.address_formatter('stroff') + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + +@StructDefine(""" +I : n_strx +B : n_type +B : n_sect +H : n_desc +I : n_value +""") +class struct_nlist(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.address_formatter('n_value') + self.func_formatter(n_type=self.ntype_fmt) + self.func_formatter(n_desc=self.ndesc_fmt) + if data: + self.unpack(data,offset) + + def ntype_fmt(self,k,x,cls=None): + if x & N_STAB: + return token_name_fmt(k,x,'lc.stab') + else: + l = [] + if x&N_TYPE == 0x0: l.append('N_UNDF') + elif x&N_TYPE == 0x2: l.append('N_ABS') + elif x&N_TYPE == 0xe: l.append('N_SECT') + elif x&N_TYPE == 0xc: l.append('N_PBUD') + elif x&N_TYPE == 0xa: l.append('N_INDR') + if x & N_EXT: l.append('N_PEXT') + s = '+'.join(l) + return highlight([(Token.Name,s)]) + + def ndesc_fmt(self,k,x,cls=None): + s = token_name_fmt(k,x&0xf,cls) + if x&0xf0: + s += '+'+ token_flag_fmt(k,x&0xf0,'lc.flag') + return s + +@StructDefine(""" +I : n_strx +B : n_type +B : n_sect +H : n_desc +Q : n_value +""") +class struct_nlist64(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.address_formatter('n_value') + self.func_formatter(n_type=self.ntype_fmt) + self.func_formatter(n_desc=self.ndesc_fmt) + if data: + self.unpack(data,offset) + + def ntype_fmt(self,k,x,cls=None): + if x & N_STAB: + return token_name_fmt(k,x,'lc.stab') + else: + l = [] + if x & N_PEXT: l.append('N_PEXT') + if x&N_TYPE == 0x0: l.append('N_UNDF') + elif x&N_TYPE == 0x2: l.append('N_ABS') + elif x&N_TYPE == 0xe: l.append('N_SECT') + elif x&N_TYPE == 0xc: l.append('N_PBUD') + elif x&N_TYPE == 0xa: l.append('N_INDR') + if x & N_EXT: l.append('N_PEXT') + s = '+'.join(l) + return highlight([(Token.Name,s)]) + + def ndesc_fmt(self,k,x,cls=None): + s = token_name_fmt(k,x&0xf,cls) + if x&0xf0: + s += '+'+ token_flag_fmt(k,x&0xf0,'lc.flag') + return s + +with Consts('lc.n_type'): + N_STAB = 0xe0 + N_PEXT = 0x10 + N_TYPE = 0x0e + N_EXT = 0x01 + N_UNDF = 0x0 + N_ABS = 0x2 + N_SECT = 0xe + N_PBUD = 0xc + N_INDR = 0xa + +with Consts('lc.stab.n_type'): + N_GSYM = 0x20 # global symbol: name,,NO_SECT,type,0 + N_FNAME = 0x22 # procedure name (f77 kludge): name,,NO_SECT,0,0 + N_FUN = 0x24 # procedure: name,,n_sect,linenumber,address + N_STSYM = 0x26 # static symbol: name,,n_sect,type,address + N_LCSYM = 0x28 # .lcomm symbol: name,,n_sect,type,address + N_BNSYM = 0x2e # begin nsect sym: 0,,n_sect,0,address + N_AST = 0x32 # AST file path: name,,NO_SECT,0,0 + N_OPT = 0x3c # emitted with gcc2_compiled and in gcc source + N_RSYM = 0x40 # register sym: name,,NO_SECT,type,register + N_SLINE = 0x44 # src line: 0,,n_sect,linenumber,address + N_ENSYM = 0x4e # end nsect sym: 0,,n_sect,0,address + N_SSYM = 0x60 # structure elt: name,,NO_SECT,type,struct_offset + N_SO = 0x64 # source file name: name,,n_sect,0,address + N_OSO = 0x66 # object file name: name,,0,0,st_mtime + N_LSYM = 0x80 # local sym: name,,NO_SECT,type,offset + N_BINCL = 0x82 # include file beginning: name,,NO_SECT,0,sum + N_SOL = 0x84 # #included file name: name,,n_sect,0,address + N_PARAMS = 0x86 # compiler parameters: name,,NO_SECT,0,0 + N_VERSION = 0x88 # compiler version: name,,NO_SECT,0,0 + N_OLEVEL = 0x8A # compiler -O level: name,,NO_SECT,0,0 + N_PSYM = 0xa0 # parameter: name,,NO_SECT,type,offset + N_EINCL = 0xa2 # include file end: name,,NO_SECT,0,0 + N_ENTRY = 0xa4 # alternate entry: name,,n_sect,linenumber,address + N_LBRAC = 0xc0 # left bracket: 0,,NO_SECT,nesting level,address + N_EXCL = 0xc2 # deleted include file: name,,NO_SECT,0,sum + N_RBRAC = 0xe0 # right bracket: 0,,NO_SECT,nesting level,address + N_BCOMM = 0xe2 # begin common: name,,NO_SECT,0,0 + N_ECOMM = 0xe4 # end common: name,,n_sect,0,0 + N_ECOML = 0xe8 # end common (local name): 0,,n_sect,0,address + N_LENG = 0xfe # second stab entry with length information + N_PC = 0x30 # global pascal symbol: name,,NO_SECT,subtype,line + +with Consts('lc.n_desc'): + REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0x0 + REFERENCE_FLAG_UNDEFINED_LAZY = 0x1 + REFERENCE_FLAG_UNDEFINED = 0x2 + REFERENCE_FLAG_PRIVATE_DEFINED = 0x3 + REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 0x4 + REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 0x5 +with Consts('lc.flag.n_desc'): + REFERENCED_DYNAMICALLY = 0x10 + N_DESC_DISCARDED = 0x20 + N_NO_DEAD_STRIP = 0x20 + N_WEAK_REF = 0x40 + N_WEAK_DEF = 0x80 + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : ilocalsym +I : nlocalsym +I : iextdefsym +I : nextdefsym +I : iundefsym +I : nundefsym +I : tocoff +I : ntoc +I : modtaboff +I : nmodtab +I : extrefsymoff +I : nextrefsyms +I : indirectsymoff +I : nindirectsyms +I : extreloff +I : nextrel +I : locreloff +I : nlocrel +""") +class struct_dysymtab_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.address_formatter('tocoff','modtaboff','extrefsymoff', + 'indirectsymoff','extreloff','locreloff') + if data: self.unpack(data,offset) + +INDIRECT_SYMBOL_LOCAL = 0x80000000 +INDIRECT_SYMBOL_ABS = 0x40000000 + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : symbol_index +I : module_index +""") +class struct_dylib_table_of_contents(MachoFormatter): + def __init__(self,data="",offset=0): + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : module_name +I : iextdefsym +I : nextdefsym +I : irefsym +I : nrefsym +I : ilocalsym +I : nlocalsym +I : iextrel +I : nextrel +I : iinit_iterm +I : ninit_nterm +I : objc_module_info_addr +I : objc_module_info_size +""") +class struct_dylib_module(MachoFormatter): + def __init__(self,data="",offset=0): + self.address_formatter('objc_module_info_addr') + self.address_formatter('objc_module_info_size') + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : module_name +I : iextdefsym +I : nextdefsym +I : irefsym +I : nrefsym +I : ilocalsym +I : nlocalsym +I : iextrel +I : nextrel +I : iinit_iterm +I : ninit_nterm +I : objc_module_info_size +Q : objc_module_info_addr +""") +class struct_dylib_module_64(MachoFormatter): + def __init__(self,data="",offset=0): + self.address_formatter('objc_module_info_addr') + self.address_formatter('objc_module_info_size') + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +B*3 : isym +B : flags +""") +class struct_dylib_reference(MachoFormatter): + def __init__(self,data="",offset=0): + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +I : nhints +""") +class struct_twolevel_hints_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.address_formatter('offset') + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +B : isub_image +B*3 : itoc +""") +class twolevel_hint(MachoFormatter): + def __init__(self,data="",offset=0): + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : cksum +""") +class struct_prebind_cksum_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +B*16 : uuid +""") +class struct_uuid_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.func_formatter(uuid=self.uuid_fmt) + if data: self.unpack(data,offset) + def uuid_fmt(self,k,x,cls=None): + return highlight([(Token.String, + '.'.join(("%02d"%v for v in x)))]) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +""") +class struct_rpath_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self._v.offset + i = data.find(b'\0',sta) + if i!=-1: + self.path = data[sta:i] + + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : dataoff +I : datasize +""") +class struct_linkedit_data_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.address_formatter('dataoff','datasize') + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : cryptoff +I : cryptsize +I : cryptid +""") +class struct_encryption_info_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.address_formatter('cryptoff','cryptsize') + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : rebase_off +I : rebase_size +I : bind_off +I : bind_size +I : weak_bind_off +I : weak_bind_size +I : lazy_bind_off +I : lazy_bind_size +I : export_off +I : export_size +""") +class struct_dyld_info_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.address_formatter('rebase_off','rebase_size') + self.address_formatter('bind_off','bind_size') + self.address_formatter('weak_bind_off','weak_bind_size') + self.address_formatter('lazy_bind_off','lazy_bind_size') + self.address_formatter('export_off','export_size') + self.func_formatter(cmd=token_cmd_fmt) + if data: self.unpack(data,offset) + +class record(object): + def __init__(self,indx,off,ordinal,typ,flags,addend,s=None): + self.seg_index = indx + self.seg_offset = off + self.lib_ordinal = ordinal + self.type = typ + self.flags = flags + self.addend = addend + self.symbol = s + def as_list(self): + s = (self.seg_index,self.seg_offset, + self.lib_ordinal, + self.type, + self.flags, + self.addend, + self.symbol) + return s + +with Consts('dyld.rebase'): + REBASE_TYPE_POINTER = 1 + REBASE_TYPE_TEXT_ABSOLUTE32 = 2 + REBASE_TYPE_TEXT_PCREL32 = 3 + REBASE_OPCODE_MASK = 0xF0 + REBASE_IMMEDIATE_MASK = 0x0F + REBASE_OPCODE_DONE = 0x00 + REBASE_OPCODE_SET_TYPE_IMM = 0x10 + REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20 + REBASE_OPCODE_ADD_ADDR_ULEB = 0x30 + REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40 + REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50 + REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60 + REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70 + REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80 + +with Consts('dyld.bind'): + BIND_TYPE_POINTER = 1 + BIND_TYPE_TEXT_ABSOLUTE32 = 2 + BIND_TYPE_TEXT_PCREL32 = 3 + BIND_SPECIAL_DYLIB_SELF = 0 + BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE =-1 + BIND_SPECIAL_DYLIB_FLAT_LOOKUP =-2 + BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1 + BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8 + BIND_OPCODE_MASK = 0xF0 + BIND_IMMEDIATE_MASK = 0x0F + BIND_OPCODE_DONE = 0x00 + BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10 + BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20 + BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30 + BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40 + BIND_OPCODE_SET_TYPE_IMM = 0x50 + BIND_OPCODE_SET_ADDEND_SLEB = 0x60 + BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70 + BIND_OPCODE_ADD_ADDR_ULEB = 0x80 + BIND_OPCODE_DO_BIND = 0x90 + BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0 + BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0 + BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0 + +with Consts('dyld.export'): + EXPORT_SYMBOL_FLAGS_KIND_MASK = 0x03 + EXPORT_SYMBOL_FLAGS_KIND_REGULAR = 0x00 + EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL = 0x01 + EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION = 0x04 + EXPORT_SYMBOL_FLAGS_INDIRECT_DEFINITION = 0x08 + EXPORT_SYMBOL_FLAGS_HAS_SPECIALIZATIONS = 0x10 + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +I : size_ +""") +class struct_symseg_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +""") +class struct_ident_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : offset +I : header_attr +""") +class struct_fvmfile_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + if data: + self.unpack(data,offset) + sta = offset+self._v.offset + i = data.find(b'\0',sta) + if i!=-1: + self.name = data[sta:i] + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +Q : entryoff +Q : stacksize +""") +class struct_entry_point_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.address_formatter('entryoff') + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : offset +H : length +H : kind +""") +class struct_data_in_code_entry(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.name_formatter('kind') + if data: self.unpack(data,offset) + +with Consts('lc.kind'): + DICE_KIND_DATA =0x0001 + DICE_KIND_JUMP_TABLE8 =0x0002 + DICE_KIND_JUMP_TABLE16 =0x0003 + DICE_KIND_JUMP_TABLE32 =0x0004 + DICE_KIND_ABS_JUMP_TABLE32 =0x0005 + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +s*16 : data_owner +Q : offset +Q : size +""") +class struct_note_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.address_formatter('offset') + if data: self.unpack(data,offset) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +Q : version +""") +class struct_source_version_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.func_formatter(version=self.version_fmt) + if data: self.unpack(data,offset) + def version_fmt(self,k,v,cls=None): + A = (v>>40) + B = (v>>30)&0x3ff + C = (v>>20)&0x3ff + D = (v>>10)&0x3ff + E = (v&0x3ff) + return highlight([(Token.String,'%d.%d.%d.%d.%d'%(A,B,C,D,E))]) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : version +I : sdk +""") +class struct_version_min_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.func_formatter(version=self.version_fmt) + self.func_formatter(sdk=self.version_fmt) + if data: + self.unpack(data,offset) + def version_fmt(self,k,v,cls=None): + x = (v&0xffff0000)>>16 + y = (v&0x0000ff00)>>8 + z = (v&0xff) + return highlight([(Token.String,'%d.%d.%d'%(x,y,z))]) + +#------------------------------------------------------------------------------ +@StructDefine(""" +I : cmd +I : cmdsize +I : platform +I : minos +I : sdk +I : ntools +""") +class struct_build_version_command(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(cmd=token_cmd_fmt) + self.name_formatter('platform') + if data: + self.unpack(data,offset) + offset += len(self) + self.tools = [] + for _ in range(self.ntools): + x = struct_build_tool_version(data,offset) + self.tools.append(x) + offset += len(x) + +with Consts('lc.platform'): + PLATFORM_MACOS = 1 + PLATFORM_IOS = 2 + PLATFORM_TVOS = 3 + PLATFORM_WATCHOS = 4 + +@StructDefine(""" +I : tool +I : version +""") +class struct_build_tool_version(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + self.func_formatter(version=self.version_fmt) + self.name_formatter('tool') + if data: + self.unpack(data,offset) + def version_fmt(self,k,v,cls=None): + x = (v&0xffff0000)>>16 + y = (v&0x0000ff00)>>8 + z = (v&0xff) + return highlight([(Token.String,'%d.%d.%d'%(x,y,z))]) + +with Consts('lc.tool'): + TOOL_CLANG = 1 + TOOL_SWIFT = 2 + TOOL_LD = 3 + +CMD_TABLE = { + LC_SEGMENT: struct_segment_command, + LC_SEGMENT_64: struct_segment_command_64, + LC_SYMTAB: struct_symtab_command, + LC_SYMSEG: struct_symseg_command, + LC_THREAD: struct_thread_command, + LC_UNIXTHREAD: struct_thread_command, + LC_LOADFVMLIB: struct_fvmlib_command, + LC_IDFVMLIB: struct_fvmlib_command, + LC_IDENT: struct_ident_command, + LC_FVMFILE: struct_fvmfile_command, + LC_DYSYMTAB: struct_dysymtab_command, + LC_LOAD_DYLIB: struct_dylib_command, + LC_LOAD_WEAK_DYLIB: struct_dylib_command, + LC_ID_DYLIB: struct_dylib_command, + LC_REEXPORT_DYLIB: struct_dylib_command, + LC_LOAD_DYLINKER: struct_dylinker_command, + LC_ID_DYLINKER: struct_dylinker_command, + LC_SUB_FRAMEWORK: struct_sub_framework_command, + LC_SUB_CLIENT: struct_sub_client_command, + LC_SUB_LIBRARY: struct_sub_library_command, + LC_SUB_UMBRELLA: struct_sub_umbrella_command, + LC_TWOLEVEL_HINTS: struct_twolevel_hints_command, + LC_PREBOUND_DYLIB: struct_prebound_dylib_command, + LC_PREBIND_CKSUM: struct_prebind_cksum_command, + LC_UUID: struct_uuid_command, + LC_RPATH: struct_rpath_command, + LC_ROUTINES: struct_routines_command, + LC_ROUTINES_64: struct_routines_command_64, + LC_DYLD_INFO: struct_dyld_info_command, + LC_DYLD_INFO_ONLY: struct_dyld_info_command, + LC_ENCRYPTION_INFO: struct_encryption_info_command, + LC_CODE_SIGNATURE: struct_linkedit_data_command, + LC_SEGMENT_SPLIT_INFO: struct_linkedit_data_command, + LC_FUNCTION_STARTS: struct_linkedit_data_command, + LC_DATA_IN_CODE: struct_linkedit_data_command, + LC_DYLIB_CODE_SIGN_DRS: struct_linkedit_data_command, + LC_VERSION_MIN_MACOSX: struct_version_min_command, + LC_VERSION_MIN_IPHONEOS: struct_version_min_command, + LC_SOURCE_VERSION: struct_source_version_command, + LC_MAIN: struct_entry_point_command, + LC_NOTE: struct_note_command, + LC_BUILD_VERSION: struct_build_version_command, +} + +@StructDefine(""" +I : r_address +I : r_symbolnum +""") +class struct_relocation_info(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + if data: + self.unpack(data,offset) + +@StructDefine(""" +I : index +""") +class struct_indirect_entry(MachoFormatter): + alt = 'lc' + def __init__(self,data="",offset=0): + if data: + self.unpack(data,offset) diff --git a/amoco/system/osx/__init__.py b/amoco/system/osx/__init__.py new file mode 100644 index 0000000..cb700d9 --- /dev/null +++ b/amoco/system/osx/__init__.py @@ -0,0 +1,9 @@ +from amoco.config import conf +from amoco.system.core import DefineLoader +from amoco.system import macho + +@DefineLoader(macho.X86_64) +def loader_osx(p): + from amoco.system.osx.x64 import OS + return OS.loader(p,conf.System) + diff --git a/amoco/system/osx/x64.py b/amoco/system/osx/x64.py new file mode 100644 index 0000000..bb83cb6 --- /dev/null +++ b/amoco/system/osx/x64.py @@ -0,0 +1,245 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2020 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +from amoco.system.macho import * +from amoco.system.core import CoreExec +from amoco.code import tag +import amoco.arch.x64.cpu_x64 as cpu + +#------------------------------------------------------------------------------ + +class OS(object): + """OS class is a provider for all the environment in which a Task runs. + It is responsible for setting up the (virtual) memory of the Task as well + as providing stubs for dynamic library calls and possibly system calls. + + In the specific case of osx.x64, the OS class will stub all libc + functions including a simulated heap memory allocator API. + """ + stubs = {} + default_stub = (lambda env,**kargs:None) + + def __init__(self,conf=None): + if conf is None: + from amoco.config import System + conf = System() + else: + self.PAGESIZE = conf.pagesize + self.ASLR = conf.aslr + self.NX = conf.nx + self.tasks = [] + self.abi = None + + @classmethod + def loader(cls,bprm,conf=None): + return cls(conf).load_macho_binary(bprm) + + def load_macho_binary(self,bprm): + "load the program into virtual memory (populate the mmap dict)" + p = Task(bprm,cpu) + p.OS = self + do_stack = False + interp = None + # do load commands: + for s in bprm.cmds: + if s.cmd == LC_LOAD_DYLINKER: + interp = s.offset + elif s.cmd == LC_SEGMENT_64: + if s.segname.startswith(b'__PAGEZERO\0'): + continue + data = bprm.readsegment(s).ljust(s.vmsize,b'\0') + p.state.mmap.write(s.vmaddr,data) + elif s.cmd in (LC_THREAD,LC_UNIXTHREAD): + if s.flavor == x86_THREAD_STATE64: + for f in s.state.fields: + r = getattr(cpu,f.name) + p.state[r] = cpu.cst(s.state[f.name],r.size) + bprm.__entry = p.state[cpu.rip] + if s.cmd==LC_UNIXTHREAD: + do_stack = True + stack_size = 2*self.PAGESIZE + elif s.cmd == LC_MAIN: + p.state[cpu.rip] = cpu.cst(s.entryoff,64) + bprm.__entry = p.state[cpu.rip] + if s.stacksize: + do_stack = True + stack_size = s.stacksize + # create the stack space: + if do_stack: + if self.ASLR: + p.state.mmap.newzone(p.cpu.rsp) + p.state[cpu.rsp] = cpu.rsp + else: + stack_base = (0x00007fffffffffff & ~(self.PAGESIZE-1)) + p.state.mmap.write(stack_base-stack_size,b'\0'*stack_size) + p.state[cpu.rsp] = cpu.cst(stack_base,64) + + # create the dynamic segments: + if bprm.dynamic and interp: + self.load_macho_interp(p,interp) + # return task: + self.tasks.append(p) + return p + + def load_macho_interp(self,p,interp): + for k,f in p.bin.la_symbol_ptr.items(): + xfunc = cpu.ext(f,size=64) + xfunc.stub = p.OS.stub(f) + p.state.mmap.write(k,xfunc) + + def stub(self,refname): + return self.stubs.get(refname,self.default_stub) + + +class Task(CoreExec): + + # seqhelper provides arch-dependent information to amoco.main classes + def seqhelper(self,seq): + for i in seq: + # some basic hints: + if i.mnemonic.startswith('RET'): + i.misc[tag.FUNC_END]=1 + continue + elif i.mnemonic in ('PUSH','ENTER'): + i.misc[tag.FUNC_STACK]=1 + if i.operands and i.operands[0] is cpu.rbp: + i.misc[tag.FUNC_START]=1 + continue + elif i.mnemonic in ('POP','LEAVE'): + i.misc[tag.FUNC_UNSTACK]=1 + if i.operands and i.operands[0] is cpu.rbp: + i.misc[tag.FUNC_END]=1 + continue + # provide hints of absolute location from relative offset: + elif i.mnemonic in ('CALL','JMP','Jcc'): + if i.mnemonic == 'CALL': + i.misc[tag.FUNC_CALL]=1 + i.misc['retto'] = i.address+i.length + else: + i.misc[tag.FUNC_GOTO]=1 + if i.mnemonic == 'Jcc': + i.misc['cond'] = i.cond + if (i.address is not None) and i.operands[0]._is_cst: + v = i.address+i.operands[0].signextend(64)+i.length + x = self.check_sym(v) + if x is not None: v=x + i.misc['to'] = v + if i.misc[tag.FUNC_CALL] and i.misc['retto']==v: + # this looks like a fake call + i.misc[tag.FUNC_CALL]=-1 + continue + # check operands (globals & .got calls): + for op in i.operands: + if op._is_mem: + if op.a.base is cpu.rbp: + if op.a.disp<0: i.misc[tag.FUNC_VAR]=True + elif op.a.disp>=16: i.misc[tag.FUNC_ARG]=True + elif op.a.base._is_cst or (op.a.base is cpu.rip): + b = op.a.base + if b is cpu.rip: b=i.address+i.length + x = self.check_sym(b+op.a.disp) + if x is not None: + op.a.base=x + op.a.disp=0 + if i.mnemonic == 'JMP': # PLT jumps: + i.misc[tag.FUNC_START]=1 + i.misc[tag.FUNC_END]=1 + elif op._is_cst: + x = self.check_sym(op) + i.misc['imm_ref'] = x + return seq + + def blockhelper(self,block): + block._helper = block_helper_ + return CoreExec.blockhelper(self,block) + + def funchelper(self,f): + # check single root node: + roots = f.cfg.roots() + if len(roots)==0: + roots = filter(lambda n:n.data.misc[tag.FUNC_START],f.cfg.sV) + if len(roots)==0: + logger.warning("no entry to function %s found"%f) + if len(roots)>1: + logger.verbose('multiple entries into function %s ?!'%f) + # check start symbol: + elif roots[0].data.address == self.bin.entrypoints[0]: + f.name = '_start' + # get section symbol if any: + f.misc['section'] = section = self.bin.getinfo(f.address.value)[0] + rets = f.cfg.leaves() + if len(rets)==0: + logger.warning("no exit to function %s found"%f) + if len(rets)>1: + logger.verbose('multiple exits in function %s'%f) + for r in rets: + # export PLT external symbol name: + if section and section.name=='.plt': + if isinstance(r.data,xfunc): f.name = section.name+r.name + if r.data.misc[tag.FUNC_CALL]: + f.misc[tag.FUNC_CALL] += 1 + if f.map: + # check vars & args: should reflect x64 register calling convention + f.misc[tag.FUNC_VAR] = [] + f.misc[tag.FUNC_ARG] = [] + for x in set(f.map.inputs()): + f.misc[tag.FUNC_IN] += 1 + if x._is_mem and x.a.base==cpu.rsp: + if x.a.disp>=8: + f.misc[tag.FUNC_ARG].append(x) + for x in set(f.map.outputs()): + if x in (cpu.rsp, cpu.rbp): continue + f.misc[tag.FUNC_OUT] += 1 + if x._is_mem and x.a.base==cpu.rsp: + if x.a.disp<0: + f.misc[tag.FUNC_VAR].append(x) + + +def block_helper_(block,m): + # annotations based on block semantics: + sta,sto = block.support + if m[cpu.mem(cpu.rbp-8,64)] == cpu.rbp: + block.misc[tag.FUNC_START]=1 + if m[cpu.rip]==cpu.mem(cpu.rsp-8,64): + block.misc[tag.FUNC_END]=1 + if m[cpu.mem(cpu.rsp,64)]==sto: + block.misc[tag.FUNC_CALL]=1 + +#---------------------------------------------------------------------------- + +# STUBS DEFINED HERE : +#---------------------------------------------------------------------------- + +from amoco.system.core import DefineStub + +@DefineStub(OS,'*',default=True) +def pop_rip(m,**kargs): + cpu.pop(m,cpu.rip) + +@DefineStub(OS,'__libc_start_main') +def libc_start_main(m,**kargs): + "tags: func_call" + m[cpu.rip] = m(cpu.rdi) + cpu.push(m,cpu.ext('exit',size=64)) + +@DefineStub(OS,'exit') +def libc_exit(m,**kargs): + m[cpu.rip] = top(64) +@DefineStub(OS,'abort') +def libc_abort(m,**kargs): + m[cpu.rip] = top(64) +@DefineStub(OS,'__assert') +def libc_assert(m,**kargs): + m[cpu.rip] = top(64) +@DefineStub(OS,'__assert_fail') +def libc_assert_fail(m,**kargs): + m[cpu.rip] = top(64) +@DefineStub(OS,'_assert_perror_fail') +def _assert_perror_fail(m,**kargs): + m[cpu.rip] = top(64) + +#---------------------------------------------------------------------------- + diff --git a/amoco/system/pe.py b/amoco/system/pe.py index 502b5b2..f3d09d5 100644 --- a/amoco/system/pe.py +++ b/amoco/system/pe.py @@ -587,7 +587,7 @@ def classname(self): 4 : 'REGISTER', 5 : 'EXTERNAL_DEF', 6 : 'LABEL', - 6 : 'UNDEFINED_LABEL', + 7 : 'UNDEFINED_LABEL', 8 : 'MEMBER_OF_STRUCT', 9 : 'ARGUMENT', 10 : 'STRUCT_TAG', diff --git a/amoco/system/structs.py b/amoco/system/structs.py index 1e3070f..4006cd4 100644 --- a/amoco/system/structs.py +++ b/amoco/system/structs.py @@ -13,10 +13,10 @@ various fields according to given types like hex numbers, dates, defined constants, etc. This module extends capabilities of :mod:`struct` by allowing formats to -include more than just the basic types and add *named* fields. +include more than just the basic types and add *named* fields. It extends :mod:`ctypes` as well by allowing formatted printing and "non-static" decoding where the way a field is decoded depends on -previously decoded fields. +previously decoded fields. Module :mod:`system.imx6` uses these classes to decode HAB structures and thus allow for precise verifications on how the boot stages are verified. @@ -85,7 +85,46 @@ def token_ver_format(k,x,cls=None): class Consts(object): """Provides a contextmanager to map constant values with their names in - order to build the associated reverse-dictionary.""" +order to build the associated reverse-dictionary. + +All revers-dict are stored inside the Consts class definition. +For example if you declare variables in a Consts('example') with-scope, +the reverse-dict will be stored in Consts.All['example']. +When StructFormatter will lookup a variable name matching a given value +for the attribute 'example', it will get Consts.All['example'][value]. + +Note: To avoid attribute name conflicts, the lookup is always prepended +the stucture class name (or the 'alt' field of the structure class). +Hence, the above 'tag' constants could have been defined as:: + + with Consts('HAB_header.tag'): + HAB_TAG_IVT = 0xd1 + HAB_TAG_DCD = 0xd2 + HAB_TAG_CSF = 0xd4 + HAB_TAG_CRT = 0xd7 + HAB_TAG_SIG = 0xd8 + HAB_TAG_EVT = 0xdb + HAB_TAG_RVT = 0xdd + HAB_TAG_WRP = 0x81 + HAB_TAG_MAC = 0xac + +Or the structure definition could have define an 'alt' attribute:: + + @StructDefine(\"\"\" + B : tag + H :> length + B : version + \"\"\") + class HAB_Header(StructFormatter): + alt = 'hab' + [...] + +in which case the variables could have been defined with:: + + with Consts('hab.tag'): + [...] + +""" All = defaultdict(dict) def __init__(self,name): self.name = name @@ -100,22 +139,39 @@ def __exit__(self,exc_type,exc_value,traceback): for k in set(G.keys())-self.globnames: self.All[self.name][G[k]] = k +#------------------------------------------------------------------------------ + def default_formatter(): return token_default_fmt def token_default_fmt(k,x,cls=None): + """The default formatter just prints value 'x' of attribute 'k' + as a literal token python string + """ return highlight([(Token.Literal,str(x))]) def token_address_fmt(k,x,cls=None): + """The address formatter prints value 'x' of attribute 'k' + as a address token hexadecimal value + """ return highlight([(Token.Address,hex(x))]) def token_constant_fmt(k,x,cls=None): + """The constant formatter prints value 'x' of attribute 'k' + as a constant token decimal value + """ return highlight([(Token.Constant,str(x))]) def token_mask_fmt(k,x,cls=None): + """The mask formatter prints value 'x' of attribute 'k' + as a constant token hexadecimal value + """ return highlight([(Token.Constant,hex(x))]) def token_name_fmt(k,x,cls=None): + """The name formatter prints value 'x' of attribute 'k' + as a name token variable symbol matching the value + """ pfx = "%s."%cls if cls!=None else "" if pfx+k in Consts.All: k = pfx+k ks = k @@ -125,6 +181,10 @@ def token_name_fmt(k,x,cls=None): return token_constant_fmt(k,x) def token_flag_fmt(k,x,cls): + """The flag formatter prints value 'x' of attribute 'k' + as a name token variable series of symbols matching + the flag value + """ s = [] pfx = "%s."%cls if cls!=None else "" if pfx+k in Consts.All: k = pfx+k @@ -134,6 +194,9 @@ def token_flag_fmt(k,x,cls): return ','.join(s) if len(s)>0 else token_mask_fmt(k,x) def token_datetime_fmt(k,x,cls=None): + """The date formatter prints value 'x' of attribute 'k' + as a date token UTC datetime string from timestamp value + """ from datetime import datetime return highlight([(Token.Date,str(datetime.utcfromtimestamp(x)))]) @@ -406,13 +469,16 @@ class StructDefine(object): 'i':4, 'I':4, 'l':4, 'L':4, 'f':4, 'q':8, 'Q':8, 'd':8, 'P':8} - integer = pp.Regex(r'[1-9][0-9]*') + integer = pp.Regex(r'[0-9][0-9]*') integer.setParseAction(lambda r: int(r[0])) + bitslen = pp.Group(pp.Suppress('#')+integer+pp.Suppress('.')+integer) symbol = pp.Regex(r'[A-Za-z_][A-Za-z0-9_]*') comment = pp.Suppress(';')+pp.restOfLine - fieldname = pp.Suppress(':')+pp.Group(pp.Optional(pp.Literal('>')|pp.Literal('<'),default=None)+symbol) + fieldname = pp.Suppress(':')+\ + pp.Group(pp.Optional(pp.Literal('>')|pp.Literal('<'),default=None)+\ + symbol) inf = pp.Regex(r'~[bBhHiI]?') - length = integer|symbol|inf + length = integer|symbol|inf|bitslen typename = pp.Group(symbol+pp.Optional(pp.Suppress('*')+length,default=0)) structfmt = pp.OneOrMore(pp.Group(typename+fieldname+pp.Optional(comment,default=''))) @@ -422,7 +488,7 @@ def __init__(self,fmt,**kargs): self.packed = kargs.get('packed',False) if 'alignments' in kargs: self.alignments = kargs['alignments'] - for l in self.structfmt.parseString(fmt,True): + for l in self.structfmt.parseString(fmt,True).asList(): f_type,f_name,f_comment = l f_order,f_name = f_name f_type,f_count = f_type @@ -445,8 +511,11 @@ def __call__(self,cls): cls.fields = self.fields cls.source = self.source cls.packed = self.packed + cls.fkeys = defaultdict(default_formatter) return cls +#------------------------------------------------------------------------------ + class UnionDefine(StructDefine): """UnionDefine is a decorator class based on StructDefine, used for defining unions. """ @@ -458,6 +527,8 @@ def __call__(self,cls): cls.union = s.index(max(s)) return cls +#------------------------------------------------------------------------------ + def TypeDefine(newname, typebase, typecount=0,align_value=0): if typebase in StructDefine.rawtypes: f_cls = RawField @@ -467,6 +538,8 @@ def TypeDefine(newname, typebase, typecount=0,align_value=0): f_align = 0 StructDefine.All[newname] = f_cls(typebase,fcount=typecount,falign=f_align,fname='typedef') +#------------------------------------------------------------------------------ + class StructCore(object): """StructCore is a ParentClass for all user-defined structures based on a StructDefine format. This class contains essentially the packing and unpacking logic of the structure. @@ -475,13 +548,27 @@ class StructCore(object): with no arguments. """ packed = False - union = False + union = False def __new__(cls,*args,**kargs): obj = super(StructCore,cls).__new__(cls) obj.fields = [f.copy() for f in cls.fields] + t = type('container',(object,),{}) + obj._v = t() return obj + def __getitem__(self,fname): + return getattr(self._v,fname) + + def __setitem__(self,fname,x): + setattr(self._v,fname,x) + + def __getattr__(self,attr): + if attr not in self.__dict__: + return getattr(self._v,attr) + else: + return self.__dict__[attr] + @classmethod def format(cls): if cls.union is False: @@ -520,13 +607,13 @@ def unpack(self,data,offset=0): for f in self.fields: if self.union is False and not self.packed: offset = f.align(offset) - setattr(self,f.name,f.unpack(data,offset)) + setattr(self._v,f.name,f.unpack(data,offset)) if self.union is False: offset += f.size() return self def pack(self,data=None): if data is None: - data = [getattr(self,f.name) for f in self.fields] + data = [getattr(self._v,f.name) for f in self.fields] parts = [] offset = 0 for f,v in zip(self.fields,data): @@ -552,17 +639,18 @@ def offset_of(self,name): raise AttributeError(name) class StructFormatter(StructCore): - """StructFormatter is the Parent Class for all user-defined structures based on a StructDefine format. - It inherits the core logic from StructCore Parent and provides all formatting facilities to pretty - print the structures based on wether the field is declared as a named constant, an integer of hex value, + """StructFormatter is the Parent Class for all user-defined structures + based on a StructDefine format. + It inherits the core logic from StructCore Parent and provides all + formatting facilities to pretty print the structures based on wether + the field is declared as a named constant, an integer of hex value, a pointer address, a string or a date. - Note: Since it inherits from StructCore, it is mandatory that any child class can be instanciated - with no arguments. - """ - fkeys = defaultdict(default_formatter) + Note: Since it inherits from StructCore, it is mandatory that any child + class can be instanciated with no arguments. +""" pfx = '' - ksz = 20 + alt = None @classmethod def func_formatter(cls,**kargs): for key,func in kargs.items(): @@ -580,17 +668,24 @@ def flag_formatter(cls,*keys): for key in keys: cls.fkeys[key] = token_flag_fmt - def strkey(self,k,cname): - fmt = u'%%s%%-%ds:%%s'%self.ksz - if hasattr(self,k): - val = getattr(self,k) + def strkey(self,k,cname,ksz=20): + fmt = u'%%s%%-%ds:%%s'%ksz + if hasattr(self._v,k): + val = getattr(self._v,k) return fmt%(self.pfx,k,self.fkeys[k](k,val,cls=cname)) else: return fmt%(self.pfx,k,"None") def __str__(self): - cname = self.__class__.__name__ - s = u'\n'.join(self.strkey(f.name,cname) for f in self.fields) - return u"[%s]\n%s"%(cname,s) + cname = self.alt or self.__class__.__name__ + ksz = max((len(f.name) for f in self.fields)) + s = [] + for f in self.fields: + fs = self.strkey(f.name,cname,ksz) + if fs.count('\n')>0: + fs = fs.replace('\n','\n '+' '*ksz) + s.append(fs) + s = u'\n'.join(s) + return u"[%s]\n%s"%(self.__class__.__name__,s) #------------------------------------------------------------------------------ diff --git a/amoco/system/vb6.py b/amoco/system/vb6.py deleted file mode 100644 index 9f38516..0000000 --- a/amoco/system/vb6.py +++ /dev/null @@ -1,388 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Amoco -# Copyright (C) 2007 Axel Tillequin (bdcht3@gmail.com) -# published under GPLv2 license - -from amoco.system.win32 import * -from amoco.system.structs import * - -class VB6(PE): - - def __init__(self,p): - PE.__init__(self,p) - self.parseVB() - - -#------------------------------------------------------------------------------ -with Consts("dwThreadFlags"): - ApartmentModel = 0x1 - RequireLicense = 0x2 - Unattended = 0x4 - SingleThreaded = 0x8 - Retained = 0x10 - -@StructDefine(""" -s*4 : szVbMagic -H : wRuntimeBuild -s*14 : szLangDll -s*14 : szSecLangDll -H : wRuntimeRevision -I : dwLCID -I : dwSecLCID -I : lpSubMain -I : lpProjectData -I : fMdlIntCtls -I : fMdlIntCtls2 -I : dwThreadFlags -I : dwThreadCount -H : wFormCount -H : wExternalCount -I : dwThunkCount -I : lpGuiTable -I : lpExternalTable -I : lpComRegisterData -I : bSZProjectDescription -I : bSZProjectExeName -I : bSZProjectHelpFile -I : bSZProjectName -""",packed=True) -class EXEPROJECTINFO(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpSubMain','lpProjectData') - self.flag_formatter('fMdlIntCtls','fMdlIntCtls2') - self.address_formatter('lpGuiTable','lpExternalTable') - self.address_formatter('lpComRegisterData') - self.flag_formatter('dwThreadFlags') - if data: - self.unpack(data,offset) - assert self.szVbMagic == b'VB5!' -#------------------------------------------------------------------------------ - -with Consts("fControlType"): - PictureBox = 0x1 - Label = 0x2 - TextBox = 0x4 - Frame = 0x8 - CommandButton = 0x10 - CheckBox = 0x20 - OptionButton = 0x40 - ComboBox = 0x80 - ListBox = 0x100 - HScrollBar = 0x200 - VScrollBar = 0x400 - Timer = 0x800 - Print = 0x1000 - Form = 0x2000 - Screen = 0x4000 - Clipboard = 0x8000 - Drive = 0x10000 - Dir = 0x20000 - FileListBox = 0x40000 - Menu = 0x80000 - MDIForm = 0x100000 - App = 0x200000 - Shape = 0x400000 - Line = 0x800000 - Image = 0x1000000 - DataQuery = 0x20 - OLE = 0x40 - UserControl = 0x100 - PropertyPage = 0x200 - Document = 0x400 - -#------------------------------------------------------------------------------ - -with Consts("dwThreadFlags"): - ApartmentModel = 0x1 - RequireLicense = 0x2 - Unattended = 0x4 - SingleThreaded = 0x8 - Retained = 0x10 - -@StructDefine(""" -I : bRegInfo -I : bSZProjectName -I : bSZHelpDirectory -I : bSZProjectDescription -c*16 : uuidProjectClsId -I : dwTlbLcid -H : wUnknown -H : wTlbVerMajor -H : wTlbVerMinor -""",packed=True) -class tagREGDATA(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - -with Consts("fObjectType"): - Designer = 0x2 - Class_Module = 0x10 - User_Control = 0x20 - User_Document = 0x80 - -@StructDefine(""" -I : bNextObject -I : bObjectName -I : bObjectDescription -I : dwInstancing -c*16 : uuidObject -I : fIsInterface -I : bUuidObjectIFace -I : bUuidEventsIFace -I : fHasEvents -I : dwMiscStatus -b : fClassType -b : fObjectType -H : wToolboxBitmap32 -H : wDefaultIcon -H : fIsDesigner -I : bDesignerData -""",packed=True) -class tagRegInfo(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.flag_formatter('fObjectType') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - -@StructDefine(""" -c*16 : uuidDesigner -I : cbStructSize -s*~I : bstrAddinRegKey -s*~I : bstrAddinName -s*~I : bstrAddinDescription -I : dwLoadBehaviour -s*~I : bstrSatelliteDll -s*~I : bstrAdditionalRegKey -I : dwCommandLineSafe -""",packed=True) -class DesignerInfo(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - if data: - self.unpack(data,offset) - -#------------------------------------------------------------------------------ - - -@StructDefine(""" -I : dwVersion -I : lpObjectTable -I : dwNull -I : lpCodeStart -I : lpCodeEnd -I : dwDataSize -I : lpThreadSpace -I : lpVbaSeh -I : lpNativeCode -s*528: szPathInformation -I : lpExternalTable -I : dwExternalCount -""",packed=True) -class ProjectInfo(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpObjectTable') - self.address_formatter('lpCodeStart','lpCodeEnd') - self.address_formatter('lpThreadSpace','lpVbaSeh') - self.address_formatter('lpNativeCode','lpExternalTable') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - -@StructDefine(""" -I : lpHeapLink -I : lpObjectTable -I : dwReserved -I : dwUnused -I : lpObjectList -I : dwUnused2 -I : szProjectDescription -I : szProjectHelpFile -I : dwReserved2 -I : dwHelpContextId -""",packed=True) -class ProjectInfo2(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpHeapLink','lpObjectTable') - self.address_formatter('lpObjectList') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - - -@StructDefine(""" -I : lpHeapLink -I : lpExecProj -I : lpProjectInfo2 -I : dwReserved -I : dwNull -I : lpProjectObject -c*16 : uuidObject -I : fCompileState -H : wTotalObjects -H : wCompiledObjects -H : wObjectsInUse -I : lpObjectArray -I : fIdeFlag -I : lpIdeData -I : lpIdeData2 -I : lpszProjectName -I : dwLcid -I : dwLcid2 -I : lpIdeData3 -I : dwIdentifier -""",packed=True) -class ObjectTable(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpHeapLink','lpExecProj','lpProjectInfo2') - self.address_formatter('lpProjectObject','lpObjectArray') - self.address_formatter('lpszProjectName') - self.flag_formatter('fCompileState','fIdeFlag') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - -@StructDefine(""" -I : lpHeapLink -I : lpObjectInfo -I*3 : dwIdeData -I : lpObjectList -I : dwIdeData2 -I : lpObjectList2 -I*3 : dwIdeData3 -I : dwObjectType -I : dwIdentifier -""",packed=True) -class PrivateObject(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpHeapLink','lpObjectInfo') - self.address_formatter('lpObjectList','lpObjectList2') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - -@StructDefine(""" -I : lpObjectInfo -I : dwReserved -I : lpPublicBytes -I : lpStaticBytes -I : lpModulePublic -I : lpModuleStatic -I : lpszObjectName -I : dwMethodCount -I : lpMethodNames -I : bStaticvars -I : fObjectType -I : dwNull -""",packed=True) -class PublicObject(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpObjectInfo') - self.address_formatter('lpPublicBytes','lpStaticBytes') - self.address_formatter('lpModulePublic','lpModuleStatic') - self.address_formatter('lpszObjectName') - self.address_formatter('lpMethodNames') - self.flag_formatter('fObjectType') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - -@StructDefine(""" -H : wRefCount -H : wObjectIndex -I : lpObjectTable -I : lpIdeData -I : lpPrivateObject -I : dwReversed -I : dwNull -I : lpObject -I : lpProjectData -H : wMethodCount -H : wMethodCount2 -I : lpMethods -H : wConstants -H : wMaxConstants -I : lpIdeData2 -I : lpIdeData3 -I : lpConstants -""",packed=True) -class ObjectInfo(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpObjectTable') - self.address_formatter('lpIdeData','lpIdeData2', 'lpIdeData3') - self.address_formatter('lpPrivateObject','lpObject', 'lpProjectData') - self.address_formatter('lpMethods','lpConstants') - self.address_formatter('lpMethodNames') - self.flag_formatter('fObjectType') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - -@StructDefine(""" -I : dwObjectGuids -I : lpObjectGuid -I : dwNull -I : lpuuidObjectTypes -I : dwObjectTypeGuids -I : lpControls2 -I : dwNull2 -I : lpObjectGuid2 -I : dwControlCount -I : lpControls -H : wEventCount -H : wPCodeCount -I : bWInitializeEvent -I : bWTerminateEvent -I : lpEvents -I : lpBasicClassObject -I : dwNull3 -I : lpIdeData -""",packed=True) -class OptionalObjectInfo(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpObjectGuid') - self.address_formatter('lpuuidObjectTypes','lpControls2', 'lpObjectGuid2') - self.address_formatter('lpControls','lpEvents') - self.address_formatter('lpBasicClassObject') - self.address_formatter('lpIdeData') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ - -@StructDefine(""" -I : fControlType -H : wEventCount -I : bWEventsOffset -I : lpGuid -I : dwIndex -I : dwNull -I : dwNull2 -I : lpEventTable -I : lpIdeData -I : lpszName -I : dwIndexCopy -""",packed=True) -class ControlInfo(StructFormatter): - order = '<' - def __init__(self,data="",offset=0): - self.address_formatter('lpGuid') - self.address_formatter('lpEventTable') - self.address_formatter('lpIdeData') - self.address_formatter('lpszName') - self.flag_formatter('fControlType') - if data: - self.unpack(data,offset) -#------------------------------------------------------------------------------ diff --git a/amoco/system/vm/dwarf.py b/amoco/system/vm/dwarf.py index 3fc01dd..bb7c526 100644 --- a/amoco/system/vm/dwarf.py +++ b/amoco/system/vm/dwarf.py @@ -326,7 +326,7 @@ def printstack(self,m,ssa=False): self.varid += 1 print("% 2d: %s"%(x,v.pp())) - def setvar(m,idx,name): + def setvar(self,m,idx,name): x = m(self.cpu.mem(self.cpu.sp+idx*8,64)) self.VAR[name] = x m[self.cpu.mem(self.cpu.sp+idx*8,64)] = self.cpu.reg(name,64) diff --git a/amoco/ui/cli.py b/amoco/ui/cli.py new file mode 100644 index 0000000..aae58d7 --- /dev/null +++ b/amoco/ui/cli.py @@ -0,0 +1,79 @@ +import cmd +from types import MethodType +from amoco.config import conf +from amoco.logger import Log +logger = Log(__name__) +logger.debug('loading module') + +def spawn_console(): + """ amoco console for interactive mode. + The console is based on IPython if found, or uses CPython otherwise. + """ + c = conf + cvars = dict(globals(),**locals()) + if c.Terminal.console.lower() == 'ipython': + try: + from IPython import start_ipython + except ImportError: + logger.verbose('ipython not found') + c.Terminal.console = 'python' + else: + ic = c.src.__class__() + ic.TerminalTerminalIPythonApp.display_banner = False + ic.InteractiveShellApp.exec_lines = ["print(conf.BANNER)"] + start_ipython(argv=[],config=ic,user_ns=cvars) + if c.Terminal.console.lower() == 'python': + from code import interact + try: + import readline,rlcompleter + readline.set_completer(rlcompleter.Completer(cvars).complete) + readline.parse_and_bind("Tab: complete") + del readline,rlcompleter + except ImportError: + logger.verbose('readline not found') + interact(banner=conf.BANNER+"\n", + local=cvars) + +def cmdcli_builder(srv): + cmdcli = type('cmdcli',(cmdcli_core,),{}) + func = cmdcli.default + for c in srv.cmds.keys(): + setattr(cmdcli,'do_%s'%c,func) + s = cmdcli(srv) + return s + +class cmdcli_core(cmd.Cmd): + intro = conf.BANNER + + def __init__(self,srv): + cmd.Cmd.__init__(self,completekey=conf.UI.completekey) + self.srv = srv + self.prompt = 'amoco[pid %d]:%s> '%(self.srv._srv.pid,self.srv.obj) + + def precmd(self,line): + if self.srv._srv.is_alive(): + return line + else: + return 'EOF' + + def onecmd(self, line): + cmd, arg, line = self.parseline(line) + if not line: + return self.emptyline() + if cmd is None: + return self.default(line) + self.lastcmd = line + if line == 'EOF' : + self.lastcmd = '' + return self.do_EOF(arg) + return self.default(line) + + def do_EOF(self,args): + print() + return True + + def default(self,line): + self.srv.msgs.put(line) + res = self.srv.outs.get() + return res + diff --git a/amoco/ui/srv.py b/amoco/ui/srv.py index d94a0b5..7a11c30 100644 --- a/amoco/ui/srv.py +++ b/amoco/ui/srv.py @@ -1,13 +1,100 @@ # -*- coding: utf-8 -*- - +import os +import signal +import ctypes import multiprocessing as mp - +import queue from amoco.config import conf -from amoco.logger import Log +from amoco.logger import Log, flush_all +from amoco.ui.cli import cmdcli_builder logger = Log(__name__) logger.debug('loading module') + +STOPPED = 0 +IDLE = 1 +WAITING = 2 +STALLED = 3 + +commands = {} + +class DefineSrvCommand(object): + """decorator that allows to "register" commands on-the-fly + """ + def __init__(self,name): + global commands + self.name = name + if not self.name in commands: + commands[self.name] = None + def __call__(self,cls): + global commands + logger.verbose('DefineCommand %s:%s'%(self.name,cls)) + commands[self.name] = cls + return cls + class srv(object): - def __init__(self): - self.cmds = mp.Queue() + def __init__(self,scope=None,obj=''): + if scope is None: + scope = globals() + self.scope = scope + self.obj = obj + self.cmds = commands + + def start(self,timeout=conf.Server.timeout): + def do_cmd(i,cmdline): + args = cmdline.split() + cmd = args.pop(0) + if cmd in self.cmds: + logger.debug('Out[%d]: %s'%(i,cmdline)) + res = self.cmds[cmd].run(self,args) + else: + logger.debug('command not found: %s'%cmd) + res = -1 + return res + def mainloop(msgs,outs): + i=0 + while True: + try: + cmdline = msgs.get() + except queue.Empty: + break + else: + p = mp.Process(target=do_cmd,args=(i,cmdline)) + p.start() + p.join(timeout) + if p.is_alive(): + p.terminate() + outs.put(p.exitcode) + i += 1 + logger.verbose('server stopped (queue is empty.)') + return i + self.shar = mp.Array(ctypes.c_ubyte,[0]*conf.Server.wbsz) + self.msgs = mp.Queue() self.outs = mp.Queue() + self._srv = mp.Process(target=mainloop,args=(self.msgs,self.outs)) + self._srv.start() + logger.verbose('server process is running.') + self.interact() + if self._srv.is_alive(): + self._srv.terminate() + logger.verbose('server process is terminated.') + self._srv = None + + def interact(self): + if conf.UI.cli == 'cmdcli': + cmdcli_builder(srv=self).cmdloop() + + + +@DefineSrvCommand('hello') +class cmd_hello(object): + @staticmethod + def run(srv,args): + print('Hello World!') + print(args) + +@DefineSrvCommand('scope') +class cmd_scope(object): + @staticmethod + def run(srv,args): + print(srv.scope) diff --git a/setup.py b/setup.py index 6ec35bf..6dbf3a0 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ setup( name = 'amoco', - version = '2.9.0', + version = '2.9.1', description = 'yet another binary analysis framework', long_description = long_descr, # Metadata diff --git a/tests/samples/x64/toc.osx/include/libtoc.h b/tests/samples/x64/toc.osx/include/libtoc.h new file mode 100644 index 0000000..1323490 --- /dev/null +++ b/tests/samples/x64/toc.osx/include/libtoc.h @@ -0,0 +1,29 @@ +//libtoc.h +#ifndef _LIBTOC_H_ +#define _LIBTOC_H_ +extern const long kTOC_MAGICAL_FUN; + +/* + a constant definition "exported" by library + i quote it because it isn't really exported, as it doesn't show up in the exports anywhere + it basically exists emphemerally in any source code which #includes this header, + and then disappears after compilation, + as no symbol is associated with it, and it returns to it's pure anonymous, + integer brothers and sisters + of course this has advantages wrt client side speed, since the value can + typically be used as an immediate, no memory reads are performed, + and the dynamic linker doesn't have to load the value into the got +*/ +#define TOC_MAX_FOO 20 + +// a global variable exported by library +// if you don't know what extern means you should probably look that up +// .. it helps to think that this file, when you #include it, will pretty much +// be transcluded into your source file, after the c preprocessor runs +extern int toc_extern_export; + +// function prototypes for a function exported by library: +extern long int toc_maximum(long int x, long int y); +extern long int toc_XX_unicode(long int x, long int y); + +#endif diff --git a/tests/samples/x64/toc.osx/lib/libtoc.c b/tests/samples/x64/toc.osx/lib/libtoc.c new file mode 100644 index 0000000..929b568 --- /dev/null +++ b/tests/samples/x64/toc.osx/lib/libtoc.c @@ -0,0 +1,11 @@ +//libtoc.c +int toc_extern_export = 0xb1b1eb0b; +const long kTOC_MAGICAL_FUN = 0xdeadbeef; + +long int toc_maximum(long int x, long int y){ + return x > y ? x : y; +} + +long int toc_XX_unicode(long int x, long int y){ + return x * y << 2; +} diff --git a/tests/samples/x64/toc.osx/lib/libtoc.dylib.mach-o b/tests/samples/x64/toc.osx/lib/libtoc.dylib.mach-o new file mode 100755 index 0000000000000000000000000000000000000000..83a61042b2db9cbf4388d9fe68fba4abe14b5d6a GIT binary patch literal 8464 zcmeHMO=}ZT6un9P!d6nmg`%{Bh+r3OkwOb@8r$epYg;gdZ0c=3rZm_jC7Ho=A!Jb( zp&(iM2V9A?-E}2Me}Mf3?v#p%SxIpro;!JCC;f6Oavz-c?s;?Ho%>Fv(D&|#pWl9+ zC5m(q(HY=tj2Hxp|; zdtzLKpW1g9QVUb&Y^hk;4|p)Y4HNI2StLBjB^)QPnZ4q(d@;xQ;z9|N2jgv-c(z#} zJQ^%MViet80nFp^WIW^$=yl8}ISL@T-H|Mq+jqmT?eiTnR zdtCFEw2yMukhAj;M!~u{w~8%il`gF8_E&=-Oy}3lP2bW^6;K6K0aZX1Pz6*0RX`O` z1yli5Kow90{$B+mj5>W3{ORaWtWm2w9|vzg+?#B4e4rDAlOdNsBg->L=0U}m%k(&mlzFmKXF62 z6m3lrb=W+Yy9{aL0Nmx;!*3Sxp=?=p5lYkN?Wy`x!In^u&T+-{GMqu>-ziJr-zMz2 A2><{9 literal 0 HcmV?d00001 diff --git a/tests/samples/x64/toc.osx/toc.c b/tests/samples/x64/toc.osx/toc.c new file mode 100644 index 0000000..ab31436 --- /dev/null +++ b/tests/samples/x64/toc.osx/toc.c @@ -0,0 +1,14 @@ +//toc.c +#include +#include "include/libtoc.h" + +int main (){ + long int max = toc_maximum(2,3); + printf ("kTOC_MAGICAL_FUN: 0x%lx\n", kTOC_MAGICAL_FUN); + printf ("toc_extern_export: 0x%x\n", toc_extern_export); + printf ("===FUNS===\n"); + printf ("toc_XX_unicode: 0x%lu\n", toc_XX_unicode(3, 5)); + printf("toc_maximum: %lu\n", max); + + return 0; +} diff --git a/tests/samples/x64/toc.osx/toc.mach-o b/tests/samples/x64/toc.osx/toc.mach-o new file mode 100755 index 0000000000000000000000000000000000000000..6d369497425c49f26dc731bf0abd8999e7843988 GIT binary patch literal 12796 zcmeHO&2Jk;6dxyigtRmt2UJ9nOsNqdl%$A8C6XnZblVMtB;d3PNHDD9O>E&W*=y>E zOQi~_Run16`~@5k2M(2hOA7)O5Cgy4cI%Vs9hG^?+j(E} z{N^{i)~xdU)8D`U^RN)HVIeLI2_YuHdk+fnk%&VQ;&HGImU1P1#(LX&bD7qDiuJ2^ z)tKiO1WLJTt*!QlsQKglHlcNtA_8WFl%Cu4da==b?;XNo;QmC1P=BBoPUS*am9p+N zvh}Vhns0Q>&o`+f0zH^8qVqkZ4SqhEcZ*fG23s`W5uI;J$5SWnT^guwz3$ZtmFFjQ|Z;ToL^qb z_`8e!>)Y30fAxs_O^_Kwla!m4(6-()-(kcm!X(ZQi1>$f*3}04bp=rv`L3eiF(hjQCUc1lRl( zlt(an$Kk_x0K5VwF+Z7Dat&b|p!U%U<}*)}@c4>2_#k+?QLjxG3)$)1Rxt;i^aPl7 zBz`OpU;6mO+~sT2?;b05&bbybT!y3<3rLgMdN6AYc&qFC%c)Zr!vm-k$l+y2CQB@(gu_hp_e@ zF820`4{<89Th_k4J!|hIKZo%%(XRTwP56}gOXw}@HazF(*-3t_3~d-JNLQKg3SSdj z*01fCF(QO-fOhK|LXJ|}ZtTPOsfxJ)j4qTnYAL{xi zu8TWQ{)Hi(=_GCUi6Sf@ zo@0+t0EI_l%k%3?7|93pyC7vEbPzMg{Sz^?o?zPBEjZ{VR0kqcL;>@FdssLEc>L{ z7s0Tx7`iq<)E@(nK?r^q!1++=QZ$7t6i!n#i)+|_I-