def run(self) -> None: LOGGER.debug('{} start'.format(self.__class__.__name__)) threads = [] while True: devices = frida.enumerate_devices() for device in devices: if device.type != 'usb': continue duplicated = False for t in threads: if t.device.id == device.id: if not t.is_alive(): threads.remove(t) break duplicated = True break if duplicated: continue try: new_thread = FridaThread(device, self.install, self.port, self.regexps, True) new_thread.start() threads.append(new_thread) except Exception as e: LOGGER.error(e) time.sleep(0.1)
def hook_apps(self): apps = set() # monitor apps while True: time.sleep(0.1) new_apps = set(p.name for p in self.device.enumerate_processes()) if not new_apps: continue incremental_apps = new_apps - apps decremental_apps = apps - new_apps for incremental_app in incremental_apps: for regexp in self.regexps: if re.search(regexp, incremental_app): # waiting for app startup completely time.sleep(0.1) try: self.hook(incremental_app) except Exception as e: LOGGER.error(e) finally: break for decremental_app in decremental_apps: for regexp in self.regexps: if re.search(regexp, decremental_app): LOGGER.info('app {} has died'.format(decremental_app)) break apps = new_apps
def run(self) -> None: LOGGER.info("{} start with hook device: id={}, name={}, type={}".format( self.__class__.__name__, self.device.id, self.device.name, self.device.type)) try: self.prepare() self.hook_apps() except Exception as e: LOGGER.error(e)
def setLoggingLevel(args): # Set FileHandler filename = os.path.join(paths.w9scan_Output_Path, "log" + "_" + str(int(time.time())) + ".txt") logger.info("The log file will be saved on: '%s'"%filename) FILE_HANDLER = logging.FileHandler(filename) FORMATTER = logging.Formatter("\r[%(asctime)s] [%(levelname)s] %(message)s", "%H:%M:%S") FILE_HANDLER.setFormatter(FORMATTER) LOGGER.addHandler(FILE_HANDLER) if args.debug: LOGGER.setLevel(CUSTOM_LOGGING.DEBUG)
def shutdown(self): LOGGER.debug('shutdown device ' + self.device.id) self.kill_frida_servers() if self.device.type == 'remote': port_manager.release_port(self.forward_port) self.adb.clear_forward(self.forward_port) if self.port: self.iptables.uninstall() self.adb.clear_reverse(self.port)
def main(): parser = argparse.ArgumentParser(description='A tool that hook all apps you need') parser.add_argument('regexps', type=str, nargs='*', help=r'Regexps for the apps you want to hook such as "^com\.baidu\.", ' r'empty for hooking all apps') parser.add_argument('-i', '--install', action='store_true', help='install frida server to /data/local/tmp automatically') parser.add_argument('-p', '--port', type=int, help='reverse tcp port, if specified, manipulate iptables automatically') parser.add_argument('-v', action='store_true', help='verbose output') args = parser.parse_args() if args.v: LOGGER.setLevel(logging.DEBUG) # set log os.makedirs(LOG_DIR, mode=0o700, exist_ok=True) log_filename = time.strftime('%Y-%m-%d_%H-%M-%S.log') log_file = open(os.path.join(LOG_DIR, log_filename), 'a', encoding='utf-8') logger_handler = ColorizingStreamHandler(log_file) logger_handler.setFormatter(FORMATTER) LOGGER.addHandler(logger_handler) Adb.start_server() try: t = WatchThread(args.install, args.port, args.regexps, True) t.start() t.join() except KeyboardInterrupt: LOGGER.info('shutdown, thank you for using frida skeleton') except Exception as e: LOGGER.error(e)
def setLoggingLevel(args): #日志文件处理函数 filename = os.path.join(paths.Ajatar_Output_PATH, "log" + "_" + str(int(time.time())) + ".txt") logger.info("Log file saved on %s" % filename) FILE_HANDLER = logging.FileHandler(filename) #日志设置文件为对象 FORMATTER = logging.Formatter( "\r[%(asctime)s] [%(levelname)s] %(message)s", "%H:%M:%S") #输出格式 FILE_HANDLER.setFormatter(FORMATTER) LOGGER.addHandler(FILE_HANDLER) if args.debug: LOGGER.setLevel(CUSTOM_LOGGING.DEBUG) #日志级别为DEBUG
def __init__(self, device, install: bool, port: int, regexps: list): super().__init__() if device.type == FakeDevice.type: # init remote device LOGGER.debug( 'device {} does not support get_usb_device, changing to get_remote_device' .format(device.id)) self.forward_port = port_manager.acquire_port() self.device = frida.get_device_manager().add_remote_device( '127.0.0.1:{}'.format(self.forward_port)) self.device.id = device.id else: self.device = device self.install = install self.port = port self.regexps = regexps if regexps else ['.*'] self.adb = Adb(self.device.id) if device.type == FakeDevice.type: self.adb.forward(self.forward_port, FRIDA_SERVER_DEFAULT_PORT) if self.port: self.iptables = Iptables(self.adb, self.port) self.arch = self.adb.unsafe_shell("getprop ro.product.cpu.abi")['out'] # maybe get 'arm64-v8a', 'arm-v7a' ... if 'arm64' in self.arch: self.arch = 'arm64' elif 'arm' in self.arch: self.arch = 'arm' elif 'x86_64' in self.arch: self.arch = 'x86_64' elif 'x86' in self.arch: self.arch = 'x86' else: raise RuntimeError('unknown arch: ' + self.arch) self.server_name = 'frida-server-{}-android-{}'.format( frida.__version__, self.arch) self.stop_flag = False thread_manager.add_thread(self)
def install_frida_server(self): server_path = os.path.join(ROOT_DIR, 'assets', self.server_name) server_path_xz = server_path + '.xz' # if not exist frida server then install it if not self.adb.unsafe_shell("ls /data/local/tmp/" + self.server_name)['out']: LOGGER.info('download {} from github ...'.format(self.server_name)) with __lock__: download('https://github.com/frida/frida/releases/download/{}/{}.xz' .format(frida.__version__, self.server_name), server_path_xz) # extract frida server with open(server_path, 'wb') as f: with lzma.open(server_path_xz) as xz: f.write(xz.read()) # upload frida server self.adb.push(server_path, '/data/local/tmp/')
def hook(self, app: str): app = app.strip() if not app: raise RuntimeError('try to hook empty app name') LOGGER.info('hook app ' + app) process = self.device.attach(app) js = 'Java.perform(function() {' # load all scripts under folder 'scripts' for (dirpath, dirnames, filenames) in os.walk(os.path.join(ROOT_DIR, 'scripts')): for filename in filenames: _ = open(os.path.join(dirpath, filename), encoding="utf-8").read() if _.startswith(r'''/*Deprecated*/'''): continue js += _ js += '\n' js += '});' script = process.create_script(js) script.on('message', self.on_message) script.load()
def shutdown(signum, frame): if signum == signal.SIGINT: LOGGER.debug('keyboard interrupt event detected') elif signum == signal.SIGTERM: LOGGER.debug('termination event detected') else: LOGGER.warn('unknown event detected') raise MainExit
def run(self) -> None: LOGGER.info( "{} start with hook device: id={}, name={}, type={}".format( self.__class__.__name__, self.device.id, self.device.name, self.device.type)) try: self.prepare() self.hook_apps() except Exception as e: LOGGER.error('device {}: {}'.format(self.device.id, e)) try: self.shutdown() except Exception as e: LOGGER.error( 'unexpected error occurred when shutdown device {}: {}'.format( self.device.id, e)) LOGGER.debug('device {} exit'.format(self.device.id)) thread_manager.del_thread(self)
def download(self, url, file_path): # get total size of file r1 = requests.get(url, stream=True, verify=False) total_size = int(r1.headers['Content-Length']) # check downloaded size if os.path.exists(file_path): temp_size = os.path.getsize(file_path) else: temp_size = 0 if temp_size == total_size: LOGGER.info('{} has downloaded completely'.format(file_path)) return if temp_size > total_size: LOGGER.error( '{} has corrupted, download it again'.format(file_path)) os.remove(file_path) return self.download(url, file_path) LOGGER.debug('{} of {} needs to be download'.format( total_size - temp_size, total_size)) # download from temp size to end headers = {'Range': 'bytes={}-'.format(temp_size)} r = requests.get(url, stream=True, verify=False, headers=headers) with open(file_path, "ab") as f: for chunk in r.iter_content(chunk_size=1024): if self.stop_flag: break if chunk: temp_size += len(chunk) f.write(chunk) f.flush() # download progress done = int(50 * temp_size / total_size) sys.stdout.write("\r[{}{}] {}%".format( '█' * done, ' ' * (50 - done), 100 * temp_size / total_size)) sys.stdout.flush() sys.stdout.write(os.linesep)
def cmd_and_debug(cls, cmd) -> map: ret = {'out': '', 'err': ''} LOGGER.debug(cmd) p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=True) err = p.stderr.read().decode().strip() if err: LOGGER.error('shell error: ' + err) ret['err'] = err out = p.stdout.read().decode().strip() if out: LOGGER.debug('shell output: ' + out) ret['out'] = out return ret
def on_message(self, message, data): try: if message['type'] == 'error': text = message['description'].strip() if not text: return LOGGER.error(text) else: text = message['payload'].strip() if message['type'] == 'send' else message.strip() if not text: return LOGGER.info(text) except Exception as e: LOGGER.error(e)
def cmd_and_debug(cls, cmd: str, debug=True) -> map: ret = {'out': '', 'err': ''} if debug: LOGGER.debug(cmd) p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=True) err = p.stderr.read().decode().strip() if err: if err != 'Unable to start: Error binding to address: Address already in use': LOGGER.error('shell error: ' + err) ret['err'] = err out = p.stdout.read().decode().strip() if out: if debug: LOGGER.debug('shell output: ' + out) ret['out'] = out return ret
def main(): parser = argparse.ArgumentParser( description='A tool that hook all apps you need') parser.add_argument( 'regexps', type=str, nargs='*', help=r'Regexps for the apps you want to hook such as "^com\.baidu\.", ' r'empty for hooking all apps') parser.add_argument( '-i', '--install', action='store_true', help='install frida server to /data/local/tmp automatically') parser.add_argument( '-p', '--port', type=int, help='reverse tcp port, if specified, manipulate iptables automatically' ) parser.add_argument('-v', action='store_true', help='verbose output') args = parser.parse_args() try: if args.v: LOGGER.setLevel(logging.DEBUG) # set log os.makedirs(LOG_DIR, mode=0o700, exist_ok=True) log_filename = time.strftime('%Y-%m-%d_%H-%M-%S.log') log_file = open(os.path.join(LOG_DIR, log_filename), 'a', encoding='utf-8') logger_handler = ColorizingStreamHandler(log_file) logger_handler.setFormatter(FORMATTER) LOGGER.addHandler(logger_handler) # set handling interrupt exceptions signal.signal(signal.SIGTERM, shutdown) signal.signal(signal.SIGINT, shutdown) Adb.start_server() watch_thread = WatchThread(args.install, args.port, args.regexps) except (KeyboardInterrupt, InterruptedError) as e: LOGGER.info(e) sys.exit(-1) try: watch_thread.start() while True: time.sleep(1) except MainExit: while True: try: LOGGER.info( 'shutdown command received, wait for clean up please...') watch_thread.cancel() break except MainExit: pass # waiting for sub threads while True: try: while True: should_we_exit() time.sleep(1) except MainExit: try: n = len(thread_manager.thread_map) if n > 0: LOGGER.info( 'running sub threads: {}, wait a second please'.format( n)) except MainExit: pass
def run(self) -> None: LOGGER.debug('{} start'.format(self.__class__.__name__)) while True: if self.stop_flag: break devices = frida.enumerate_devices() # usb devices from frida api usb_devices = [ device for device in devices if device.type == 'usb' ] usb_devices_ids = [device.id for device in usb_devices] # devices strings from "adb devices" adb_devices_strings = Shell.cmd_and_debug( 'adb devices', debug=False)['out'].split('\n')[1:] adb_devices_strings = [ _.split('\t')[0] for _ in adb_devices_strings ] # we need to access these devices remotely remote_devices_strings = set(adb_devices_strings) - set( usb_devices_ids) remote_devices = [] for _ in remote_devices_strings: new_device = FakeDevice() new_device.id = _ remote_devices.append(new_device) for device in usb_devices + remote_devices: duplicated = False for t in self.frida_threads: if t.device.id == device.id: if not t.is_alive(): self.frida_threads.remove(t) break duplicated = True break if duplicated: continue try: frida_thread = FridaThread(device, self.install, self.port, self.regexps) except RuntimeError as e: LOGGER.error( 'error occurred when init frida thread: {}'.format(e)) else: frida_thread.start() self.frida_threads.append(frida_thread) time.sleep(0.1) self.shutdown() LOGGER.debug('watch thread exit')
def should_we_exit(): if thread_manager.is_empty(): LOGGER.info('sub threads exit completely, bye!') sys.exit(0)