# -*- coding: utf-8 -*- import os import re import sys PATTERNS = [ r'FirebaseMessagingService', r'onMessageReceived', r'NotificationManager', r'NotificationCompat', r'NotificationChannel', r'PushNotification', r'PushMessage', r'Transaction', r'transaction', r'InboxMessage', r'AlertMessage', r'showNotification', r'postNotification', r'NotificationReceiver', r'FCM', r'FirebaseMessaging', r'MessagingService', r'PaymentNotification', r'TransferNotification', r'BankNotification', ] CLASS_LIKE = re.compile(r'[A-Za-z][\w$/]{3,120}') def extract_strings(data, min_len=4): out = set() for m in re.finditer(rb'[\x20-\x7e]{' + str(min_len).encode() + rb',}', data): try: out.add(m.group().decode('ascii')) except Exception: pass return out def scan_file(path): with open(path, 'rb') as f: data = f.read() strings = extract_strings(data, 5) hits = {} for pat in PATTERNS: rx = re.compile(pat, re.I) matched = sorted({s for s in strings if rx.search(s)}) if matched: hits[pat] = matched[:40] # interesting fully-qualified class names fqcn = sorted({ s for s in strings if ('/' in s or s.startswith('L')) and any(k in s.lower() for k in ( 'notif', 'push', 'fcm', 'firebase', 'message', 'transaction', 'alert', 'inbox', 'payment', 'transfer' )) }) return hits, fqcn[:80] def main(root): for app in sorted(os.listdir(root)): app_dir = os.path.join(root, app) if not os.path.isdir(app_dir): continue print('\n' + '=' * 70) print('APP:', app) print('=' * 70) dex_files = [f for f in os.listdir(app_dir) if f.endswith('.dex')] all_hits = {} all_fqcn = set() for dex in sorted(dex_files): path = os.path.join(app_dir, dex) hits, fqcn = scan_file(path) for k, v in hits.items(): all_hits.setdefault(k, set()).update(v) all_fqcn.update(fqcn) for pat in PATTERNS: if pat in all_hits: print('\n[%s]' % pat) for s in sorted(all_hits[pat])[:25]: print(' ', s) print('\n[interesting class-like strings]') for s in sorted(all_fqcn)[:60]: print(' ', s) if __name__ == '__main__': root = sys.argv[1] if len(sys.argv) > 1 else 'extracted' main(root)