Example #1
0
def pprint_logic(exp, indent=2):
    if type(exp) == list:
        for idx, line in enumerate(exp):
            pprint_logic(line, indent)

    elif opcode(exp) == 'or' and len(exp)>1:
        print(' '*indent + 'if')
        pprint_logic(exp[1], indent+2)
        for line in exp[2:]:
            print(' '*indent + 'or')
            pprint_logic(line, indent+2)
    else:
        print(' '*indent + prettify(exp, add_color=True))
Example #2
0
    def _print(self):
        set_func(self.hash)
        set_func_params_if_none(self.params)

        if self.const is not None:

            val = self.const
            if opcode(val) == "return":
                val = val[1]

            return [
                COLOR_HEADER + "const " + ENDC +
                str(self.color_name.split("()")[0]) + " = " + COLOR_BOLD +
                prettify(val) + ENDC
            ]

        else:
            comment = ""

            if not self.payable:
                comment = "# not payable"

            if self.name == "_fallback()":
                if self.payable:
                    comment = "# default function"
                else:
                    comment = "# not payable, default function"  # qweqw

            header = [
                color("def ", C.header) + self.color_name +
                (color(" payable", C.header) if self.payable else "") + ": " +
                color(comment, C.gray)
            ]

            if self.ast is not None:
                res = list(pprint_logic(self.ast))
            else:
                res = list(pprint_logic(self.trace))

            if len(res) == 0:
                res = ["  stop"]

            return header + res
Example #3
0
        def store_to_set(line):
            if line ~ ('store', :size, :off, :idx, :val):
                return ('set', ('stor', size, off, idx), val)
            else:
                return line

        def loc_to_name(exp):
            if exp ~ ('loc', int:num):
                if num < 1000:
                    return ('name', 'stor' + str(num), num)
                else:
                    return ('name', 'stor' + hex(num)[2:6].upper(), num)

            if exp ~ ('loc', :num):
                return ('name', 'stor' + prettify(num, add_color=False, parentheses=True), num)
            else:
                return exp

        def arr_rem_mul(exp):
            if exp ~ ('array', ('mask_shl', :size, :off, int:shl, :idx), :loc):
                r = 2 ** shl
                e_loc = get_loc(loc)

                for s in self.stor_defs:
                    assert s ~ ('def', _, :d_loc, :d_def)
                    if s ~ ('def', _, e_loc, ('array', ('struct', r))):
                        return ('array', ('mask_shl', size, off, 0, idx), loc)

#                exit()
Example #4
0
def pprint(exp):
    for e in exp:
        print(' ', prettify(e, add_color=True))
Example #5
0
    def analyse(self):
        assert len(self.trace) > 0

        def find_returns(exp):
            if opcode(exp) == 'return':
                return [exp]
            else:
                return []

        exp_text = []

        self.returns = find_f_list(self.trace, find_returns)

        exp_text.append(('possible return values', prettify(self.returns)))

        first = self.trace[0]

        if opcode(first) == 'if' and simplify_bool(first[1]) == 'callvalue'   \
                and (first[2][0] == ('revert', 0) or opcode(first[2][0]) == 'invalid'):
            self.trace = self.trace[0][3]
            self.payable = False
        elif opcode(first) == 'if' and simplify_bool(first[1]) == ('iszero', 'callvalue')   \
                and (first[3][0] == ('revert', 0) or opcode(first[3][0]) == 'invalid'):
            self.trace = self.trace[0][2]
            self.payable = False
        else:
            self.payable = True

        exp_text.append(('payable', self.payable))

        self.read_only = True
        for op in ['store', 'selfdestruct', 'call', 'delegatecall', 'codecall', 'create']:
            if f"'{op}'" in str(self.trace):
                self.read_only = False

        exp_text.append(('read_only', self.read_only))


        '''
            const func detection
        '''

        self.const = self.read_only
        for exp in ['storage', 'calldata', 'calldataload', 'store', 'cd']:
            if exp in str(self.trace) or len(self.returns)!=1:
                self.const = False

        if self.const:
            self.const = self.returns[0]
            if len(self.const) == 3 and opcode(self.const[2]) == 'data':
                self.const = self.const[2]
            if len(self.const) == 3 and opcode(self.const[2]) == 'mask_shl':
                self.const = self.const[2]
            if len(self.const) == 3 and type(self.const[2]) == int:
                self.const = self.const[2]
        else:
            self.const = None

        if self.const:
            exp_text.append(('const', self.const))

        '''
            getter detection
        '''

        self.getter = None
        self.simplify_string_getter_from_storage()
        if self.const is None and \
           self.read_only and \
           len(self.returns) == 1:
                ret = self.returns[0][1]
                if ret ~ ('bool', ('storage', _, _, :loc)):
Example #6
0
                    # kitties getKitten - with more cases this and the above could be uniformed
                    if self.getter is None:
                        prev_loc = -1
                        for e in terms:
                            def l2(x):
                                if x ~ ('sha3', ('data', _, int:l)):
                                    if l < 1000:
                                        return l
                                if x ~ ('sha3', int:l) and l < 1000:
                                    return l
                                return None

                            loc = find_f(e, l2)
                            if not loc or (prev_loc != -1 and prev_loc != loc):
                                break
                            prev_loc = loc

                        else:
                            self.getter = ('struct', ('loc', loc))

                else:
                    pass

        if self.getter:
            exp_text.append((f'getter for', prettify(self.getter)))

        explain_text('function traits', exp_text)

        return self
 def __str__(self):
     return "[" + (", ".join([prettify(el, parentheses=False) for el in self.stack])) + "]"
Example #8
0
            else:
                return line

        def loc_to_name(exp):
            if m := match(exp, ("loc", ":int:num")):
                num = m.num
                if num < 1000:
                    return ("name", "stor" + str(num), num)
                else:
                    return ("name", "stor" + hex(num)[2:6].upper(), num)

            if m := match(exp, ("loc", ":num")):
                return (
                    "name",
                    "stor" +
                    prettify(m.num, add_color=False, parentheses=True),
                    m.num,
                )

            return exp

        def arr_rem_mul(exp):
            if m := match(
                    exp,
                ("array",
                 ("mask_shl", ":size", ":off", ":int:shl", ":idx"), ":loc"),
            ):
                size, off, shl, idx, loc = m.size, m.off, m.shl, m.idx, m.loc
                r = 2**shl
                e_loc = get_loc(loc)
Example #9
0
    def analyse(self):
        assert len(self.trace) > 0

        def find_returns(exp):
            if opcode(exp) == "return":
                return [exp]
            else:
                return []

        exp_text = []

        self.returns = find_f_list(self.trace, find_returns)

        exp_text.append(("possible return values", prettify(self.returns)))

        first = self.trace[0]

        if (opcode(first) == "if" and simplify_bool(first[1]) == "callvalue"
                and (first[2][0] == ("revert", 0)
                     or opcode(first[2][0]) == "invalid")):
            self.trace = self.trace[0][3]
            self.payable = False
        elif (opcode(first) == "if"
              and simplify_bool(first[1]) == ("iszero", "callvalue")
              and (first[3][0] == ("revert", 0)
                   or opcode(first[3][0]) == "invalid")):
            self.trace = self.trace[0][2]
            self.payable = False
        else:
            self.payable = True

        exp_text.append(("payable", self.payable))

        self.read_only = True
        for op in [
                "store",
                "selfdestruct",
                "call",
                "delegatecall",
                "codecall",
                "create",
        ]:
            if f"'{op}'" in str(self.trace):
                self.read_only = False

        exp_text.append(("read_only", self.read_only))
        """
            const func detection
        """

        self.const = self.read_only
        for exp in ["storage", "calldata", "calldataload", "store", "cd"]:
            if exp in str(self.trace) or len(self.returns) != 1:
                self.const = False

        if self.const:
            self.const = self.returns[0]
            if len(self.const) == 3 and opcode(self.const[2]) == "data":
                self.const = self.const[2]
            if len(self.const) == 3 and opcode(self.const[2]) == "mask_shl":
                self.const = self.const[2]
            if len(self.const) == 3 and type(self.const[2]) == int:
                self.const = self.const[2]
        else:
            self.const = None

        if self.const:
            exp_text.append(("const", self.const))
        """
            getter detection
        """

        self.getter = None
        self.simplify_string_getter_from_storage()
        if self.const is None and self.read_only and len(self.returns) == 1:
            ret = self.returns[0][1]
            if match(ret, ("bool", ("storage", Any, Any, ":loc"))):
                self.getter = (
                    ret  # we have to be careful when using this for naming purposes,
                )
                # because sometimes the storage can refer to array length

            elif opcode(ret) == "mask_shl" and opcode(ret[4]) == "storage":
                self.getter = ret[4]
            elif opcode(ret) == "storage":
                self.getter = ret
            elif opcode(ret) == "data":
                terms = ret[1:]
                # for structs, we check if all the parts of the struct are storage from the same
                # location. if so, we return the location number

                t0 = terms[
                    0]  # 0xFAFfea71A6da719D6CAfCF7F52eA04Eb643F6De2 - documents
                if m := match(t0, ("storage", 256, 0, ":loc")):
                    loc = m.loc
                    for e in terms[1:]:
                        if not match(e,
                                     ("storage", 256, 0, ("add", Any, loc))):
                            break
                    else:
                        self.getter = t0

                # kitties getKitten - with more cases this and the above could be uniformed
                if self.getter is None:
                    prev_loc = -1
                    for e in terms:

                        def l2(x):
                            if m := match(x, ("sha3", ("data", Any, ":l"))):
                                if type(m.l) == int and m.l < 1000:
                                    return m.l
                            if (opcode(x) == "sha3" and type(x[1]) == int
                                    and x[1] < 1000):
                                return x[1]
                            return None

                        loc = find_f(e, l2)
                        if not loc or (prev_loc != -1 and prev_loc != loc):
                            break
                        prev_loc = loc

                    else:
                        self.getter = ("struct", ("loc", loc))
Example #10
0
                if self.getter is None:
                    prev_loc = -1
                    for e in terms:

                        def l2(x):
                            if m := match(x, ("sha3", ("data", Any, ":l"))):
                                if type(m.l) == int and m.l < 1000:
                                    return m.l
                            if (opcode(x) == "sha3" and type(x[1]) == int
                                    and x[1] < 1000):
                                return x[1]
                            return None

                        loc = find_f(e, l2)
                        if not loc or (prev_loc != -1 and prev_loc != loc):
                            break
                        prev_loc = loc

                    else:
                        self.getter = ("struct", ("loc", loc))

            else:
                pass

        if self.getter:
            exp_text.append((f"getter for", prettify(self.getter)))

        explain_text("function traits", exp_text)

        return self