├── README.md ├── get_schemas.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # get_schemas 2 | 3 | Pretty straightforward, just gets all the URL schemas and the corresponding activities and prints them all out. 4 | 5 | You're going to want to decompile the app with `apktool` before running this so that you have a decoded copy of: 6 | 7 | - AndroidManifest.xml 8 | - res/values/strings.xml 9 | 10 | Both of these files are required for this tool to work as it substitutes any `@string/x_y_z` values. 11 | 12 | ## Requirements 13 | - Python 3.7+ 14 | 15 | ## Installation 16 | 17 | ``` 18 | $ pip3 install -r requirements.txt 19 | ``` 20 | 21 | ## Usage 22 | 23 | ``` 24 | $ python3 get_schemas.py -h 25 | usage: get_schemas.py [-h] [-m MANIFEST] [-s STRINGS] 26 | 27 | optional arguments: 28 | -h, --help show this help message and exit 29 | -m MANIFEST, --manifest MANIFEST 30 | Path to AndroidManifest.xml (default: 31 | ./AndroidManifest.xml) 32 | -s STRINGS, --strings STRINGS 33 | Path to strings.xml (default: 34 | ./res/values/strings.xml) 35 | ``` 36 | 37 | ## Example 38 | 39 | ``` 40 | $ python3 get_schemas.py -m ./com.twitter.android/AndroidManifest.xml -s ./com.twitter.android/res/values/strings.xml 41 | com.twitter.android.ProfileActivity (exported=False) 42 | content://com.android.contacts 43 | com.twitter.app.deeplink.UrlInterpreterActivity (exported=False) 44 | http://mobile.twitter.com/.* 45 | http://twitter.com/.* 46 | http://www.twitter.com/.* 47 | https://ads.twitter.com/mobile 48 | https://mobile.twitter.com/.* 49 | https://twitter.com/.* 50 | https://www.twitter.com/.* 51 | twitter://account 52 | twitter://alerts/landing_page/.* 53 | twitter://bouncer 54 | twitter://broadcasts 55 | twitter://collection 56 | twitter://connect_people 57 | twitter://dm_conversation 58 | twitter://events/timeline/.* 59 | twitter://events/tv_show/.* 60 | twitter://explore 61 | twitter://flow/setup_profile 62 | ... 63 | ``` 64 | -------------------------------------------------------------------------------- /get_schemas.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | from bs4 import BeautifulSoup 5 | import itertools 6 | import re 7 | 8 | is_scheme_data_tag = lambda tag: tag.name == 'data' and \ 9 | any(f'android:{x}' in tag.attrs for x in \ 10 | ('scheme', 'host', 'port', 'path', 'pathPrefix', 'pathPattern') \ 11 | ) 12 | 13 | def main(): 14 | parser = argparse.ArgumentParser() 15 | parser.add_argument('-m', '--manifest', 16 | required=False, 17 | default='AndroidManifest.xml', 18 | type=argparse.FileType('r', encoding='utf-8'), 19 | help='Path to AndroidManifest.xml (default: ./AndroidManifest.xml)') 20 | parser.add_argument('-s', '--strings', 21 | required=False, 22 | default='res/values/strings.xml', 23 | type=argparse.FileType('r', encoding='utf-8'), 24 | help='Path to strings.xml (default: ./res/values/strings.xml)') 25 | args = parser.parse_args() 26 | 27 | strings_xml = BeautifulSoup(args.strings, 'xml') 28 | strings = {d['name']: d.text for d in strings_xml.find_all('string', {'name': True})} 29 | 30 | raw_manifest = args.manifest.read() 31 | raw_manifest = re.sub(r'"@string/(?P[^"]+)"', lambda g: '"{}"'.format(strings.get(g.group('string_name'), 'UNKNOWN_STRING')), raw_manifest) 32 | manifest_xml = BeautifulSoup(raw_manifest, 'xml') 33 | 34 | exported_components = manifest_xml.find_all(True, {'android:exported': 'true'}) 35 | for (_type, _name) in sorted([(e.name, e["android:name"]) for e in exported_components], key=lambda x: x[0]): 36 | print(f'Exported <{_type}>: {_name}') 37 | 38 | print(f'\n{"-"*50}\n') 39 | activity_handlers = {} 40 | for intent_filter in manifest_xml.find_all('intent-filter'): 41 | scheme_items = intent_filter.find_all(is_scheme_data_tag) 42 | if len(scheme_items) > 0: 43 | activity_name = None 44 | activity_exported = False 45 | 46 | target_activity = intent_filter.find_parent(['activity', 'activity-alias', 'service', 'receiver']) 47 | if not target_activity: 48 | continue 49 | 50 | if target_activity.name == 'activity-alias': 51 | target_activity_name = target_activity['android:targetActivity'] 52 | target_activity = manifest_xml.find('activity', {'android:name': target_activity_name}) 53 | 54 | if target_activity: 55 | activity_name = target_activity.get('android:name', None) 56 | activity_exported = bool(target_activity.get('android:exported', False)) 57 | 58 | if activity_name is not None: 59 | schemes, hosts, ports, paths = [], [], [], [] 60 | for item in scheme_items: 61 | schemes.append(item.get('android:scheme')) 62 | hosts.append(item.get('android:host')) 63 | ports.append(item.get('android:port')) 64 | 65 | for k in ('path', 'pathPrefix', 'pathPattern'): 66 | paths.append(item.get(f'android:{k}')) 67 | 68 | schemes, hosts, ports, paths = map(list, map(set, map(lambda x: filter(None, x), [schemes, hosts, ports, paths]))) 69 | no_port_path = (len(ports) == 0 and len(paths) == 0) 70 | if len(ports) == 0: ports.append('') 71 | if len(paths) == 0: paths.append('') 72 | if len(hosts) == 0: hosts.append('') # for filters with only :// 73 | for scheme, host, port, path in list(itertools.product(schemes, hosts, ports, paths)): 74 | if scheme: 75 | uri = f'{scheme}://' 76 | if host: 77 | uri += host 78 | if not no_port_path: 79 | if port: uri += f':{port}' 80 | if path: uri += f'{"/" if not path.startswith("/") else ""}{path}' 81 | 82 | activity_key = (activity_name, activity_exported) 83 | activity_handlers[activity_key] = activity_handlers.get(activity_key, []) + [uri] 84 | 85 | for (activity, exported), handlers in activity_handlers.items(): 86 | print(f'{activity} (exported={exported})') 87 | print('\n'.join(f' {h}' for h in sorted(set(handlers)))) 88 | 89 | 90 | if __name__ == '__main__': 91 | main() 92 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4 2 | lxml 3 | --------------------------------------------------------------------------------