Example #1
0
 def load_api(self, level):
     if level > 16:
         level = 16
     elif level < 3:
         level = 3
     self.android_api = AndroidAPI()
     data_path = os.path.join(self.db_dir, "android-%d.db" % level)
     while not os.path.exists(data_path):
         level += 1
         data_path = os.path.join(self.db_dir, "android-%d.db" % level)
     self.android_api.load(data_path)
     return level
Example #2
0
class APIMonitor(object):

    def __init__(self, db_dir, entries=[], config=""):
        self.db_dir = ""
        self.entries = []
        self.method_descs = [] 
        self.config = ""
        self.stub_classes = {}
        self.method_map = {}
        self.api_dict = {}
        self.api_name_dict = {}
        self.class_map = {}
        self.helper = ClassNode(buf=DEFAULT_HELPER)
        self.android_api = None 

        self.db_dir = db_dir
        self.entries = entries
        if (not entries) and config:
            if os.path.isfile(config):
                f = open(config, 'r')
                line = f.readline()
                while line:
                    if line.isspace():
                        line = f.readline()
                        continue
                    line = line.strip()
                    segs = line.split(None, 1)
                    if segs[0][0] == '#':
                        line = f.readline()
                        continue
                    if not line in self.entries:
                        self.entries.append(line)
                    line = f.readline()
                f.close()
            else:
                print "[error] Config file not found: %s" % config
                sys.exit(1)

    def __repr__(self):
        return '\n'.join(self.method_descs)
    
    def load_api(self, level):
        if level > 16:
            level = 16
        elif level < 3:
            level = 3
        self.android_api = AndroidAPI()
        data_path = os.path.join(self.db_dir, "android-%d.db" % level)
        while not os.path.exists(data_path):
            level += 1
            data_path = os.path.join(self.db_dir, "android-%d.db" % level)
        self.android_api.load(data_path)
        return level

    def inject(self, smali_tree, level):
        # get a copy of smali tree
        st = copy.deepcopy(smali_tree)

        # load api database
        print "Loading and processing API database..."
        level = self.load_api(level)
        print "Target API Level: %d" % level
        # check and fix apis in API_LIST
        method_descs = []
        for m in self.entries:
            c = ""
            api_name = ""
            method_name = ""

            ia = m.find("->")
            ilb = m.find('(')

            if ia >= 0:
                c = m[:ia]
                if ilb >= 0:
                    method_name = m[ia + 2:ilb]
                    api_name = m[ia + 2:]
                else:
                    method_name = m[ia + 2:]
            else:
                c = m

            if not self.android_api.classes.has_key(c):
                print "[Warn] Class not found in API-%d db: %s" % (level, m)
                continue
            # just class name
            if not method_name:
                ms = self.android_api.classes[c].methods.keys()
                method_descs.extend(ms)
            # full signature
            elif api_name:
                if not self.android_api.classes[c].methods.has_key(m):
                    if method_name == "<init>":
                        print "[Warn] Method not found in API-%d db: %s" % (level, m)
                        continue
                    c_obj = self.android_api.classes[c]
                    existed = False
                    q = c_obj.supers
                    while q:
                        cn = q.pop(0)
                        c_obj = self.android_api.classes[cn]
                        nm = c_obj.desc + "->" + api_name
                        if c_obj.methods.has_key(nm):
                            existed = True
                            if not nm in self.entries:
                                print "[Warn] Inferred API: %s" % (nm, )
                                method_descs.append(nm)
                        else:
                            q.extend(self.android_api.classes[cn].supers)

                    if not existed:
                        print "[Warn] Method not found in API-%d db: %s" % (level, m)
                else:
                    method_descs.append(m)
            # signature without parameters
            else:
                own = False
                if self.android_api.classes[c].methods_by_name.has_key(method_name):
                    ms = self.android_api.classes[c].methods_by_name[method_name]
                    method_descs.extend(ms)
                    own = True

                if method_name == "<init>":
                    continue
                c_obj = self.android_api.classes[c]
                existed = False
                q = c_obj.supers
                while q:
                    cn = q.pop(0)
                    c_obj = self.android_api.classes[cn]
                    if c_obj.methods_by_name.has_key(method_name):
                        existed = True
                        inferred = "%s->%s" % (c_obj.desc, method_name)
                        if not inferred in self.entries:
                            print "[Warn] Inferred API: %s" % inferred
                            method_descs.extend(c_obj.methods_by_name[method_name])
                    else:
                        q.extend(self.android_api.classes[cn].supers)

                if (not own) and (not existed):
                    print "[Warn] Method not found in API-%d db: %s" % (level, m)

        self.method_descs = list(set(method_descs))

        """ 
        print "**************************"
        self.method_descs.sort()
        print "\n".join(self.method_descs)
        print "**************************"
        """
        for m in self.method_descs:
            self.api_dict[m] = ""
            ia = m.find("->")
            ilb = m.find('(')
            if m[ia + 2:ilb] != "<init>":
                self.api_name_dict[m[ia + 2:]] = m[:ia]
        print "Done!"

        print "Injecting..."
        for c in st.classes:
            class_ = AndroidClass()
            class_.isAPI = False

            class_.desc = c.name
            class_.name= c.name[1:-1].replace('/', '.')
            class_.access = c.access
            if "interface" in c.access:
                class_.supers.extend(c.implements)
            else:
                class_.implements = c.implements
                class_.supers.append(c.super_name)

            for m in c.methods:
                method = AndroidMethod()
                method.isAPI = False
                method.desc = "%s->%s" % (c.name, m.descriptor)
                method.name = m.descriptor.split('(', 1)[0]
                #print method.desc
                method.sdesc = method.desc[:method.desc.rfind(')') + 1]
                method.access = m.access
                class_.methods[method.sdesc] = method 
            self.android_api.add_class(class_)
        self.android_api.build_connections(False)
        #self.android_api.show_not_API()

        for c in st.classes:
            for m in c.methods:
                i = 0
                while i < len(m.insns):
                    insn = m.insns[i]
                    if insn.fmt == "35c":
                        md = insn.obj.method_desc
                        on = insn.opcode_name
                        irb = md.find(')')
                        smd = md[:irb + 1]
                        if self.api_dict.has_key(smd):
                            method_type = METHOD_TYPE_BY_OPCODE[on]
                            new_on = OPCODE_MAP[on]
                            if not self.method_map.has_key(md):
                                self.add_stub_method(on, md)
                            if method_type == "constructor":
                                insn_m = copy.deepcopy(insn)
                                insn_m.obj.replace(new_on, \
                                        self.method_map[md])
                                r = insn_m.obj.registers.pop(0)
                                m.insert_insn(insn_m, i , 0)
                                i += 1
                                """
                                insn.obj.replace(new_on, \
                                        self.method_map[md])
                                r = insn.obj.registers.pop(0)
                                m.insert_insn(InsnNode(\
"move-result-object %s" % r), i + 1, 0)
                                i += 1
                                """
                            else:
                                insn.obj.replace(new_on, \
                                                 self.method_map[md])
                        else:
                            ia = md.find("->")
                            cn = md[:ia]
                            api_name = smd[ia + 2:]
                            if self.api_name_dict.has_key(api_name):
                                if self.android_api.classes.has_key(cn):
                                    if not self.android_api.classes[cn].methods.has_key(smd):
                                        api_cn = self.api_name_dict[api_name]
                                        if api_cn in self.android_api.classes[cn].ancestors:
                                            self.api_dict[smd] = ""
                                            i -= 1

                    elif insn.fmt == "3rc":
                        md = insn.obj.method_desc
                        on = insn.opcode_name
                        smd = md[:md.rfind(')') + 1]
                        if self.api_dict.has_key(smd):
                            method_type = METHOD_TYPE_BY_OPCODE[on]
                            new_on = OPCODE_MAP[on]
                            if not self.method_map.has_key(md):
                                self.add_stub_method(on, md)
                            if method_type == "constructor":
                                insn_m = copy.deepcopy(insn)
                                insn_m.obj.replace(new_on, \
                                        self.method_map[md])
                                r = insn_m.obj.reg_start
                                nr = r[0] + str(int(r[1:]) + 1)
                                insn_m.obj.set_reg_start(nr)
                                m.insert_insn(insn_m, i , 0)
                                i += 1
                                """
                                insn.obj.replace(new_on, \
                                        self.method_map[md])
                                r = insn.obj.reg_start
                                nr = r[0] + str(int(r[1:]) + 1)
                                insn.obj.set_reg_start(nr)
                                m.insert_insn(InsnNode(\
"move-result-object %s" % r), i + 1, 0)
                                i += 1
                                """
                            else:
                                insn.obj.replace(new_on, \
                                                 self.method_map[md])
                        else:
                            ia = md.find("->")
                            cn = md[:ia]
                            api_name = smd[ia + 2:]
                            if self.api_name_dict.has_key(api_name):
                                if self.android_api.classes.has_key(cn):
                                    if not self.android_api.classes[cn].methods.has_key(smd):
                                        api_cn = self.api_name_dict[api_name]
                                        if api_cn in self.android_api.classes[cn].ancestors:
                                            self.api_dict[smd] = ""
                                            i -= 1
                    i += 1

        for c in self.stub_classes.values():
            st.add_class(c)

        st.add_class(self.helper)
        print "Done!"

        return st

    def add_stub_method(self, on, m):
        #segs = m.split(':', 1)
        #method_type = segs[0]
        #m = segs[1]
        method_type = METHOD_TYPE_BY_OPCODE[on]
        segs = m.rsplit("->", 1)

        if self.stub_classes.has_key(segs[0]):
            stub_class = self.stub_classes[segs[0]]
        else:
            stub_class = ClassNode()
            stub_class.set_name("L" + PKG_PREFIX + "/" + segs[0][1:])
            stub_class.add_access("public")
            stub_class.set_super_name("Ljava/lang/Object;")

            self.stub_classes[segs[0]] = stub_class
            self.class_map[segs[0]] = "L" + PKG_PREFIX + "/" + segs[0][1:]

            #.method public constructor <init>()V
            #    .registers 1
            #    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
            #    return-void
            #.end method
            method = MethodNode()
            method.set_desc("<init>()V")
            method.add_access(["public", "constructor"])
            method.set_registers(1)
            i1 = InsnNode("invoke-direct {p0}, Ljava/lang/Object;-><init>()V")
            i2 = InsnNode("return-void")
            method.add_insn([i1, i2])
            stub_class.add_method(method)

        method_name = segs[1][:segs[1].find("(")]
        if method_type == "constructor":
            self.__add_stub_cons2(stub_class, m)
        elif method_type == "instance":
            self.__add_stub_inst(stub_class, on, m)
        elif method_type == "static":
            self.__add_stub_static(stub_class, m)


    def __add_stub_inst(self, stub_class, on, m):
        segs = m.rsplit("->", 1)

        method = MethodNode()
        method.set_desc(segs[1])
        method.add_para(TypeNode(segs[0]))
        method.add_access(["public", "static"])

        para_num = len(method.paras)
        reg_num = method.get_paras_reg_num()
        ri = 1

        if reg_num <= 5:
            if on.find('/') >= 0:
                on = on[:on.find('/')]
            i = "%s {%s}, %s" % \
                    (on, \
                     ", ".join(["p%d" % k for k in range(reg_num)]), m)
        else:
            i = "%s {p0 .. p%d}, %s" % (on, reg_num - 1, m) 

        method.add_insn(InsnNode(i)) 

        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("move-result v1"))
                    ri += 1
                else:
                    method.add_insn(InsnNode("move-result-wide v1"))
                    ri += 2
            else:
                method.add_insn(InsnNode("move-result-object v1"))
                ri += 1

        method.add_insn(InsnNode("new-instance \
v%d, Ljava/lang/StringBuilder;" % ri))
        method.add_insn(InsnNode("invoke-direct \
{v%d}, Ljava/lang/StringBuilder;-><init>()V" % ri))

        method.add_insn(InsnNode("const-string v%d,\"%s(\"" % \
                                 (ri + 1, m.split('(', 1)[0])))
        append_i = InsnNode("invoke-virtual \
{v%d, v%d}, Ljava/lang/StringBuilder;->\
append(Ljava/lang/String;)Ljava/lang/StringBuilder;" % \
                            (ri, ri + 1))
        method.add_insn(append_i)
        
        # print parameters
        pi = 1
        for k in range(1, para_num):
            p = method.paras[k]
            method.add_insn(InsnNode("const-string v%d, \"%s=\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)

            if p.basic and p.dim == 0:
                if p.words == 1:
                    method.add_insn(InsnNode("invoke-static {p%d}, \
Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             (pi, p.get_desc())))
                    pi += 1
                else:
                    method.add_insn(InsnNode("invoke-static \
{p%d, p%d}, Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                        (pi, pi + 1, p.get_desc())))
                    pi += 2
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)
            else:
                method.add_insn(InsnNode("invoke-static {p%d}, \
Ldroidbox/apimonitor/Helper;->toString(Ljava/lang/Object;)Ljava/lang/String;" % (pi, )))
                pi += 1
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)

            if k < para_num - 1:
                method.add_insn(InsnNode("const-string v%d, \" | \"" % \
                                         (ri + 1)))
                method.add_insn(append_i)

        method.add_insn(InsnNode("const-string v%d, \")\"" % (ri + 1)))
        method.add_insn(append_i)

        # print return value
        p = method.ret
        if p.void:
            method.add_insn(InsnNode("const-string v%d, \"%s\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)
        else:
            method.add_insn(InsnNode("const-string v%d, \"%s=\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)
            if p.basic and p.dim == 0:
                if p.words == 1:
                    method.add_insn(InsnNode("invoke-static {v1}, \
Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             p.get_desc()))
                else:
                    method.add_insn(InsnNode("invoke-static \
{v1, v2}, Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             p.get_desc()))
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)
            else:
                method.add_insn(InsnNode("invoke-static {v1}, \
Ldroidbox/apimonitor/Helper;->toString(Ljava/lang/Object;)Ljava/lang/String;"))
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)

        method.add_insn(InsnNode("invoke-virtual {v%d}, \
Ljava/lang/StringBuilder;->toString()Ljava/lang/String;" % ri))
        method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
        method.add_insn(InsnNode("invoke-static {v%d}, \
Ldroidbox/apimonitor/Helper;->log(Ljava/lang/String;)V" % \
                                 (ri + 1, )))
        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("return v1"))
                else:
                    method.add_insn(InsnNode("return-wide v1"))
            else:
                method.add_insn(InsnNode("return-object v1"))
        else:
            method.add_insn(InsnNode("return-void"))

        start = LabelNode(":droidbox_try_start", 0)
        end = LabelNode(":droidbox_try_end", 1)
        index = len(method.insns)
        ret = LabelNode(":droidbox_return", index - 1)
        handler = LabelNode(":droidbox_handler", index)
        line = ".catch Ljava/lang/Exception; {:droidbox_try_start .. \
:droidbox_try_end} :droidbox_handler"
        TryNode(line, start, end, handler)
        method.add_label([start, end, ret, handler])

        method.add_insn(InsnNode("move-exception v0"))
        method.add_insn(InsnNode("invoke-virtual {v0}, \
Ljava/lang/Exception;->printStackTrace()V"))
        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("const/4 v1, 0x0"))
                else:
                    method.add_insn(InsnNode("const-wide/16 v1, 0x0"))
            else:
                method.add_insn(InsnNode("const/4 v1, 0x0"))
        method.add_insn(InsnNode("goto :droidbox_return"))

        method.set_registers(reg_num + ri + 2)
        stub_class.add_method(method)

        i = m.find('(')
        self.method_map[m] = "L" + PKG_PREFIX + "/" + segs[0][1:] + "->" + \
                method.get_desc()


    def __add_stub_cons2(self, stub_class, m):
        segs = m.rsplit("->", 1)
        desc = segs[1].replace("<init>", "droidbox_cons")
        i = desc.find(')')
        desc = desc[:i + 1] + 'V'
        method = MethodNode()
        method.set_desc(desc)
        method.add_access(["public", "static"])

        para_num = len(method.paras)
        reg_num = method.get_paras_reg_num()
        ri = 0

        method.add_insn(InsnNode("new-instance \
v%d, Ljava/lang/StringBuilder;" % ri))
        method.add_insn(InsnNode("invoke-direct \
{v%d}, Ljava/lang/StringBuilder;-><init>()V" % ri))

        method.add_insn(InsnNode("const-string v%d,\"%s(\"" % \
                                 (ri + 1, m.split('(', 1)[0])))
        append_i = InsnNode("invoke-virtual \
{v%d, v%d}, Ljava/lang/StringBuilder;->\
append(Ljava/lang/String;)Ljava/lang/StringBuilder;" % \
                            (ri, ri + 1))
        method.add_insn(append_i)
        
        # print parameters
        pi = 0
        for k in range(0, para_num):
            p = method.paras[k]
            method.add_insn(InsnNode("const-string v%d, \"%s=\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)

            if p.basic and p.dim == 0:
                if p.words == 1:
                    method.add_insn(InsnNode("invoke-static {p%d}, \
Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             (pi, p.get_desc())))
                    pi += 1
                else:
                    method.add_insn(InsnNode("invoke-static \
{p%d, p%d}, Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                        (pi, pi + 1, p.get_desc())))
                    pi += 2
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)
            else:
                method.add_insn(InsnNode("invoke-static {p%d}, \
Ldroidbox/apimonitor/Helper;->toString(Ljava/lang/Object;)Ljava/lang/String;" % (pi, )))
                pi += 1
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)

            if k < para_num - 1:
                method.add_insn(InsnNode("const-string v%d, \" | \"" % \
                                         (ri + 1)))
                method.add_insn(append_i)

        method.add_insn(InsnNode("const-string v%d, \")\"" % (ri + 1)))
        method.add_insn(append_i)

        # print return value
        p = method.ret
        if p.void:
            method.add_insn(InsnNode("const-string v%d, \"%s\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)
        else:
            method.add_insn(InsnNode("const-string v%d, \"%s=\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)
            if p.basic and p.dim == 0:
                if p.words == 1:
                    method.add_insn(InsnNode("invoke-static {v1}, \
Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             p.get_desc()))
                else:
                    method.add_insn(InsnNode("invoke-static \
{v1, v2}, Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             p.get_desc()))
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)
            else:
                method.add_insn(InsnNode("invoke-static {v1}, \
Ldroidbox/apimonitor/Helper;->toString(Ljava/lang/Object;)Ljava/lang/String;"))
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)

        method.add_insn(InsnNode("invoke-virtual {v%d}, \
Ljava/lang/StringBuilder;->toString()Ljava/lang/String;" % ri))
        method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
        method.add_insn(InsnNode("invoke-static {v%d}, \
Ldroidbox/apimonitor/Helper;->log(Ljava/lang/String;)V" % \
                                 (ri + 1, )))
        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("return v1"))
                else:
                    method.add_insn(InsnNode("return-wide v1"))
            else:
                method.add_insn(InsnNode("return-object v1"))
        else:
            method.add_insn(InsnNode("return-void"))

        method.set_registers(reg_num + ri + 2)
        stub_class.add_method(method)

        i = m.find('(')
        self.method_map[m] = "L" + PKG_PREFIX + "/" + segs[0][1:] + "->" + \
                method.get_desc()

    def __add_stub_cons(self, stub_class, m):
        segs = m.rsplit("->", 1)
        desc = segs[1].replace("<init>", "droidbox_cons")
        i = desc.find(')')
        desc = desc[:i + 1] + segs[0]
        method = MethodNode()
        method.set_desc(desc)
        method.add_access(["public", "static"])

        para_num = len(method.paras)
        reg_num = method.get_paras_reg_num()
        ri = 1

        method.add_insn(InsnNode("new-instance v1, %s" % segs[0]))

        reg_v = 1
        if reg_num <= 4:
            i = "invoke-direct {v1, %s}, %s" % \
                    (", ".join(["p%d" % k for k in range(reg_num)]), \
                     m)
            method.add_insn(InsnNode(i)) 
        else:
            for k in range(reg_num):
                method.add_insn(InsnNode("move-object v%d, p%d" % (k + 2, k)))
            i = "invoke-direct/range {v1 .. v%d}, %s" % \
                    (reg_num + 1, m)
            method.add_insn(InsnNode(i)) 
            reg_v = reg_num + 1

        ri += 1

        method.add_insn(InsnNode("new-instance \
v%d, Ljava/lang/StringBuilder;" % ri))
        method.add_insn(InsnNode("invoke-direct \
{v%d}, Ljava/lang/StringBuilder;-><init>()V" % ri))

        method.add_insn(InsnNode("const-string v%d,\"%s(\"" % \
                                 (ri + 1, m.split('(', 1)[0])))
        append_i = InsnNode("invoke-virtual \
{v%d, v%d}, Ljava/lang/StringBuilder;->\
append(Ljava/lang/String;)Ljava/lang/StringBuilder;" % \
                            (ri, ri + 1))
        method.add_insn(append_i)
        
        # print parameters
        pi = 0
        for k in range(0, para_num):
            p = method.paras[k]
            method.add_insn(InsnNode("const-string v%d, \"%s=\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)

            if p.basic and p.dim == 0:
                if p.words == 1:
                    method.add_insn(InsnNode("invoke-static {p%d}, \
Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             (pi, p.get_desc())))
                    pi += 1
                else:
                    method.add_insn(InsnNode("invoke-static \
{p%d, p%d}, Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                        (pi, pi + 1, p.get_desc())))
                    pi += 2
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)
            else:
                method.add_insn(InsnNode("invoke-static {p%d}, \
Ldroidbox/apimonitor/Helper;->toString(Ljava/lang/Object;)Ljava/lang/String;" % (pi, )))
                pi += 1
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)

            if k < para_num - 1:
                method.add_insn(InsnNode("const-string v%d, \" | \"" % \
                                         (ri + 1)))
                method.add_insn(append_i)

        method.add_insn(InsnNode("const-string v%d, \")\"" % (ri + 1)))
        method.add_insn(append_i)

        # print return value
        p = method.ret
        if p.void:
            method.add_insn(InsnNode("const-string v%d, \"%s\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)
        else:
            method.add_insn(InsnNode("const-string v%d, \"%s=\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)
            if p.basic and p.dim == 0:
                if p.words == 1:
                    method.add_insn(InsnNode("invoke-static {v1}, \
Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             p.get_desc()))
                else:
                    method.add_insn(InsnNode("invoke-static \
{v1, v2}, Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             p.get_desc()))
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)
            else:
                method.add_insn(InsnNode("invoke-static {v1}, \
Ldroidbox/apimonitor/Helper;->toString(Ljava/lang/Object;)Ljava/lang/String;"))
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)

        method.add_insn(InsnNode("invoke-virtual {v%d}, \
Ljava/lang/StringBuilder;->toString()Ljava/lang/String;" % ri))
        method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
        method.add_insn(InsnNode("invoke-static {v%d}, \
Ldroidbox/apimonitor/Helper;->log(Ljava/lang/String;)V" % \
                                 (ri + 1, )))
        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("return v1"))
                else:
                    method.add_insn(InsnNode("return-wide v1"))
            else:
                method.add_insn(InsnNode("return-object v1"))
        else:
            method.add_insn(InsnNode("return-void"))

        start = LabelNode(":droidbox_try_start", 0)
        end = LabelNode(":droidbox_try_end", 2)
        index = len(method.insns)
        ret = LabelNode(":droidbox_return", index - 1)
        handler = LabelNode(":droidbox_handler", index)
        line = ".catch Ljava/lang/Exception; {:droidbox_try_start .. \
:droidbox_try_end} :droidbox_handler"
        TryNode(line, start, end, handler)
        method.add_label([start, end, ret, handler])

        method.add_insn(InsnNode("move-exception v0"))
        method.add_insn(InsnNode("invoke-virtual {v0}, \
Ljava/lang/Exception;->printStackTrace()V"))
        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("const/4 v1, 0x0"))
                else:
                    method.add_insn(InsnNode("const-wide/16 v1, 0x0"))
            else:
                method.add_insn(InsnNode("const/4 v1, 0x0"))
        method.add_insn(InsnNode("goto :droidbox_return"))

        method.set_registers(reg_num + max(ri + 1, reg_v) + 1)
        stub_class.add_method(method)

        i = m.find('(')
        self.method_map[m] = "L" + PKG_PREFIX + "/" + segs[0][1:] + "->" + \
                method.get_desc()

#invoke-static {v1}, Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;
    def __add_stub_static(self, stub_class, m):
        segs = m.rsplit("->", 1)

        method = MethodNode()
        method.set_desc(segs[1])
        method.add_access(["public", "static"])

        para_num = len(method.paras)
        reg_num = method.get_paras_reg_num()
        ri = 1

        if reg_num <= 5:
            i = "invoke-static {%s}, %s" % \
                    (", ".join(["p%d" % k for k in range(reg_num)]), m)
        else:
            i = "invoke-static/range {p0 .. p%d}, %s" % (reg_num - 1, m) 

        method.add_insn(InsnNode(i)) 

        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("move-result v1"))
                    ri += 1
                else:
                    method.add_insn(InsnNode("move-result-wide v1"))
                    ri += 2
            else:
                method.add_insn(InsnNode("move-result-object v1"))
                ri += 1

        method.add_insn(InsnNode("new-instance \
v%d, Ljava/lang/StringBuilder;" % ri))
        method.add_insn(InsnNode("invoke-direct \
{v%d}, Ljava/lang/StringBuilder;-><init>()V" % ri))

        method.add_insn(InsnNode("const-string v%d,\"%s(\"" % \
                                 (ri + 1, m.split('(', 1)[0])))
        append_i = InsnNode("invoke-virtual \
{v%d, v%d}, Ljava/lang/StringBuilder;->\
append(Ljava/lang/String;)Ljava/lang/StringBuilder;" % \
                            (ri, ri + 1))
        method.add_insn(append_i)
        
        # print parameters
        pi = 0
        for k in range(0, para_num):
            p = method.paras[k]
            method.add_insn(InsnNode("const-string v%d, \"%s=\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)

            if p.basic and p.dim == 0:
                if p.words == 1:
                    method.add_insn(InsnNode("invoke-static {p%d}, \
Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             (pi, p.get_desc())))
                    pi += 1
                else:
                    method.add_insn(InsnNode("invoke-static \
{p%d, p%d}, Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                        (pi, pi + 1, p.get_desc())))
                    pi += 2
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)
            else:
                method.add_insn(InsnNode("invoke-static {p%d}, \
Ldroidbox/apimonitor/Helper;->toString(Ljava/lang/Object;)Ljava/lang/String;" % (pi, )))
                pi += 1
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)

            if k < para_num - 1:
                method.add_insn(InsnNode("const-string v%d, \" | \"" % \
                                         (ri + 1)))
                method.add_insn(append_i)

        method.add_insn(InsnNode("const-string v%d, \")\"" % (ri + 1)))
        method.add_insn(append_i)

        # print return value
        p = method.ret
        if p.void:
            method.add_insn(InsnNode("const-string v%d, \"%s\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)
        else:
            method.add_insn(InsnNode("const-string v%d, \"%s=\"" % (ri + 1,
                                     p.get_desc())))
            method.add_insn(append_i)
            if p.basic and p.dim == 0:
                if p.words == 1:
                    method.add_insn(InsnNode("invoke-static {v1}, \
Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             p.get_desc()))
                else:
                    method.add_insn(InsnNode("invoke-static \
{v1, v2}, Ljava/lang/String;->valueOf(%s)Ljava/lang/String;" % \
                                             p.get_desc()))
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)
            else:
                method.add_insn(InsnNode("invoke-static {v1}, \
Ldroidbox/apimonitor/Helper;->toString(Ljava/lang/Object;)Ljava/lang/String;"))
                method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
                method.add_insn(append_i)

        method.add_insn(InsnNode("invoke-virtual {v%d}, \
Ljava/lang/StringBuilder;->toString()Ljava/lang/String;" % ri))
        method.add_insn(InsnNode("move-result-object v%d" % (ri + 1)))
        method.add_insn(InsnNode("invoke-static {v%d}, \
Ldroidbox/apimonitor/Helper;->log(Ljava/lang/String;)V" % \
                                 (ri + 1, )))
        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("return v1"))
                else:
                    method.add_insn(InsnNode("return-wide v1"))
            else:
                method.add_insn(InsnNode("return-object v1"))
        else:
            method.add_insn(InsnNode("return-void"))

        start = LabelNode(":droidbox_try_start", 0)
        end = LabelNode(":droidbox_try_end", 1)
        index = len(method.insns)
        ret = LabelNode(":droidbox_return", index - 1)
        handler = LabelNode(":droidbox_handler", index)
        line = ".catch Ljava/lang/Exception; {:droidbox_try_start .. \
:droidbox_try_end} :droidbox_handler"
        TryNode(line, start, end, handler)
        method.add_label([start, end, ret, handler])

        method.add_insn(InsnNode("move-exception v0"))
        method.add_insn(InsnNode("invoke-virtual {v0}, \
Ljava/lang/Exception;->printStackTrace()V"))
        if not method.ret.void:
            if method.ret.basic and method.ret.dim == 0:
                if method.ret.words == 1:
                    method.add_insn(InsnNode("const/4 v1, 0x0"))
                else:
                    method.add_insn(InsnNode("const-wide/16 v1, 0x0"))
            else:
                method.add_insn(InsnNode("const/4 v1, 0x0"))
        method.add_insn(InsnNode("goto :droidbox_return"))

        method.set_registers(reg_num + ri + 2)
        stub_class.add_method(method)

        i = m.find('(')
        self.method_map[m] = "L" + PKG_PREFIX + "/" + segs[0][1:] + "->" + \
                method.get_desc()
class Injector(object):

    def to_java_notation(self, clazz, meth):
        qualified_name = clazz.name.replace('/','.').replace(';','.') + meth.descriptor.replace(' (', '(')
        if qualified_name[0]=='L':
            qualified_name = qualified_name[1:]
        return qualified_name

    def __init__(self, db_dir, entries=[], config=""):
        self.db_dir = ""
        self.entries = []
        self.method_descs = [] 
        self.android_api = None 

        self.db_dir = db_dir
        self.entries = entries
        if (not entries) and config:
            if os.path.isfile(config):
                f = open(config, 'r')
                line = f.readline()
                while line:
                    if line.isspace():
                        line = f.readline()
                        continue
                    line = line.strip()
                    segs = line.split(None, 1)
                    if segs[0][0] == '#':
                        line = f.readline()
                        continue
                    if not line in self.entries:
                        self.entries.append(line)
                    line = f.readline()
                f.close()
            else:
                print "[error] Config file not found: %s" % config
                sys.exit(1)

    def __repr__(self):
        return '\n'.join(self.method_descs)
    
    def load_api(self, level):
        if level > 16:
            level = 16
        elif level < 3:
            level = 3
        self.android_api = AndroidAPI()
        data_path = os.path.join(self.db_dir, "android-%d.db" % level)
        while not os.path.exists(data_path):
            level += 1
            data_path = os.path.join(self.db_dir, "android-%d.db" % level)
        self.android_api.load(data_path)
        return level
    
    def count_params(self,m):
        """
        Count the parameters of a method, in terms of "p" registers. This can differ from len(m.paras), if wide registers (64b) are used as parameters
        """
        ps = []
        i=0
        if 'static' not in m.access:
            ps.append('p0')
            i = 1
        for param in m.paras:
            if param.words==2:
                ps.append('p%d'%i)
                i += 1
            ps.append('p%d'%i)
            i += 1
        return len(ps)
                
        
    
    def inject(self, smali_tree, level, hooks):
        # get a copy of smali tree
        assert isinstance(hooks,dict)
        st = copy.deepcopy(smali_tree)
        print "Injecting hooks at %s"%hooks
        
        # load api database
        print "Loading and processing API database..."
        level = self.load_api(level)
        print "Target API Level: %d" % level
        # check and fix apis in API_LIST
        method_descs = []
        for m in self.entries: #self.entries = list of APIs to instrument
            c = ""
            api_name = ""
            method_name = ""

            ia = m.find("->")
            ilb = m.find('(')

            if ia >= 0:
                c = m[:ia] # c is the class name (e.g. Landroid/content/Intent;)
                if ilb >= 0:
                    method_name = m[ia + 2:ilb]
                    api_name = m[ia + 2:]
                else:
                    method_name = m[ia + 2:] # name of method (e.g., <init>)
            else:
                c = m

            if not self.android_api.classes.has_key(c):
                print "[Warn] Class not found in API-%d db: %s" % (level, m)
                continue
            # just class name
            if not method_name:
                ms = self.android_api.classes[c].methods.keys()
                method_descs.extend(ms)
            # full signature
            elif api_name:
                if not self.android_api.classes[c].methods.has_key(m):
                    if method_name == "<init>":
                        print "[Warn] Method not found in API-%d db: %s" % (level, m)
                        continue
                    c_obj = self.android_api.classes[c]
                    existed = False
                    q = c_obj.supers
                    while q:
                        cn = q.pop(0)
                        c_obj = self.android_api.classes[cn]
                        nm = c_obj.desc + "->" + api_name
                        if c_obj.methods.has_key(nm):
                            existed = True
                            if not nm in self.entries:
                                print "[Warn] Inferred API: %s" % (nm, )
                                method_descs.append(nm)
                        else:
                            q.extend(self.android_api.classes[cn].supers)

                    if not existed:
                        print "[Warn] Method not found in API-%d db: %s" % (level, m)
                else:
                    method_descs.append(m)
            # signature without parameters
            else:
                own = False
                if self.android_api.classes[c].methods_by_name.has_key(method_name):
                    ms = self.android_api.classes[c].methods_by_name[method_name]
                    method_descs.extend(ms)
                    own = True

                if method_name == "<init>":
                    continue
                c_obj = self.android_api.classes[c] # an instance of AndroidClass
                existed = False
                q = c_obj.supers    # super classes of the current class represented by c_obj
                while q:
                    cn = q.pop(0)
                    c_obj = self.android_api.classes[cn]
                    if c_obj.methods_by_name.has_key(method_name):
                        existed = True
                        inferred = "%s->%s" % (c_obj.desc, method_name)
                        if not inferred in self.entries:
                            print "[Warn] Inferred API: %s" % inferred
                            method_descs.extend(c_obj.methods_by_name[method_name])
                    else:
                        q.extend(self.android_api.classes[cn].supers)

                if (not own) and (not existed):
                    print "[Warn] Method not found in API-%d db: %s" % (level, m)

        self.method_descs = list(set(method_descs))


        print "Injecting..."
        for c in st.classes:    # we now transform a SmaliTree-Class to and AndroidClass
            class_ = AndroidClass()
            class_.isAPI = False

            class_.desc = c.name
            class_.name= c.name[1:-1].replace('/', '.') # the "real" class name in canonical form
            class_.access = c.access
            if "interface" in c.access:                 # if this is an Interface ...
                class_.supers.extend(c.implements)
            else:
                class_.implements = c.implements
                class_.supers.append(c.super_name)

            for m in c.methods:
                method = AndroidMethod()
                method.isAPI = False
                method.desc = "%s->%s" % (c.name, m.descriptor)
                method.name = m.descriptor.split('(', 1)[0]
                #print method.desc
                method.sdesc = method.desc[:method.desc.rfind(')') + 1]
                method.access = m.access
                class_.methods[method.sdesc] = method 
            self.android_api.add_class(class_)
        self.android_api.build_connections(False)
        
        #add TrackerClass
        st.classes.append(ClassNode(os.path.join(os.path.dirname(__file__), 'TrackerClass.smali')))
        
        # Number of registers added to a method
        MORE_REGS = 6
        
        methods_to_fix = {} #key: classname.methodname, value: method object

        ######## CHECK FOR NEED OF FIXING PARAMS SHIFTED ABOVE v15 AND REPLACE pX by vY ################
        for c in st.classes:                # iterate over all classes ...
            for m in c.methods:             # ... and all methods.
                qualified_name = self.to_java_notation(c,m)
#                 if qualified_name in hooks:
                    #Test if we need to rewrite registers (has pX been moved to X>15?)
                if m.registers-len(m.paras)+MORE_REGS>15 and m.registers-len(m.paras)-1 <= 15:
                    if ''.join([c.name,m.name]) not in methods_to_fix:
                        for i, insn in enumerate(m.insns):
                            if insn.opcode_name in OPCODE_MAP:
                                if len([value for key, value in hooks.items() if key in insn.buf])>0:
                                    print insn
                                    print m.descriptor
                                    methods_to_fix[''.join([c.name,m.name,m.descriptor])] = m
                                    all_ps = re.findall(r'[\s|\{]p(\d)', str(insn))
                                    ins_buf = insn.buf
                                    for p in all_ps:
                                        pos = m.registers-self.count_params(m) + int(p[0])                                  
                                        print '        this statement uses parameter %s which should be rewritten to v%s'%(p,pos)
                                        ins_buf = ins_buf.replace('p%s'%p,'v%d'%pos)
                                        new_ins = InsnNode(ins_buf)
                                        print '          the new ins is now %s '%new_ins
                                        m.insns[i] = new_ins
                                    
        ################ MOVE PARAMETERS TO THEIR ORIGINAL POSITIONS ###############################
        for methodname in methods_to_fix:
            m = methods_to_fix[methodname]
            #p0 is not contained in m.paras
            pos = m.registers-self.count_params(m)
            i=0
            if 'static' not in m.access:    #non-static methods have this->p0
                instr = InsnNode("move-object/16 v%d, p0"%(pos))
                m.insert_insn(instr, 0, 0)
                i += 1
            for p in m.paras:
                print 'Fixing %s in %s'%(i,methodname)
                # Rename pX to v(old_reg_count + MORE_REGS)
                pos = m.registers-self.count_params(m) + i
                if p.basic and not p.dim > 0:
                    if p.words <= 1:
                        instr = InsnNode("move/16 v%d, p%s"%(pos,i))
                        i += 1
                    elif p.words >= 2:
                        instr = InsnNode("move-wide/16 v%d, p%s"%(pos,i)) #@todo have wide regs been considered when adding registers to the method?
                        i += 2
                else:
                    instr = InsnNode("move-object/16 v%d, p%s"%(pos,i))
                    i+=1
                m.insert_insn(instr, 0, 0)          
        #########################################################################
        
        for c in st.classes:                # iterate over all classes ...
            assert isinstance(c, ClassNode)
            print "CLASS %s"%c.name
            for f in c.fields:
                print "  %s"%f
            for intf in c.implements:
                print "  %s"%intf
            for m in c.methods:             # ... and all methods.
                assert isinstance(m, MethodNode)
                print "   METHOD %s %s"%(m.name,m.descriptor)
                i = 0 
                ADDED_LINES = 0
                if ''.join([c.name,m.name,m.descriptor]) in methods_to_fix:
                    ADDED_LINES = len(m.paras)+1
                    i += ADDED_LINES
                    if 'static' in m.access:
                        i -= 1
                        ADDED_LINES -= 1
                new_i = i #new_i: saves position of new code and jumps over newly added code
                #print "new_i starts at %d (%d parameters and %d added hooks. This is 0:%s)"%(new_i,len(m.paras),ADDED_LINES, (i-ADDED_LINES))
                
                # Check if method contains call to instrument
                skip_method = True
                for ix in m.insns:
                    print "      INS %s"%ix.buf
                    if ix.opcode_name.startswith('invoke'):
                        if any(func in ix.buf for func,_ in hooks.items()):
                            skip_method = False
                            print "YEAH: %s"%ix.buf
                            break
                if skip_method:
                    continue
                            
                print "relevant: %s"%ix.opcode_name                      

                
                #Add MORE_REGS more registers for additional info
                m.set_registers(m.registers+MORE_REGS)
                new_regs = []
                for i in range(0,MORE_REGS-1):
                    print "j: %s , %s"%(i,m.registers-MORE_REGS+i)
                    new_regs.append("v%d"%(m.registers-MORE_REGS+i)) 
                       
                while new_i < len(m.insns):     # m.insns = list of bytecode instructions as strings (e.g. "invoke-direct {p0}, ..")                    
                    insn = m.insns[new_i]       # iterate over all statements of the current method 
                    if str(insn).startswith('#'):   #      getter and setter comments in smali which would otherwise be considered a statement 
                        new_i += 1
                        insn = m.insns[new_i]
                    if insn.buf.startswith('invoke-'):
                        applicable_hooks = [value for key, value in hooks.items() if key in insn.buf]
                        if len(applicable_hooks)>0:
                            print "YES: %s"%insn.buf
                        else:                                                        
                            print "NO: %s"%insn.buf

                        if len(applicable_hooks)>0:                            
                            # Get parameters
                            assert isinstance(insn, InsnNode)
                            
                            print "Searching for parameters in %s"%(insn.buf)
                            params = self._parse_paras(insn.buf)
                            regs = insn.obj.registers

                            for j,p in enumerate(params):
                                if p.basic:
                                    if str(p) == 'I': # cast int to Integer
                                        instr = InsnNode("new-instance %s, Ljava/lang/Integer;"%(new_regs[1]))
                                        m.insert_insn(instr, new_i, 0)
                                        new_i += 1
                                        instr = InsnNode("invoke-direct {%s, %s}, Ljava/lang/Integer;-><init>(I)V"%(new_regs[1], insn.obj.registers[j]))
                                        m.insert_insn(instr, new_i, 0)
                                        new_i += 1
                                        instr = InsnNode("return-object %s"%(new_regs[1]))
                                        m.insert_insn(instr, new_i, 0)
                                        new_i += 1                                    
                            
                            if 'range' in insn.opcode_name:
                                print "Invoke-Range not yet implemented!: %s, %s"%regs,insn.buf
                            #create an array
                            instr = InsnNode("const/4 %s, %d"%(new_regs[1], len(regs)))
                            m.insert_insn(instr, new_i, 0)
                            new_i += 1
                            instr = InsnNode("new-array %s, %s, [Ljava/lang/Object;"%(new_regs[0],new_regs[1])) #TODO type inference required
                            m.insert_insn(instr, new_i, 0)
                            new_i += 1
                            for j,r in enumerate(regs):
                                instr = InsnNode("aput-object %s, %s, %s "%(new_regs[j+2],new_regs[0],r))
                                m.insert_insn(instr, new_i, 0)
                                new_i += 1
                            # Call hook
                            instr = InsnNode("invoke-static %s %s"%(new_regs[0],applicable_hooks[0])) #TODO call more than one hook, if any
                            m.insert_insn(instr, new_i, 0)
                            new_i += 1
                            # Copy back results
                            instr = InsnNode("move-result %s"%(new_regs[0]))
                            m.insert_insn(instr, new_i, 0)
                            new_i += 1
                            for j,r in enumerate(regs):
                                instr = InsnNode("aget-object %s, %s, %s "%(new_regs[j+2],new_regs[0],r))
                                m.insert_insn(instr, new_i, 0)
                                new_i += 1
                                
                            for p in params:
                                if p.basic:
                                    instr = InsnNode("CASTMEBACK %s, %d"%(new_regs[1], len(regs))) #TODO Cast from Box typ
                                    m.insert_insn(instr, new_i, 0)
                                    new_i += 1

                                                                                  
                    i += 1  
                    new_i += 1  
  
        print "Instrumentation done!"

        return st

    
    def _parse_paras(self, insn):
        p1 = insn.find('(')
        p2 = insn.find(')')
        result = []
        paras = insn[p1 + 1:p2]
        index = 0
        dim = 0
        while index < len(paras):
            c = paras[index]
            if c == '[':
                dim += 1
                index += 1
            elif BASIC_TYPES.has_key(c):
                result.append(TypeNode(paras[index - dim:index + 1]))
                index += 1
                dim = 0
            else:
                tmp = paras.find(';', index)
                result.append(TypeNode(paras[index - dim:tmp + 1]))
                index = tmp + 1
                dim = 0
        return result
    
    def _parse_regs(self, insn):
        p1 = insn.find('(')
        p2 = insn.find(')')
        result = []
        paras = insn[p1 + 1:p2]
        index = 0
        dim = 0
        while index < len(paras):
            c = paras[index]
            if c == '[':
                dim += 1
                index += 1
            elif BASIC_TYPES.has_key(c):
                result.append(TypeNode(paras[index - dim:index + 1]))
                index += 1
                dim = 0
            else:
                tmp = paras.find(';', index)
                result.append(TypeNode(paras[index - dim:tmp + 1]))
                index = tmp + 1
                dim = 0
        return result
    
    def _add_stub_class(self, className, superClassName, st ):
        """
        @param className: Name of new class; example: Lcom/test;
        @param superClassName: Name of super class; example: Ljava/lang/Object;
        @param st: Smali tree to save class
        """
        
        stub_class = ClassNode()
        stub_class.set_name("L" + PKG_PREFIX + "/" + className[1:])
        stub_class.add_access("public")
        stub_class.set_super_name(superClassName)

        self.stub_classes[className] = stub_class
        self.class_map[className] = "L" + PKG_PREFIX + "/" + className[1:]

        method = MethodNode()
        method.set_desc("<init>()V")
        method.add_access(["public", "constructor"])
        method.set_registers(1)
#           i1 = InsnNode("invoke-direct {p0}, Ljava/lang/Object;-><init>()V")    #Error! Must not create instance of Object, but of superclass
        i1 = InsnNode("invoke-direct {p0}, "+superClassName+"-><init>()V")
        i2 = InsnNode("return-void")
        method.add_insn([i1, i2])
        stub_class.add_method(method)
        st.add_class(stub_class)

    def _add_stub_method(self, on, m, class_, registers=1):
        """
        @param on: opcode name
        @param m: method descriptor (string); example: Lcom/test;->test()V
        @param class_: class of the corresponding method
        @param registers: number of registers for that method
        """
        #segs = m.split(':', 1)
        #method_type = segs[0]
        #m = segs[1]
        method_type = METHOD_TYPE_BY_OPCODE[on]
        segs = m.rsplit("->", 1)
        
        #method_name = segs[1][:segs[1].find("(")]
        if method_type == "constructor":
            self.__add_stub_cons2(class_, m, registers)
        elif method_type == "instance":
            self.__add_stub_inst(class_, on, m, registers)
        elif method_type == "static":
            self.__add_stub_static(class_, m, registers)
            
            
    def _insert_lines(self, class_, method, lines, linenr=-1):
        #add some smali lines of code to method method in class class_ at line linenr (-1 means add it at the end)
        m = method
        for line in lines:
            instr = InsnNode(line)
            if linenr == -1:
                m.add_insn(instr)
            else:
                m.insert_insn(line, linenr, 0)
        
    def _add_method_from_file(self, on, m, class_, file, regs):
        """
        @param on: opcode name
        @param m: method descriptor (string); example: Lcom/test;->test()V
        @param class_: class of the new file (ClassNode)
        @param file: path to file with instructions
        @param regs: amount of registers of that method
        This is just a convenience method for me
        """
        segs = m.rsplit("->", 1)
        
        self._add_stub_method(on, m, class_, regs)
        method = class_.get_method(segs[1])
        
        f = open(file, "r")
        for line in f:
            if line.strip() != "":
                self._insert_lines(class_, method, [line.strip()])


    def __add_stub_inst(self, stub_class, on, m, regs=1):
        segs = m.rsplit("->", 1)

        method = MethodNode()
        method.set_desc(segs[1])
        method.add_para(TypeNode(segs[0]))
        method.add_access(["public", "static"])

        method.set_registers(regs)
        stub_class.add_method(method)

        i = m.find('(')
        self.method_map[m] = "L" + PKG_PREFIX + "/" + segs[0][1:] + "->" + \
                method.get_desc()


    def __add_stub_cons2(self, stub_class, m, regs=1):
        segs = m.rsplit("->", 1)
        desc = segs[1].replace("<init>", "droidbox_cons")
        i = desc.find(')')
        desc = desc[:i + 1] + 'V'
        method = MethodNode()
        method.set_desc(desc)
        method.add_access(["public", "static"])


        method.set_registers(regs)
        stub_class.add_method(method)

        i = m.find('(')
        self.method_map[m] = "L" + PKG_PREFIX + "/" + segs[0][1:] + "->" + \
                method.get_desc()

    def __add_stub_cons(self, stub_class, m, regs=1):
        segs = m.rsplit("->", 1)
        desc = segs[1].replace("<init>", "droidbox_cons")
        i = desc.find(')')
        desc = desc[:i + 1] + segs[0]
        method = MethodNode()
        method.set_desc(desc)
        method.add_access(["public", "static"])

        method.set_registers(regs)
        stub_class.add_method(method)

        i = m.find('(')
        self.method_map[m] = "L" + PKG_PREFIX + "/" + segs[0][1:] + "->" + \
                method.get_desc()

    def __add_stub_static(self, stub_class, m, regs=1):
        segs = m.rsplit("->", 1)

        method = MethodNode()
        method.set_desc(segs[1])
        method.add_access(["public", "static"])

        para_num = len(method.paras)
        reg_num = method.get_paras_reg_num()
        ri = 1

        method.set_registers(regs)
        stub_class.add_method(method)

        i = m.find('(')
        self.method_map[m] = "L" + PKG_PREFIX + "/" + segs[0][1:] + "->" + \
                method.get_desc()