# -*- 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)
