├── .gitignore ├── README.md ├── frida-dump-so.py ├── requirements.txt └── so-fix ├── SoFixer-Linux-32 ├── SoFixer-Linux-64 ├── SoFixer-Windows-32.exe ├── SoFixer-Windows-64.exe ├── SoFixer-macOS-32 └── SoFixer-macOS-64 /.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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # frida-dump-android-so 2 | ## refs 3 | - [hook dlopen看一下so的加载流程](https://bbs.kanxue.com/thread-277034.htm) 4 | - [frida hook init_array](https://bbs.kanxue.com/thread-267430.htm) 5 | 6 | ## How to use 7 | 8 | ```bash 9 | python frida-dump-so.py com.abc libDexHelper.so 10 | ``` 11 | 12 | fix so 13 | - [https://github.com/F8LEFT/SoFixer](https://github.com/F8LEFT/SoFixer) 14 | 15 | 注意: frida inline hook 会在函数开头增加 trampoline 16 | 17 | ```bash 18 | ./SoFixer-Linux-64 -s libDexHelper.so -o libDexHelper-fixed.so -m 0x78017d8000 -d 19 | # sofixer -s soruce.so -o fix.so -m 0x78017d8000 -d 20 | # -s 待修復的so路徑 21 | # -o 修復後的so路徑 22 | # -m 內存dump的基地址(16位) 0xABC 23 | # -d 輸出debug信息 24 | 25 | ``` -------------------------------------------------------------------------------- /frida-dump-so.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import time 4 | import sys 5 | import argparse 6 | import frida 7 | 8 | ss = """ 9 | const sleep = new NativeFunction(Module.getExportByName('libc.so', 'sleep'), 'uint', ['uint']); 10 | 11 | function dumpSo(so_name){{ 12 | const libso = Process.findModuleByName(so_name); 13 | if (libso == null) {{ 14 | console.log(`module ${{so_name}} not found`); 15 | return -1; 16 | }} 17 | Memory.protect(ptr(libso.base), libso.size, 'rwx'); 18 | const libso_buffer = ptr(libso.base).readByteArray(libso.size); 19 | send(so_name, libso_buffer); 20 | }} 21 | 22 | function hook_JNI_OnLoad(so_name){{ 23 | const module = Process.findModuleByName(so_name); 24 | console.log(`${{so_name}} module base: ${{module["base"]}}`) 25 | const JNI_OnLoad = Module.getExportByName(so_name, 'JNI_OnLoad'); 26 | console.log(`${{so_name}} JNI_OnLoad addr: ${{JNI_OnLoad}}`) 27 | Interceptor.attach(JNI_OnLoad, {{ 28 | onEnter(args){{ 29 | console.log(`JNI_OnLoad onEnter: ${{so_name}}`); 30 | // 也可在JNI_OnLoad进入时dump so 31 | console.log('start: dump so on JNI_OnLoad'); 32 | dumpSo(so_name); 33 | console.log('finish: dump so on JNI_OnLoad'); 34 | sleep(10); 35 | }}, 36 | onLeave: function(retval){{ 37 | console.log(`JNI_OnLoad onLeave: ${{so_name}}`); 38 | console.log('start: dump so on JNI_Leave'); 39 | dumpSo(so_name); 40 | console.log('finish: dump so on JNI_Leave'); 41 | sleep(10); 42 | }} 43 | }}) 44 | }} 45 | 46 | function hook_constructor() {{ 47 | let linker = null; 48 | if (Process.pointerSize == 4) {{ 49 | linker = Process.findModuleByName("linker"); 50 | }} else {{ 51 | linker = Process.findModuleByName("linker64"); 52 | }} 53 | 54 | let addr_call_function = null; 55 | let addr_g_ld_debug_verbosity = null; 56 | let addr_async_safe_format_log = null; 57 | if (linker) {{ 58 | //console.log("found linker"); 59 | let symbols = linker.enumerateSymbols(); 60 | for (let i = 0; i < symbols.length; i++) {{ 61 | let name = symbols[i].name; 62 | if (name.indexOf("call_function") >= 0) {{ 63 | addr_call_function = symbols[i].address; 64 | // console.log("call_function",JSON.stringify(symbols[i])); 65 | }} 66 | else if(name.indexOf("g_ld_debug_verbosity") >=0) {{ 67 | addr_g_ld_debug_verbosity = symbols[i].address; 68 | ptr(addr_g_ld_debug_verbosity).writeInt(2); 69 | }} else if(name.indexOf("async_safe_format_log") >=0 && name.indexOf('va_list') < 0) {{ 70 | addr_async_safe_format_log = symbols[i].address; 71 | }} 72 | }} 73 | }} 74 | if(addr_async_safe_format_log){{ 75 | Interceptor.attach(addr_async_safe_format_log,{{ 76 | onEnter: function(args){{ 77 | this.log_level = args[0]; 78 | this.tag = ptr(args[1]).readCString() 79 | this.fmt = ptr(args[2]).readCString() // [Calling / Done calling ...] 80 | if(this.fmt.indexOf("Calling c-tor") >= 0 || this.fmt.indexOf("Done calling c-tor") >=0) {{ 81 | this.function_type = ptr(args[3]).readCString(), // func_type 82 | this.so_path = ptr(args[5]).readCString(); 83 | var strs = new Array(); //定义一数组 84 | strs = this.so_path.split("/"); //字符分割 85 | this.so_name = strs.pop(); 86 | this.func_offset = ptr(args[4]).sub(Module.findBaseAddress(this.so_name)) 87 | console.log("fmt:", this.fmt, "; func_type:", this.function_type, '; so_name:',this.so_name, '; func_offset:',this.func_offset); 88 | // hook代码在这加 89 | if(this.so_name.indexOf("{so_name}") >= 0){{ 90 | console.log('start: dump so call_function on c-tor'); 91 | dumpSo("{so_name}"); 92 | console.log('finish: dump so call_function on c-tor'); 93 | sleep(1); 94 | }} 95 | }} 96 | 97 | if(this.fmt.indexOf("Calling d-tor") >= 0 || this.fmt.indexOf("Done calling d-tor") >= 0){{ 98 | this.function_type = ptr(args[3]).readCString(), // func_type 99 | this.so_path = ptr(args[5]).readCString(); 100 | var strs = new Array(); //定义一数组 101 | strs = this.so_path.split("/"); //字符分割 102 | this.so_name = strs.pop(); 103 | this.func_offset = ptr(args[4]).sub(Module.findBaseAddress(this.so_name)) 104 | console.log("fmt:", this.fmt, "; func_type:", this.function_type, '; so_name:',this.so_name, '; func_offset:',this.func_offset); 105 | // hook代码在这加 106 | if(this.so_name.indexOf("{so_name}") >= 0){{ 107 | console.log('start: dump so on call_function d-tor'); 108 | dumpSo("{so_name}"); 109 | console.log('finish: dump so on call_function d-tor'); 110 | sleep(1); 111 | }} 112 | }} 113 | 114 | 115 | }}, 116 | onLeave: function(retval){{ 117 | }} 118 | }}) 119 | }} 120 | }} 121 | 122 | 123 | function hookDlopen() {{ 124 | console.log("start hook dl open"); 125 | const android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); 126 | Interceptor.attach(android_dlopen_ext, {{ 127 | onEnter: function(args){{ 128 | this.name = args[0].readCString(); 129 | console.log(`dlopen onEnter ${{this.name}}`); 130 | }}, 131 | onLeave: function(retval){{ 132 | console.log(`dlopen onLeave: ${{this.name}}`); 133 | if (this.name != null && this.name.indexOf('{so_name}') >= 0) {{ 134 | hook_JNI_OnLoad(this.name); 135 | const module = Process.findModuleByName('{so_name}'); 136 | send(module["base"]); 137 | console.log(`{so_name} module base: ${{module["base"]}}`) 138 | const JNI_OnLoad = Module.getExportByName('{so_name}', 'JNI_OnLoad'); 139 | console.log(`{so_name} JNI_OnLoad addr: ${{JNI_OnLoad}}`) 140 | console.log('start: dump so on dl_open leave'); 141 | dumpSo("{so_name}"); 142 | console.log('finish: dump so on dl_open leave'); 143 | sleep(10); 144 | }} 145 | }} 146 | }}) 147 | }} 148 | 149 | setImmediate(hookDlopen); 150 | setImmediate(hook_constructor); 151 | """ 152 | 153 | 154 | 155 | if __name__ == "__main__": 156 | module_base = None 157 | parser = argparse.ArgumentParser() 158 | parser.add_argument("--host", type=str, help="Specify frida-server host") 159 | parser.add_argument("--port", type=str, help="Specify frida-server port") 160 | parser.add_argument("app_name", type=str, help="specify app name here, e.g. com.abc.abc") 161 | parser.add_argument("so_name", type=str, help="specify so file here, e.g. libtmp.so") 162 | args = parser.parse_args() 163 | app_name = args.app_name 164 | so_name = args.so_name 165 | 166 | def on_message(message, data): 167 | global module_base 168 | if not "payload" in message: 169 | return 170 | 171 | if message["payload"].startswith("0x"): 172 | module_base = message["payload"] 173 | return 174 | 175 | if message["payload"].endswith(".so"): 176 | filename = f"{time.time()}-{so_name}" 177 | print(f"Saving {filename}") 178 | with open(filename, "wb") as f: 179 | f.write(data) 180 | # print(f"received module base = {module_base}") 181 | return 182 | # TODO: auto so fix 183 | 184 | device = None 185 | if args.host is not None and args.port is not None: 186 | device = frida.get_device_manager().add_remote_device(f'{args.host}:{ args.port}') 187 | else: 188 | device = frida.get_usb_device() 189 | pid = device.spawn([app_name]) 190 | session = device.attach(pid) 191 | script = session.create_script(ss.format(so_name=so_name)) 192 | script.on('message', on_message) 193 | script.load() 194 | device.resume(pid) 195 | sys.stdin.read() 196 | 197 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | frida-tools -------------------------------------------------------------------------------- /so-fix/SoFixer-Linux-32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qweraqq/frida-dump-android-so/0cd3f888b0cdbb51daadc503e3ab6693e792387e/so-fix/SoFixer-Linux-32 -------------------------------------------------------------------------------- /so-fix/SoFixer-Linux-64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qweraqq/frida-dump-android-so/0cd3f888b0cdbb51daadc503e3ab6693e792387e/so-fix/SoFixer-Linux-64 -------------------------------------------------------------------------------- /so-fix/SoFixer-Windows-32.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qweraqq/frida-dump-android-so/0cd3f888b0cdbb51daadc503e3ab6693e792387e/so-fix/SoFixer-Windows-32.exe -------------------------------------------------------------------------------- /so-fix/SoFixer-Windows-64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qweraqq/frida-dump-android-so/0cd3f888b0cdbb51daadc503e3ab6693e792387e/so-fix/SoFixer-Windows-64.exe -------------------------------------------------------------------------------- /so-fix/SoFixer-macOS-32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qweraqq/frida-dump-android-so/0cd3f888b0cdbb51daadc503e3ab6693e792387e/so-fix/SoFixer-macOS-32 -------------------------------------------------------------------------------- /so-fix/SoFixer-macOS-64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qweraqq/frida-dump-android-so/0cd3f888b0cdbb51daadc503e3ab6693e792387e/so-fix/SoFixer-macOS-64 --------------------------------------------------------------------------------