├── .gitignore ├── README.md ├── hrida.py └── rpc.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hrida 2 | Hrida is a http interface for frida. The idea is inspired by [Brida](https://github.com/federicodotta/Brida/). 3 | 4 | Write a frida script that exports [rpc](https://www.frida.re/docs/javascript-api/#rpc) funtions. You can use http requests to pass arguments and retrieve the return value. 5 | 6 | 7 | ## Dependencies 8 | * [frida](https://www.frida.re/) 9 | * [flask](http://flask.pocoo.org/docs/) 10 | 11 | ## Ussage 12 | ``` 13 | Usage: hrida.py [options] frida_script 14 | 15 | Options: 16 | -h HOST Listen address. 17 | -p PORT Listen port. 18 | -a APPLICATION_ID Application that frida will attach to. 19 | ``` 20 | 21 | ### call 22 | ``` 23 | URL: http://:/call 24 | Method: POST 25 | Params: method, args 26 | ``` 27 | 28 | method: method name that is exported by frida_script 29 | 30 | args: arguments for the method as a json array (`json.dumps([arg1, arg2])`) 31 | 32 | Example: 33 | 34 | method=init&args=["md5", "salt"] 35 | 36 | ### reload frida script 37 | ``` 38 | URL: http://:/reload 39 | Method: GET 40 | ``` 41 | 42 | reload the frida script 43 | 44 | ### attach to the application 45 | ``` 46 | URL: http://:/spawn?app= 47 | Method: GET 48 | ``` 49 | 50 | application_id: the target application's package name 51 | 52 | ## Contact 53 | 54 | http://5alt.me 55 | 56 | md5_salt [AT] qq.com -------------------------------------------------------------------------------- /hrida.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | """ 4 | Hrida is a http interface for Frida 5 | md5_salt[at]qq.com (http://5alt.me) 6 | """ 7 | import frida 8 | import codecs 9 | import sys 10 | import json 11 | import os 12 | from urllib import unquote 13 | import optparse 14 | 15 | from flask import Flask, request 16 | app = Flask(__name__) 17 | 18 | def on_message(message, data): 19 | if message['type'] == 'send': 20 | print("[*] {0}".format(message['payload'])) 21 | else: 22 | print(message) 23 | 24 | class FridaInterface: 25 | def __init__(self, frida_script): 26 | self.frida_script = frida_script 27 | self.application_id = None 28 | self.pid = None 29 | self.device = None 30 | self.session = None 31 | 32 | def load(self): 33 | with codecs.open(self.frida_script, 'r', 'utf-8') as f: 34 | source = f.read() 35 | return source 36 | 37 | def check_session(self): 38 | if self.session: 39 | try: 40 | self.session.disable_debugger() 41 | return True 42 | except: 43 | pass 44 | return False 45 | 46 | def spawn_application(self,application_id=None,remote=True): 47 | 48 | if self.check_session(): 49 | return 50 | 51 | os.system("adb forward tcp:27042 tcp:27042") 52 | 53 | self.application_id = application_id if application_id else self.application_id 54 | 55 | if not self.application_id: 56 | return 57 | 58 | if remote == True: 59 | self.device = frida.get_remote_device() 60 | else: 61 | self.device = frida.get_usb_device() 62 | 63 | for app in self.device.enumerate_applications(): 64 | if app.identifier == application_id and hasattr(app, 'pid'): 65 | 66 | self.pid = app.pid 67 | 68 | self.session = self.device.attach(self.pid) 69 | self.script = self.session.create_script(self.load()) 70 | self.script.load() 71 | 72 | return 73 | 74 | if not self.pid: 75 | 76 | self.pid = self.device.spawn([self.application_id]) 77 | self.session = self.device.attach(self.pid) 78 | 79 | self.script = self.session.create_script(self.load()) 80 | self.script.load() 81 | 82 | self.device.resume(self.pid) 83 | 84 | return 85 | 86 | def reload_script(self): 87 | 88 | with codecs.open(self.frida_script, 'r', 'utf-8') as f: 89 | source = f.read() 90 | 91 | self.script = self.session.create_script(source) 92 | self.script.load() 93 | 94 | return 95 | 96 | def disconnect_application(self): 97 | 98 | self.device.kill(self.pid) 99 | return 100 | 101 | def callexportfunction(self, methodName, args): 102 | method_to_call = getattr(self.script.exports, methodName) 103 | 104 | # Take the Java list passed as argument and create a new variable list of argument 105 | # (necessary for bridge Python - Java, I think) 106 | s = [] 107 | for i in args: 108 | s.append(i) 109 | 110 | return_value = method_to_call(*s) 111 | return return_value 112 | 113 | @app.route("/spawn") 114 | def spawn(): 115 | if request.args.get('app', None): 116 | interface.spawn_application(request.args.get('app')) 117 | return "1" 118 | return "failed to attach to app" 119 | 120 | @app.route("/call", methods=['POST']) 121 | def call(): 122 | if not request.method == 'POST': 123 | #return "method is not allowed" 124 | return "" 125 | 126 | if not interface.application_id: 127 | #return "spawn an application first" 128 | return "" 129 | 130 | if not request.form.get('method', None): 131 | #return "method is empty" 132 | return "" 133 | 134 | method = request.form.get('method') 135 | 136 | if not request.form.get('args', None): 137 | args = [] 138 | else: 139 | args = json.loads(unquote(request.form.get('args'))) 140 | if type(args) != type([]): 141 | #return "bad args type" 142 | return "" 143 | 144 | if not interface.check_session(): 145 | interface.spawn_application() 146 | 147 | return interface.callexportfunction(method, args) 148 | #return "1" 149 | 150 | @app.route("/reload") 151 | def reload(): 152 | interface.reload_script() 153 | return "1" 154 | 155 | if __name__ == '__main__': 156 | parser = optparse.OptionParser('usage: %prog [options] frida_script', add_help_option=False) 157 | parser.add_option('-h', dest='host', default='127.0.0.1', 158 | help='Listen address.') 159 | parser.add_option('-p', dest='port', default=8800, 160 | help='Listen port.') 161 | parser.add_option('-a', dest='application_id', default=None, 162 | help='Application that frida will attach to.') 163 | 164 | (options, args) = parser.parse_args() 165 | if len(args) < 1: 166 | parser.print_help() 167 | sys.exit(0) 168 | 169 | interface = FridaInterface(args[0]) 170 | if options.application_id: 171 | interface.spawn_application(options.application_id) 172 | 173 | app.run(host=options.host,port=options.port, debug=True) 174 | 175 | -------------------------------------------------------------------------------- /rpc.js: -------------------------------------------------------------------------------- 1 | var result = null 2 | 3 | rpc.exports = { 4 | build: function(apiName, data){ 5 | Java.perform(function () { 6 | try{ 7 | Hrida = Java.use('me.5alt.hrida') 8 | hrida = Hrida.$new(); 9 | result = hrida.getInfo().toString() 10 | console.log(result) 11 | }catch(e){ 12 | console.log(e) 13 | } 14 | }); 15 | return result 16 | } 17 | } --------------------------------------------------------------------------------