Beispiel #1
0
    def introduce_taint(self, taint, instruction):
        if not instruction["error"] and instruction["depth"] - 1 < len(
                self.callstack):
            records = self.callstack[instruction["depth"] - 1]

            mutator = SymbolicTaintAnalyzer.stack_taint_table[
                instruction["op"]]
            for i in range(1, mutator[1] + 1):
                if not records[-1].stack[-i]:
                    records[-1].stack[-i] = [taint]
                else:
                    record = list(records[-1].stack[-i])
                    if not taint in record:
                        record.insert(0, taint)
                    records[-1].stack[-i] = record

            if instruction["op"] in [
                    "CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"
            ]:
                if not records[-1].output:
                    records[-1].output = []
                records[-1].output += [taint]
            elif instruction["op"] == "CALLDATACOPY":
                records[-1].memory[convert_stack_value_to_int(
                    instruction["stack"][-1])] = []
                records[-1].memory[convert_stack_value_to_int(
                    instruction["stack"][-1])].append(taint)
Beispiel #2
0
 def mutate_sha3(record, instruction):
     record.stack.pop()
     offset = convert_stack_value_to_int(instruction["stack"][-1])
     record.stack.pop()
     size = convert_stack_value_to_int(instruction["stack"][-2])
     value = SymbolicTaintAnalyzer.extract_taint_from_memory(
         record.memory, offset, size)
     record.stack.append(value)
Beispiel #3
0
 def mutate_copy(record, op, instruction):
     if op == "EXTCODECOPY":
         record.stack.pop()
         index = convert_stack_value_to_int(instruction["stack"][-2])
     else:
         index = convert_stack_value_to_int(instruction["stack"][-1])
     record.stack.pop()
     record.stack.pop()
     record.memory[index] = record.stack.pop()
     record.memory = collections.OrderedDict(sorted(record.memory.items()))
 def detect_transaction_order_dependency(self, current_instruction,
                                         tainted_record, individual,
                                         transaction_index):
     if current_instruction["op"] == "SSTORE":
         if tainted_record and tainted_record.stack and tainted_record.stack[
                 -2] and is_expr(tainted_record.stack[-2][0]):
             index = convert_stack_value_to_int(
                 current_instruction["stack"][-1])
             if index not in self.sstores:
                 self.sstores[index] = (
                     tainted_record.stack[-2][0],
                     individual.chromosome[transaction_index]["arguments"]
                     [0], individual.solution[transaction_index]
                     ["transaction"]["from"], current_instruction["pc"])
     elif current_instruction["op"] == "SLOAD":
         index = convert_stack_value_to_int(
             current_instruction["stack"][-1])
         if index in self.sstores and self.sstores[index][
                 1] != individual.chromosome[transaction_index][
                     "arguments"][0]:
             self.sloads[index] = (
                 self.sstores[index][0],
                 individual.chromosome[transaction_index]["arguments"][0],
                 individual.solution[transaction_index]["transaction"]
                 ["from"], self.sstores[index][3])
     elif current_instruction["op"] == "CALL":
         if tainted_record and tainted_record.stack and tainted_record.stack[
                 -3] and is_expr(tainted_record.stack[-3][0]):
             for index in self.sloads:
                 if index in self.sstores and self.sloads[index][
                         0] == tainted_record.stack[-3][0] and self.sloads[
                             index][1] == individual.chromosome[
                                 transaction_index]["arguments"][0]:
                     return self.sloads[index][3]
         if tainted_record and tainted_record.stack and tainted_record.stack[
                 -2]:
             value = convert_stack_value_to_int(
                 current_instruction["stack"][-3])
             if value > 0 or tainted_record and tainted_record.stack and tainted_record.stack[
                     -3]:
                 for i in range(transaction_index + 1,
                                len(individual.chromosome)):
                     if self.sstores and individual.chromosome[
                             transaction_index][
                                 "arguments"] == individual.chromosome[i][
                                     "arguments"] and individual.solution[
                                         transaction_index]["transaction"][
                                             "from"] != individual.solution[
                                                 i]["transaction"]["from"]:
                         return list(self.sstores.values())[0][-1]
     return None
 def detect_unhandled_exception(self, previous_instruction,
                                current_instruction, tainted_record):
     # Register all exceptions
     if previous_instruction and previous_instruction["op"] in [
             "CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"
     ] and convert_stack_value_to_int(
             current_instruction["stack"][-1]) == 1:
         if tainted_record and tainted_record.stack and tainted_record.stack[
                 -1] and is_expr(tainted_record.stack[-1][0]):
             self.exceptions[tainted_record.stack[-1]
                             [0]] = previous_instruction["pc"]
     # Remove all handled exceptions
     elif current_instruction["op"] == "JUMPI" and self.exceptions:
         if tainted_record and tainted_record.stack and tainted_record.stack[
                 -2] and is_expr(tainted_record.stack[-2][0]):
             for var in get_vars(tainted_record.stack[-2][0]):
                 if var in self.exceptions:
                     del self.exceptions[var]
     # Report all unhandled exceptions at termination
     elif current_instruction["op"] in [
             "RETURN", "STOP", "SUICIDE", "SELFDESTRUCT"
     ] and self.exceptions:
         for exception in self.exceptions:
             return self.exceptions[exception]
     return None
Beispiel #6
0
 def check_taint(self, instruction, source=None):
     if not instruction["error"] and instruction["depth"] - 1 < len(
             self.callstack):
         records = self.callstack[instruction["depth"] - 1]
         if len(records) < 2:
             return None
         mutator = SymbolicTaintAnalyzer.stack_taint_table[
             instruction["op"]]
         values = []
         for i in range(0, mutator[0]):
             if not records[-2].stack:
                 break
             if i + 1 < len(records[-2].stack):
                 record = records[-2].stack[-(i + 1)]
                 if instruction[
                         "op"] in SymbolicTaintAnalyzer.memory_access:
                     if not i in SymbolicTaintAnalyzer.memory_access[
                             instruction["op"]]:
                         if record:
                             values += record
                 else:
                     if record:
                         values += record
         if instruction["op"] in SymbolicTaintAnalyzer.memory_access:
             mutator = SymbolicTaintAnalyzer.memory_access[
                 instruction["op"]]
             offset = convert_stack_value_to_int(
                 instruction["stack"][-(mutator[0] + 1)])
             size = convert_stack_value_to_int(
                 instruction["stack"][-(mutator[1] + 1)])
             taint = SymbolicTaintAnalyzer.extract_taint_from_memory(
                 records[-2].memory, offset, size)
             if taint:
                 values += taint
         if source:
             for value in values:
                 if value == source:
                     return records[-2]
         else:
             if values:
                 return records[-2]
         return None
Beispiel #7
0
 def detect_reentrancy(self, tainted_record, current_instruction):
     # Remember sloads
     if current_instruction["op"] == "SLOAD":
         if tainted_record and tainted_record.stack and tainted_record.stack[
                 -1]:
             storage_index = convert_stack_value_to_int(
                 current_instruction["stack"][-1])
             self.sloads[storage_index] = current_instruction["pc"]
     # Remember calls with more than 2300 gas and where the value is larger than zero/symbolic or where destination is symbolic
     elif current_instruction["op"] == "CALL" and self.sloads:
         gas = convert_stack_value_to_int(current_instruction["stack"][-1])
         value = convert_stack_value_to_int(
             current_instruction["stack"][-3])
         if gas > 2300 and (value > 0
                            or tainted_record and tainted_record.stack
                            and tainted_record.stack[-3]):
             self.calls.add(current_instruction["pc"])
         if gas > 2300 and tainted_record and tainted_record.stack and tainted_record.stack[
                 -2]:
             self.calls.add(current_instruction["pc"])
             for pc in self.sloads.values():
                 if pc < current_instruction["pc"]:
                     return current_instruction["pc"]
     # Check if this sstore is happening after a call and if it is happening after an sload which shares the same storage index
     elif current_instruction["op"] == "SSTORE" and self.calls:
         if tainted_record and tainted_record.stack and tainted_record.stack[
                 -1]:
             storage_index = convert_stack_value_to_int(
                 current_instruction["stack"][-1])
             if storage_index in self.sloads:
                 for pc in self.calls:
                     if pc < current_instruction["pc"]:
                         return pc
     # Clear sloads and calls from previous transactions
     elif current_instruction["op"] in [
             "STOP", "RETURN", "REVERT", "ASSERTFAIL", "INVALID", "SUICIDE",
             "SELFDESTRUCT"
     ]:
         self.sloads = {}
         self.calls = set()
     return None
 def detect_block_dependency(self, tainted_record, current_instruction,
                             previous_branch):
     # Check for a call with transfer of ether (check if amount is greater than zero or symbolic)
     if current_instruction["op"] == "CALL" and (convert_stack_value_to_int(current_instruction["stack"][-3]) or tainted_record and tainted_record.stack[-3]) or \
        current_instruction["op"] in ["SELFDESTRUCT", "SUICIDE", "CREATE", "DELEGATECALL"]:
         # Check if there is a block dependency by analyzing previous branch expression
         for expression in previous_branch:
             if "blockhash" in str(expression) or \
                "coinbase" in str(expression) or \
                "timestamp" in str(expression) or \
                "number" in str(expression) or \
                "difficulty" in str(expression) or \
                "gaslimit" in str(expression):
                 self.block_dependency = True
     # Check if block related information flows into condition
     elif current_instruction and current_instruction["op"] in [
             "LT", "GT", "SLT", "SGT", "EQ"
     ]:
         if tainted_record and tainted_record.stack:
             if tainted_record.stack[-1]:
                 for expression in tainted_record.stack[-1]:
                     if "blockhash" in str(expression) or \
                        "coinbase" in str(expression) or \
                        "timestamp" in str(expression) or \
                        "number" in str(expression) or \
                        "difficulty" in str(expression) or \
                        "gaslimit" in str(expression):
                         self.block_dependency = True
             if tainted_record.stack[-2]:
                 for expression in tainted_record.stack[-2]:
                     if "blockhash" in str(expression) or \
                        "coinbase" in str(expression) or \
                        "timestamp" in str(expression) or \
                        "number" in str(expression) or \
                        "difficulty" in str(expression) or \
                        "gaslimit" in str(expression):
                         self.block_dependency = True
     # Register block related information
     elif current_instruction["op"] in [
             "BLOCKHASH", "COINBASE", "TIMESTAMP", "NUMBER", "DIFFICULTY",
             "GASLIMIT"
     ]:
         self.block_instruction = current_instruction["pc"]
     # Check if execution stops withour exception
     if self.block_dependency and current_instruction["op"] in [
             "STOP", "SELFDESTRUCT", "RETURN"
     ]:
         return self.block_instruction
     return None
Beispiel #9
0
    def detect_integer_overflow(self, previous_instruction, current_instruction, tainted_record):
        if previous_instruction and previous_instruction["op"] == "ADD":
            a = convert_stack_value_to_int(previous_instruction["stack"][-2])
            b = convert_stack_value_to_int(previous_instruction["stack"][-1])
            c = convert_stack_value_to_int(current_instruction["stack"][-1])
            if a + b != c:
                if tainted_record and tainted_record.stack and tainted_record.stack[-1]:
                    if get_vars(tainted_record.stack[-1][0]):
                        self.overflows[previous_instruction["pc"]] = get_vars(tainted_record.stack[-1][0])

        elif previous_instruction and previous_instruction["op"] == "MUL":
            a = convert_stack_value_to_int(previous_instruction["stack"][-2])
            b = convert_stack_value_to_int(previous_instruction["stack"][-1])
            c = convert_stack_value_to_int(current_instruction["stack"][-1])
            if a * b != c:
                if tainted_record and tainted_record.stack and tainted_record.stack[-1]:
                    if get_vars(tainted_record.stack[-1][0]):
                        self.overflows[previous_instruction["pc"]] = get_vars(tainted_record.stack[-1][0])

        elif previous_instruction and previous_instruction["op"] == "SUB":
            a = convert_stack_value_to_int(previous_instruction["stack"][-2])
            b = convert_stack_value_to_int(previous_instruction["stack"][-1])
            c = convert_stack_value_to_int(current_instruction["stack"][-1])
            if a - b != c:
                if tainted_record and tainted_record.stack and tainted_record.stack[-1]:
                    if get_vars(tainted_record.stack[-1][0]):
                        self.underflows[previous_instruction["pc"]] = get_vars(tainted_record.stack[-1][0])

        if current_instruction and current_instruction["op"] == "SSTORE":
            if tainted_record and tainted_record.stack and tainted_record.stack[-2]:
                for pc in self.overflows:
                    for var1 in get_vars(tainted_record.stack[-2][0]):
                        for var2 in self.overflows[pc]:
                            if var1 == var2:
                                return pc, "overflow"
                for pc in self.underflows:
                    for var1 in get_vars(tainted_record.stack[-2][0]):
                        for var2 in self.underflows[pc]:
                            if var1 == var2:
                                return pc, "underflow"
        return None, None
Beispiel #10
0
 def detect_leaking_ether(self, current_instruction, taint_record,
                          individual, transaction_index, previous_branch):
     if current_instruction["op"] == "STOP":
         if individual.solution[transaction_index]["transaction"][
                 "value"] > 0:
             self.spenders.add(individual.solution[transaction_index]
                               ["transaction"]["from"])
         if transaction_index in self.leaks:
             if individual.solution[transaction_index]["transaction"][
                     "from"] not in self.spenders:
                 return self.leaks[transaction_index]
     elif current_instruction["op"] == "CALL":
         to = "0x" + convert_stack_value_to_hex(
             current_instruction["stack"][-2]).lstrip("0")
         # Check if the destination of the call is an attacker
         if to in settings.ATTACKER_ACCOUNTS and to == individual.solution[
                 transaction_index]["transaction"]["from"]:
             # Check if the value of the call is larger than zero or the contract balance
             if convert_stack_value_to_int(
                     current_instruction["stack"][-3]
             ) > 0 or taint_record and taint_record.stack[-3] and is_expr(
                     taint_record.stack[-3][0]) and "balance" in str(
                         taint_record.stack[-3][0]):
                 # Check if the destination did not spend ether
                 if not to in self.spenders:
                     # Check if the destination was not previously passed as argument by a trusted user
                     address_passed_as_argument = False
                     for i in range(transaction_index):
                         for argument in individual.chromosome[i][
                                 "arguments"]:
                             if argument in settings.ATTACKER_ACCOUNTS and individual.solution[
                                     i]["transaction"][
                                         "from"] not in settings.ATTACKER_ACCOUNTS:
                                 address_passed_as_argument = True
                     if not address_passed_as_argument:
                         self.leaks[
                             transaction_index] = current_instruction["pc"]
     return None
Beispiel #11
0
    def detect_integer_overflow(self, mfe, tainted_record,
                                previous_instruction, current_instruction,
                                individual, transaction_index):
        if previous_instruction and previous_instruction[
                "op"] == "NOT" and current_instruction and current_instruction[
                    "op"] == "ADD":
            self.compiler_value_negation = True
        # Addition
        elif previous_instruction and previous_instruction["op"] == "ADD":
            a = convert_stack_value_to_int(previous_instruction["stack"][-2])
            b = convert_stack_value_to_int(previous_instruction["stack"][-1])
            #print(convert_stack_value_to_int(previous_instruction["stack"][-2]))
            #print(convert_stack_value_to_int(previous_instruction["stack"][-1]))
            if a + b != convert_stack_value_to_int(
                    current_instruction["stack"]
                [-1]) and not self.compiler_value_negation:
                #print("!!!!!!!!")
                #print("!!!!!!!!")
                #print("addition overflow")
                #print("!!!!!!!!")
                #print("!!!!!!!!")
                if tainted_record and tainted_record.stack and tainted_record.stack[
                        -1]:
                    index = ''.join(
                        str(taint) for taint in tainted_record.stack[-1])
                    if "calldataload" in index or "callvalue" in index:
                        _function_hash = individual.chromosome[
                            transaction_index]["arguments"][0]
                        _is_string = False
                        for _argument_index in [
                                int(a.split("_")[-1]) for a in index.split()
                                if a.startswith("calldataload_" +
                                                str(transaction_index) + "_")
                        ]:
                            if individual.generator.interface[_function_hash][
                                    _argument_index] == "string":
                                _is_string = True
                        if not _is_string:
                            self.overflows[index] = previous_instruction["pc"]

        # Multiplication
        elif previous_instruction and previous_instruction["op"] == "MUL":
            a = convert_stack_value_to_int(previous_instruction["stack"][-2])
            b = convert_stack_value_to_int(previous_instruction["stack"][-1])
            if a * b != convert_stack_value_to_int(
                    current_instruction["stack"][-1]):
                """print(convert_stack_value_to_int(previous_instruction["stack"][-2]))
                print(convert_stack_value_to_int(previous_instruction["stack"][-1]))
                print(convert_stack_value_to_hex(previous_instruction["stack"][-1]))
                print(convert_stack_value_to_int(current_instruction["stack"][-1]))
                print(convert_stack_value_to_hex(current_instruction["stack"][-1]))
                print(" ")"""
                #print("Multiplication overflow")
                if tainted_record and tainted_record.stack and tainted_record.stack[
                        -1]:
                    index = ''.join(
                        str(taint) for taint in tainted_record.stack[-1])
                    #print("yes")
                    if "calldataload" in index or "callvalue" in index:
                        #print("added")
                        self.overflows[index] = previous_instruction["pc"]
                        #tainted_record.stack[-2] = [BitVec("_".join(["overflow", hex(previous_instruction["pc"])]), 256)]
                        #index = ''.join(str(taint) for taint in tainted_record.stack[-2])
                        #self.overflows[index] = previous_instruction["pc"]
                #else:
                #    print("nope")
                #print("")
        # Subtraction
        elif previous_instruction and previous_instruction["op"] == "SUB":
            a = convert_stack_value_to_int(previous_instruction["stack"][-1])
            b = convert_stack_value_to_int(previous_instruction["stack"][-2])
            if a - b != convert_stack_value_to_int(
                    current_instruction["stack"][-1]):
                if tainted_record and tainted_record.stack and tainted_record.stack[
                        -1]:
                    index = ''.join(
                        str(taint) for taint in tainted_record.stack[-1])
                    self.underflows[index] = previous_instruction["pc"]
                else:
                    tainted_record = mfe.symbolic_taint_analyzer.get_tainted_record(
                        index=-1)
                    if tainted_record:
                        tainted_record.stack[-2] = [
                            BitVec(
                                "_".join([
                                    "underflow",
                                    hex(previous_instruction["pc"])
                                ]), 256)
                        ]
                        index = ''.join(
                            str(taint) for taint in tainted_record.stack[-2])
                        self.underflows[index] = previous_instruction["pc"]

        # Check if overflow flows into storage
        if current_instruction and current_instruction["op"] == "SSTORE":
            #print("sstore")
            if tainted_record and tainted_record.stack and tainted_record.stack[
                    -2]:  # Storage value
                index = ''.join(
                    str(taint) for taint in tainted_record.stack[-2])
                #print("sstore index")
                #print(index)

                if index in self.overflows:
                    return self.overflows[index], "overflow"
                if index in self.underflows:
                    return self.underflows[index], "underflow"
        # Check if overflow flows into call
        elif current_instruction and current_instruction["op"] == "CALL":
            if tainted_record and tainted_record.stack and tainted_record.stack[
                    -3]:  # Call value
                #print(tainted_record.stack)
                index = ''.join(
                    str(taint) for taint in tainted_record.stack[-3])
                if index in self.overflows:
                    #print("!!!!!!!")
                    #print("!!!!!!!")
                    #print("yolo")
                    #print("!!!!!!!")
                    #print("!!!!!!!")
                    return self.overflows[index], "overflow"
                if index in self.underflows:
                    return self.underflows[index], "underflow"
        # Check if overflow flows into condition
        elif current_instruction and current_instruction["op"] in [
                "LT", "GT", "SLT", "SGT", "EQ"
        ]:
            #print(tainted_record.stack)
            if tainted_record and tainted_record.stack:
                if tainted_record.stack[-1]:  # First operand
                    index = ''.join(
                        str(taint) for taint in tainted_record.stack[-1])
                    if index in self.overflows:
                        return self.overflows[index], "overflow"
                    if index in self.underflows:
                        return self.underflows[index], "underflow"
                if tainted_record.stack[-2]:  # Second operand
                    index = ''.join(
                        str(taint) for taint in tainted_record.stack[-2])
                    if index in self.overflows:
                        return self.overflows[index], "overflow"
                    if index in self.underflows:
                        return self.underflows[index], "underflow"
        return None, None
Beispiel #12
0
 def mutate_mstore(record, instruction):
     record.stack.pop()
     index, value = convert_stack_value_to_int(
         instruction["stack"][-1]), record.stack.pop()
     record.memory[index] = value
     record.memory = collections.OrderedDict(sorted(record.memory.items()))
Beispiel #13
0
 def mutate_mload(record, instruction):
     record.stack.pop()
     index = convert_stack_value_to_int(instruction["stack"][-1])
     record.stack.append(
         SymbolicTaintAnalyzer.extract_taint_from_memory(
             record.memory, index, 32))
Beispiel #14
0
 def get_operand(record, instruction, index):
     if record.stack[-1]:
         return simplify(record.stack[-index][0])
     return BitVecVal(
         convert_stack_value_to_int(instruction["stack"][-index]), 256)
Beispiel #15
0
    def mutate_stack_symbolically(record, mutator, instruction):
        if instruction["op"] in [
                # Arithmetic Operations
                "ADD",
                "MUL",
                "SUB",
                "DIV",
                "SDIV",
                "MOD",
                "SMOD",
                "ADDMOD",
                "MULMOD",
                "EXP",
                "SHL",
                "SHR",
                "SAR",
                # Comparison Operations
                "LT",
                "GT",
                "SLT",
                "SGT",
                "EQ",
                "ISZERO",
                #  Bitwise Logic Operations
                "AND",
                "OR",
                "XOR",
                "NOT"
        ]:

            # Detect loops
            if instruction["pc"] not in SymbolicTaintAnalyzer.visited_pcs:
                SymbolicTaintAnalyzer.visited_pcs.add(instruction["pc"])
            else:
                for i in range(mutator[0]):
                    record.stack.pop()
                record.stack.append(False)
                return

            # First Operand
            op1 = None
            if mutator[0] > 0:
                if record.stack[-1]:
                    op1 = simplify(record.stack[-1][0])
                else:
                    op1 = BitVecVal(
                        convert_stack_value_to_int(instruction["stack"][-1]),
                        256)

            # Second Operand
            op2 = None
            if mutator[0] > 1:
                if record.stack[-2]:
                    op2 = simplify(record.stack[-2][0])
                else:
                    op2 = BitVecVal(
                        convert_stack_value_to_int(instruction["stack"][-2]),
                        256)

            # Third Operand
            op3 = None
            if mutator[0] > 2:
                if record.stack[-3]:
                    op3 = simplify(record.stack[-3][0])
                else:
                    op3 = BitVecVal(
                        convert_stack_value_to_int(instruction["stack"][-3]),
                        256)

            # Check if at least one of the operands is a symbolic expression
            if record and ((is_expr(op1) and record.stack[-1]) or
                           (is_expr(op2) and record.stack[-2]) or
                           (is_expr(op3) and record.stack[-3])):

                # Pop old values from stack
                for i in range(mutator[0]):
                    record.stack.pop()

                # Push new symbolic expression to stack
                # Arithmetic Operations
                if instruction["op"] == "ADD":
                    if is_fixed(op1) and op1.as_long() == 0:
                        record.stack.append([op2])
                    elif is_fixed(op2) and op2.as_long() == 0:
                        record.stack.append([op1])
                    else:
                        record.stack.append([op1 + op2])
                elif instruction["op"] == "MUL":
                    if (is_fixed(op1) and op1.as_long() == 0) or \
                       (is_fixed(op2) and op2.as_long() == 0):
                        record.stack.append([BIT_VEC_VAL_ZERO])
                    else:
                        record.stack.append([op1 * op2])
                elif instruction["op"] == "SUB":
                    record.stack.append([op1 - op2])
                elif instruction["op"] == "DIV":
                    if (is_fixed(op1) and op1.as_long() == 0) or \
                       (is_fixed(op2) and op2.as_long() == 0):
                        record.stack.append([BIT_VEC_VAL_ZERO])
                    else:
                        record.stack.append([UDiv(op1, op2)])
                elif instruction["op"] == "SDIV":
                    if (is_fixed(op1) and op1.as_long() == 0) or \
                       (is_fixed(op2) and op2.as_long() == 0):
                        record.stack.append([BIT_VEC_VAL_ZERO])
                    else:
                        record.stack.append([op1 / op2])
                elif instruction["op"] == "MOD":
                    record.stack.append(
                        [BIT_VEC_VAL_ZERO if op2 == 0 else URem(op1, op2)])
                elif instruction["op"] == "SMOD":
                    record.stack.append(
                        [BIT_VEC_VAL_ZERO if op2 == 0 else SRem(op1, op2)])
                elif instruction["op"] == "ADDMOD":
                    record.stack.append(
                        [URem(URem(op1, op3) + URem(op2, op3), op3)])
                elif instruction["op"] == "MULMOD":
                    record.stack.append(
                        [URem(URem(op1, op3) * URem(op2, op3), op3)])
                elif instruction["op"] == "EXP":
                    if is_bv_value(op1) and is_bv_value(op2):
                        record.stack.append([
                            BitVecVal(
                                pow(op1.as_long(), op2.as_long(), 2**256), 256)
                        ])
                    else:
                        record.stack.append(False)
                elif instruction["op"] == "SHL":
                    record.stack.append([op1 << op2])
                elif instruction["op"] == "SHR":
                    record.stack.append([LShR(op1, op2)])
                elif instruction["op"] == "SAR":
                    record.stack.append([op1 >> op2])

                # Comparison Operations
                elif instruction["op"] == "LT":
                    record.stack.append(
                        [If(ULT(op1, op2), BIT_VEC_VAL_ONE, BIT_VEC_VAL_ZERO)])
                elif instruction["op"] == "GT":
                    record.stack.append(
                        [If(UGT(op1, op2), BIT_VEC_VAL_ONE, BIT_VEC_VAL_ZERO)])
                elif instruction["op"] == "SLT":
                    record.stack.append(
                        [If(op1 < op2, BIT_VEC_VAL_ONE, BIT_VEC_VAL_ZERO)])
                elif instruction["op"] == "SGT":
                    record.stack.append(
                        [If(op1 > op2, BIT_VEC_VAL_ONE, BIT_VEC_VAL_ZERO)])
                elif instruction["op"] == "EQ":
                    record.stack.append(
                        [If(op1 == op2, BIT_VEC_VAL_ONE, BIT_VEC_VAL_ZERO)])
                elif instruction["op"] == "ISZERO":
                    record.stack.append(
                        [If(op1 == 0, BIT_VEC_VAL_ONE, BIT_VEC_VAL_ZERO)])

                #  Bitwise Logic Operations
                elif instruction["op"] == "AND":
                    if (is_fixed(op1) and op1.as_long() == 0) or \
                       (is_fixed(op2) and op2.as_long() == 0):
                        record.stack.append([BIT_VEC_VAL_ZERO])
                    else:
                        record.stack.append([op1 & op2])
                elif instruction["op"] == "OR":
                    record.stack.append([op1 | op2])
                elif instruction["op"] == "XOR":
                    if is_fixed(op1) and op1.as_long() == 0:
                        record.stack.append([op2])
                    elif is_fixed(op2) and op2.as_long() == 0:
                        record.stack.append([op1])
                    else:
                        record.stack.append([op1 ^ op2])
                elif instruction["op"] == "NOT":
                    record.stack.append([~op1])
            else:
                SymbolicTaintAnalyzer.mutate_stack(record, mutator)
        else:
            SymbolicTaintAnalyzer.mutate_stack(record, mutator)