class FridaGUI: def __init__(self): print("[+] FridaGUI __init__") self.apk_dir = os.path.join("./apk/") self.js_dir = os.path.join("./static/frida_script/") self.serial = "" self.package_name = "" self.frida_device = "" self.is_ios = False if not os.path.exists(self.apk_dir): os.mkdir(self.apk_dir) def get_device_list(self): try: self.devices = frida.enumerate_devices() device_list = [] for device in self.devices[2:]: device_list.append({"serial":device.id, "name":device.name, "type":device.type}) return device_list except Exception as e: self.err = str(e) return None def get_current_device(self): self.frida_device = frida.get_device(self.serial) return {"serial":self.frida_device.id, "name":self.frida_device.name, "type":self.frida_device.type} def installed_list(self, serial): self.serial = serial self.adb_device = AdbClient().device(self.serial) p = re.compile("package:(.*)=(.*)") packages = [] try: for pm in self.adb_device.shell("pm list packages -f").split("\n"): if pm == "": break matched = p.search(pm) package = {"name":matched.group(2), "path":matched.group(1), "downloaded":0} apk_path = "%s%s.apk" % (self.apk_dir, package['name']) if os.path.exists(apk_path): package["downloaded"] = 1 packages.append(package) return packages except: traceback.print_exc() return [] def apk_pull(self, pkg): apk_path = "%s%s.apk" % (self.apk_dir, pkg['name']) self.adb_device.pull(pkg['path'], apk_path) self.is_AWSSDK(pkg['name']) def get_downloaded_list(self): downloaded_list = [] for f in os.listdir(self.apk_dir): package_name = f[:-4] downloaded_list.append(package_name) return downloaded_list def apk_remove(self, pkg): apk_path = "%s%s.apk" % (self.apk_dir, pkg) os.remove(apk_path) def get_ios_process_list(self, serial): print("serial", serial) if serial == "": return [] self.serial = serial print("serial", self.serial) self.frida_device = frida.get_device(self.serial) if self.frida_device != "": process_list = [] self.is_ios = False for app in self.frida_device.enumerate_processes(): if app.pid == 1 and app.name == "launchd": self.is_ios = True process_list.append({"name":app.name, "pid":app.pid}) if self.is_ios: return process_list else: return [] else: return [] def get_dex(self, pkg): self.is_ios = False apk_path = "%s%s.apk" % (self.apk_dir, pkg) self.dp = DexParse(apk_path) return self.dp.get_classes() def get_classes(self, pid): self.pid = int(pid) for app in self.frida_device.enumerate_applications(): if app.name == self.package_name: self.app_id = app.identifier break js = self.get_common_script("common") js += "\nget_classes();\n" classes = [] def callback(message, data): if "payload" in message: for cls in message['payload']: classes.append(cls) self.loaded = False else: print(message) self.load(self.pid, js, callback) return classes def get_methods(self, class_name): if self.is_ios: return self.get_ios_methods(class_name) else: return self.get_android_methods(class_name) def get_android_methods(self, class_name): method_names = self.dp.get_methods(class_name) if method_names == None: return None i = 0 overloads = [] for method in method_names: for prot in self.dp.get_overloads(class_name, method): overloads.append({"index":i, "method":method, "ret":prot[0], "args":", ".join(prot[1])}) i+=1 return overloads def get_ios_methods(self, cls): js = self.get_common_script("common") js += """get_methods("{0}");""".format(cls) methods = [] def callback(message, data): if "payload" in message: for method in message['payload']: methods.append(method) print(method) self.loaded = False self.load(self.pid, js, callback) method_names = [] self.method_dict = dict() for m in methods: method_names.append(m['method']) self.method_dict[m['method']] = m return method_names def get_frida_device(self, serial): self.serial = serial self.frida_device = frida.get_device(self.serial) return self.frida_device def get_process(self): if self.serial == "": dl = self.get_device_list() if len(dl) == 1: self.serial = dl[0]['serial'] else: return None self.frida_device = frida.get_device(self.serial) print(self.frida_device) if self.frida_device != "": process_list = [] for app in self.frida_device.enumerate_processes(): if app.name.find(self.package_name) != -1: process_list.append({"name":app.name, "pid":app.pid}) return process_list else: return None def spawn(self): if self.is_ios: pid = self.frida_device.spawn([self.app_id]) self.pid = pid else: pid = self.frida_device.spawn(self.package_name) for app in self.frida_device.enumerate_processes(): if app.pid == pid: if app.name.find(self.package_name) == -1: return {"name":self.package_name, "pid":app.pid} else: return {"name":app.name, "pid":app.pid} def resume(self, pid): self.frida_device.resume(pid) def get_common_script(self, script_name): tmp_dir = self.js_dir if self.is_ios: tmp_dir += "ios/" else: tmp_dir += "android/" js = open(tmp_dir+script_name+".js").read() return js def get_custom_script(self, script_names): tmp_dir = self.js_dir+"custom/" js = "" for script_name in script_names: js =+ open(tmp_dir+script_name+".js").read()+"\n" return js def load_and_resume(self, pid, js, callback): js += self.get_common_script("send") session = self.frida_device.attach(pid) script = session.create_script(js) script.on("message", callback) script.load() self.resume(pid) self.loaded = True while self.loaded: pass script.unload() script.off("message", callback) session.detach() def load(self, pid, js, callback): js += self.get_common_script("send") session = self.frida_device.attach(pid) script = session.create_script(js) script.on("message", callback) self.loaded = True script.load() while self.loaded: pass script.unload() script.off("message", callback) session.detach() def get_classes_memory(self, pid): script = self.get_common_script("common") script+= "get_classes()" classes = [] self.complete = False def callback(message, data): if "payload" in message: if message['payload'] == "complete": self.complete = True for cls in message['payload']: classes.append(cls) self.load(pid, script, callback) while True: if self.complete: break return classes def intercept_code(self, class_name, method): if self.is_ios: over = self.method_dict[method] sm = ScriptMaker_IOS(class_name, method) args_code = sm.arg_make(over['args']) ret_code = sm.ret_make(over['ret']) intercept = self.get_common_script("intercept") hook_name = "%s_%s" % (class_name, method.replace("- ","").replace("+ ","").replace(":","").replace(".","")) code = intercept.format(class_name, method, hook_name, args_code, ret_code) return code else: index = method methods = self.get_methods(class_name) over = methods[int(index)] sm = ScriptMaker(class_name, over['method']) if over['args'] != "": overload = ",".join("'%s'" % x for x in over['args'].strip().split(", ")) args_code = sm.arg_make(over['args'].split(", ")) else: overload = "" args_code = "" ret_code = sm.ret_make(over['ret']) intercept = self.get_common_script("intercept") code = intercept.format(class_name, over['method'], overload, args_code, ret_code) return code def is_AWSSDK(self, pkgid): asset = Assets() apkfinal_path = os.path.join("./apk/") + pkgid + '.apk' if re.search(b'(?i)aws-android-sdk', open(apkfinal_path,"rb").read()): if asset.exist(pkgid) == False: asset.add(pkgid, "", 0, "") asset.exist_sdk(pkgid, True) return True
class Avd: _TASKLIST_CMD = ( "python", "a_swapper = {}", "o_tasks = {}", "o_pid = {}", "addr = gdb.execute('x/a %d'%(a_swapper + o_tasks), to_string=True).split(':\\t')[1]", "addr = int(addr, 16) - o_tasks", "while addr != a_swapper:", " pid = gdb.execute('x/wx %d'%(addr + o_pid), to_string=True).split(':\\t')[1]", " pid = int(pid.replace('\\n', ''), 16)", " print('#%d;%d'%(addr, pid))", " addr = gdb.execute('x/a %d'%(addr + o_tasks), to_string=True).split(':\\t')[1]", " addr = int(addr, 16) - o_tasks", "end") _CAPABILITIES_OFFSETS = [0x30, 0x34, 0x38, 0x3c, 0x40, 0x44] _IDS_OFFSETS = [0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24] def __init__(self, device: str, host: str, port: int): self._tasklist = None try: self.device = AdbClient(host=host, port=port).device(device) except RuntimeError as err: raise AVDError(err) if self.device is None: raise AVDError("Can't connect to emulator through ADB") @property @lru_cache(maxsize=1) def kernel(self): config_name = "{}.yaml".format( self.device.shell("uname -rm").replace(" ", "_").strip()) root_path = Path(__file__).resolve().parent.parent try: kernel = Kernel.load(root_path / "config" / "kernel" / config_name, self.device) except FileNotFoundError: raise AVDError("Kernel is not supported") return kernel @property def tasklist(self): if self._tasklist is not None: return self._tasklist debug("Retrieving tasklist from memory") cmd = "\n".join(Avd._TASKLIST_CMD).format( self.kernel.swapper_address, self.kernel.config.task.offset.tasklist, self.kernel.config.task.offset.pid) try: results = self.kernel.gdb.execute_and_retry( cmd, msg="Wait for kernel memory mapping") if len(results) == 0: info("Can't retrieve tasklist. Updating gdbstub...") self.kernel.gdb.update() results = self.kernel.gdb.execute(cmd) except GdbError as err: raise AVDError(err) tasklist = dict() for result in results: addr, pid = result.get("payload").replace("\\n", "").replace("#", "", 1).split(";") tasklist[int(pid)] = int(addr) self._tasklist = tasklist if len(tasklist) > 0 else None return self._tasklist def find_process(self, pid: int): info(f"Kernel base address found at 0x{self.kernel.base_address:x}") tasklist = self.tasklist if tasklist is None: raise AVDError("Can't retrieve tasklist from emulator memory") paddr = tasklist.get(int(pid)) info(f"Process [{pid}] found at 0x{paddr:x}") return paddr def get_pid(self, pname: str) -> int: pids = self.device.shell(f"pidof {pname}").replace("\\n", "").split() if len(pids) > 1: raise AmbiguousProcessNameError() return int(pids[0]) if len(pids) > 0 else None def overwrite_credentials(self, pid): address = self.find_process(pid) + self.kernel.config.task.offset.creds cmd = [f"x/a {address}", "set $addr = $__"] for offset in Avd._CAPABILITIES_OFFSETS: cmd.append("set *(unsigned int*) ($addr + {}) = {}".format( offset, 0xffffffff)) for offset in Avd._IDS_OFFSETS: cmd.append("set *(unsigned int*) ($addr + {}) = {}".format( offset, 0x00000000)) info(f"Overwriting process [{pid}] credentials") self.kernel.gdb.execute("\n".join(cmd)) def selinux_setenforce(self, mode: int): self.kernel.enforce = mode def close(self): try: self.kernel.gdb.exit() except AVDError: pass