def add_stub_method(self, on, m, parent_class, parent_method, package): #segs = m.split(':', 1) #method_type = segs[0] #m = segs[1] #print on,m method_type = METHOD_TYPE_BY_OPCODE[on] segs = m.rsplit("->", 1) #print on,m,method_type,segs #print parent_class.name, segs[0] # if package in parent_class.name: # print "removing",parent_class.name,package,segs[0] # self.stub_classes.pop(segs[0],None) if self.stub_classes.has_key(segs[0]): stub_class = self.stub_classes[segs[0]] else: stub_class = ClassNode() #stub_class.set_name = "Ldroidbox/java/lang/String" for Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; invoke-static 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("(")] #print stub_class.name,method_name,method_type,parent_class.name if method_type == "constructor": self.__add_stub_cons2(stub_class, m, parent_class, parent_method) elif method_type == "instance": self.__add_stub_inst(stub_class, on, m, parent_class, parent_method) elif method_type == "static": self.__add_stub_static(stub_class, m, parent_class, parent_method)
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_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()
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 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(m) 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) if nr <= insn_m.obj.reg_end: insn_m.obj.set_reg_start(nr) else: insn_m = InsnNode("invoke-static {}, %s" % \ self.method_map[md]) 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