Exemple #1
0
def getInitialConditionsAlloc():
    ret_op = Operand("eax", "DWORD")
    ret_val = 0
    initial_values_at_alloc = dict()
    initial_values_at_alloc[ret_op] = Operand(str(ret_val), "DWORD")

    return initial_values_at_alloc
Exemple #2
0
    def fixMemoryAccess(self, mem_access):
        assert (mem_access <> None)
        #print self.instruction

        if (self.instruction == "ldm"):

            self.write_operands = [self.operands[2]]
            self.mem_reg = self.operands[0]

            #if (mem_regs):
            #  self.read_operands  = [self.operands[0]]

            mem_source = mem_access["source"]
            mem_offset = mem_access["offset"]
            for i in range(self.operands[2].size):
                name = mem_source + "@" + str(mem_offset + i)
                self.read_operands.append(
                    Operand(name, "BYTE", mem_source, mem_offset + i))

            #self.read_operands.append(self.operands[0])

        # stm: [op_2] = op_0
        elif (self.instruction == "stm"):

            self.read_operands.append(self.operands[0])
            self.mem_reg = self.operands[2]

            mem_source = mem_access["source"]
            mem_offset = mem_access["offset"]
            for i in range(self.operands[0].size):
                name = mem_source + "@" + str(mem_offset + i)
                self.write_operands.append(
                    Operand(name, "BYTE", mem_source, mem_offset + i))
        else:
            assert (False)
Exemple #3
0
    def __init__(self, pbase=None, pars=None):

        self.internal_size = 256 + 4

        if (type(pbase) <> type(None)):
            self.pbase = pbase
            for (ptype, size, disp, needed) in self.parameter_typs:
                self.parameter_locs.append(
                    (ptype, self.__locateParameter__(disp, size), needed))
        else:
            self.__loadParameters__(pars)

            # populate read operands

            self.read_operands.append(self.parameter_locs[0])
            self.read_operands.append(self.parameter_locs[1])

            for i in range(self.internal_size):
                msrc = self.parameter_vals[1].mem_source
                if ("arg[" in msrc):  #it's an argument!
                    self.read_operands.append(
                        Operand(msrc + ":" + str(i), "BYTE"))
                else:
                    assert (0)
                #self.read_operands.append(Operand("i:"+str(i), "BYTE"))

            # populate write operands

            for i in range(self.internal_size):
                mem_source = self.parameter_vals[0].mem_source
                mem_offset = self.parameter_vals[0].mem_offset + i
                name = mem_source + "@" + str(mem_offset)
                self.write_operands.append(
                    Operand(name, "BYTE", mem_source, mem_offset))
Exemple #4
0
def parse_inputs(inputs):

    r = dict()

    for s in inputs:
        #print s
        s = s.strip("(").strip(")")
        (rop, rval) = s.split(",")
        ropsize, rop = rop.split(" ")
        rvalsize, rval = rval.split(" ")

        mem_source = None
        mem_offset = None

        if ('arg[' in rop):
            pass

        elif ('@' in rop):
            mem_source, mem_offset = rop.split('@')
            mem_offset = int(mem_offset)

        if (ropsize == rvalsize and rvalsize == "VAR"):
            for i, c in enumerate(rval):
                # TODO: this is only for inputs!
                r[Operand(rop + str(i), "BYTE", mem_source,
                          mem_offset)] = Operand(str(ord(c)), "BYTE")
        else:
            r[Operand(rop, ropsize, mem_source,
                      mem_offset)] = Operand(rval, rvalsize)

    return r
Exemple #5
0
    def __getESPdifference__(self, reil_code, initial_esp):
        #print "inf:", reil_code.first, reil_code.last
        if len(reil_code) == 0:
            return initial_esp

        esp_op = Operand("esp", "DWORD")
        initial_values = dict([(esp_op, Operand(str(0), "DWORD"))])
        return getValueFromCode(reil_code, initial_values,
                                esp_op) + initial_esp
Exemple #6
0
def mkVal(val_type, val):
    if val_type == "imm":
        return Operand(str(val), "")
    elif "s." in val_type or "h." in val_type or "arg" in val_type:
        return Operand(val_type + "@" + str(val),
                       "",
                       mem_source=val_type,
                       mem_offset=val)
    else:
        assert (0)
Exemple #7
0
def detectType(mvars, ins, counter, callstack):

    if (len(mvars) == 0):
        return None

    # dection of parameters of main

    name = "s." + hex(callstack.callstack[1]) + ".1"

    # argv
    argv_bytes = []

    for i in range(12, 16):
        argv_bytes.append(Operand(name + "@" + str(i), "BYTE"))

    argv_bytes = set(argv_bytes)

    if argv_bytes.issubset(mvars):
        return "argv[]"

    ## argc
    #argc_bytes = []
    #
    #for i in range(8,12):
    #  argc_bytes.append(Operand(name+"@"+str(i),"BYTE"))
    #
    #argc_bytes = set(argv_bytes)
    #
    #if argc_bytes.issubset(mvars):
    #  return "argc"

    # argv[0], argv[1], ... argv[10]
    for i in range(0, 40, 4):
        op = Operand("argv[]@" + str(i), "BYTE")
        if op in mvars:
            return "arg[" + str(i / 4) + "]"

    if ins.instruction == "call" and ins.called_function == "malloc":

        # heap pointers
        if set([Operand("eax", "DWORD")]).issubset(mvars):
            return "h." + "0x" + ins.address + "." + str(counter)

    elif ins.instruction == "call" and ins.called_function == None:

        # stack pointers
        if mvars.issubset(
                set([Operand("esp", "DWORD"),
                     Operand("ebp", "DWORD")])):
            return "s." + hex(callstack.currentCall()) + "." + str(
                callstack.currentCounter())

    # No type deduced
    return None
Exemple #8
0
    def __init__(self, raw_ins, mem_regs=True):

        pins = reil.parseString(raw_ins)
        self.address = pins.address
        self.instruction = pins.instruction
        self.operands = []

        # for memory instructions
        self.mem_reg = None

        # for call instructions
        self.called_function = None

        aopers = pins.augmented_operands
        for (i, x) in enumerate(aopers):
            if x == ",":
                self.operands.append(Operand(aopers[i - 1], aopers[i - 2]))
        self.operands.append(Operand(aopers[-1], aopers[-2]))

        self.read_operands = []
        self.write_operands = []

        # ldm: op_2 = [op_0]
        if (pins.instruction == "ldm"):

            self.write_operands = [self.operands[2]]
            self.mem_reg = self.operands[0]

        # stm: [op_2] = op_0
        elif (pins.instruction == "stm"):

            self.read_operands.append(self.operands[0])
            self.mem_reg = self.operands[2]

        elif (pins.instruction == "jcc"):
            self.read_operands = filter(lambda o: not o.isEmpty(),
                                        self.operands[0:2])
            self.write_operands = []

        elif (pins.instruction == "call"):
            #print "n:", self.operands[0].name
            #print self.operands[0].name
            if (self.operands[0].name <> "EMPTY"):
                self.called_function = self.operands[0].name

        else:

            self.read_operands = filter(lambda o: not o.isEmpty(),
                                        self.operands[0:2])
            self.write_operands = filter(lambda o: not o.isEmpty(),
                                         self.operands[2:3])
Exemple #9
0
    def __init__(self, pbase=None, pars=None):

        self.internal_size = 84

        if (type(pbase) <> type(None)):
            self.pbase = pbase
            for (ptype, size, disp, needed) in self.parameter_typs:
                self.parameter_locs.append(
                    (ptype, self.__locateParameter__(disp, size), needed))
        else:
            self.__loadParameters__(pars)

            # populate read operands

            self.read_operands.append(self.parameter_locs[0])

            # it is not necesary, since these locations won't be modified!
            #for i in range(self.internal_size):
            #  self.read_operands.append(Operand("stdin:"+str(i), "BYTE"))

            # populate write operands

            for i in range(self.internal_size):
                mem_source = self.parameter_vals[0].mem_source
                mem_offset = self.parameter_vals[0].mem_offset + i
                name = mem_source + "@" + str(mem_offset)
                self.write_operands.append(
                    Operand(name, "BYTE", mem_source, mem_offset))
Exemple #10
0
    def detectFuncParameters(self, reil_code, memaccess, callstack, inputs,
                             counter):

        pins = parse_reil(reil_code[-1])
        ins = Instruction(pins, None)

        assert (ins.instruction == "call" and ins.called_function <> None)

        # first we locate the stack pointer to know where the parameters are located
        esp = Operand("esp", "DWORD")
        pbase = getTypedValueFromCode(reil_code, callstack, inputs, memaccess,
                                      esp)

        #print pbase.name
        #print pbase.mem_source
        #
        func_cons = funcs.get(ins.called_function, Function)
        func = func_cons(pbase=pbase)

        parameters = []

        for (par_type, location, needed) in func.getParameterLocations():
            #print (ins.called_function, par_type, location.mem_source, needed)
            if needed:
                reil_code.reverse()
                reil_code.reset()
                val = getTypedValueFromCode(reil_code, callstack, inputs,
                                            memaccess, location)
                #print  "parameter of",ins.called_function, "at", str(location) , "has value:", val.name
                parameters.append((location, val))
            else:
                parameters.append((None, None))

        if parameters <> []:
            self.parameters[counter] = self.__getParameters__(ins, parameters)
Exemple #11
0
    def disassemble(self, filename):
        #Exécution d'opdis et récupération du XML
        xml = subprocess.Popen(
            ["opdis", "-q", "-d", "-f", "xml", "-N", "main", filename],
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL).stdout.read()
        document = parseString(xml).documentElement

        #Création de la liste des instructions
        instructions_table = []
        vma_instructions_table = {}

        for instructionNode in document.getElementsByTagName("instruction"):
            instruction = Instruction.Instruction(
                self.get_xml_child_value(instructionNode, "offset"),
                self.get_xml_child_value(instructionNode, "vma"),
                self.get_xml_child_value(instructionNode, "ascii"),
                self.get_xml_child_value(instructionNode, "mnemonic"))

            if (self.xml_node_has_child(instructionNode, "operands")):
                for operandNode in instructionNode.getElementsByTagName(
                        "operand"):
                    operand = Operand.Operand(
                        operandNode.attributes["name"] if
                        ("name" in operandNode.attributes) else "",
                        self.get_xml_child_value(operandNode, "ascii"))
                    instruction.add_operand(operand)

            instructions_table.append(instruction)
            vma_instructions_table[int(instruction.vma, 0)] = instruction

        return (instructions_table, vma_instructions_table)
Exemple #12
0
    def getEq(self, mvars):

        r = []

        #print self.src, self.dst

        if (self.src.isReg()):
            src = self.src.name
        #  self.src.size = self.size
        #  srcs = self.getOperands([self.src])
        #  print srcs
        else:
            assert (0)

        old_sname, new_sname, offset = Memvars.write(self.dst)

        old_array = mkArray(old_sname)
        array = mkArray(new_sname)

        for i in range(self.size):

            dst_op = Operand(self.dst.mem_source + "@" + str(offset + i),
                             "BYTE")
            src_var = z3.BitVec(src + ":" + str(i) + "(0)", 8)

            if (dst_op in mvars):
                array = z3.Store(array, offset + i, src_var)

            r.append(src_var <> 0)

        r.append((old_array == array))

        return r
Exemple #13
0
    def __locateParameter__(self, disp, size):

        #assert(disp<=0)

        mem_source = self.pbase.mem_source
        mem_offset = self.pbase.mem_offset + disp
        name = mem_source + "@" + str(mem_offset)
        return Operand(name, size, mem_source, mem_offset)
Exemple #14
0
def addAditionalConditions(mvars, ins, ssa, callstack, smt_conds):

    if len(mvars) == 0:
        return mvars

    # auxiliary eq condition
    eq = Eq(None, None)

    # if the instruction was a call
    if ins.instruction == "call" and ins.called_function == "malloc":

        if (Operand("eax", "DWORD") in mvars):
            initial_values_at_alloc = getInitialConditionsAlloc()
            setInitialConditions(ssa, initial_values_at_alloc, smt_conds)
            mvars.remove(Operand("eax", "DWORD"))

    elif ins.instruction == "call" and ins.called_function == None:
        initial_values_at_call = getInitialConditionsCall(callstack)

        for iop in initial_values_at_call.keys():
            #print "iop:",iop
            if not (iop in mvars):
                del initial_values_at_call[iop]

        setInitialConditions(ssa, initial_values_at_call, smt_conds)
        mvars = set(
            filter(lambda o: not (o in initial_values_at_call.keys()), mvars))

        new_mvars = set()
        for v in mvars:
            # we convert stack memory variables from one frame to the previous one
            if callstack.currentCounter() > 1 and v.isStackMem(
            ) and v.mem_offset >= 4:
                eop = callstack.convertStackMemOp(v)
                smt_conds.add(eq.getEq(v, eop))
                new_mvars.add(eop)
            else:
                new_mvars.add(v)

        mvars = set(
            filter(lambda o: not (o.isStackMem() and o.mem_offset >= 4),
                   mvars))
        mvars = mvars.union(new_mvars)

    return mvars
Exemple #15
0
    def decode(self):
        #self.inst  = inst

        self.op = self.bits(0, 0xF0000000)
        # Large embedded operand
        if len(self.inst) == 2 and self.bits(1, 3) == 3:
            self.immediate = True
        else:
            self.immediate = False
        # Flow control instruction
        if self.bits(0, 2):
            self.system = True
        else:
            self.system = False
        # Is a second, full word present?
        self.fullinst = (len(self.inst) == 2) and not self.immediate
        # Changes some instructions
        # Seems to choose an alternative instruction set, for example,
        # add becomes sub. It is also used on mul24.lo.s32 sometimes
        # but I have no idea what its effect is there.
        # inst.alt = (inst.flags & 1)

        # Predication
        if self.fullinst:
            # Predicated execution
            self.pred_op = self.bits(1, 0x00000F80)
            self.pred = self.bits(1, 0x00003000)
            # Predication (set)
            # This should be an operand
            if self.bits(1, 0x0000040):
                self.dst_operands.append(
                    Operand(OP_TYPE_PRED, OP_SIGN_NONE, 1,
                            OP_SOURCE_PRED_REGISTER, OP_INDIRECTION_NONE,
                            self.bits(1, 0x00000030)))

        else:
            self.pred_op = None  # Don't get in the way

        # Ignore out
        #if self.fullinst:
        #    self.output_reg = self.bits(1,0x0000008)
        #else:
        #    self.output_reg = False

        if self.bits(0, 3) == 2:
            self.warnings.append("Unknown marker 0.2")
        if self.fullinst and self.bits(1, 3) == 2:
            #self.warnings.append("Unknown marker 1.2")
            # join point?
            self.modifiers.append(".join")
        if self.fullinst and self.bits(1, 3) == 1:
            self.modifiers.append(".end")

        if len(self.inst) == 1:
            self.modifiers.append(".half")
Exemple #16
0
    def convertStackMemOp(self, op):

        #print str(op)

        self.index = self.index - 1

        mem_source = "s." + hex(self.currentCall()) + "." + str(
            self.currentCounter())
        if self.index == 1:
            mem_offset = (op.mem_offset) + self.currentStackDiff() - 4  #+16
        else:
            mem_offset = (op.mem_offset) + self.currentStackDiff()  #+16
        name = mem_source + "@" + str(mem_offset)

        self.index = self.index + 1

        return Operand(name, "BYTE", mem_source, mem_offset)
Exemple #17
0
    def decode(self):
        super(ldofs0, self).decode()
        src = self.offset_reg()
        if src:
            # We can add something to an offset register by
            # providing an offset register as source for this instruction
            self.base = "add"
        else:
            self.base = "mov"

        type = (OP_SIGN_NONE, 32, OP_TYPE_INT)
        self.dst_operands.append(self.decode_operand(type, 2, doffset=True))
        if src:
            self.src_operands.append(
                Operand(OP_TYPE_INT, OP_SIGN_NONE, 32,
                        OP_SOURCE_OFFSET_REGISTER, OP_INDIRECTION_NONE, src))
        self.src_operands.append(self.decode_operand(type, 5))
Exemple #18
0
    def __init__(self, pbase=None, pars=None):

        self.internal_size = 10

        if (type(pbase) <> type(None)):
            self.pbase = pbase
            for (ptype, size, disp, needed) in self.parameter_typs:
                self.parameter_locs.append(
                    (ptype, self.__locateParameter__(disp, size), needed))
        else:
            self.__loadParameters__(pars)

            # populate read operands

            self.read_operands.append(self.parameter_locs[0])

            # return value
            self.write_operands.append(Operand("eax", "DWORD"))
Exemple #19
0
def getInitialConditionsCall(callstack):

    initial_values_at_call = dict()

    if callstack.index == 1:

        esp_val = 4

        for i in range(0, 40):
            arg_i = Operand("argv[]@" + str(i),
                            "BYTE",
                            mem_source="argv[]",
                            mem_offset=i)
            initial_values_at_call[arg_i] = Operand(str(0), "BYTE")

        name = "s." + hex(callstack.callstack[1]) + ".1"

        for i in range(12, 16):
            arg_v = Operand(name + "@" + str(i),
                            "BYTE",
                            mem_source=name,
                            mem_offset=i)
            initial_values_at_call[arg_v] = Operand(str(0), "BYTE")

        #print ""
    else:
        esp_val = 8

    ebp_val = 0

    esp_op = Operand("esp", "DWORD")
    ebp_op = Operand("ebp", "DWORD")

    initial_values_at_call[esp_op] = Operand(str(esp_val), "DWORD")
    initial_values_at_call[ebp_op] = Operand(str(ebp_val), "DWORD")

    return initial_values_at_call
Exemple #20
0
    def getEq(self, mvars):

        r = []

        old_sname, new_sname, offset = Memvars.write(self.dst)

        old_array = mkArray(old_sname)
        array = mkArray(new_sname)

        for i in range(self.size):

            op = Operand(self.dst.mem_source + "@" + str(offset + i), "BYTE")

            if (op in mvars):
                array = z3.Store(array, offset + i,
                                 z3.BitVec("stdin:" + str(i) + "(0)", 8))

            r.append(z3.BitVec("stdin:" + str(i) + "(0)", 8) <> 10)
            r.append(z3.BitVec("stdin:" + str(i) + "(0)", 8) <> 0)

        r.append((old_array == array))

        return r
Exemple #21
0
    def __init__(self, regs, opc):
        self.opcode = opc
        self.operations = []
        self.operands = []
        self.valid = False

        opc_cmdname = opc.getDisasm().split(' ')[0].lower()
        opc_cmdtype = opc.getCmdType()

        #Populate operands
        for i in range(0, 3):
            oprnd = None
            opc_oprnd = opc.operand[i]
            otype = opc_oprnd[0]
            osize = opc_oprnd[1]

            if (otype & DECR_ISREG):
                self.operands.append(
                    Operand(OPRND_REG, osize,
                            [IMMREG_MAP[osize][opc_oprnd[2].index(1)]]))

            elif (otype & DEC_CONST):
                self.operands.append(
                    Operand(OPRND_CONST, osize, [opc_oprnd[3]]))

            #Special case for lea
            elif (otype == DEC_UNKNOWN and osize == 4):
                for i in range(len(opc_oprnd[2])):
                    if (not opc_oprnd[2][i]): continue
                    self.operands.append(
                        Operand(OPRND_REG, osize, [IMMREG_MAP[osize][i]]))

            elif (otype & DEC_TYPEMASK):
                self.operands.append(
                    Operand(OPRND_MEM, osize, [
                        IMMREG_MAP[osize][i]
                        for i in len(opc_oprnd[2]) if opc_oprnd[2][i]
                    ], calcaddr(opc_oprnd, regs)))

        #Figure out what type of operation it is and parse accordingly
        if (opc_cmdtype == C_CMD):
            if (opc_cmdname in [
                    'adc', 'add', 'sub', 'sbb', 'and', 'or', 'xor', 'shld',
                    'shrd'
            ]):
                self.operations.append(
                    Operation(OPRTN_ARITH,
                              [self.operands[0], self.operands[1]],
                              [self.operands[0]]))

            elif (opc_cmdname in [
                    'inc', 'dec', 'not', 'neg', 'shl', 'shr', 'sal', 'sar',
                    'shr', 'rcl', 'rcr', 'rol', 'ror'
            ]):
                self.operations.append(
                    Operation(OPRTN_ARITH, [self.operands[0]],
                              [self.operands[0]]))

            elif (opc_cmdname in ['mov', 'movsx', 'movzx']):
                self.operations.append(
                    Operation(OPRTN_OVERWRITE, [self.operands[1]],
                              [self.operands[0]]))

            elif (opc_cmdname in ['lea']):
                self.operations.append(
                    Operation(OPRTN_ARITH, self.operands[1:],
                              [self.operands[0]]))

            elif (opc_cmdname
                  in ['div', 'idiv', 'mul', 'imul', 'imul', 'imul']):
                ocount = len(self.operands)
                if (ocount == 1):
                    oxsize = self.operands[0].size
                    dest = []
                    if (oxsize == 1):
                        dest.append(Operand(OPRND_REG, 2, [REG_MAP['AX']]))
                    elif (oxsize == 2):
                        dest += [
                            Operand(OPRND_REG, 2, [REG_MAP['DX']]),
                            Operand(OPRND_REG, 2, [REG_MAP['AX']])
                        ]
                    elif (oxsize == 4):
                        dest += [
                            Operand(OPRND_REG, 4, [REG_MAP['EDX']]),
                            Operand(OPRND_REG, 4, [REG_MAP['EAX']])
                        ]
                    self.operations.append(
                        Operation(OPRTN_ARITH, [
                            self.operands[0],
                            Operand(OPRND_REG, oxsize, [IMMREG_MAP[oxsize][0]])
                        ], dest))
                elif (ocount == 2):
                    self.operations.append(
                        Operation(OPRTN_ARITH,
                                  [self.operands[0], self.operands[1]],
                                  [self.operands[0]]))
                elif (ocount == 3):
                    self.operations.append(
                        Operation(OPRTN_ARITH,
                                  [self.operands[1], self.operands[2]],
                                  [self.operands[0]]))

            elif (opc_cmdname in ['cbw', 'cwd']):
                self.operations.append(
                    Operation(OPRTN_ARITH,
                              [Operand(OPRND_REG, 1, [REG_MAP['AL']])], [
                                  Operand(OPRND_REG, 2, [REG_MAP['DX']]),
                                  Operand(OPRND_REG, 2, [REG_MAP['AX']])
                              ]))

            elif (opc_cmdname in ['cwde', 'cdq']):
                dest = [Operand(OPRND_REG, 4, [REG_MAP['EAX']])]
                if (opc_cmdname == 'cdq'):
                    dest.append(Operand(OPRND_REG, 4, [REG_MAP['EDX']]))
                self.operations.append(
                    Operation(OPRTN_ARITH,
                              [Operand(OPRND_REG, 2, [REG_MAP['AX']])], dest))

            elif (opc_cmdname in ['smsw']):
                self.operations.append(
                    Operation(OPRTN_OVERWRITE, [Operand(OPRND_CLEAN, 2)],
                              [self.operands[0]]))

            elif (opc_cmdname in ['xchg', 'xadd']):
                self.operations.append(
                    Operation(OPRTN_OVERWRITE, [self.operands[0]],
                              [self.operands[1]]))
                type = OPRTN_OVERWRITE
                src = [self.operands[1]]
                if (opc_cmdname == 'xadd'):
                    type = OPRTN_ARITH
                    src.append(self.operands[0])
                self.operations.append(Operation(type, src,
                                                 [self.operands[0]]))

            elif (opc_cmdname in ['cmp', 'test', 'nop']):
                pass

            else:
                imm.log('Not implemented!' + opc_cmdname)
                return

        elif (opc_cmdtype == C_PSH):
            self.operations.append(
                Operation(
                    OPRTN_OVERWRITE, [self.operands[0]],
                    [Operand(OPRND_MEM, 4, [REG_MAP['ESP']], regs['ESP'] - 4)
                     ]))

        elif (opc_cmdtype == C_POP):
            self.operations.append(
                Operation(
                    OPRTN_OVERWRITE,
                    [Operand(OPRND_MEM, 4, [REG_MAP['ESP']], regs['ESP'])],
                    [self.operands[0]]))

        elif (opc_cmdtype == C_MMX):
            imm.log('Not implemented!' + opc_cmdname)
            return

        elif (opc_cmdtype == C_FLT):
            imm.log('Not implemented!' + opc_cmdname)
            return

        elif (opc_cmdtype == C_JMP):
            #Don't care about this instruction type
            pass

        elif (opc_cmdtype == C_JMC):
            #Don't care about this instruction type
            pass

        elif (opc_cmdtype == C_CAL):
            #Don't care about this instruction type
            pass

        elif (opc_cmdtype == C_RET):
            #Don't care about this instruction type
            pass

        elif (opc_cmdtype == C_FLG):
            imm.log('Not implemented!' + opc_cmdname)
            return

        elif (opc_cmdtype == C_RTF):
            imm.log('Not implemented!' + opc_cmdname)
            return

        elif (opc_cmdtype == C_REP):
            imm.log('Not implemented!' + opc_cmdname)
            return

        elif (opc_cmdtype == C_PRI):
            imm.log('Not implemented!' + opc_cmdname)
            return

        elif (opc_cmdtype == C_SSE):
            imm.log('Not implemented!' + opc_cmdname)
            return

        elif (opc_cmdtype == C_NOW):
            imm.log('Not implemented!' + opc_cmdname)
            return

        elif (opc_cmdtype == C_BAD):
            imm.log('Bad op encountered')
            return

        else:
            imm.log('Unknown op encountered')
            return

        #If we're here, the opcode is valid
        self.valid = True
Exemple #22
0
    def decode(self):
        super(tex, self).decode()

        if self.subop == 0x0:
            self.base = "tex"
        elif self.subop == 0x3:
            self.base = "txq"
        op = self.bits(0, 0x01C00000)
        # Dimensionality
        args = 4
        if op in [0, 4]:
            self.modifiers.append(".1d")
            args = 1
        if op in [1, 6]:
            self.modifiers.append(".2d")
            args = 2
        if op in [2, 7]:
            if self.bits(0, 0x08000000):
                self.modifiers.append(".cube")
            else:
                self.modifiers.append(".3d")
            args = 3

        (ssign, stype) = (OP_SIGN_NONE, OP_TYPE_INT)
        if op in [4, 6, 7]:
            (ssign, stype) = (OP_SIGN_SIGNED, OP_TYPE_INT)
        elif op in [0, 1, 2]:
            (ssign, stype) = (OP_SIGN_NONE, OP_TYPE_FLOAT)

        if op in [3, 5]:  # This one is unknown
            self.modifiers.append(".%i" % op)

        if self.fullinst and not self.bits(1, 0x00000004):
            self.warnings.append("unknown bit not set")

        # Calculate destination bitfield and source/destination registers
        value2 = self.bits(0, 0x000001FC)  # reg id
        if self.fullinst:
            bitfield = self.bits(0, 0x06000000) | self.bits(1, 0x0000c000)
        else:
            if not (value2 & 0x40):
                # XXX this changed bit field
                self.warnings.append("not (value2 & 0x40)")
            bitfield = 0xF
            value2 &= 0x3F

        src_set = [value2 + x for x in xrange(0, args)]
        count = 0
        dst_set = []
        for x in xrange(0, 4):
            if bitfield & (1 << x):
                dst_set.append(value2 + count)
                count += 1
            else:
                dst_set.append(-1)

        self.dst_operands.append(
            Operand(value=dst_set,
                    source=OP_SOURCE_REGSET,
                    type=OP_TYPE_INT,
                    sign=OP_SIGN_NONE))
        # These two are the texture
        value1 = self.bits(0, 0x00007E00)
        value3 = self.bits(0, 0x003E0000)
        self.src_operands.append(
            Operand(value=value1,
                    source=OP_SOURCE_TEXTURE,
                    type=OP_TYPE_TEXTURE))
        if value1 != value3:
            self.warnings.append("value3 is %i" % value3)
        self.src_operands.append(
            Operand(value=src_set,
                    source=OP_SOURCE_REGSET,
                    type=stype,
                    sign=ssign))
Exemple #23
0
    def parse(self, text):
        """Parse instruction text"""
        # [@<pred>] <base>[.<mod1>[.<mod2>...] <dest>[|<dest2>], <src1>, <src2>, ...
        text = text.strip()
        
        # Predication
        if text.startswith('@'):
            # Find and remove predicate string
            pred_end = text.index(" ")
            pred = text[1:pred_end]
            text = text[pred_end+1:]

            match = re.match("(\!?)\$p([0-9]+)\.([0-9a-z]+)", pred)
            if not match:
                raise ValueError("Invalid predicate expression %s" % pred)
            (neg, pred, cc) = match.groups()
            # [!]$<pred>.<cc>
            try:
                cc = numlookup(condition_codes_rev, cc)
            except KeyError:
                raise ValueError("Invalid predicate condition code %s" % cc)
            if neg == "!":
                # Invert predicate
                cc = cc ^ 0xF
            # XXX we should be able to leave out the condition code if we just want
            # to evaluate the output of a set instruction
            self.pred_op = cc
            self.pred = int(pred)
        else:
            self.pred_op = 15 # always true
            self.pred = 0
        
        try:
            base_end = text.index(" ")
            ins = text[0:base_end]
            args = text[base_end+1:].split(",")
        except ValueError:
            ins = text
            args = []
        mods = ins.split(".")
        self.base = mods[0]
        mods = ["."+x for x in mods[1:]]
        
        # Filter out and separate types
        types = []
        typere = re.compile("\.([subf][0-9]+|label)")
        for x in mods:
            if typere.match(x):
                types.append(x)
            else:
                self.modifiers.append(x)

        # Parse operands
        try:
            dest = args[0]
            dst_args = [x.strip() for x in dest.split("|")]
        except IndexError:
            dst_args = []
        src_args = [x.strip() for x in args[1:]]
        
        i = 0
        for x in dst_args:
            op = Operand()
            if x.startswith("$p"):
                # predicates have no type specifier
                type = ""
            else:
                try:
                    type = types[i]
                except IndexError:
                    raise ValueError("Not enough operand types in instruction")
                if len(types)!=1:
                    i += 1
            op.parse(x, type)
            self.dst_operands.append(op)
        
        for x in src_args:
            op = Operand()
            if x.startswith("$p"):
                # predicates have no type specifier
                type = ""
            else:
                try:
                    type = types[i]
                except IndexError:
                    raise ValueError("Not enough operand types in instruction")
                if len(types)!=1:
                    i += 1
            op.parse(x, type)
            self.src_operands.append(op)
Exemple #24
0
    def decode_operand(self,
                       oper_type,
                       operid,
                       msize_bits=True,
                       imm=False,
                       indir=None,
                       reg=False,
                       regscale=True,
                       doffset=False,
                       spred=False,
                       flip=False,
                       tex=False,
                       opt_imm=False):
        """What operands do we want, and in which order
        operid 1 is the first source operand in word 0
               2 is the destination operand (usually)
               3 immediate(spans both words) or non immediate (upper part of first word)
               4 subsub operation or fourth operand
               5 1 and 3 merged
        """
        (sign, size, type) = oper_type

        source = OP_SOURCE_REGISTER
        indirection = OP_INDIRECTION_NONE
        offset = OP_OFFSET_NONE
        multiplier = False
        offset_inc = False

        # Offset (integrate this into operand)
        #t_offset = (self.flags&0x30)>>4
        t_offset = self.offset_reg()

        if operid == 1:  # first source operand, in main word
            if self.fullinst:  # Depending on this, source is 7 or 6 bits
                # seems that bit 6 of oper1 has another meaning when this is a immediate
                # instruction (namely "32 bit")
                value = self.bits(0, 0x0000FE00)
            else:
                value = self.bits(0, 0x00007E00)

            # Process parameter numbers in shared memory
            if self.fullinst and self.bits(
                    1, 0x00200000
            ) and msize_bits:  # MSIZE bits collide with these
                indirection = OP_INDIRECTION_SHARED
                source = OP_SOURCE_IMMEDIATE

                t = (value & 0x60) >> 5
                value &= 0x1F
                (nsign, nsize, _) = sharedmem_types[t]
                if nsize != size:  # XXX propagate sign
                    self.warnings.append("shared memory operand type mismatch")
            if not self.fullinst and self.bits(0, 0x01000000):
                indirection = OP_INDIRECTION_SHARED
                source = OP_SOURCE_IMMEDIATE

                t = (value & 0x30) >> 4
                value &= 0xF
                (nsign, nsize, _) = sharedmem_types[t]
                if nsize != size:  # XXX propagate sign
                    self.warnings.append("shared memory operand type mismatch")
        elif operid == 2:  # destination operand, in main word
            value = self.bits(0, 0x000001FC)
            if self.fullinst and self.bits(1, 0x0000008):
                # to output register
                source = OP_SOURCE_OUTPUT_REGISTER
        elif operid == 3:  # immediate(spans both words) or non immediate (upper part of first word)
            # Extract and print operands
            cflag = False
            if self.bits(0, 0x00800000):
                # operand 3 comes from constant (in segment 0)
                indirection = OP_INDIRECTION_CONST0
                source = OP_SOURCE_IMMEDIATE

                #if self.fullinst and self.bits(1,0x00400000) and msize_bits: # MSIZE bits collide with these
                #    # operand 3 or 4 comes from constant in segment 1
                #    indirection = OP_INDIRECTION_CONST1
                #    source = OP_SOURCE_IMMEDIATE
                #    #self.warnings.append("constbit")
                if self.fullinst and msize_bits:
                    indirection += self.bits(1, 0x00C00000)
                if not self.fullinst:
                    indirection += self.bits(0, 0x00200000)
                    cflag = True
            if self.immediate:
                # Immediate data
                value = (self.bits(1, 0x0FFFFFFC) << 6) | self.bits(
                    0, 0x003F0000)
                source = OP_SOURCE_IMMEDIATE
            else:
                if cflag:
                    # half instruction, upper bit is segment
                    value = self.bits(0, 0x001F0000)
                else:
                    value = self.bits(0, 0x007F0000)

            if opt_imm and self.fullinst and self.bits(1, 0x00100000):
                # Operand 3 is immediate
                source = OP_SOURCE_IMMEDIATE

        elif operid == 4:  # sub operation or fourth operand
            if not self.fullinst:
                raise DecodeError("No operand 4 in this instruction")
            #if (inst[0]&0x00800000):
            #    # operand 3 comes from constant (in segment 0)
            #    indirection = OP_INDIRECTION_CONST0
            #    source = OP_SOURCE_IMMEDIATE
            if self.fullinst and self.bits(0, 0x01000000):
                # operand 4 comes from constant (in seg 0)
                indirection = OP_INDIRECTION_CONST0
                source = OP_SOURCE_IMMEDIATE

                #if self.fullinst and self.bits(1,0x00400000) and msize_bits: # MSIZE bits collide with these
                #    # operand 3 or 4 comes from constant in segment 1
                #    indirection = OP_INDIRECTION_CONST1
                #    source = OP_SOURCE_IMMEDIATE
                #    #self.warnings.append("constbit")
                if self.fullinst and msize_bits:
                    indirection += self.bits(1, 0x03C00000)

            value = self.bits(1, 0x001fc000)

        elif operid == 5:  # use as much of word 0 as possible by merging 1 and 3
            source = OP_SOURCE_IMMEDIATE
            value = self.bits(0, 0x003FFE00)
        elif operid == 6:
            source = OP_SOURCE_OFFSET_REGISTER
            value = t_offset

        # Sometimes we need to force immediate operand
        if imm:
            source = OP_SOURCE_IMMEDIATE
        # Force register
        if reg:
            source = OP_SOURCE_REGISTER
            indirection = OP_INDIRECTION_NONE
        # Set some indirection based on the instruction
        if indir != None:
            indirection = indir
        # Offset (integrate this into operand)
        if indirection in [
                OP_INDIRECTION_LOCAL, OP_INDIRECTION_GLOBAL,
                OP_INDIRECTION_CONST0, OP_INDIRECTION_CONST1,
                OP_INDIRECTION_SHARED
        ] and t_offset:
            # ld.offset0.b32 $r01 -> 0x10
            # ld.offset1.shl.b32 $r01 -> 0x10
            # ld.offset1.shl.b32 $r02 -> 0x20
            source = OP_SOURCE_IMMEDIATE
            offset = t_offset
            if self.bits(0, 0x02000000):
                offset_inc = True
        # Scale registers if half
        if regscale and source == OP_SOURCE_REGISTER and size in [
                8, 16
        ] and indirection == OP_INDIRECTION_NONE:
            source = OP_SOURCE_HALF_REGISTER
        # Dest offset register?
        if doffset:
            source = OP_SOURCE_OFFSET_REGISTER
        # Pred register
        if spred:
            source = OP_SOURCE_PRED_REGISTER
        # Texture
        if tex:
            source = OP_SOURCE_TEXTURE
        # Address multiplier
        if source == OP_SOURCE_IMMEDIATE and (
                indirection == OP_INDIRECTION_SHARED or
            (indirection >= OP_INDIRECTION_CONST0
             and indirection <= OP_INDIRECTION_CONST15)):
            multiplier = None  # ??
            if size == 8:
                multiplier = 1
            elif size == 16:
                multiplier = 2
            elif size == 32:
                multiplier = 4
            if multiplier != None:
                value *= multiplier
            else:
                self.warnings.append("Invalid multiplier")

        return Operand(type, sign, size, source, indirection, value, offset,
                       flip, offset_inc)
Exemple #25
0
    def __init__(self, pins, mem_access, mem_regs=True):
        self.address = pins.address
        self.instruction = pins.instruction
        #print pins.instruction, mem_access
        self.operands = []

        # for memory instructions
        self.mem_reg = None

        # for call instructions
        self.called_function = None

        aopers = pins.augmented_operands
        for (i, x) in enumerate(aopers):
            if x == ",":
                self.operands.append(Operand(aopers[i - 1], aopers[i - 2]))
        self.operands.append(Operand(aopers[-1], aopers[-2]))

        self.read_operands = []
        self.write_operands = []

        # ldm: op_2 = [op_0]
        if (pins.instruction == "ldm"):

            self.write_operands = [self.operands[2]]
            self.mem_reg = self.operands[0]

            if (mem_access <> None):

                #if (mem_regs):
                #  self.read_operands  = [self.operands[0]]

                mem_source = mem_access["source"]
                mem_offset = mem_access["offset"]
                for i in range(self.operands[2].size):
                    name = mem_source + "@" + str(mem_offset + i)
                    self.read_operands.append(
                        Operand(name, "BYTE", mem_source, mem_offset + i))

                #self.read_operands.append(self.operands[0])
            #else:
            #  print "#WARNING: No memory access information of ldm in", self.address

        # stm: [op_2] = op_0
        elif (pins.instruction == "stm"):

            self.read_operands.append(self.operands[0])
            self.mem_reg = self.operands[2]

            if (mem_access <> None):
                #  if (mem_regs):
                #    self.write_operands = [self.operands[2]]

                mem_source = mem_access["source"]
                mem_offset = mem_access["offset"]
                for i in range(self.operands[0].size):
                    name = mem_source + "@" + str(mem_offset + i)
                    self.write_operands.append(
                        Operand(name, "BYTE", mem_source, mem_offset + i))
            #else:
            #  print "#WARNING: No memory access information of stm in", self.address

        elif (pins.instruction == "jcc"):
            self.read_operands = filter(lambda o: not o.isEmpty(),
                                        self.operands[0:2])
            self.write_operands = []

        elif (pins.instruction == "call"):
            #print self.operands[0].name
            if (self.operands[0].name <> "EMPTY"):
                self.called_function = self.operands[0].name

        else:

            self.read_operands = filter(lambda o: not o.isEmpty(),
                                        self.operands[0:2])
            self.write_operands = filter(lambda o: not o.isEmpty(),
                                         self.operands[2:3])
Exemple #26
0
    def parse(self, text):
        """Parse instruction text"""
        # [@<pred>] <base>[.<mod1>[.<mod2>...] <dest>[|<dest2>], <src1>, <src2>, ...
        text = text.strip()

        # Predication
        if text.startswith('@'):
            # Find and remove predicate string
            pred_end = text.index(" ")
            pred = text[1:pred_end]
            text = text[pred_end + 1:]

            match = re.match("(\!?)\$p([0-9]+)\.([0-9a-z]+)", pred)
            if not match:
                raise ValueError("Invalid predicate expression %s" % pred)
            (neg, pred, cc) = match.groups()
            # [!]$<pred>.<cc>
            try:
                cc = numlookup(condition_codes_rev, cc)
            except KeyError:
                raise ValueError("Invalid predicate condition code %s" % cc)
            if neg == "!":
                # Invert predicate
                cc = cc ^ 0xF
            # XXX we should be able to leave out the condition code if we just want
            # to evaluate the output of a set instruction
            self.pred_op = cc
            self.pred = int(pred)
        else:
            self.pred_op = 15  # always true
            self.pred = 0

        try:
            base_end = text.index(" ")
            ins = text[0:base_end]
            args = text[base_end + 1:].split(",")
        except ValueError:
            ins = text
            args = []
        mods = ins.split(".")
        self.base = mods[0]
        mods = ["." + x for x in mods[1:]]

        # Filter out and separate types
        types = []
        typere = re.compile("\.([subf][0-9]+|label)")
        for x in mods:
            if typere.match(x):
                types.append(x)
            else:
                self.modifiers.append(x)

        # Parse operands
        try:
            dest = args[0]
            dst_args = [x.strip() for x in dest.split("|")]
        except IndexError:
            dst_args = []
        src_args = [x.strip() for x in args[1:]]

        i = 0
        for x in dst_args:
            op = Operand()
            if x.startswith("$p"):
                # predicates have no type specifier
                type = ""
            else:
                try:
                    type = types[i]
                except IndexError:
                    raise ValueError("Not enough operand types in instruction")
                if len(types) != 1:
                    i += 1
            op.parse(x, type)
            self.dst_operands.append(op)

        for x in src_args:
            op = Operand()
            if x.startswith("$p"):
                # predicates have no type specifier
                type = ""
            else:
                try:
                    type = types[i]
                except IndexError:
                    raise ValueError("Not enough operand types in instruction")
                if len(types) != 1:
                    i += 1
            op.parse(x, type)
            self.src_operands.append(op)
Exemple #27
0
def getValueFromCode(reil_code, callstack, memory, addr_op, addr, val_op, val):

  assert(reil_code <> [])
  
  free_variables = []
  
  # code should be copied and reversed
  inss = list(reil_code) 
  inss.reverse()
  
  # counter is set
  counter = len(reil_code)
  
  tracked_stack_frame = callstack.index
  
  # especial operands in a call

  ssa = SSA()
  smt_conds  = SMT()
 
  # we will track op
  mvars = set([addr_op, val_op])    
  ssa_map = ssa.getMap(mvars, set(), set())
  eq = Eq(None, None)
  
  addr = Operand(str(addr), "DWORD")
  val = Operand(str(val), "BYTE")
  val.size = val_op.size
  
  smt_conds.add(eq.getEq(ssa_map[addr_op.name],addr))
  smt_conds.add(eq.getEq(ssa_map[val_op.name],val))

  for ins_str in inss:
    #print ins_str.strip("\n")
    
    pins = parse_reil(ins_str)
    
    ins = Instruction(pins, memory.getAccess(counter), mem_regs = False)
  
    ins_write_vars = set(ins.getWriteVarOperands())
    ins_read_vars = set(ins.getReadVarOperands())

    if pins.instruction == "jcc" or len(ins_write_vars.intersection(mvars)) > 0: 
    #if len(ins_write_vars.intersection(mvars)) > 0: 
      
      ssa_map = ssa.getMap(ins_read_vars.difference(mvars), ins_write_vars, ins_read_vars.intersection(mvars))

      cons = conds.get(pins.instruction, Condition)
      condition = cons(ins, ssa_map)
     
      mvars = mvars.difference(ins_write_vars) 
      mvars = ins_read_vars.union(mvars)
   
      smt_conds.add(condition.getEq())
      
    counter = counter - 1
    
    if len(mvars) > 0:
      tracked_stack_frame = callstack.index
    
    if pins.instruction == "call":
      
      if callstack.index == 1:
        esp_val = 4
      else:
        esp_val = 8
 
      ebp_val = 0
  
      esp_op = Operand("esp","DWORD")
      ebp_op = Operand("ebp","DWORD")
  
      initial_values_at_call = dict()
      initial_values_at_call[esp_op] = Operand(str(esp_val), "DWORD")
      initial_values_at_call[ebp_op] = Operand(str(ebp_val), "DWORD") 

      
      for iop in initial_values_at_call.keys():
        if not (iop in mvars):
          del initial_values_at_call[iop]
      
      ssa_map = ssa.getMap(set(), set(), set(initial_values_at_call.keys()))
      eq = Eq(None, None)
    
      for iop in initial_values_at_call:
        smt_conds.add(eq.getEq(ssa_map[iop.name],initial_values_at_call[iop]))
      
      mvars = set(filter(lambda o: not (o in initial_values_at_call.keys()), mvars))
      
      if (counter == 0 and len(mvars)>0):
        
        #cond = Initial_Cond(None, None)
        #
        #for v in mvars:
        #  print str(v),
        #  smt_conds.add(cond.getEq(v))
        
        #print "are free"
        
        #print smt_conds.solver
        free_variables = mvars
        break
      
      new_mvars = set()
      for v in mvars:
        if v.isMem(): # this should work for stack memory 
          eop = callstack.convertStackMemOp(v)
          #print eop
          smt_conds.add(eq.getEq(v,eop))
          new_mvars.add(eop)
      
      mvars = set(filter(lambda o: not (o.isMem()), mvars))
      mvars = mvars.union(new_mvars)
    
    # we update the current call for next instruction
    callstack.prevInstruction(ins_str) 
    
  #op.name = op.name+"_0"
  smt_conds.solve()
  smt_conds.write_smtlib_file("exp.smt2")
  smt_conds.write_sol_file("exp.sol")
  
  if (smt_conds.is_sat()):
    print "Solution:",
    for v in free_variables:
      if v.isReg():
        if (v in ssa_map):
          print v,smt_conds.getValue(ssa_map[v])
      elif v.isMem():
        sname, offset = stack.read(v)
        v.mem_source = sname
        print v, smt_conds.getValue(v)
  else:
    print "Not exploitable"
Exemple #28
0
def getTypedValueFromCode(inss, callstack, initial_values, memory, op, debug = False):
  
  # Initialization
  
  # we reverse the code order
  inss.reverse()
  
  # we reset the used memory variables
  Memvars.reset()
  
  # we save the current callstack
  last_index = callstack.index  # TODO: create a better interface
  
  # we set the instruction counter
  counter = len(inss)-1
  
  # ssa and smt objects
  ssa = SSA()
  smt_conds  = SMT()
  
  val_type = None
  mvars = set()
 
  if (op.isImm()):
    return op
  elif (op.isMem()):
    for i in range(op.size):
      name = op.mem_source+"@"+str(op.mem_offset+i)
      mvars.add(Operand(name, "BYTE", op.mem_source, op.mem_offset+i))
      #print name
  else:
    # we will start tracking op
    mvars.add(op)
    
  # we start without free variables
  fvars = set()
  
  ssa.getMap(mvars, set(), set())

  for ins_str in inss:
    #print inss.current, "->", ins_str.strip("\n")
    #print ins_str.strip("\n")
    #for v in mvars:
    #  print v,
    #
    #print ""
    #
    pins = parse_reil(ins_str)
    ins = Instruction(pins, memory.getAccess(counter), mem_regs = False)
  
    ins_write_vars = set(ins.getWriteVarOperands())
    ins_read_vars = set(ins.getReadVarOperands())
 
    if len(ins_write_vars.intersection(mvars)) > 0: 
      
      ssa_map = ssa.getMap(ins_read_vars.difference(mvars), ins_write_vars, ins_read_vars.intersection(mvars))

      cons = conds.get(pins.instruction, Condition)
      condition = cons(ins, ssa_map)
     
      mvars = mvars.difference(ins_write_vars) 
      mvars = ins_read_vars.union(mvars)
   
      smt_conds.add(condition.getEq())
    
    # simple typing
    new_val_type = detectType(mvars, ins, counter, callstack)
    
    # additional conditions
    mvars = addAditionalConditions(mvars, ins, ssa, callstack, smt_conds)
    
    val_type = max(val_type, new_val_type)


    # no more things to do
    # we update the counter 
    counter = counter - 1    
    # we update the current call for next instruction
    callstack.prevInstruction(ins_str) 
  
  if val_type == None:
    val_type = "imm"
  
  for v in mvars:
    if not (v in initial_values):
      print "#Warning__", str(v), "is free!" 
  
  setInitialConditions(ssa, initial_values, smt_conds)
  smt_conds.solve(debug)
  
  if op.isReg():
    op.name = op.name+"_0"
    
  elif op.isMem():
    op.mem_source = op.mem_source+"_0"
  
  callstack.index = last_index  # TODO: create a better interface
  if (debug):
    print val_type, op, smt_conds.getValue(op)
  return mkVal(val_type, smt_conds.getValue(op))
from AdditionSpecification import *
from DivisionSpecification import *
from SubtractionSpecification import *
from sympy.ntheory import factorint

from Operand import *

print type(factorint(12).keys())

# Addition stuff
operand1 = Operand()
operand1.digits = {1.0: 8, 10.0: 1}

operand2 = Operand()
operand2.digits = {1.0: 3, 10.0: 8}

operand3 = Operand()
operand3.digits = {1.0: 1}

operand4 = Operand()
operand4.digits = {1.0: 3}

problemSpecification = AdditionSpecification(operand1, operand2, [],
                                             lambda x, y: (x + y), {}, {})

print problemSpecification.digitAdjustments(operand1, operand2)
print problemSpecification.digitAdjustments(operand1, operand3)
print problemSpecification.digitAdjustments(operand1, operand4)

# Subtraction stuff
operand1 = Operand()