Example #1
0
                if (m := match(
                        exp,
                    ("if", ":cond", ":if_true", ":if_false"))) and str(
                        ("cd", 0)) in str(m.cond):
                    if find_f_list(m.if_false, func_calls) == []:
                        fi = m.if_false[0]
                        if m2 := match(fi, ("jd", ":jd")):
                            return int(m2.jd)

                    if find_f_list(m.if_true, func_calls) == []:
                        fi = m.if_true[0]
                        if m2 := match(fi, ("jd", ":jd")):
                            return int(m2.jd)

            default = find_f(trace, find_default) if func_list else None
            self.add_func(default or 0, name="_fallback()")

        except Exception:
            logger.exception("Loader issue.")
            self.add_func(0, name="_fallback()")

        abi = make_abi(self.hash_targets)
        for hash, (target, stack) in self.hash_targets.items():
            fname = get_func_name(hash)
            self.func_list.append((hash, fname, target, stack))

    def next_line(self, i):
        i += 1
        while i not in self.lines and self.last_line > i:
            i += 1
            def find_default(exp):
                if exp ~ ('if', :cond, :if_true, :if_false) and str(('cd', 0)) in str(cond):
                    if find_f_list(if_false, func_calls) == []:
                        fi = if_false[0]
                        if fi ~ ('jd', :jd):
                            return int(jd)
                            
                    if find_f_list(if_true, func_calls) == []:
                        fi = if_true[0]
                        if fi ~ ('jd', :jd):
                            return int(jd)

                else:
                    return None

            default = find_f(trace, find_default)
            self.add_func(default or 0, name='_fallback()')

        except:
            logger.error(f"Loader issues%s\n%s", ENDC, traceback.format_exc())
            self.add_func(0, name='_fallback()')

        abi = make_abi(self.hash_targets)
        for hash, target in self.hash_targets.items():
            fname = get_func_name(hash)
            self.func_list.append((hash, fname ,target, ))

    def next_line(self, i):
        i += 1
        while i not in self.lines and self.last_line > i:
            i += 1
Example #3
0
                        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 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)
Example #4
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))