├── .gitignore ├── .idea ├── .name ├── codeStyles │ └── codeStyleConfig.xml ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml ├── remote-mappings.xml └── vcs.xml ├── README.md ├── docs ├── android.md ├── ios.md ├── pics │ ├── javamethodtracepic.jpg │ └── jnitracelog.jpg └── use_as_npm_node.md ├── examples ├── TraceJavaMethods.ts ├── bypass_cronet.ts ├── dump_dex_with_loadAllClass.ts ├── dump_module.ts ├── ios_hook_all_base64.ts ├── ios_hook_func.ts ├── replaceMemoryData.ts └── traceJni.ts ├── index.ts ├── main.py ├── package-lock.json ├── package.json ├── python └── android │ ├── FixDumpSo.py │ ├── check_apk_class.py │ ├── decompress_multi_dex.py │ ├── idaPreDebug.py │ ├── idaSearchSvc.py │ ├── idaServerListener.py │ ├── subprocess_inject.py │ └── traceLogCleaner.py ├── setupAndorid.py ├── tsconfig.json └── utils ├── FCAnd.ts ├── FCCommon.ts ├── FCiOS.ts ├── StdString.ts ├── android ├── Anti.ts ├── UnpinningPlus.js ├── jni │ ├── jni_env.json │ └── method_data.ts ├── jnimgr.ts ├── libs │ └── gson-2.8.6.jar ├── multi_unpinning.js ├── repinning.js └── unpack │ ├── fridaUnpack.js │ └── fridaUnpackSimply.js ├── common └── StringUtils.ts ├── dmlog.ts ├── ios └── AntiIOS.ts └── repinning_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/*.iml 2 | .idea/misc.xml 3 | python/android/tdc_dir 4 | __handlers__/ 5 | agent/ 6 | test/ 7 | /_agent.js 8 | /node_modules 9 | ### Python template 10 | # Byte-compiled / optimized / DLL files 11 | __pycache__/ 12 | *.py[cod] 13 | *$py.class 14 | 15 | # C extensions 16 | *.so 17 | 18 | # Distribution / packaging 19 | .Python 20 | build/ 21 | develop-eggs/ 22 | dist/ 23 | downloads/ 24 | eggs/ 25 | .eggs/ 26 | lib/ 27 | lib64/ 28 | parts/ 29 | sdist/ 30 | var/ 31 | wheels/ 32 | pip-wheel-metadata/ 33 | share/python-wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .nox/ 53 | .coverage 54 | .coverage.* 55 | .cache 56 | nosetests.xml 57 | coverage.xml 58 | *.cover 59 | .hypothesis/ 60 | .pytest_cache/ 61 | 62 | # Translations 63 | *.mo 64 | *.pot 65 | 66 | # Django stuff: 67 | *.log 68 | local_settings.py 69 | db.sqlite3 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # celery beat schedule file 102 | celerybeat-schedule 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | ### JetBrains template 135 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 136 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 137 | 138 | # User-specific stuff 139 | .idea/**/workspace.xml 140 | .idea/**/tasks.xml 141 | .idea/**/usage.statistics.xml 142 | .idea/**/dictionaries 143 | .idea/**/shelf 144 | 145 | # Generated files 146 | .idea/**/contentModel.xml 147 | 148 | # Sensitive or high-churn files 149 | .idea/**/dataSources/ 150 | .idea/**/dataSources.ids 151 | .idea/**/dataSources.local.xml 152 | .idea/**/sqlDataSources.xml 153 | .idea/**/dynamic.xml 154 | .idea/**/uiDesigner.xml 155 | .idea/**/dbnavigator.xml 156 | 157 | # Gradle 158 | .idea/**/gradle.xml 159 | .idea/**/libraries 160 | 161 | # Gradle and Maven with auto-import 162 | # When using Gradle or Maven with auto-import, you should exclude module files, 163 | # since they will be recreated, and may cause churn. Uncomment if using 164 | # auto-import. 165 | # .idea/modules.xml 166 | # .idea/*.iml 167 | # .idea/modules 168 | # *.iml 169 | # *.ipr 170 | 171 | # CMake 172 | cmake-build-*/ 173 | 174 | # Mongo Explorer plugin 175 | .idea/**/mongoSettings.xml 176 | 177 | # File-based project format 178 | *.iws 179 | 180 | # IntelliJ 181 | out/ 182 | 183 | # mpeltonen/sbt-idea plugin 184 | .idea_modules/ 185 | 186 | # JIRA plugin 187 | atlassian-ide-plugin.xml 188 | 189 | # Cursive Clojure plugin 190 | .idea/replstate.xml 191 | 192 | # Crashlytics plugin (for Android Studio and IntelliJ) 193 | com_crashlytics_export_strings.xml 194 | crashlytics.properties 195 | crashlytics-build.properties 196 | fabric.properties 197 | 198 | # Editor-based Rest Client 199 | .idea/httpRequests 200 | 201 | # Android studio 3.1+ serialized cache file 202 | .idea/caches/build_file_checksums.ser 203 | 204 | .idea/GrepConsole.xml 205 | _fcagent.js 206 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | fridaContainer -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/remote-mappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FridaContainer 2 | 3 | FridaContainer 整合了网上流行的和自己编写的常用的 frida 脚本,为逆向工作提效之用。 4 | 5 | npm build 后,用 Pycharm 打开编辑,可以看到 frida api 代码补全提示。 6 | 7 | 8 | ## 1. 编译和使用 9 | 10 | ### 1.1 源码直接使用【推荐】 11 | 12 | 需要根据自己的需求修改 index.ts,编写实际操作内容。 13 | 使用 index.ts 入口方式可以按照以下方式编译和调用。 14 | 15 | ```sh 16 | $ git clone https://github.com/deathmemory/FridaContainer.git 17 | $ cd FridaContainer/ 18 | $ npm install 19 | ## after edit index.ts 20 | $ npm run build 21 | $ frida -U -f com.example.android --no-pause -l _fcagent.js 22 | ``` 23 | 24 | - 开发实时编译 25 | 26 | ```sh 27 | $ npm run watch 28 | ``` 29 | 30 | - Setup for android 31 | 32 | 为 Andriod 手机初始化环境以应用第三方库(gson) 33 | 34 | ```shell script 35 | $ python setupAndroid.py 36 | ``` 37 | 38 | ### 1.2 作为 npm node 模块使用 39 | 作为 npm ndoe 使用在新版本中会有问题,具体原因目前还没有时间看,建议用上面的源码推荐方式使用。 40 | 41 | ~~支持作为 npm node 模拟直接嵌入 typescript 项目中。~~ 42 | 43 | ~~[详细引入方式请看这里](docs/use_as_npm_node.md)~~ 44 | 45 | ### 1.3 赘述几句我当前的使用习惯 46 | 47 | 1. 使用 `pycharm` 做开发(其他 IDE 也一样) 48 | 2. clone 仓库后,在项目根目录创建 agent 目录(已加入 gitignore)在这里开发业务脚本 49 | 3. 修改 `index.ts` 引入 agent 目录下的类 50 | 4. 单开一个 shell 跑 `npm run watch` 实时编译脚本 51 | 5. 不断修改 index 或 agent 的脚本,注入、测试,达到目的。 52 | 53 | ## 2. 功能简介 54 | 55 | 本仓库会持续补充更新。 56 | 57 | ### 2.1 Android 58 | 59 | - [Android 详细文档](docs/android.md) 60 | 61 | 1. 一键去常规反调试 62 | 2. 打印堆栈 63 | 3. 通用的 Dump dex 方法 64 | 4. 过 ssl pinning (新增 cronet bypass) 65 | 5. Hook JNI 66 | 6. Java methods trace 67 | 7. JNI trace 68 | 8. frida multi dex hook(java use) 69 | 9. ...... 70 | 71 | ### 2.2 iOS 72 | 73 | - [iOS 详细文档](docs/ios.md) 74 | 75 | 1. 便捷的获取函数地址 76 | 2. 模糊查找函数地址 77 | 3. 打印堆栈 78 | 4. dump ui 结构 79 | 5. 常见数据类型转换及打印 80 | 6. ...... 81 | 82 | ### 2.3 FCCommon 跨平台通用方法 83 | 84 | | 方法 | 说明 | 85 | | ----- | ---------------------------- | 86 | | showStacksModInfo| 打印指定层数的 sp,并输出 module 信息 (如果有)| 87 | | getModuleByAddr | 根据地址获取模块信息 | 88 | | getLR | 获取 LR 寄存器值 | 89 | | dump_module | dump 指定模块并存储到指定目录 | 90 | 91 | ## 3. 感谢 92 | [todo 引用参考] 93 | 94 | 由于引用较多,且时间比较久了,也很难都列出来,以后慢慢列举吧。 95 | 感谢无私的代码分享者们。 96 | 97 |
98 | 感谢参考与引用 99 | 100 | - [universal-android-ssl-pinning-bypass-with-frida](https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/) 101 | - [rida-multiple-unpinning](https://codeshare.frida.re/@akabe1/frida-multiple-unpinning/) 102 | - [art methods tracer](https://github.com/hluwa/ZenTracer) 103 | - [JNI-Frida-Hook](https://github.com/Areizen/JNI-Frida-Hook) 104 | - [jnitrace](https://github.com/chame1eon/jnitrace) 105 | - [frida_hook_libart](https://github.com/lasting-yang/frida_hook_libart) 106 | - [使用Frida简单实现函数粒度脱壳](https://bbs.kanxue.com/thread-260540.htm) 107 |
108 | -------------------------------------------------------------------------------- /docs/android.md: -------------------------------------------------------------------------------- 1 | # Android 使用文档 2 | 3 | ## 增加 gson 库,可使用 gson.toJson 等功能 4 | 5 | 将 Java 对象转化成 Json,仓库已经集成了 gson 库,即使 APP 没有内置 gson 也可以使用。 6 | 当 gson 功能遇到瓶颈崩溃时,会用自实现的方法做兜底转换。 7 | 8 | ```typescript 9 | FCAnd.toJSONString(javaObject); 10 | ``` 11 | 12 | ## 一键去常规反调试 13 | 14 | ```typescript 15 | FCAnd.anti.anti_debug(); 16 | ``` 17 | 18 | ## trace java methods 19 | 20 | 在 `index.ts` 中启用下面函数 21 | 22 | ```typescript 23 | /** 24 | * java 方法追踪 25 | * @param clazzes 要追踪类数组 ['M:Base64', 'E:java.lang.String'] ,类前面的 M 代表 match 模糊匹配,E 代表 equal 精确匹配 26 | * @param whitelist 指定某类方法 Hook 细则,可按白名单或黑名单过滤方法。 27 | * { '类名': {white: true, methods: ['toString', 'getBytes']} } 28 | * @stackFilter 按匹配字串打印堆栈。如果要匹配 bytes 数组需要十进制无空格字串,例如:"104,113,-105" 29 | * traceArtMethods 改为 traceJavaMethods 的别称存在 30 | */ 31 | FCAnd.traceJavaMethods( 32 | ['M:MainActivity', 'E:java.lang.String'], 33 | {'java.lang.String': {white: true, methods:['substring', 'getChars']}}, 34 | "match_str_show_stacks" 35 | ); 36 | ``` 37 | 38 | 通过 `python/android/traceLogCleaner.py` 脚本收集 trace 日志,可以按线程、格式化输出日志 39 | 40 | 格式化 trace 效果 41 | 42 | ![javamethodtracepic](./pics/javamethodtracepic.jpg) 43 | 44 | * 注:如果 java trace 出现崩溃可以尝试调用纯净模式 `FCAnd.traceJavaMethods_custom`,这里没有默认 trace 的类 `FCAnd.tjm_default_cls`和默认单类白名单`FCAnd.tjm_default_white_detail`,需要自己手动附加,可以减少默认 trace 的类来判断崩溃的原因。若还有崩溃,请提交 issue 。 45 | ```typescript 46 | FCAnd.traceJavaMethods_custom(['E:java.net.URI'], 47 | {'java.net.URI': {white: true, methods: ['$init']}}, 48 | "match_str_show_stacks"); 49 | ``` 50 | 51 | 52 | ## trace jni 53 | 54 | 本功能是 [jnitrace](https://github.com/chame1eon/jnitrace) 的一个简化和嵌入版。 55 | 56 | 在 `index.ts` 中启用下面函数 57 | 58 | ```typescript 59 | FCAnd.jni.traceAllJNISimply(); 60 | ``` 61 | 62 | 通过 `python/android/traceLogCleaner.py` 脚本收集 trace 日志,可以按线程、格式化输出日志 63 | 64 | 输出样例: 65 | 66 | ![jnitracepic](./pics/jnitracelog.jpg) 67 | 68 | ## frida multi dex hook(java use) 69 | 目前支持通过 DexClassLoader | InMemoryDexClassLoader | BaseDexClassLoader | LoadClass 动态加载的 Dex 70 | 71 | ```typescript 72 | FCAnd.useWithDexClassLoader('com.cls.name', function (cls: Wrapper) { 73 | DMLog.i('tag', JSON.stringify(cls)); 74 | }); 75 | 76 | FCAnd.useWithInMemoryDexClassLoader('com.cls.name', function (cls: Wrapper) { 77 | DMLog.i('tag', JSON.stringify(cls)); 78 | }); 79 | 80 | FCAnd.useWithBaseDexClassLoader('com.cls.name', function (cls: Wrapper) { 81 | DMLog.i('tag', JSON.stringify(cls)); 82 | }); 83 | 84 | FCAnd.useWhenLoadClass('com.cls.name', function (cls: Wrapper) { 85 | DMLog.i('tag', JSON.stringify(cls)); 86 | }); 87 | ``` 88 | 89 | ## 打印堆栈 90 | ```typescript 91 | FCAnd.showStacks(); 92 | ``` 93 | 94 | ## 通用的 Dump dex 方法 95 | ```typescript 96 | FCAnd.dump_dex_common(); 97 | ``` 98 | ## load 自定义 ssl 证书 99 | 100 | 将证书 `cert-der.crt` 传到手机,然后调用下面的语句 101 | 102 | ```typescript 103 | FCAnd.anti.anti_sslLoadCert("/data/local/tmp/cert-der.crt"); 104 | ``` 105 | 106 | ## ssl unpinning 107 | 108 | 相当于 frida 版的 JustTrustMe 109 | 110 | ```typescript 111 | FCAnd.anti.anti_ssl_unpinning(); 112 | ``` 113 | ## chrome cronet bypass 114 | 115 | ```typescript 116 | import {FCAnd} from "./FCAnd"; 117 | 118 | FCAnd.anti.anti_ssl_cronet_32(); 119 | ``` 120 | 121 | ## Hook JNI 122 | 方便的 JNI Hook 123 | ```typescript 124 | FCAnd.jni.hookJNI('NewStringUTF', { 125 | onEnter: function (args) { 126 | var str = args[1].readCString(); 127 | DMLog.i('NewStringUTF', 'str: ' + str); 128 | if (null != str) { 129 | if (str == 'mesh' || str.startsWith('6962')) { 130 | var lr = FCAnd.getLR(this.context); 131 | DMLog.i('NewStringUTF', '(' + Process.arch + ')lr: ' + lr 132 | + ', foundso:' + FCAnd.getModuleByAddr(lr) ); 133 | // FCCommon.getStacksModInfo(this.context, 100); 134 | } 135 | } 136 | } 137 | }); 138 | ``` 139 | 140 | ## 打印 registNatives 141 | 142 | ```typescript 143 | FCAnd.jni.hook_registNatives(); 144 | ``` 145 | 146 | 该功能拆分出了一个独立模块,使用频率高的朋友可以使用独立模块 147 | 地址:https://github.com/deathmemory/fridaRegstNtv 148 | 149 | ## 动态加载 dex 150 | 151 | 在利用 InMemoryDexClassLoader 加载内存 Dex 找不到类的情况下适用。 152 | 153 | ```typescript 154 | FCAnd.anti.anti_InMemoryDexClassLoader(function(){ 155 | const cls = Java.use("find/same/multi/dex/class"); 156 | // ... 157 | }); 158 | ``` 159 | 160 | ## 其它 161 | 162 | 1. 根据地址获取所在 module 的信息 163 | 2. 获取模块地址 164 | 3. 获取 LR 寄存器值 165 | 4. 现成的 Hook url/json/map ... 166 | 167 | -------------------------------------------------------------------------------- /docs/ios.md: -------------------------------------------------------------------------------- 1 | # iOS 文档 2 | 3 | ## 获取函数地址 4 | 5 | ```typescript 6 | const addr = FCiOS.getFuncAddr('*[NVEnvironment deviceId]'); 7 | ``` 8 | 9 | ## 模糊查找函数地址 10 | 11 | ```typescript 12 | const targets = FCiOS.findAllByPattern('*[* base64EncodedDataWithOptions*]'); 13 | targets.forEach(function (target: any) { 14 | DMLog.i('base64EncodedDataWithOptions', 'target.name: ' + target.name + ', target.address: ' + target.address); 15 | Interceptor.attach(target.address, { 16 | onEnter: function (args) { 17 | FCiOS.showStacks(this); 18 | }, 19 | onLeave: function (retval) { 20 | DMLog.i('base64EncodedDataWithOptions', 'retval: ' + FCiOS.nsdataToString(retval)); 21 | } 22 | }) 23 | }); 24 | ``` 25 | 26 | ## 打印堆栈 27 | 28 | ```typescript 29 | FCiOS.showStacks(this); 30 | ``` 31 | 32 | ## dump ui 33 | ```typescript 34 | console.log(FCiOS.dump_ui()); 35 | ``` 36 | 37 | ## trace openURL 38 | ```typescript 39 | FCiOS.trace_url(); 40 | ``` 41 | 42 | ## trace NSLog 43 | ```typescript 44 | FCiOS.trace_NSLog(); 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/pics/javamethodtracepic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deathmemory/FridaContainer/cd8abd8ca63951b5d912ef0906f9ef1fe15cbbd5/docs/pics/javamethodtracepic.jpg -------------------------------------------------------------------------------- /docs/pics/jnitracelog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deathmemory/FridaContainer/cd8abd8ca63951b5d912ef0906f9ef1fe15cbbd5/docs/pics/jnitracelog.jpg -------------------------------------------------------------------------------- /docs/use_as_npm_node.md: -------------------------------------------------------------------------------- 1 | # 作为 npm node 模块使用 2 | 3 | ## 配置参考 4 | 5 | ### package.json 6 | 7 | ```json 8 | { 9 | // ...... 10 | "devDependencies": { 11 | "@dmemory/fridacontainer": "latest" 12 | } 13 | } 14 | ``` 15 | 16 | ### tsconfig.json 17 | 18 | ```json 19 | { 20 | "compilerOptions": { 21 | "target": "ESNEXT", 22 | "module": "ESNEXT", 23 | "allowJs": true, 24 | "strict": true, 25 | "moduleResolution": "node", 26 | "baseUrl": "./", 27 | "paths": { 28 | "fridacontainer/*": [ 29 | "./node_modules/@dmemory/fridacontainer/dist/*" 30 | ] 31 | }, 32 | "skipLibCheck": true, 33 | "allowSyntheticDefaultImports": true, 34 | "esModuleInterop": true, 35 | "forceConsistentCasingInFileNames": true 36 | } 37 | } 38 | ``` 39 | 40 | ## 安装 node 41 | ```bash 42 | sudo npm install 43 | ``` 44 | 45 | ## 引用 46 | 47 | 因为在 `tsconfig.json` 中 `baseUrl` 和 `paths` 重定向了搜索路径为 `fridacontainer`,所以下面可以缩短导入路径长度。 48 | 49 | ```typescript 50 | import {FCAnd} from "fridacontainer/FCAnd" 51 | 52 | function main() { 53 | FCAnd.hook_url(true); 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /examples/TraceJavaMethods.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: TraceJavaMethods.js 5 | * @time: 2020/12/10 11:00 上午 6 | * @desc: trace java methods example 7 | * 建议从 python/android/traceLogCleaner.py 启动, 8 | * 默认 attach 当前打开的应用,并将日志格式化输出到当前目录的 tdc_dir 文件夹中 9 | * 方便搜索 10 | */ 11 | 12 | import {FCAnd} from "../utils/FCAnd"; 13 | 14 | if (Java.available) { 15 | Java.perform(() => { 16 | // [1] trace by default value 17 | FCAnd.traceJavaMethods(); 18 | // [2] trace art 作为别称使用 19 | FCAnd.traceArtMethods(); 20 | // [3] trace custom methods 21 | FCAnd.traceJavaMethods( 22 | ['M:MainActivity', 'E:java.lang.String'], 23 | {'java.lang.String': {white: true, methods: ['substring', 'getChars']}}, 24 | "match_str_show_stacks" 25 | ); 26 | // [4] trace custom methods without defaults, you need to do it yourself 27 | FCAnd.traceJavaMethods_custom( 28 | FCAnd.tjm_default_cls, 29 | FCAnd.tjm_default_white_detail, 30 | "match_str_show_stacks" 31 | ); 32 | // [5] trace java constructors 33 | FCAnd.traceJavaMethods_custom(['E:java.net.URI'], 34 | {'java.net.URI': {white: true, methods: ['$init']}}, 35 | "match_str_show_stacks"); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /examples/bypass_cronet.ts: -------------------------------------------------------------------------------- 1 | import {FCAnd} from "../utils/FCAnd"; 2 | 3 | /** 4 | * chrome cronet bypass 使用示例。 5 | */ 6 | if (Java.available) { 7 | Java.perform(() => { 8 | FCAnd.afterSoLoad("libsscronet.so", mod => { 9 | FCAnd.anti.anti_ssl_cronet_32(); 10 | }); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /examples/dump_dex_with_loadAllClass.ts: -------------------------------------------------------------------------------- 1 | import {FCAnd} from "../utils/FCAnd"; 2 | 3 | /** 4 | * 启动完成后,调用 rpc.exports.ddc() 完成 dump dex 5 | */ 6 | 7 | if (Java.available) { 8 | Java.perform(() => { 9 | FCAnd.dump_dex_loadAllClass(); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /examples/dump_module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: android_dump_module.js 5 | * @time: 2020/11/12 8:29 下午 6 | * @desc: 7 | */ 8 | 9 | import {FCCommon} from "../utils/FCCommon"; 10 | 11 | if (Java.available) { 12 | Java.perform(() => { 13 | FCCommon.dump_module('libmtguard.so', '/data/data/com.dianping.v1'); 14 | }); 15 | } 16 | 17 | if (ObjC.available) { 18 | FCCommon.dump_module('Hopper Disassembler v4', "./"); 19 | } 20 | -------------------------------------------------------------------------------- /examples/ios_hook_all_base64.ts: -------------------------------------------------------------------------------- 1 | import {FCiOS} from "../utils/FCiOS"; 2 | import {DMLog} from "../utils/dmlog"; 3 | 4 | if (ObjC.available) { 5 | const targets = FCiOS.findAllByPattern('*[* base64EncodedDataWithOptions*]'); 6 | targets.forEach(function (target: any) { 7 | DMLog.i('base64EncodedDataWithOptions', 'target.name: ' + target.name + ', target.address: ' + target.address); 8 | Interceptor.attach(target.address, { 9 | onEnter: function (args) { 10 | FCiOS.showStacks(this); 11 | }, 12 | onLeave: function (retval) { 13 | DMLog.i('base64EncodedDataWithOptions', 'retval: ' + FCiOS.nsdataToString(retval)); 14 | } 15 | }) 16 | }); 17 | } -------------------------------------------------------------------------------- /examples/ios_hook_func.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: ios_hook_func.js 5 | * @time: 2020/9/16 12:37 PM 6 | * @desc: 7 | */ 8 | 9 | import {FCiOS} from "../utils/FCiOS"; 10 | import {DMLog} from "../utils/dmlog"; 11 | 12 | if (ObjC.available) { 13 | const addr = FCiOS.getFuncAddr('*[NVEnvironment deviceId]'); 14 | Interceptor.attach(addr, { 15 | onEnter: function (args) { 16 | 17 | }, 18 | onLeave: function (retval) { 19 | retval.replace(ObjC.classes.NSString.stringWithString_('random_deviceidxxxxxxxxx')); 20 | // 87e041d4c2abb75fda2b2390474c993a70fcc0ff 21 | DMLog.d('deviceId', 'retval: ' + ObjC.classes.NSString.stringWithString_(retval)); 22 | } 23 | }) 24 | } -------------------------------------------------------------------------------- /examples/replaceMemoryData.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: replaceMemoryData.js 5 | * @time: 2021/8/26 2:34 下午 6 | * @desc: 内存数据替换 7 | */ 8 | 9 | import {DMLog} from "../utils/dmlog"; 10 | import {FCAnd} from "../utils/FCAnd"; 11 | import {FCCommon} from "../utils/FCCommon"; 12 | 13 | rpc.exports = { 14 | ms() { 15 | // 写入测试数据 16 | let am = Memory.alloc(0x60); 17 | DMLog.i('fc', 'alloc: ' + am); 18 | am.writeByteArray([0, 1, 2, 3, 4]); 19 | am.add(5).writeUtf8String("3C8F4F55D4B548E4EDBB1157EFAC3FC1"); 20 | am.add(40).writeUtf8String("3C8F4F55D4B548E4EDBB1157EFAC3FC1"); 21 | DMLog.i('fc', hexdump(am)); 22 | // 替换数据 23 | FCAnd.replaceMemoryData(am, 0x60, 24 | FCCommon.str2hexstr("3C8F4F55D4B548E4EDBB1157EFAC3FC1"), 25 | FCCommon.str2hexArray("kkkkkkk"), false); 26 | // 验证数据 27 | DMLog.i('fc after', hexdump(am)); 28 | let tgtarr = am.add(5).readByteArray(32); 29 | } 30 | } -------------------------------------------------------------------------------- /examples/traceJni.ts: -------------------------------------------------------------------------------- 1 | import {FCAnd} from "../utils/FCAnd"; 2 | 3 | /** 4 | * trace jni 两种用法 5 | * 结合 `python/android/traceLogCleaner.py` 使用效果更佳 6 | */ 7 | if (Java.available) { 8 | Java.perform(function () { 9 | // 直接 trace 所有 Jni 函数 10 | FCAnd.jni.traceAllJNISimply(); 11 | 12 | // 只 trace 指定的 jni 函数 13 | FCAnd.jni.traceJNI(['CallStaticObjectMethod', 'CallObjectMethod']); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: AntiDexLoader.js 5 | * @time: 2020/4/16 5:03 PM 6 | * @desc: 7 | */ 8 | import {DMLog} from "./utils/dmlog"; 9 | import {FCCommon} from "./utils/FCCommon"; 10 | // import {DianPing} from "./agent/dp/dp"; 11 | import {FCAnd} from "./utils/FCAnd"; 12 | 13 | function main() { 14 | DMLog.d('MAIN', 'HELLO FridaContainer, please add code on the index.ts'); 15 | // FCAnd.Anti.anti_ptrace(); 16 | // FCAnd.Anti.anti_fgets(); 17 | // and.anti.Anti.anti_fgets(); 18 | 19 | // FCAnd.anti.anti_debug(); 20 | /// dp 21 | // DianPing.anti_debug(); 22 | // DianPing.hook_cx_stacks(); 23 | /// 24 | // FCAnd.showStacks(); 25 | // FCAnd.dump_dex_common(); 26 | // FCAnd.Anti.anti_sslPinning("/data/local/tmp/cert-der.crt"); 27 | 28 | // FCCommon.dump_module('libmtguard.so', '/data/data/com.dianping.v1'); 29 | // DianPing.hook_stuffs(); 30 | // call mtgsig 31 | // DianPing.test_call_mtgsig(); 32 | // DianPing.hook_zlog(); 33 | // FCAnd.anti.anti_debug(); 34 | // coord: (0,203,25) | addr: Lcom.dianping.nvnetwork.tunnel.Encrypt.SocketSecureManager;->getB2keyByB2(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; | loc: ? 35 | // FCAnd.traceArtMethods(['E:com.dianping.nvnetwork.tunnel.Encrypt.SocketSecureManager'], null, "122,108,111,103,46,98,105,110"); // "zlog.bin" 36 | // FCAnd.anti.anti_ssl_unpinning(); 37 | // DianPing.hook_stuffs(); 38 | // DianPing.hook_net(); 39 | // DianPing.modify_devinfo(); 40 | // DianPing.hook_stuffs(); 41 | // FCAnd.hook_uri(true); 42 | // FCAnd.hook_url(true); 43 | // FCAnd.jni.traceAllJNISimply(); 44 | // FCAnd.traceArtMethods(['M:retrofit2']); 45 | // rpc.exports = { 46 | // test() { 47 | // Java.perform(() => { 48 | // FCAnd.jni.traceAllJNISimply(); 49 | // }); 50 | // } 51 | // } 52 | } 53 | 54 | if (Java.available) { 55 | DMLog.i("JAVA", "available"); 56 | Java.perform(function () { 57 | main(); 58 | }); 59 | } 60 | 61 | if (ObjC.available) { 62 | DMLog.i("ObjC", "available"); 63 | FCCommon.printModules(); 64 | FCCommon.dump_module("Hopper Disassembler v4", "/Users/dmemory/Downloads/"); 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | ''' 3 | @author: xingjun.xyf 4 | @contact: deathmemory@163.com 5 | @file: main.py 6 | @time: 2020/6/8 10:20 AM 7 | @desc: 8 | ''' 9 | import codecs 10 | import sys 11 | 12 | import frida 13 | import time 14 | 15 | def on_message(message, data): 16 | global _script, url, body 17 | if message['type'] == 'send': 18 | try: 19 | payload = message['payload'] 20 | print(payload) 21 | except IOError as e: 22 | print(e) 23 | else: 24 | print(message) 25 | 26 | 27 | def attach(device, packagename): 28 | session = None 29 | while session is None: 30 | print("wait to attach ...") 31 | try: 32 | session = device.attach(packagename) 33 | except frida.ProcessNotFoundError as err: 34 | print(err) 35 | time.sleep(1) 36 | return session 37 | 38 | 39 | def spawn(device, packagename): 40 | pid = device.spawn([packagename]) 41 | device.resume(pid) 42 | session = device.attach(pid) 43 | return session 44 | 45 | 46 | def doHook(packagename, scriptfile, bSpawn=False): 47 | global device, _script 48 | with codecs.open(scriptfile, "r", "utf-8") as jsfile: 49 | jscode = jsfile.read() 50 | 51 | if (bSpawn): 52 | session = spawn(device, packagename) 53 | else: 54 | session = attach(device, packagename) 55 | 56 | script = session.create_script(jscode) 57 | script.on('message', on_message) 58 | script.load() 59 | 60 | 61 | if __name__ == '__main__': 62 | # device = frida.get_device_manager()\ 63 | # .add_remote_device("127.0.0.1:3333") 64 | device = frida.get_usb_device(1) 65 | # processes = device.enumerate_processes() 66 | 67 | # doHook("com.sdu.didi.psnger", "frida_didi.js", False) 68 | # doHook(u'滴滴出行', "OneTravel.js", False); 69 | # spawn(device, 'com.autonavi.cprotectortest') 70 | # doHook('com.autonavi.cprotectortest', '_fcagent.js', True) 71 | # doHook('com.autonavi.minimap', '_fcagent.js', True) 72 | # doHook('com.ss.android.ugc.aweme', '_fcagent.js', True) 73 | doHook('com.google.android.apps.maps', '_fcagent.js', True) 74 | # doHook('com.baidu.BaiduMap', '_fcagent.js', True) 75 | 76 | # script.post({ 77 | # 'type': "input", 78 | # 'payload': DDUtil.converUrlParams(url) + DDUtil.converBody(body) 79 | # }) 80 | 81 | print("[*] running frida") 82 | sys.stdin.read() 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dmemory/fridacontainer", 3 | "version": "1.0.10", 4 | "description": "fridaContainer written in TypeScript and JavaScript", 5 | "private": false, 6 | "main": "index.ts", 7 | "scripts": { 8 | "prepare": [ 9 | "npm run build", 10 | "tsc" 11 | ], 12 | "build": "frida-compile index.ts -o _fcagent.js", 13 | "watch": "frida-compile index.ts -o _fcagent.js -w" 14 | }, 15 | "bundleDependencies": false, 16 | "devDependencies": { 17 | "@types/frida-gum": "^18.1.0", 18 | "@types/node": "^18.0.6", 19 | "frida-compile": "^10.2.5" 20 | }, 21 | "dependencies": { 22 | "@types/base64-js": "^1.3.0", 23 | "@types/node": "^14.14.2", 24 | "base64-ts": "^2.0.1", 25 | "frida-compile": "^10.0.0" 26 | }, 27 | "directories": { 28 | "doc": "docs", 29 | "example": "examples", 30 | "test": "test" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/deathmemory/FridaContainer.git" 35 | }, 36 | "author": "deathmemory", 37 | "license": "ISC", 38 | "bugs": { 39 | "url": "https://github.com/deathmemory/FridaContainer/issues" 40 | }, 41 | "homepage": "https://github.com/deathmemory/FridaContainer#readme", 42 | "files": [ 43 | "dist/**/*" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /python/android/FixDumpSo.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | ''' 3 | @author: xingjun.xyf 4 | @contact: deathmemory@163.com 5 | @file: bb.py 6 | @time: 2021/8/6 8:46 下午 7 | @desc: 8 | ''' 9 | import json 10 | import sys 11 | 12 | import lief 13 | from lief import ELF 14 | from lief.ELF import SEGMENT_TYPES 15 | 16 | 17 | class FixDumpSo: 18 | 19 | def __init__(self): 20 | pass 21 | 22 | def fix_dump_so(self, sopath, savepath): 23 | pobj = lief.parse(sopath) 24 | if isinstance(pobj, ELF.Binary) is not True: 25 | return 26 | header = pobj.header 27 | header.section_header_offset = 0 28 | header.section_header_offset = 0 29 | header.numberof_sections = 0 30 | header.section_name_table_idx = 0 31 | 32 | segments = pobj.segments 33 | # fix segment 34 | for segment in segments: 35 | if isinstance(segment, ELF.Segment): 36 | segment.file_offset = segment.virtual_address 37 | print(f'segment: {segment}') 38 | if segment.type == SEGMENT_TYPES.DYNAMIC: 39 | print(f'found {segment.type}') 40 | pobj.write(savepath) 41 | pass 42 | 43 | def remove_section_table(self, filename, output): 44 | binary = lief.parse(filename) # Build an ELF binary 45 | 46 | header = binary.header 47 | header.section_header_offset = 0 48 | header.numberof_sections = 0 49 | 50 | binary.write(output) 51 | 52 | def print_elf(self, sopath): 53 | binary = lief.parse(sopath) 54 | json_data = json.loads(lief.to_json(binary)) 55 | print(json.dumps(json_data, sort_keys=True, indent=4)) 56 | 57 | 58 | if __name__ == '__main__': 59 | if 2 != len(sys.argv): 60 | print('修复内存 Dump 出的so') 61 | print(f'Usage:\n\t{sys.argv[0]} /so/path/libxxx.so') 62 | else: 63 | fdso = FixDumpSo() 64 | sopath = sys.argv[1] 65 | savepath = sopath + "_saved" 66 | 67 | print(f'input so: {sopath}') 68 | print(f'saved so: {savepath}') 69 | print('working ...') 70 | # fdso.print_elf(savepath) 71 | fdso.fix_dump_so(sopath=sopath, savepath=savepath) 72 | # bjbus.remove_section_table(sopath, savepath) 73 | print('done .') 74 | pass 75 | -------------------------------------------------------------------------------- /python/android/check_apk_class.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import subprocess 4 | import zipfile 5 | import argparse 6 | from pathlib import Path 7 | import tempfile 8 | 9 | def extract_apk(apk_path, output_dir): 10 | """使用 apktool 反编译 APK""" 11 | try: 12 | subprocess.run(["apktool", "d", apk_path, "-o", output_dir, "-f"], check=True) 13 | return True 14 | except subprocess.CalledProcessError as e: 15 | print(f"反编译失败: {e}") 16 | return False 17 | except FileNotFoundError: 18 | print("未找到 apktool,请确保已安装并添加到 PATH") 19 | return False 20 | 21 | def search_in_smali(smali_dir, target_class): 22 | """在 smali 文件中搜索类名""" 23 | target_path = target_class.replace(".", "/") + ".smali" 24 | for root, _, files in os.walk(smali_dir): 25 | for file in files: 26 | if file.endswith(".smali"): 27 | full_path = os.path.join(root, file) 28 | rel_path = os.path.relpath(full_path, smali_dir) 29 | if rel_path.replace(os.sep, "/") == target_path: 30 | return True 31 | return False 32 | 33 | def search_in_dex(apk_path, target_class): 34 | """直接在 dex 文件中搜索类名(无需反编译)""" 35 | target_bytes = target_class.replace(".", "/").encode("utf-8") 36 | with zipfile.ZipFile(apk_path, "r") as z: 37 | for name in z.namelist(): 38 | if name.startswith("classes") and name.endswith(".dex"): 39 | with z.open(name) as dex_file: 40 | content = dex_file.read() 41 | if target_bytes in content: 42 | return True 43 | return False 44 | 45 | def check_class_in_apk(apk_path, target_class, use_dex=False): 46 | """检查 APK 中是否包含指定类""" 47 | apk_path = os.path.abspath(apk_path) 48 | if not os.path.exists(apk_path): 49 | print(f"APK 文件不存在: {apk_path}") 50 | return False 51 | 52 | print(f"检查 APK: {apk_path}") 53 | print(f"目标类: {target_class}") 54 | 55 | if use_dex: 56 | print("使用快速模式(直接扫描 dex 文件)...") 57 | return search_in_dex(apk_path, target_class) 58 | else: 59 | print("使用详细模式(反编译 APK)...") 60 | with tempfile.TemporaryDirectory() as temp_dir: 61 | if extract_apk(apk_path, temp_dir): 62 | smali_dir = os.path.join(temp_dir, "smali") 63 | if os.path.exists(smali_dir): 64 | return search_in_smali(smali_dir, target_class) 65 | return False 66 | 67 | if __name__ == "__main__": 68 | parser = argparse.ArgumentParser(description="检查 APK 中是否包含指定类") 69 | parser.add_argument("apk_path", help="APK 文件路径") 70 | parser.add_argument("class_name", help="要查找的完整类名(如 com.example.Test)") 71 | parser.add_argument("--fast", action="store_true", help="使用快速模式(直接扫描 dex 文件)") 72 | args = parser.parse_args() 73 | 74 | if check_class_in_apk(args.apk_path, args.class_name, args.fast): 75 | print(f"找到类: {args.class_name}") 76 | else: 77 | print(f"未找到类: {args.class_name}") 78 | -------------------------------------------------------------------------------- /python/android/decompress_multi_dex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # name: decompress_multi_dex.py 4 | 5 | import os 6 | 7 | path = r'/path/dex/dir/' # dex 文件夹目录 8 | out_path =r'/path/out/dir/' #输出文件夹 9 | 10 | files = os.listdir(path) 11 | 12 | for file in files: # 遍历文件夹 13 | if file.find("dex") > 0: ## 查找dex 文件 14 | sh = f'jadx -j 1 -r -d {out_path} {path}/{file}' 15 | print(sh) 16 | os.system(sh) 17 | -------------------------------------------------------------------------------- /python/android/idaPreDebug.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import os 3 | import time 4 | 5 | ''' 6 | @author: xingjun.xyf 7 | @contact: deathmemory@163.com 8 | @file: pass.py 9 | @time: 2020/11/9 7:43 下午 10 | @desc: 启动 app 挂起, 附加 IDA 后按回车键继续运行 11 | ''' 12 | 13 | # 穿越 14 | packagename = "com.autonavi.cprotectortest" 15 | launcherAct = "com.autonavi.cprotectortest.MainActivity" 16 | 17 | 18 | def getPidByLine(line): 19 | retarray = line.split(" ") 20 | for cell in retarray: 21 | if cell.isdigit(): 22 | return cell 23 | return None 24 | 25 | 26 | def killLastProc(): 27 | os.system("adb shell am force-stop " + packagename) 28 | retval = os.popen("adb shell ps | grep " + packagename) 29 | line = retval.readline() 30 | pid = getPidByLine(line) 31 | if pid is not None: 32 | os.system("adb shell su -c kill -9 " + pid) 33 | print("kill last proc") 34 | 35 | 36 | def main(): 37 | killLastProc() 38 | 39 | lauchFull = packagename + "/" + launcherAct 40 | os.system("adb shell am start -D -n " + lauchFull) 41 | time.sleep(1) 42 | retval = os.popen("adb shell ps | grep " + packagename) 43 | line = retval.readline() 44 | print("ps line:\n" + line) 45 | pid = getPidByLine(line) 46 | print("pid: " + pid) 47 | adbforward = "adb forward tcp:7788 jdwp:" + pid 48 | os.system(adbforward) 49 | print(adbforward) 50 | raw_input("wait for ida attach ...\nif ida has been attached press [Enter] key") 51 | jdbconnect = "jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=7788" 52 | os.system(jdbconnect) 53 | print(jdbconnect) 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /python/android/idaSearchSvc.py: -------------------------------------------------------------------------------- 1 | from idautils import * 2 | from idc import * 3 | 4 | import ida_idp 5 | ida_idp.get_idp_name() 6 | # 定义要搜索的字节序列 7 | # arm 8 | # search_bytes = b"\x00\x00\x00\xd4" 9 | # arm64 10 | search_bytes = b"\x01\x00\x00\xd4" 11 | result = [] 12 | # 遍历整个二进制文件的可执行段 13 | seg = ida_segment.get_segm_by_name(".text") 14 | 15 | # 在每个段中搜索字节序列 16 | for address in range(seg.start_ea, seg.end_ea): 17 | if get_bytes(address, len(search_bytes)) == search_bytes: 18 | print("Match found at address: 0x%x" % address) 19 | result.append(address) 20 | 21 | print([hex(n) for n in result]) 22 | -------------------------------------------------------------------------------- /python/android/idaServerListener.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import os 3 | ''' 4 | @author: xingjun.xyf 5 | @contact: deathmemory@163.com 6 | @file: pass.py 7 | @time: 2020/11/9 7:43 下午 8 | @desc: 运行 IDA server 脚本 9 | ''' 10 | 11 | # servername = "dmserv7_64" 12 | servername = "dmserv7" 13 | port = "2333" 14 | 15 | def main(): 16 | killserver = "adb shell su -c killall -9 " + servername 17 | os.system("adb shell su -c killall -9 dmserv7_64") 18 | os.system("adb shell su -c killall -9 dmserv7") 19 | print(killserver) 20 | adbforward = "adb forward tcp:" + port + " tcp:" + port 21 | os.system(adbforward) 22 | print(adbforward) 23 | adbserver = "adb shell su -c /data/local/tmp/" + servername + " -p" + port + " &" 24 | os.system(adbserver) 25 | print(adbserver) 26 | 27 | if __name__ == '__main__': 28 | main() 29 | -------------------------------------------------------------------------------- /python/android/subprocess_inject.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import frida 4 | import sys 5 | 6 | # 修改目标应用的包名为实际的应用包名 7 | TARGET_APP = "com.package.name" # 替换为实际的应用包名 8 | SCRIPT_PATH = "../../_fcagent.js" 9 | 10 | 11 | def on_spawn_added(spawn): 12 | name = spawn.identifier 13 | print(f"[+] Spawn detected: {name} (PID {spawn.pid})") 14 | 15 | # 仅处理目标 app 及其子进程 16 | if name == TARGET_APP or name.startswith(f"{TARGET_APP}:"): 17 | try: 18 | print(f"[*] Attaching to {name}...") 19 | session = device.attach(spawn.pid) 20 | 21 | # 读取并注入脚本 22 | with open(SCRIPT_PATH, "r", encoding="utf-8") as f: 23 | script = session.create_script(f.read(), runtime="v8") 24 | script.on("message", on_message) 25 | script.enable_debugger(port=9229) 26 | script.load() 27 | 28 | print(f"[+] Script injected into {name}") 29 | device.resume(spawn.pid) 30 | print(f"[*] Resumed {name}") 31 | except Exception as e: 32 | print(f"[!] Error injecting into {name}: {e}") 33 | else: 34 | print(f"[-] Ignoring unrelated spawn: {name}") 35 | device.resume(spawn.pid) # 不挂起无关进程 36 | 37 | 38 | def on_message(message, data): 39 | print(f"[script message] {message}") 40 | 41 | 42 | # 初始化 43 | device = frida.get_usb_device() 44 | device.on("spawn-added", on_spawn_added) 45 | time.sleep(1) 46 | 47 | # 启动目标应用 48 | pid = device.spawn(TARGET_APP) 49 | print(f"[*] Spawned {TARGET_APP} with PID {pid}") 50 | 51 | device.enable_spawn_gating() 52 | print("[*] Spawn gating enabled. Waiting for target app...") 53 | 54 | # 恢复目标应用的执行 55 | device.resume(pid) 56 | print(f"[*] Resumed {TARGET_APP}") 57 | 58 | # 保持运行 59 | try: 60 | sys.stdin.read() 61 | except KeyboardInterrupt: 62 | print("\n[!] Stopping...") 63 | device.disable_spawn_gating() -------------------------------------------------------------------------------- /python/android/traceLogCleaner.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | ''' 3 | @author: xingjun.xyf 4 | @contact: deathmemory@163.com 5 | @file: TraceLogCleaner.py 6 | @time: 2020/12/15 7:14 下午 7 | @desc: trace日志清洁工。默认 attach Android 当前打开的应用,接收 trace 内容并按 threadid 分文件记录 8 | 如果检测数据为字符数组,则会尝试转换成 string 到 trystr 字段,也会尝试转换成 hex 9 | 到 tryhex 字段,方便搜索。 10 | 保存结果在脚本同目录下的 tdc_dir 目录中。 11 | ''' 12 | import codecs 13 | import json 14 | import os 15 | import shutil 16 | import sys 17 | 18 | import frida 19 | 20 | 21 | class TraceLogCleaner: 22 | 23 | ''' 24 | @bFmt 是否以格式化方式记录日志 25 | ''' 26 | def __init__(self, bFmt): 27 | self.saveDir = 'tdc_dir' 28 | self.bFmt = bFmt 29 | pass 30 | 31 | def washFile(self, path): 32 | self.mkdirSaveDir() 33 | with open(path, 'r') as f: 34 | line = f.readline() 35 | while (line != ''): 36 | self.washLine(line) 37 | line = f.readline() 38 | f.close() 39 | pass 40 | 41 | def mkdirSaveDir(self): 42 | if not os.path.isdir(self.saveDir): 43 | os.makedirs(self.saveDir) 44 | 45 | def washLine(self, line): 46 | if line == '': 47 | return 48 | line = line.strip() 49 | # print ('current line: {}'.format(line)) 50 | jobj = json.loads(line) 51 | filename = str(jobj['tid']) 52 | # wash args 53 | status = jobj['status'] 54 | if status == 'msg': 55 | result = line 56 | elif status == 'jnitrace': 57 | result = line 58 | if self.bFmt is True: 59 | fmtstr = self.getJniFormatString(jobj) 60 | result += '\n' + fmtstr + '\n' 61 | else: 62 | if status == 'entry': 63 | args = list(jobj['args'].values()) 64 | else: 65 | args = [] 66 | if ('retval' in jobj): 67 | args.append(jobj['retval']) 68 | if isinstance(args, list): 69 | tryval = {} 70 | for i in range(len(args)): 71 | try: 72 | arg = args[i] 73 | if isinstance(arg, list): 74 | try: 75 | trystr = "".join(map(chr, arg)) 76 | except: 77 | trystr = '' 78 | try: 79 | tryhex = ','.join('{:02x}'.format(x & 0xff) for x in arg) 80 | except: 81 | tryhex = '' 82 | tryval['p{:d}'.format(i)] = {'trystr': trystr, 'tryhex': tryhex} 83 | except: 84 | pass 85 | jobj['tryval'] = tryval 86 | 87 | result = json.dumps(jobj) 88 | if self.bFmt is True: 89 | fmtstr = self.getJavaMethodFormatString(jobj) 90 | result += '\n' + fmtstr + '\n' 91 | 92 | with open(os.path.join(self.saveDir, filename), 'a+') as f: 93 | f.write(result) 94 | f.write('\n') 95 | f.close() 96 | 97 | def clean(self): 98 | if os.path.isdir(self.saveDir): 99 | shutil.rmtree(self.saveDir) 100 | 101 | def washWithSpawn(self, jspath, packagename): 102 | dev = frida.get_usb_device() 103 | app_pid = dev.spawn(packagename) 104 | session = dev.attach(app_pid) 105 | self._doWash(session, jspath) 106 | dev.resume(app_pid) 107 | 108 | sys.stdin.read() 109 | session.detach() 110 | 111 | def _doWash(self, session, jspath): 112 | with codecs.open(jspath, 'r', 'utf-8') as f: 113 | jscode = f.read() 114 | f.close() 115 | self.mkdirSaveDir() 116 | script = session.create_script(jscode, runtime="v8") 117 | # session.enable_jit() 118 | session.enable_debugger() 119 | script.on('message', self.onMessage) 120 | script.load() 121 | 122 | def washOnMessage(self, jspath): 123 | with codecs.open(jspath, 'r', 'utf-8') as f: 124 | jscode = f.read() 125 | self.mkdirSaveDir() 126 | dev = frida.get_usb_device() 127 | app = dev.get_frontmost_application() 128 | print (app) 129 | session = dev.attach(app.pid) 130 | script = session.create_script(jscode, runtime="v8") 131 | # session.enable_jit() 132 | session.enable_debugger() 133 | script.on('message', self.onMessage) 134 | script.load() 135 | f.close() 136 | sys.stdin.read() 137 | session.detach() 138 | pass 139 | 140 | def onMessage(self, msg, data): 141 | try: 142 | self.washLine(msg['payload']) 143 | except: 144 | print('except line: ' + str(msg)) 145 | 146 | def getJavaMethodFormatString(self, jobj): 147 | tryval = jobj['tryval'] 148 | status = jobj['status'] 149 | if status == 'exit': 150 | try: 151 | if 'retval' in jobj: 152 | tryblock = '' 153 | if 'p0' in tryval: 154 | tryblock = '\n|(str)== \"{trystr}\"\n|(hex)== {tryhex}'\ 155 | .format(trystr=tryval['p0']['trystr'], tryhex=tryval['p0']['tryhex']) 156 | vals = '|= {retval}{tryblock}'.format(retval=str(jobj['retval']), tryblock=tryblock) 157 | else: 158 | vals = '' 159 | except: 160 | print('except exit:', json.dumps(jobj)) 161 | else: 162 | args = list(jobj['args'].values()) 163 | vals = [] 164 | try: 165 | for i in range(len(args)): 166 | arg = args[i] 167 | tryblock = '' 168 | if isinstance(arg, list): 169 | pkey = 'p%d' % i 170 | if pkey in tryval: 171 | tryblock = '\n|(str)-- \"{trystr}\"\n|(hex)-- {tryhex}'\ 172 | .format(trystr=tryval[pkey]['trystr'], tryhex=tryval[pkey]['tryhex']) 173 | tmp = '|- {arg}{tryblock}'.format(arg=str(arg), tryblock=tryblock) 174 | vals.append(tmp) 175 | except: 176 | print('except entry:', json.dumps(jobj)) 177 | vals = '\n'.join(vals) 178 | fmt = '[+] ({status}) {clsname}\n|-> {methodname}\n{vals}'.format(status=status, clsname=jobj['classname'], methodname=jobj['method'], vals=vals) 179 | return fmt 180 | 181 | def getJniFormatString(self, jobj): 182 | try: 183 | data = jobj['data'] 184 | jnival = data['jnival'] 185 | backtrace = data['backtrace'] 186 | 187 | argsfmt = [] 188 | for arg in jnival['args']: 189 | tmp = '|- {argType}\t\t: {argValue}'.format(argType=arg['argType'].ljust(10, ' '), argValue=arg['argVal'].strip()) 190 | argsfmt.append(tmp) 191 | 192 | backtraceFmt = [] 193 | try: 194 | for bt in backtrace: 195 | tmp = '|-> {address}: ({module_name}:{module_base}) {path}'\ 196 | .format(address=bt['address'], module_name=bt['module']['name'], module_base=bt['module']['base'], path=bt['module']['path']) 197 | backtraceFmt.append(tmp) 198 | except: 199 | pass 200 | fmt = '[+] {methodname}\n{args}\n|= {retType}\t\t: {retValue}\n|-> BackTrace: \n{backtraceFmt}'\ 201 | .format(methodname=data['methodname'], args='\n'.join(argsfmt), 202 | retType=jnival['ret']['retType'].ljust(10, ' '), retValue=jnival['ret']['retVal'].strip(), 203 | backtraceFmt='\n'.join(backtraceFmt)) 204 | except: 205 | print('except:' + json.dumps(jobj)) 206 | return "" 207 | return fmt 208 | 209 | 210 | 211 | if __name__ == '__main__': 212 | tdc = TraceLogCleaner(bFmt=True) 213 | tdc.clean() 214 | tdc.washOnMessage('../../_fcagent.js') 215 | # tdc.washWithSpawn('../../_fcagent.js', 'com.baidu.BaiduMap') 216 | 217 | # tdc.washFile(path='tdc_dir/test_31523') 218 | 219 | print ('done !') 220 | -------------------------------------------------------------------------------- /setupAndorid.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | ''' 3 | @author: xingjun.xyf 4 | @contact: deathmemory@163.com 5 | @file: setupAndorid.py 6 | @time: 2021/3/18 4:50 下午 7 | @desc: 8 | ''' 9 | import os 10 | 11 | if __name__ == '__main__': 12 | os.system('adb push utils/android/libs/gson-2.8.6.jar /data/local/tmp/fclibs/gson.jar') 13 | pass 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | // "module": "", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | "lib": ["ESNext"], /* Specify library files to be included in the compilation. */ 8 | "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | "outDir": "./dist", /* Redirect output structure to the directory. */ 16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | "skipLibCheck": true, 49 | "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | 64 | /* Advanced Options */ 65 | "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ 66 | "resolveJsonModule": true 67 | }, 68 | "include": [ 69 | "utils" 70 | ], 71 | "exclude": [ 72 | "node_modules", 73 | "agent", 74 | "examples", 75 | "test", 76 | "_fcagent.js", 77 | "dist", 78 | "*.d.ts" 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /utils/FCCommon.ts: -------------------------------------------------------------------------------- 1 | import {DMLog} from "./dmlog"; 2 | import {StdString} from "./StdString"; 3 | 4 | /** 5 | * @author: xingjun.xyf 6 | * @contact: deathmemory@163.com 7 | * @file: FCCommon.js 8 | * @time: 2020/10/13 3:23 PM 9 | * @desc: 跨平台可通用的方法 10 | */ 11 | 12 | export namespace FCCommon { 13 | 14 | /** 15 | * 打印指定层数的 sp,并输出 module 信息 (如果有) 16 | * @param {CpuContext} context 17 | * @param {number} number 18 | */ 19 | export function showStacksModInfo(context: CpuContext, number: number) { 20 | var sp: NativePointer = context.sp; 21 | 22 | for (var i = 0; i < number; i++) { 23 | var curSp = sp.add(Process.pointerSize * i); 24 | DMLog.i('showStacksModInfo', 'curSp: ' + curSp + ', val: ' + curSp.readPointer() 25 | + ', module: ' + FCCommon.getModuleByAddr(curSp.readPointer())); 26 | } 27 | } 28 | 29 | 30 | /** 31 | * 根据地址获取模块信息 32 | * @param {NativePointer} addr 33 | * @returns {string} 34 | */ 35 | export function getModuleByAddr(addr: NativePointer): Module | null { 36 | var result = null; 37 | Process.enumerateModules().forEach(function (module: Module) { 38 | if (module.base <= addr && addr <= (module.base.add(module.size))) { 39 | result = JSON.stringify(module); 40 | return false; // 跳出循环 41 | } 42 | }); 43 | return result; 44 | } 45 | 46 | 47 | /** 48 | * 获取 LR 寄存器值 49 | * @param {CpuContext} context 50 | * @returns {NativePointer} 51 | */ 52 | export function getLR(context: CpuContext) { 53 | if (Process.arch == 'arm') { 54 | return (context as ArmCpuContext).lr; 55 | } 56 | else if (Process.arch == 'arm64') { 57 | return (context as Arm64CpuContext).lr; 58 | } 59 | else { 60 | DMLog.e('getLR', 'not support current arch: ' + Process.arch); 61 | } 62 | return ptr(0); 63 | } 64 | 65 | /** 66 | * dump 指定模块并存储到指定目录 67 | * @param {string} moduleName 68 | * @param {string} saveDir 如果 Android 环境下应该保存在 /data/data/com.package.name/ 目录下, 69 | * 否则可能会遇到权限问题,导致保存失败。 70 | */ 71 | export function dump_module(moduleName: string, saveDir: string) { 72 | const tag = 'dump_module'; 73 | const module = Process.getModuleByName(moduleName); 74 | const base = module.base; 75 | const size = module.size; 76 | const savePath: string = saveDir + "/" + moduleName + "_" + base + "_" + size + ".fcdump"; 77 | DMLog.i(tag, "base: " + base + ", size: " + size); 78 | DMLog.i(tag, "save path: " + savePath); 79 | Memory.protect(base, size, "rwx"); 80 | 81 | let readed = base.readByteArray(size); 82 | try { 83 | const f = new File(savePath, "wb"); 84 | if (f) { 85 | if (readed) { 86 | f.write(readed); 87 | f.flush(); 88 | } 89 | f.close(); 90 | } 91 | } 92 | catch (e) { 93 | const fopen_ptr = Module.getExportByName(null, 'fopen'); 94 | const fwrite_ptr = Module.getExportByName(null, 'fwrite'); 95 | const fclose_ptr = Module.getExportByName(null, 'fclose'); 96 | if (fopen_ptr && fwrite_ptr && fclose_ptr) { 97 | const fopen_func = new NativeFunction(fopen_ptr, 'pointer', ['pointer', 'pointer']); 98 | const fwrite_func = new NativeFunction(fwrite_ptr, 'int', ['pointer', 'int', 'int', 'pointer']); 99 | const fclose_func = new NativeFunction(fclose_ptr, 'int', ['pointer']); 100 | 101 | let savePath_ptr = Memory.alloc(savePath.length + 1); 102 | savePath_ptr.writeUtf8String(savePath); 103 | const f = fopen_func(savePath_ptr, Memory.alloc(3).writeUtf8String("wb")); 104 | DMLog.i(tag, 'fopen: ' + f); 105 | if (f != null && readed) { 106 | const readed_ptr = Memory.alloc(readed.byteLength); 107 | readed_ptr.writeByteArray(readed); 108 | fwrite_func(readed_ptr, readed.byteLength, 1, f); 109 | fclose_func(f); 110 | } 111 | else { 112 | DMLog.e(tag, 'failed: f->' + f + ', readed->' + readed); 113 | } 114 | } 115 | } 116 | } 117 | 118 | export function dump2file(addr: NativePointer, size: number, savePath: string) { 119 | DMLog.i('dump2file', `addr: ${addr.toString(16)}, size: ${size}`); 120 | let file = new File(savePath, "w+"); 121 | let byteArr = addr.readByteArray(size); 122 | if (null != byteArr) { 123 | file.write(byteArr); 124 | } 125 | file.close(); 126 | } 127 | 128 | export function printModules() { 129 | Process.enumerateModules().forEach(function (module) { 130 | DMLog.i('enumerateModules', JSON.stringify(module)); 131 | }); 132 | } 133 | 134 | export function str2hexstr(str: string) { 135 | let res = str.split("").map(x => x.charCodeAt(0).toString(16).padStart(2, "0")).join(""); 136 | return res; 137 | } 138 | 139 | export function str2hexArray(str: string) { 140 | return str.split("").map(x => x.charCodeAt(0)); 141 | } 142 | 143 | export function arrayBuffer2Hex(buf: any) { 144 | return [...new Uint8Array(buf)] 145 | .map(x => x.toString(16).padStart(2, '0')) 146 | .join(' '); 147 | } 148 | 149 | /** 150 | * stalker trace 功能 151 | * 由于函数内使用 Stalker.exclude 每次使用建议重启进程,否则可能会有莫名其妙的段、访问错误 152 | * @param moduleName 模块(so) 名称 153 | * @param address 要监控的函数地址 154 | * 155 | * 用例 FCCommon.stalkerTrace("libxxx.so", addr_2333F); 156 | */ 157 | export function stalkerTrace(moduleName: string, address: NativePointer) { 158 | const tag = 'stalkerTrace'; 159 | let module_object = Process.findModuleByName(moduleName); 160 | if (null == module_object) { 161 | DMLog.e(tag, "module is null"); 162 | return; 163 | } 164 | const module_start = module_object.base; 165 | const module_end = module_object.base.add(module_object.size); 166 | // 开始 trace 167 | let pre_regs = {}; 168 | // let address = module_object.base.add(offset_address); 169 | // 排除不需要trace 的模块 170 | Process.enumerateModules().forEach(function (md) { 171 | if (md.name != moduleName) { 172 | let memoryRange = {base: md.base, size: md.size}; 173 | Stalker.exclude(memoryRange); 174 | } 175 | }); 176 | let threadId = Process.getCurrentThreadId(); 177 | Interceptor.attach(address, { 178 | onEnter: function (args) { 179 | this.tid = threadId; 180 | if (threadId == this.threadId) { 181 | this.startFollow = true; 182 | Stalker.follow(this.tid, { 183 | events: { 184 | call: true, 185 | ret: false, 186 | exec: true, 187 | block: false, 188 | compile: false 189 | }, 190 | transform(iterator: any) { 191 | let instruction = iterator.next(); 192 | do { 193 | const startAddress = instruction.address; 194 | const isModuleCode = startAddress.compare(module_start) >= 0 && startAddress.compare(module_end) === -1; 195 | if (isModuleCode) { 196 | iterator.putCallout(function (context: any) { 197 | let pc = context.pc; 198 | let module = Process.findModuleByAddress(pc); 199 | if (module) { 200 | try { 201 | let diff_regs = get_diff_regs(context, pre_regs); 202 | if (module.name == module_object?.name) { 203 | DMLog.i(tag, `${module.name} ! ${pc.sub(module.base)} ${Instruction.parse(pc)} ${JSON.stringify(diff_regs)}`); 204 | // console.log(module.name + " ! " + pc.sub(module.base), Instruction.parse(ptr(pc)), JSON.stringify(diff_regs)); 205 | } 206 | } 207 | catch (e: any) { 208 | DMLog.e(tag, e.toString()); 209 | } 210 | } 211 | }) 212 | } 213 | iterator.keep(); 214 | } while ((instruction = iterator.next()) != null); 215 | } 216 | }); 217 | } 218 | }, 219 | onLeave: function (retval) { 220 | if (this.startFollow != undefined && this.startFollow == true) { 221 | Stalker.unfollow(this.tid); 222 | this.startFollow = false; 223 | } 224 | } 225 | }); 226 | } 227 | 228 | export function get_diff_regs(context: any, pre_regs: any) { 229 | var diff_regs = {}; 230 | for (const [reg_name, reg_value] of 231 | Object.entries(JSON.parse(JSON.stringify(context)))) { 232 | if (reg_name != "pc" && pre_regs[reg_name] !== reg_value) { 233 | pre_regs[reg_name] = reg_value; 234 | // @ts-ignore 235 | diff_regs[reg_name] = reg_value; 236 | } 237 | } 238 | return diff_regs; 239 | } 240 | 241 | export function newStdString() { 242 | return new StdString(); 243 | } 244 | 245 | // 定义复制文件的函数 246 | export function copyFile(srcPath: string, dstPath: string) { 247 | let tmp = File.readAllBytes(srcPath); 248 | File.writeAllBytes(dstPath, tmp); 249 | } 250 | 251 | export const NOP_ARM64 = [0x1F, 0x20, 0x03, 0xD5]; 252 | export function patchCode(addr: any, patchCode: any[]) { 253 | // 修改内存权限 254 | const pageSize = Process.pageSize; 255 | const pageBase = addr.and(ptr((1 << pageSize) - 1).not()); 256 | Memory.protect(pageBase, pageSize, 'rwx'); 257 | 258 | // 写入 NOP 259 | addr.writeByteArray(patchCode); 260 | DMLog.d("check_addr", `patch applied at address: ${addr}`); 261 | } 262 | 263 | } 264 | 265 | -------------------------------------------------------------------------------- /utils/FCiOS.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: FCiOS.js 5 | * @time: 2020/9/16 12:39 PM 6 | * @desc: 7 | */ 8 | import {DMLog} from "./dmlog"; 9 | import {AntiIOS} from "./ios/AntiIOS"; 10 | 11 | export namespace FCiOS { 12 | 13 | export const anti = AntiIOS; 14 | 15 | export let nil = ObjC.available ? new ObjC.Object(ptr("0x0")) : null; 16 | 17 | // generic getFuncAddr 18 | export function getFuncAddr(pattern: string): NativePointer { 19 | var tag = 'getFuncAddr'; 20 | let targets = FCiOS.findAllByPattern(pattern); 21 | var targetAddr = NULL; 22 | targets.forEach(function (target: any) { 23 | DMLog.d(tag, 'target.name: ' + target.name + ', target.address: ' + target.address); 24 | targetAddr = target.address; 25 | // end forEach 26 | return false; 27 | }); 28 | 29 | return targetAddr; 30 | } 31 | 32 | /** 33 | * 模糊查找所有符合规则的函数 34 | * 示例参考: examples/ios_hook_all_base64.ts 35 | * @param pattern 36 | */ 37 | export function findAllByPattern(pattern: string) { 38 | var tag = 'findAllByPattern'; 39 | var type: ApiResolverType = (pattern.indexOf(" ") === -1) ? "module" : "objc"; 40 | DMLog.d(tag, 'getFuncAddr type: ' + type); 41 | var res: ApiResolver = new ApiResolver(type); 42 | DMLog.d(tag, 'getFuncAddr ApiResolver: ' + JSON.stringify(res)); 43 | var matches = res.enumerateMatches(pattern); 44 | DMLog.d(tag, 'getFuncAddr matches: ' + JSON.stringify(matches)); 45 | var targets = uniqBy(matches, JSON.stringify); 46 | return targets; 47 | } 48 | 49 | // remove duplicates from array 50 | export function uniqBy(array: any, key: any) { 51 | var seen: any = {}; 52 | return array.filter(function (item: any) { 53 | var k = key(item); 54 | return seen.hasOwnProperty(k) ? false : (seen[k] = true); 55 | }); 56 | } 57 | 58 | export function showStacks(thiz: any) { 59 | DMLog.i('showStacks', '\tBacktrace:\n\t' + Thread.backtrace(thiz.context, 60 | Backtracer.ACCURATE).map(DebugSymbol.fromAddress) 61 | .join('\n\t')); 62 | } 63 | 64 | export function dump_ui() { 65 | try { 66 | var current_window = ObjC.classes.UIWindow.keyWindow(); 67 | return current_window.recursiveDescription().toString(); 68 | } catch (e) { 69 | return e; 70 | } 71 | 72 | } 73 | 74 | /** 75 | * trace openURL 76 | */ 77 | export function trace_url() { 78 | //Twitter: https://twitter.com/xploresec 79 | //GitHub: https://github.com/interference-security 80 | // Get a reference to the openURL selector 81 | var openURL = ObjC.classes.UIApplication["- openURL:"]; 82 | 83 | // Intercept the method 84 | Interceptor.attach(openURL.implementation, { 85 | onEnter: function (args) { 86 | // As this is an ObjectiveC method, the arguments are as follows: 87 | // 0. 'self' 88 | // 1. The selector (openURL:) 89 | // 2. The first argument to the openURL selector 90 | var myNSURL = new ObjC.Object(args[2]); 91 | // Convert it to a JS string 92 | var myJSURL = myNSURL.absoluteString().toString(); 93 | // Log it 94 | DMLog.d('openURL', "Launching URL: " + myJSURL); 95 | //send(myJSURL); 96 | } 97 | }); 98 | 99 | } 100 | 101 | export function trace_NSLog() { 102 | const NSLog_ptr = Module.findExportByName("Foundation", "NSLog"); 103 | DMLog.i('NSLog_ptr', 'addr: ' + NSLog_ptr); 104 | if (NSLog_ptr) { 105 | Interceptor.attach(NSLog_ptr, { 106 | onEnter: function (args) { 107 | DMLog.d('NSLog', new ObjC.Object(args[0]).toString()); 108 | } 109 | }); 110 | } 111 | 112 | const NSLogv_ptr = Module.findExportByName("Foundation", "NSLogv"); 113 | DMLog.i('NSLogv_ptr', 'addr: ' + NSLogv_ptr); 114 | if (NSLogv_ptr) { 115 | Interceptor.attach(NSLogv_ptr, { 116 | onEnter: function (args) { 117 | DMLog.d('NSLogv', new ObjC.Object(args[0]).toString()); 118 | } 119 | }); 120 | } 121 | } 122 | 123 | /** 124 | * var NSString = ObjC.use("NSString"); 125 | var str = ObjC.cast(ptr("0x1234"), NSString); 126 | * -- or -- 127 | * var str = ObjC.Object(ptr("0x1234")); 128 | * @param val 129 | */ 130 | export function newString(val: any) { 131 | try { 132 | return ObjC.classes.NSString.stringWithString_(val); 133 | } catch (e) { 134 | return val; 135 | } 136 | } 137 | 138 | export function nsdataToString(nsdata: any) { 139 | return ObjC.classes.NSString.alloc().initWithData_encoding_(nsdata, 4); 140 | } 141 | 142 | export function getClassName(id: any) { 143 | return new ObjC.Object(id).$className; 144 | } 145 | 146 | export function printNSDictionary(id: any) { 147 | var dict = new ObjC.Object(id); 148 | var enumerator = dict.keyEnumerator(); 149 | var key; 150 | while ((key = enumerator.nextObject()) !== null) { 151 | var value = dict.objectForKey_(key); 152 | DMLog.d('printNSDictionary', "key: " + key + ", val: " + value); 153 | } 154 | } 155 | 156 | export function justTouch(pattern: string) { 157 | let tgtarr; 158 | tgtarr = FCiOS.findAllByPattern(pattern); 159 | tgtarr.forEach(function (target: any) { 160 | DMLog.d('justTouch', `${target.name} attach ed`); 161 | Interceptor.attach(target.address, { 162 | onEnter: function (args) { 163 | DMLog.d('justTouch', `==== name: ${target.name} onEnter ====`); 164 | } 165 | }); 166 | }); 167 | } 168 | } -------------------------------------------------------------------------------- /utils/StdString.ts: -------------------------------------------------------------------------------- 1 | const STD_STRING_SIZE = 3 * Process.pointerSize; 2 | export class StdString { 3 | private handle: NativePointer; 4 | 5 | constructor() { 6 | this.handle = Memory.alloc(STD_STRING_SIZE); 7 | } 8 | 9 | dispose() { 10 | const [data, isTiny] = this._getData(); 11 | if (!isTiny) { 12 | // @ts-ignore 13 | Java.api.$delete(data); 14 | } 15 | } 16 | 17 | disposeToString() { 18 | const result = this.toString(); 19 | this.dispose(); 20 | return result; 21 | } 22 | 23 | toString() { 24 | const [data] = this._getData(); 25 | return (data as NativePointer).readUtf8String(); 26 | } 27 | 28 | _getData() { 29 | const str = this.handle; 30 | const isTiny = (str.readU8() & 1) === 0; 31 | const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer(); 32 | return [data, isTiny]; 33 | } 34 | } -------------------------------------------------------------------------------- /utils/android/Anti.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: AntiDexLoader.js 5 | * @time: 2020/4/16 5:03 PM 6 | * @desc: 7 | */ 8 | import {DMLog} from "../dmlog"; 9 | import {FCCommon} from "../FCCommon"; 10 | import {FCAnd} from "../FCAnd"; 11 | 12 | 13 | const sslPinningPass = require("./repinning"); 14 | const unpinning = require("./multi_unpinning"); 15 | 16 | export namespace Anti { 17 | 18 | /** 19 | * 动态加载 dex 20 | * 在利用 InMemoryDexClassLoader 加载内存 Dex 找不到类的情况下适用。 21 | * 调用方式: 22 | * FCAnd.anti.anti_InMemoryDexClassLoader(function(){ 23 | * const cls = Java.use('find/same/multi/dex/class'); 24 | * // ... 25 | * }); 26 | * 27 | * 实现原理: 28 | * const InMemoryDexClassLoader = Java.use('dalvik.system.InMemoryDexClassLoader'); 29 | InMemoryDexClassLoader.$init.overload('java.nio.ByteBuffer', 'java.lang.ClassLoader') 30 | .implementation = function (buff, loader) { 31 | this.$init(buff, loader); 32 | var oldcl = Java.classFactory.loader; 33 | Java.classFactory.loader = this; 34 | callbackfunc(); 35 | Java.classFactory.loader = oldcl; 36 | 37 | return undefined; 38 | } 39 | * @param callbackfunc 40 | * 41 | * @deprecated The method should not be used 42 | */ 43 | export function anti_InMemoryDexClassLoader(callbackfunc: any) { 44 | throw new Error("deprecated method, should use: FCAnd.useWithInMemoryDexClassLoader"); 45 | } 46 | 47 | export function anti_1__cxa_throw() { 48 | const cxa_throw_ptr = Module.findExportByName(null, "__cxa_throw"); 49 | if (null == cxa_throw_ptr) { 50 | DMLog.e('anti_1__cxa_throw', "_ZSt9__throw_1PKc not found"); 51 | return; 52 | } 53 | else { 54 | DMLog.i('anti_1__cxa_throw', 'addr: ' + cxa_throw_ptr); 55 | } 56 | 57 | Interceptor.replace(cxa_throw_ptr, new NativeCallback(function () { 58 | DMLog.i('anti_1__cxa_throw', 'Caught __cxa_throw call (preventing exception)'); 59 | FCAnd.showAllStacks(this.context); // 打印完整堆栈 60 | // 可选:模拟返回,避免崩溃 61 | // return ...; 62 | }, 'void', [])); // 根据实际参数调整类型 63 | } 64 | 65 | 66 | export function anti_debug() { 67 | anti_fgets(); 68 | anti_exit(); 69 | anti_fork(); 70 | anti_kill(); 71 | anti_ptrace(); 72 | anti_1__cxa_throw(); 73 | } 74 | 75 | export function anti_exit() { 76 | const exit_ptr = Module.findExportByName(null, '_exit'); 77 | DMLog.i('anti_exit', "exit_ptr : " + exit_ptr); 78 | if (null == exit_ptr) { 79 | return; 80 | } 81 | Interceptor.replace(exit_ptr, new NativeCallback(function (code) { 82 | if (null == this) { 83 | return 0; 84 | } 85 | var lr = FCCommon.getLR(this.context); 86 | DMLog.i('exit debug', 'entry, lr: ' + lr); 87 | return 0; 88 | }, 'int', ['int', 'int'])); 89 | } 90 | 91 | export function anti_kill() { 92 | const kill_ptr = Module.findExportByName(null, 'kill'); 93 | DMLog.i('anti_kill', "kill_ptr : " + kill_ptr); 94 | 95 | if (null == kill_ptr) { 96 | return; 97 | } 98 | Interceptor.replace(kill_ptr, new NativeCallback(function (ptid, code) { 99 | if (null == this) { 100 | return 0; 101 | } 102 | var lr = FCCommon.getLR(this.context); 103 | DMLog.i('kill debug', 'entry, lr: ' + lr); 104 | FCAnd.showNativeStacks(this.context); 105 | return 0; 106 | }, 'int', ['int', 'int'])); 107 | } 108 | 109 | /** 110 | * @state_name: cat /proc/xxx/stat ==> ...() S... 111 | * 112 | * anti fgets function include : 113 | * status->TracerPid, SigBlk, S (sleeping) 114 | * State->(package) S 115 | * wchan->SyS_epoll_wait 116 | */ 117 | export function anti_fgets() { 118 | const tag = 'anti_fgets'; 119 | const fgetsPtr = Module.findExportByName(null, 'fgets'); 120 | DMLog.i(tag, 'fgets addr: ' + fgetsPtr); 121 | if (null == fgetsPtr) { 122 | return; 123 | } 124 | var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']); 125 | Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp): NativePointer { 126 | var logTag = null; 127 | // 进入时先记录现场 128 | const lr = FCCommon.getLR(this.context); 129 | // 读取原 buffer 130 | var retval = fgets(buffer, size, fp); 131 | var bufstr = buffer.readCString(); 132 | 133 | if (null != bufstr) { 134 | if (bufstr.indexOf("TracerPid:") > -1) { 135 | buffer.writeUtf8String("TracerPid:\t0"); 136 | logTag = 'TracerPid'; 137 | } 138 | //State: S (sleeping) 139 | else if (bufstr.indexOf("State:\tt (tracing stop)") > -1) { 140 | buffer.writeUtf8String("State:\tS (sleeping)"); 141 | logTag = 'State'; 142 | } 143 | // ptrace_stop 144 | else if (bufstr.indexOf("ptrace_stop") > -1) { 145 | buffer.writeUtf8String("sys_epoll_wait"); 146 | logTag = 'ptrace_stop'; 147 | } 148 | 149 | //(sankuai.meituan) t 150 | else if (bufstr.indexOf(") t") > -1) { 151 | buffer.writeUtf8String(bufstr.replace(") t", ") S")); 152 | logTag = 'stat_t'; 153 | } 154 | 155 | // SigBlk 156 | else if (bufstr.indexOf('SigBlk:') > -1) { 157 | buffer.writeUtf8String('SigBlk:\t0000000000001204'); 158 | logTag = 'SigBlk'; 159 | } 160 | 161 | // frida 162 | else if (bufstr.indexOf('frida') > -1) { 163 | // 直接回写空有可能引起崩溃 164 | buffer.writeUtf8String("dmemory"); 165 | logTag = 'frida'; 166 | } 167 | 168 | if (logTag) { 169 | DMLog.i(tag + " " + logTag, bufstr + " -> " + buffer.readCString() + ' lr: ' + lr 170 | + "(" + FCCommon.getModuleByAddr(lr) + ")"); 171 | FCAnd.showNativeStacks(this?.context); 172 | } 173 | } 174 | return retval; 175 | }, 'pointer', ['pointer', 'int', 'pointer'])); 176 | } 177 | 178 | export function anti_ptrace() { 179 | var ptrace = Module.findExportByName(null, "ptrace"); 180 | if (null != ptrace) { 181 | DMLog.i('anti_ptrace', "ptrace addr: " + ptrace); 182 | // Interceptor.attach(ptrace, { 183 | // onEnter: function (args) { 184 | // DMLog.i('anti_ptrace', 'entry'); 185 | // } 186 | // }); 187 | Interceptor.replace(ptrace, new NativeCallback(function (p1: any, p2: any, p3: any, p4: any) { 188 | DMLog.i('anti_ptrace', 'entry'); 189 | return 1; 190 | }, 'long', ['int', "int", 'pointer', 'pointer'])); 191 | } 192 | } 193 | 194 | /** 195 | * 适用于每日优鲜的反调试 196 | */ 197 | export function anti_fork() { 198 | var fork_addr = Module.findExportByName(null, "fork"); 199 | DMLog.i('anti_fork', "fork_addr : " + fork_addr); 200 | if (null != fork_addr) { 201 | // Interceptor.attach(fork_addr, { 202 | // onEnter: function (args) { 203 | // DMLog.i('fork_addr', 'entry'); 204 | // } 205 | // }); 206 | Interceptor.replace(fork_addr, new NativeCallback(function () { 207 | DMLog.i('fork_addr', 'entry'); 208 | return -1; 209 | }, 'int', [])); 210 | } 211 | } 212 | 213 | export function anti_sslLoadCert(cerPath: string) { 214 | sslPinningPass.ssl_load_cert(cerPath); 215 | } 216 | 217 | export function anti_ssl_unpinning() { 218 | setTimeout(unpinning.multi_unpinning, 0); 219 | } 220 | 221 | /** 222 | * chrome cronet bypass (针对 32 位) 223 | * 定位:".Android" 字符串,向上引用,查找返回值赋值函数。 224 | * 225 | * 搜索特征: 226 | * 01 06 44 BF 6F F0 CE 00 70 47 81 04 44 BF 6F F0 227 | * 95 00 70 47 41 01 44 BF 6F F0 D8 00 70 47 41 06 228 | * 44 BF 6F F0 CD 00 70 47 41 07 44 BF 6F F0 C9 00 229 | * 70 47 C1 07 1C BF 6F F0 C7 00 70 47 C1 01 44 BF 230 | */ 231 | export function anti_ssl_cronet_32() { 232 | var moduleName = "libsscronet.so"; // 模块名 233 | var searchBytes = '01 06 44 BF 6F F0 CE 00 70 47 81 04 44 BF 6F F0 95 00 70 47 41 01 44 BF 6F F0 D8 00 70 47 41 06 44 BF 6F F0 CD 00 70 47 41 07 44 BF 6F F0 C9 00 70 47 C1 07 1C BF 6F F0 C7 00 70 47 C1 01 44 BF'; // 搜索的特征值 234 | 235 | // 获取模块基址和大小 236 | var module = Process.getModuleByName(moduleName); 237 | var baseAddr = module.base; 238 | var size = module.size; 239 | 240 | // 在模块地址范围内搜索特征值 241 | var matches = Memory.scan(baseAddr, size, searchBytes, { 242 | onMatch: function (address, size) { 243 | DMLog.i("anti_ssl_cronet", "[*] Match found at: " + address); 244 | // 将地址转换为静态偏移地址 245 | var offset = address.sub(baseAddr); 246 | DMLog.i("anti_ssl_cronet", "[+] Static Offset: " + offset); 247 | Interceptor.attach(address.or(1), { 248 | onLeave: function (retval) { 249 | retval.replace(ptr(0)); 250 | DMLog.w('anti_ssl_cronet retval', 'replace value: ' + retval); 251 | } 252 | }) 253 | }, 254 | onComplete: function () { 255 | DMLog.i("anti_ssl_cronet", "[*] Search completed!"); 256 | } 257 | }); 258 | 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /utils/android/UnpinningPlus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: UnpinningPlus.js 5 | * @time: 2020/6/29 2:35 PM 6 | * @desc: 7 | */ 8 | 9 | exports.UnpinningPlus = function () { 10 | 11 | Java.perform(function() { 12 | 13 | /* 14 | hook list: 15 | 1.SSLcontext 16 | 2.okhttp 17 | 3.webview 18 | 4.XUtils 19 | 5.httpclientandroidlib 20 | 6.JSSE 21 | 7.network\_security\_config (android 7.0+) 22 | 8.Apache Http client (support partly) 23 | 9.OpenSSLSocketImpl 24 | 10.TrustKit 25 | 11.Cronet 26 | */ 27 | 28 | // Attempts to bypass SSL pinning implementations in a number of 29 | // ways. These include implementing a new TrustManager that will 30 | // accept any SSL certificate, overriding OkHTTP v3 check() 31 | // method etc. 32 | var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); 33 | var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier'); 34 | var SSLContext = Java.use('javax.net.ssl.SSLContext'); 35 | var quiet_output = false; 36 | 37 | // Helper method to honor the quiet flag. 38 | 39 | function quiet_send(data) { 40 | 41 | if (quiet_output) { 42 | 43 | return; 44 | } 45 | 46 | send(data) 47 | } 48 | 49 | 50 | // Implement a new TrustManager 51 | // ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8 52 | // Java.registerClass() is only supported on ART for now(201803). 所以android 4.4以下不兼容,4.4要切换成ART使用. 53 | /* 54 | 06-07 16:15:38.541 27021-27073/mi.sslpinningdemo W/System.err: java.lang.IllegalArgumentException: Required method checkServerTrusted(X509Certificate[], String, String, String) missing 55 | 06-07 16:15:38.542 27021-27073/mi.sslpinningdemo W/System.err: at android.net.http.X509TrustManagerExtensions.(X509TrustManagerExtensions.java:73) 56 | at mi.ssl.MiPinningTrustManger.(MiPinningTrustManger.java:61) 57 | 06-07 16:15:38.543 27021-27073/mi.sslpinningdemo W/System.err: at mi.sslpinningdemo.OkHttpUtil.getSecPinningClient(OkHttpUtil.java:112) 58 | at mi.sslpinningdemo.OkHttpUtil.get(OkHttpUtil.java:62) 59 | at mi.sslpinningdemo.MainActivity$1$1.run(MainActivity.java:36) 60 | */ 61 | var X509Certificate = Java.use("java.security.cert.X509Certificate"); 62 | var TrustManager; 63 | try { 64 | TrustManager = Java.registerClass({ 65 | name: 'org.wooyun.TrustManager', 66 | implements: [X509TrustManager], 67 | methods: { 68 | checkClientTrusted: function(chain, authType) {}, 69 | checkServerTrusted: function(chain, authType) {}, 70 | getAcceptedIssuers: function() { 71 | // var certs = [X509Certificate.$new()]; 72 | // return certs; 73 | return []; 74 | } 75 | } 76 | }); 77 | } catch (e) { 78 | quiet_send("registerClass from X509TrustManager >>>>>>>> " + e.message); 79 | } 80 | 81 | 82 | 83 | 84 | 85 | // Prepare the TrustManagers array to pass to SSLContext.init() 86 | var TrustManagers = [TrustManager.$new()]; 87 | 88 | try { 89 | // Prepare a Empty SSLFactory 90 | var TLS_SSLContext = SSLContext.getInstance("TLS"); 91 | TLS_SSLContext.init(null, TrustManagers, null); 92 | var EmptySSLFactory = TLS_SSLContext.getSocketFactory(); 93 | } catch (e) { 94 | quiet_send(e.message); 95 | } 96 | 97 | send('Custom, Empty TrustManager ready'); 98 | 99 | // Get a handle on the init() on the SSLContext class 100 | var SSLContext_init = SSLContext.init.overload( 101 | '[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); 102 | 103 | // Override the init method, specifying our new TrustManager 104 | SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) { 105 | 106 | quiet_send('Overriding SSLContext.init() with the custom TrustManager'); 107 | 108 | SSLContext_init.call(this, null, TrustManagers, null); 109 | }; 110 | 111 | /*** okhttp3.x unpinning ***/ 112 | 113 | 114 | // Wrap the logic in a try/catch as not all applications will have 115 | // okhttp as part of the app. 116 | try { 117 | 118 | var CertificatePinner = Java.use('okhttp3.CertificatePinner'); 119 | 120 | quiet_send('OkHTTP 3.x Found'); 121 | 122 | CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() { 123 | 124 | quiet_send('OkHTTP 3.x check() called. Not throwing an exception.'); 125 | } 126 | 127 | } catch (err) { 128 | 129 | // If we dont have a ClassNotFoundException exception, raise the 130 | // problem encountered. 131 | if (err.message.indexOf('ClassNotFoundException') === 0) { 132 | 133 | throw new Error(err); 134 | } 135 | } 136 | 137 | // Appcelerator Titanium PinningTrustManager 138 | 139 | // Wrap the logic in a try/catch as not all applications will have 140 | // appcelerator as part of the app. 141 | try { 142 | 143 | var PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager'); 144 | 145 | send('Appcelerator Titanium Found'); 146 | 147 | PinningTrustManager.checkServerTrusted.implementation = function() { 148 | 149 | quiet_send('Appcelerator checkServerTrusted() called. Not throwing an exception.'); 150 | } 151 | 152 | } catch (err) { 153 | 154 | // If we dont have a ClassNotFoundException exception, raise the 155 | // problem encountered. 156 | if (err.message.indexOf('ClassNotFoundException') === 0) { 157 | 158 | throw new Error(err); 159 | } 160 | } 161 | 162 | /*** okhttp unpinning ***/ 163 | 164 | 165 | try { 166 | var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient"); 167 | OkHttpClient.setCertificatePinner.implementation = function(certificatePinner) { 168 | // do nothing 169 | quiet_send("OkHttpClient.setCertificatePinner Called!"); 170 | return this; 171 | }; 172 | 173 | // Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation) 174 | var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner"); 175 | CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(p0, p1) { 176 | // do nothing 177 | quiet_send("okhttp Called! [Certificate]"); 178 | return; 179 | }; 180 | CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(p0, p1) { 181 | // do nothing 182 | quiet_send("okhttp Called! [List]"); 183 | return; 184 | }; 185 | } catch (e) { 186 | quiet_send("com.squareup.okhttp not found"); 187 | } 188 | 189 | /*** WebView Hooks ***/ 190 | 191 | /* frameworks/base/core/java/android/webkit/WebViewClient.java */ 192 | /* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */ 193 | var WebViewClient = Java.use("android.webkit.WebViewClient"); 194 | 195 | WebViewClient.onReceivedSslError.implementation = function(webView, sslErrorHandler, sslError) { 196 | quiet_send("WebViewClient onReceivedSslError invoke"); 197 | //执行proceed方法 198 | sslErrorHandler.proceed(); 199 | return; 200 | }; 201 | 202 | WebViewClient.onReceivedError.overload('android.webkit.WebView', 'int', 'java.lang.String', 'java.lang.String').implementation = function(a, b, c, d) { 203 | quiet_send("WebViewClient onReceivedError invoked"); 204 | return; 205 | }; 206 | 207 | WebViewClient.onReceivedError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function() { 208 | quiet_send("WebViewClient onReceivedError invoked"); 209 | return; 210 | }; 211 | 212 | /*** JSSE Hooks ***/ 213 | 214 | /* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */ 215 | /* public final TrustManager[] getTrustManager() */ 216 | /* TrustManagerFactory.getTrustManagers maybe cause X509TrustManagerExtensions error */ 217 | // var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory"); 218 | // TrustManagerFactory.getTrustManagers.implementation = function(){ 219 | // quiet_send("TrustManagerFactory getTrustManagers invoked"); 220 | // return TrustManagers; 221 | // } 222 | 223 | var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); 224 | /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */ 225 | /* public void setDefaultHostnameVerifier(HostnameVerifier) */ 226 | HttpsURLConnection.setDefaultHostnameVerifier.implementation = function(hostnameVerifier) { 227 | quiet_send("HttpsURLConnection.setDefaultHostnameVerifier invoked"); 228 | return null; 229 | }; 230 | /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */ 231 | /* public void setSSLSocketFactory(SSLSocketFactory) */ 232 | HttpsURLConnection.setSSLSocketFactory.implementation = function(SSLSocketFactory) { 233 | quiet_send("HttpsURLConnection.setSSLSocketFactory invoked"); 234 | return null; 235 | }; 236 | /* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */ 237 | /* public void setHostnameVerifier(HostnameVerifier) */ 238 | HttpsURLConnection.setHostnameVerifier.implementation = function(hostnameVerifier) { 239 | quiet_send("HttpsURLConnection.setHostnameVerifier invoked"); 240 | return null; 241 | }; 242 | 243 | /*** Xutils3.x hooks ***/ 244 | //Implement a new HostnameVerifier 245 | var TrustHostnameVerifier; 246 | try { 247 | TrustHostnameVerifier = Java.registerClass({ 248 | name: 'org.wooyun.TrustHostnameVerifier', 249 | implements: [HostnameVerifier], 250 | method: { 251 | verify: function(hostname, session) { 252 | return true; 253 | } 254 | } 255 | }); 256 | 257 | } catch (e) { 258 | //java.lang.ClassNotFoundException: Didn't find class "org.wooyun.TrustHostnameVerifier" 259 | quiet_send("registerClass from hostnameVerifier >>>>>>>> " + e.message); 260 | } 261 | 262 | try { 263 | var RequestParams = Java.use('org.xutils.http.RequestParams'); 264 | RequestParams.setSslSocketFactory.implementation = function(sslSocketFactory) { 265 | sslSocketFactory = EmptySSLFactory; 266 | return null; 267 | } 268 | 269 | RequestParams.setHostnameVerifier.implementation = function(hostnameVerifier) { 270 | hostnameVerifier = TrustHostnameVerifier.$new(); 271 | return null; 272 | } 273 | 274 | } catch (e) { 275 | quiet_send("Xutils hooks not Found"); 276 | } 277 | 278 | /*** httpclientandroidlib Hooks ***/ 279 | try { 280 | var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); 281 | AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String', '[Ljava.lang.String', 'boolean').implementation = function() { 282 | quiet_send("httpclientandroidlib Hooks"); 283 | return null; 284 | } 285 | } catch (e) { 286 | quiet_send("httpclientandroidlib Hooks not found"); 287 | } 288 | 289 | /*** 290 | android 7.0+ network_security_config TrustManagerImpl hook 291 | apache httpclient partly 292 | ***/ 293 | var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl"); 294 | // try { 295 | // var Arrays = Java.use("java.util.Arrays"); 296 | // //apache http client pinning maybe baypass 297 | // //https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#471 298 | // TrustManagerImpl.checkTrusted.implementation = function (chain, authType, session, parameters, authType) { 299 | // quiet_send("TrustManagerImpl checkTrusted called"); 300 | // //Generics currently result in java.lang.Object 301 | // return Arrays.asList(chain); 302 | // } 303 | // 304 | // } catch (e) { 305 | // quiet_send("TrustManagerImpl checkTrusted nout found"); 306 | // } 307 | 308 | try { 309 | // Android 7+ TrustManagerImpl 310 | TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) { 311 | quiet_send("TrustManagerImpl verifyChain called"); 312 | // Skip all the logic and just return the chain again :P 313 | //https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/november/bypassing-androids-network-security-configuration/ 314 | // https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650 315 | return untrustedChain; 316 | } 317 | } catch (e) { 318 | quiet_send("TrustManagerImpl verifyChain nout found below 7.0"); 319 | } 320 | // OpenSSLSocketImpl 321 | try { 322 | var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl'); 323 | OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, authMethod) { 324 | quiet_send('OpenSSLSocketImpl.verifyCertificateChain'); 325 | } 326 | 327 | quiet_send('OpenSSLSocketImpl pinning') 328 | } catch (err) { 329 | quiet_send('OpenSSLSocketImpl pinner not found'); 330 | } 331 | // Trustkit 332 | try { 333 | var Activity = Java.use("com.datatheorem.android.trustkit.pinning.OkHostnameVerifier"); 334 | Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(str) { 335 | quiet_send('Trustkit.verify1: ' + str); 336 | return true; 337 | }; 338 | Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(str) { 339 | quiet_send('Trustkit.verify2: ' + str); 340 | return true; 341 | }; 342 | 343 | quiet_send('Trustkit pinning') 344 | } catch (err) { 345 | quiet_send('Trustkit pinner not found') 346 | } 347 | 348 | try { 349 | //cronet pinner hook 350 | //weibo don't invoke 351 | 352 | var netBuilder = Java.use("org.chromium.net.CronetEngine$Builder"); 353 | 354 | //https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/CronetEngine.Builder.html#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean) 355 | netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.implementation = function(arg) { 356 | 357 | //weibo not invoke 358 | console.log("Enables or disables public key pinning bypass for local trust anchors = " + arg); 359 | 360 | //true to enable the bypass, false to disable. 361 | var ret = netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this, true); 362 | return ret; 363 | }; 364 | 365 | netBuilder.addPublicKeyPins.implementation = function(hostName, pinsSha256, includeSubdomains, expirationDate) { 366 | console.log("cronet addPublicKeyPins hostName = " + hostName); 367 | 368 | //var ret = netBuilder.addPublicKeyPins.call(this,hostName, pinsSha256,includeSubdomains, expirationDate); 369 | //this 是调用 addPublicKeyPins 前的对象吗? Yes,CronetEngine.Builder 370 | return this; 371 | }; 372 | 373 | } catch (err) { 374 | console.log('[-] Cronet pinner not found') 375 | } 376 | }); 377 | 378 | }; -------------------------------------------------------------------------------- /utils/android/jni/jni_env.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "reserved0", 4 | "args": [ 5 | 6 | ], 7 | "ret": "void" 8 | }, 9 | { 10 | "name": "reserved1", 11 | "args": [ 12 | 13 | ], 14 | "ret": "void" 15 | }, 16 | { 17 | "name": "reserved2", 18 | "args": [ 19 | 20 | ], 21 | "ret": "void" 22 | }, 23 | { 24 | "name": "reserved3", 25 | "args": [ 26 | 27 | ], 28 | "ret": "void" 29 | }, 30 | { 31 | "name": "GetVersion", 32 | "args": [ 33 | "JNIEnv*" 34 | ], 35 | "ret": "jint" 36 | }, 37 | { 38 | "name": "DefineClass", 39 | "args": [ 40 | "JNIEnv*", 41 | "char*", 42 | "jobject", 43 | "jbyte*", 44 | "jsize" 45 | ], 46 | "ret": "jclass" 47 | }, 48 | { 49 | "name": "FindClass", 50 | "args": [ 51 | "JNIEnv*", 52 | "char*" 53 | ], 54 | "ret": "jclass" 55 | }, 56 | { 57 | "name": "FromReflectedMethod", 58 | "args": [ 59 | "JNIEnv*", 60 | "jobject" 61 | ], 62 | "ret": "jmethodID" 63 | }, 64 | { 65 | "name": "FromReflectedField", 66 | "args": [ 67 | "JNIEnv*", 68 | "jobject" 69 | ], 70 | "ret": "jfieldID" 71 | }, 72 | { 73 | "name": "ToReflectedMethod", 74 | "args": [ 75 | "JNIEnv*", 76 | "jclass", 77 | "jmethodID", 78 | "jboolean" 79 | ], 80 | "ret": "jobject" 81 | }, 82 | { 83 | "name": "GetSuperclass", 84 | "args": [ 85 | "JNIEnv*", 86 | "jclass" 87 | ], 88 | "ret": "jclass" 89 | }, 90 | { 91 | "name": "IsAssignableFrom", 92 | "args": [ 93 | "JNIEnv*", 94 | "jclass", 95 | "jclass" 96 | ], 97 | "ret": "jboolean" 98 | }, 99 | { 100 | "name": "ToReflectedField", 101 | "args": [ 102 | "JNIEnv*", 103 | "jclass", 104 | "jfieldID", 105 | "jboolean" 106 | ], 107 | "ret": "jobject" 108 | }, 109 | { 110 | "name": "Throw", 111 | "args": [ 112 | "JNIEnv*", 113 | "jthrowable" 114 | ], 115 | "ret": "jint" 116 | }, 117 | { 118 | "name": "ThrowNew", 119 | "args": [ 120 | "JNIEnv*", 121 | "jclass", 122 | "char*" 123 | ], 124 | "ret": "jint" 125 | }, 126 | { 127 | "name": "ExceptionOccurred", 128 | "args": [ 129 | "JNIEnv*" 130 | ], 131 | "ret": "jthrowable" 132 | }, 133 | { 134 | "name": "ExceptionDescribe", 135 | "args": [ 136 | "JNIEnv*" 137 | ], 138 | "ret": "void" 139 | }, 140 | { 141 | "name": "ExceptionClear", 142 | "args": [ 143 | "JNIEnv*" 144 | ], 145 | "ret": "void" 146 | }, 147 | { 148 | "name": "FatalError", 149 | "args": [ 150 | "JNIEnv*", 151 | "char*" 152 | ], 153 | "ret": "void" 154 | }, 155 | { 156 | "name": "PushLocalFrame", 157 | "args": [ 158 | "JNIEnv*", 159 | "jint" 160 | ], 161 | "ret": "jint" 162 | }, 163 | { 164 | "name": "PopLocalFrame", 165 | "args": [ 166 | "JNIEnv*", 167 | "jobject" 168 | ], 169 | "ret": "jobject" 170 | }, 171 | { 172 | "name": "NewGlobalRef", 173 | "args": [ 174 | "JNIEnv*", 175 | "jobject" 176 | ], 177 | "ret": "jobject" 178 | }, 179 | { 180 | "name": "DeleteGlobalRef", 181 | "args": [ 182 | "JNIEnv*", 183 | "jobject" 184 | ], 185 | "ret": "void" 186 | }, 187 | { 188 | "name": "DeleteLocalRef", 189 | "args": [ 190 | "JNIEnv*", 191 | "jobject" 192 | ], 193 | "ret": "void" 194 | }, 195 | { 196 | "name": "IsSameObject", 197 | "args": [ 198 | "JNIEnv*", 199 | "jobject", 200 | "jobject" 201 | ], 202 | "ret": "jboolean" 203 | }, 204 | { 205 | "name": "NewLocalRef", 206 | "args": [ 207 | "JNIEnv*", 208 | "jobject" 209 | ], 210 | "ret": "jobject" 211 | }, 212 | { 213 | "name": "EnsureLocalCapacity", 214 | "args": [ 215 | "JNIEnv*", 216 | "jint" 217 | ], 218 | "ret": "jint" 219 | }, 220 | { 221 | "name": "AllocObject", 222 | "args": [ 223 | "JNIEnv*", 224 | "jclass" 225 | ], 226 | "ret": "jobject" 227 | }, 228 | { 229 | "name": "NewObject", 230 | "args": [ 231 | "JNIEnv*", 232 | "jclass", 233 | "jmethodID", 234 | "..." 235 | ], 236 | "ret": "jobject" 237 | }, 238 | { 239 | "name": "NewObjectV", 240 | "args": [ 241 | "JNIEnv*", 242 | "jclass", 243 | "jmethodID", 244 | "va_list" 245 | ], 246 | "ret": "jobject" 247 | }, 248 | { 249 | "name": "NewObjectA", 250 | "args": [ 251 | "JNIEnv*", 252 | "jclass", 253 | "jmethodID", 254 | "jvalue*" 255 | ], 256 | "ret": "jobject" 257 | }, 258 | { 259 | "name": "GetObjectClass", 260 | "args": [ 261 | "JNIEnv*", 262 | "jobject" 263 | ], 264 | "ret": "jclass" 265 | }, 266 | { 267 | "name": "IsInstanceOf", 268 | "args": [ 269 | "JNIEnv*", 270 | "jobject", 271 | "jclass" 272 | ], 273 | "ret": "jboolean" 274 | }, 275 | { 276 | "name": "GetMethodID", 277 | "args": [ 278 | "JNIEnv*", 279 | "jclass", 280 | "char*", 281 | "char*" 282 | ], 283 | "ret": "jmethodID" 284 | }, 285 | { 286 | "name": "CallObjectMethod", 287 | "args": [ 288 | "JNIEnv*", 289 | "jobject", 290 | "jmethodID", 291 | "..." 292 | ], 293 | "ret": "jobject" 294 | }, 295 | { 296 | "name": "CallObjectMethodV", 297 | "args": [ 298 | "JNIEnv*", 299 | "jobject", 300 | "jmethodID", 301 | "va_list" 302 | ], 303 | "ret": "jobject" 304 | }, 305 | { 306 | "name": "CallObjectMethodA", 307 | "args": [ 308 | "JNIEnv*", 309 | "jobject", 310 | "jmethodID", 311 | "jvalue*" 312 | ], 313 | "ret": "jobject" 314 | }, 315 | { 316 | "name": "CallBooleanMethod", 317 | "args": [ 318 | "JNIEnv*", 319 | "jobject", 320 | "jmethodID", 321 | "..." 322 | ], 323 | "ret": "jboolean" 324 | }, 325 | { 326 | "name": "CallBooleanMethodV", 327 | "args": [ 328 | "JNIEnv*", 329 | "jobject", 330 | "jmethodID", 331 | "va_list" 332 | ], 333 | "ret": "jboolean" 334 | }, 335 | { 336 | "name": "CallBooleanMethodA", 337 | "args": [ 338 | "JNIEnv*", 339 | "jobject", 340 | "jmethodID", 341 | "jvalue*" 342 | ], 343 | "ret": "jboolean" 344 | }, 345 | { 346 | "name": "CallByteMethod", 347 | "args": [ 348 | "JNIEnv*", 349 | "jobject", 350 | "jmethodID", 351 | "..." 352 | ], 353 | "ret": "jbyte" 354 | }, 355 | { 356 | "name": "CallByteMethodV", 357 | "args": [ 358 | "JNIEnv*", 359 | "jobject", 360 | "jmethodID", 361 | "va_list" 362 | ], 363 | "ret": "jbyte" 364 | }, 365 | { 366 | "name": "CallByteMethodA", 367 | "args": [ 368 | "JNIEnv*", 369 | "jobject", 370 | "jmethodID", 371 | "jvalue*" 372 | ], 373 | "ret": "jbyte" 374 | }, 375 | { 376 | "name": "CallCharMethod", 377 | "args": [ 378 | "JNIEnv*", 379 | "jobject", 380 | "jmethodID", 381 | "..." 382 | ], 383 | "ret": "jchar" 384 | }, 385 | { 386 | "name": "CallCharMethodV", 387 | "args": [ 388 | "JNIEnv*", 389 | "jobject", 390 | "jmethodID", 391 | "va_list" 392 | ], 393 | "ret": "jchar" 394 | }, 395 | { 396 | "name": "CallCharMethodA", 397 | "args": [ 398 | "JNIEnv*", 399 | "jobject", 400 | "jmethodID", 401 | "jvalue*" 402 | ], 403 | "ret": "jchar" 404 | }, 405 | { 406 | "name": "CallShortMethod", 407 | "args": [ 408 | "JNIEnv*", 409 | "jobject", 410 | "jmethodID", 411 | "..." 412 | ], 413 | "ret": "jshort" 414 | }, 415 | { 416 | "name": "CallShortMethodV", 417 | "args": [ 418 | "JNIEnv*", 419 | "jobject", 420 | "jmethodID", 421 | "va_list" 422 | ], 423 | "ret": "jshort" 424 | }, 425 | { 426 | "name": "CallShortMethodA", 427 | "args": [ 428 | "JNIEnv*", 429 | "jobject", 430 | "jmethodID", 431 | "jvalue*" 432 | ], 433 | "ret": "jshort" 434 | }, 435 | { 436 | "name": "CallIntMethod", 437 | "args": [ 438 | "JNIEnv*", 439 | "jobject", 440 | "jmethodID", 441 | "..." 442 | ], 443 | "ret": "jint" 444 | }, 445 | { 446 | "name": "CallIntMethodV", 447 | "args": [ 448 | "JNIEnv*", 449 | "jobject", 450 | "jmethodID", 451 | "va_list" 452 | ], 453 | "ret": "jint" 454 | }, 455 | { 456 | "name": "CallIntMethodA", 457 | "args": [ 458 | "JNIEnv*", 459 | "jobject", 460 | "jmethodID", 461 | "jvalue*" 462 | ], 463 | "ret": "jint" 464 | }, 465 | { 466 | "name": "CallLongMethod", 467 | "args": [ 468 | "JNIEnv*", 469 | "jobject", 470 | "jmethodID", 471 | "..." 472 | ], 473 | "ret": "jlong" 474 | }, 475 | { 476 | "name": "CallLongMethodV", 477 | "args": [ 478 | "JNIEnv*", 479 | "jobject", 480 | "jmethodID", 481 | "va_list" 482 | ], 483 | "ret": "jlong" 484 | }, 485 | { 486 | "name": "CallLongMethodA", 487 | "args": [ 488 | "JNIEnv*", 489 | "jobject", 490 | "jmethodID", 491 | "jvalue*" 492 | ], 493 | "ret": "jlong" 494 | }, 495 | { 496 | "name": "CallFloatMethod", 497 | "args": [ 498 | "JNIEnv*", 499 | "jobject", 500 | "jmethodID", 501 | "..." 502 | ], 503 | "ret": "jfloat" 504 | }, 505 | { 506 | "name": "CallFloatMethodV", 507 | "args": [ 508 | "JNIEnv*", 509 | "jobject", 510 | "jmethodID", 511 | "va_list" 512 | ], 513 | "ret": "jfloat" 514 | }, 515 | { 516 | "name": "CallFloatMethodA", 517 | "args": [ 518 | "JNIEnv*", 519 | "jobject", 520 | "jmethodID", 521 | "jvalue*" 522 | ], 523 | "ret": "jfloat" 524 | }, 525 | { 526 | "name": "CallDoubleMethod", 527 | "args": [ 528 | "JNIEnv*", 529 | "jobject", 530 | "jmethodID", 531 | "..." 532 | ], 533 | "ret": "jdouble" 534 | }, 535 | { 536 | "name": "CallDoubleMethodV", 537 | "args": [ 538 | "JNIEnv*", 539 | "jobject", 540 | "jmethodID", 541 | "va_list" 542 | ], 543 | "ret": "jdouble" 544 | }, 545 | { 546 | "name": "CallDoubleMethodA", 547 | "args": [ 548 | "JNIEnv*", 549 | "jobject", 550 | "jmethodID", 551 | "jvalue*" 552 | ], 553 | "ret": "jdouble" 554 | }, 555 | { 556 | "name": "CallVoidMethod", 557 | "args": [ 558 | "JNIEnv*", 559 | "jobject", 560 | "jmethodID", 561 | "..." 562 | ], 563 | "ret": "void" 564 | }, 565 | { 566 | "name": "CallVoidMethodV", 567 | "args": [ 568 | "JNIEnv*", 569 | "jobject", 570 | "jmethodID", 571 | "va_list" 572 | ], 573 | "ret": "void" 574 | }, 575 | { 576 | "name": "CallVoidMethodA", 577 | "args": [ 578 | "JNIEnv*", 579 | "jobject", 580 | "jmethodID", 581 | "jvalue*" 582 | ], 583 | "ret": "void" 584 | }, 585 | { 586 | "name": "CallNonvirtualObjectMethod", 587 | "args": [ 588 | "JNIEnv*", 589 | "jobject", 590 | "jclass", 591 | "jmethodID", 592 | "..." 593 | ], 594 | "ret": "jobject" 595 | }, 596 | { 597 | "name": "CallNonvirtualObjectMethodV", 598 | "args": [ 599 | "JNIEnv*", 600 | "jobject", 601 | "jclass", 602 | "jmethodID", 603 | "va_list" 604 | ], 605 | "ret": "jobject" 606 | }, 607 | { 608 | "name": "CallNonvirtualObjectMethodA", 609 | "args": [ 610 | "JNIEnv*", 611 | "jobject", 612 | "jclass", 613 | "jmethodID", 614 | "jvalue*" 615 | ], 616 | "ret": "jobject" 617 | }, 618 | { 619 | "name": "CallNonvirtualBooleanMethod", 620 | "args": [ 621 | "JNIEnv*", 622 | "jobject", 623 | "jclass", 624 | "jmethodID", 625 | "..." 626 | ], 627 | "ret": "jboolean" 628 | }, 629 | { 630 | "name": "CallNonvirtualBooleanMethodV", 631 | "args": [ 632 | "JNIEnv*", 633 | "jobject", 634 | "jclass", 635 | "jmethodID", 636 | "va_list" 637 | ], 638 | "ret": "jboolean" 639 | }, 640 | { 641 | "name": "CallNonvirtualBooleanMethodA", 642 | "args": [ 643 | "JNIEnv*", 644 | "jobject", 645 | "jclass", 646 | "jmethodID", 647 | "jvalue*" 648 | ], 649 | "ret": "jboolean" 650 | }, 651 | { 652 | "name": "CallNonvirtualByteMethod", 653 | "args": [ 654 | "JNIEnv*", 655 | "jobject", 656 | "jclass", 657 | "jmethodID", 658 | "..." 659 | ], 660 | "ret": "jbyte" 661 | }, 662 | { 663 | "name": "CallNonvirtualByteMethodV", 664 | "args": [ 665 | "JNIEnv*", 666 | "jobject", 667 | "jclass", 668 | "jmethodID", 669 | "va_list" 670 | ], 671 | "ret": "jbyte" 672 | }, 673 | { 674 | "name": "CallNonvirtualByteMethodA", 675 | "args": [ 676 | "JNIEnv*", 677 | "jobject", 678 | "jclass", 679 | "jmethodID", 680 | "jvalue*" 681 | ], 682 | "ret": "jbyte" 683 | }, 684 | { 685 | "name": "CallNonvirtualCharMethod", 686 | "args": [ 687 | "JNIEnv*", 688 | "jobject", 689 | "jclass", 690 | "jmethodID", 691 | "..." 692 | ], 693 | "ret": "jchar" 694 | }, 695 | { 696 | "name": "CallNonvirtualCharMethodV", 697 | "args": [ 698 | "JNIEnv*", 699 | "jobject", 700 | "jclass", 701 | "jmethodID", 702 | "va_list" 703 | ], 704 | "ret": "jchar" 705 | }, 706 | { 707 | "name": "CallNonvirtualCharMethodA", 708 | "args": [ 709 | "JNIEnv*", 710 | "jobject", 711 | "jclass", 712 | "jmethodID", 713 | "jvalue*" 714 | ], 715 | "ret": "jchar" 716 | }, 717 | { 718 | "name": "CallNonvirtualShortMethod", 719 | "args": [ 720 | "JNIEnv*", 721 | "jobject", 722 | "jclass", 723 | "jmethodID", 724 | "..." 725 | ], 726 | "ret": "jshort" 727 | }, 728 | { 729 | "name": "CallNonvirtualShortMethodV", 730 | "args": [ 731 | "JNIEnv*", 732 | "jobject", 733 | "jclass", 734 | "jmethodID", 735 | "va_list" 736 | ], 737 | "ret": "jshort" 738 | }, 739 | { 740 | "name": "CallNonvirtualShortMethodA", 741 | "args": [ 742 | "JNIEnv*", 743 | "jobject", 744 | "jclass", 745 | "jmethodID", 746 | "jvalue*" 747 | ], 748 | "ret": "jshort" 749 | }, 750 | { 751 | "name": "CallNonvirtualIntMethod", 752 | "args": [ 753 | "JNIEnv*", 754 | "jobject", 755 | "jclass", 756 | "jmethodID", 757 | "..." 758 | ], 759 | "ret": "jint" 760 | }, 761 | { 762 | "name": "CallNonvirtualIntMethodV", 763 | "args": [ 764 | "JNIEnv*", 765 | "jobject", 766 | "jclass", 767 | "jmethodID", 768 | "va_list" 769 | ], 770 | "ret": "jint" 771 | }, 772 | { 773 | "name": "CallNonvirtualIntMethodA", 774 | "args": [ 775 | "JNIEnv*", 776 | "jobject", 777 | "jclass", 778 | "jmethodID", 779 | "jvalue*" 780 | ], 781 | "ret": "jint" 782 | }, 783 | { 784 | "name": "CallNonvirtualLongMethod", 785 | "args": [ 786 | "JNIEnv*", 787 | "jobject", 788 | "jclass", 789 | "jmethodID", 790 | "..." 791 | ], 792 | "ret": "jlong" 793 | }, 794 | { 795 | "name": "CallNonvirtualLongMethodV", 796 | "args": [ 797 | "JNIEnv*", 798 | "jobject", 799 | "jclass", 800 | "jmethodID", 801 | "va_list" 802 | ], 803 | "ret": "jlong" 804 | }, 805 | { 806 | "name": "CallNonvirtualLongMethodA", 807 | "args": [ 808 | "JNIEnv*", 809 | "jobject", 810 | "jclass", 811 | "jmethodID", 812 | "jvalue*" 813 | ], 814 | "ret": "jlong" 815 | }, 816 | { 817 | "name": "CallNonvirtualFloatMethod", 818 | "args": [ 819 | "JNIEnv*", 820 | "jobject", 821 | "jclass", 822 | "jmethodID", 823 | "..." 824 | ], 825 | "ret": "jfloat" 826 | }, 827 | { 828 | "name": "CallNonvirtualFloatMethodV", 829 | "args": [ 830 | "JNIEnv*", 831 | "jobject", 832 | "jclass", 833 | "jmethodID", 834 | "va_list" 835 | ], 836 | "ret": "jfloat" 837 | }, 838 | { 839 | "name": "CallNonvirtualFloatMethodA", 840 | "args": [ 841 | "JNIEnv*", 842 | "jobject", 843 | "jclass", 844 | "jmethodID", 845 | "jvalue*" 846 | ], 847 | "ret": "jfloat" 848 | }, 849 | { 850 | "name": "CallNonvirtualDoubleMethod", 851 | "args": [ 852 | "JNIEnv*", 853 | "jobject", 854 | "jclass", 855 | "jmethodID", 856 | "..." 857 | ], 858 | "ret": "jdouble" 859 | }, 860 | { 861 | "name": "CallNonvirtualDoubleMethodV", 862 | "args": [ 863 | "JNIEnv*", 864 | "jobject", 865 | "jclass", 866 | "jmethodID", 867 | "va_list" 868 | ], 869 | "ret": "jdouble" 870 | }, 871 | { 872 | "name": "CallNonvirtualDoubleMethodA", 873 | "args": [ 874 | "JNIEnv*", 875 | "jobject", 876 | "jclass", 877 | "jmethodID", 878 | "jvalue*" 879 | ], 880 | "ret": "jdouble" 881 | }, 882 | { 883 | "name": "CallNonvirtualVoidMethod", 884 | "args": [ 885 | "JNIEnv*", 886 | "jobject", 887 | "jclass", 888 | "jmethodID", 889 | "..." 890 | ], 891 | "ret": "void" 892 | }, 893 | { 894 | "name": "CallNonvirtualVoidMethodV", 895 | "args": [ 896 | "JNIEnv*", 897 | "jobject", 898 | "jclass", 899 | "jmethodID", 900 | "va_list" 901 | ], 902 | "ret": "void" 903 | }, 904 | { 905 | "name": "CallNonvirtualVoidMethodA", 906 | "args": [ 907 | "JNIEnv*", 908 | "jobject", 909 | "jclass", 910 | "jmethodID", 911 | "jvalue*" 912 | ], 913 | "ret": "void" 914 | }, 915 | { 916 | "name": "GetFieldID", 917 | "args": [ 918 | "JNIEnv*", 919 | "jclass", 920 | "char*", 921 | "char*" 922 | ], 923 | "ret": "jfieldID" 924 | }, 925 | { 926 | "name": "GetObjectField", 927 | "args": [ 928 | "JNIEnv*", 929 | "jobject", 930 | "jfieldID" 931 | ], 932 | "ret": "jobject" 933 | }, 934 | { 935 | "name": "GetBooleanField", 936 | "args": [ 937 | "JNIEnv*", 938 | "jobject", 939 | "jfieldID" 940 | ], 941 | "ret": "jboolean" 942 | }, 943 | { 944 | "name": "GetByteField", 945 | "args": [ 946 | "JNIEnv*", 947 | "jobject", 948 | "jfieldID" 949 | ], 950 | "ret": "jbyte" 951 | }, 952 | { 953 | "name": "GetCharField", 954 | "args": [ 955 | "JNIEnv*", 956 | "jobject", 957 | "jfieldID" 958 | ], 959 | "ret": "jchar" 960 | }, 961 | { 962 | "name": "GetShortField", 963 | "args": [ 964 | "JNIEnv*", 965 | "jobject", 966 | "jfieldID" 967 | ], 968 | "ret": "jshort" 969 | }, 970 | { 971 | "name": "GetIntField", 972 | "args": [ 973 | "JNIEnv*", 974 | "jobject", 975 | "jfieldID" 976 | ], 977 | "ret": "jint" 978 | }, 979 | { 980 | "name": "GetLongField", 981 | "args": [ 982 | "JNIEnv*", 983 | "jobject", 984 | "jfieldID" 985 | ], 986 | "ret": "jlong" 987 | }, 988 | { 989 | "name": "GetFloatField", 990 | "args": [ 991 | "JNIEnv*", 992 | "jobject", 993 | "jfieldID" 994 | ], 995 | "ret": "jfloat" 996 | }, 997 | { 998 | "name": "GetDoubleField", 999 | "args": [ 1000 | "JNIEnv*", 1001 | "jobject", 1002 | "jfieldID" 1003 | ], 1004 | "ret": "jdouble" 1005 | }, 1006 | { 1007 | "name": "SetObjectField", 1008 | "args": [ 1009 | "JNIEnv*", 1010 | "jobject", 1011 | "jfieldID", 1012 | "jobject" 1013 | ], 1014 | "ret": "void" 1015 | }, 1016 | { 1017 | "name": "SetBooleanField", 1018 | "args": [ 1019 | "JNIEnv*", 1020 | "jobject", 1021 | "jfieldID", 1022 | "jboolean" 1023 | ], 1024 | "ret": "void" 1025 | }, 1026 | { 1027 | "name": "SetByteField", 1028 | "args": [ 1029 | "JNIEnv*", 1030 | "jobject", 1031 | "jfieldID", 1032 | "jbyte" 1033 | ], 1034 | "ret": "void" 1035 | }, 1036 | { 1037 | "name": "SetCharField", 1038 | "args": [ 1039 | "JNIEnv*", 1040 | "jobject", 1041 | "jfieldID", 1042 | "jchar" 1043 | ], 1044 | "ret": "void" 1045 | }, 1046 | { 1047 | "name": "SetShortField", 1048 | "args": [ 1049 | "JNIEnv*", 1050 | "jobject", 1051 | "jfieldID", 1052 | "jshort" 1053 | ], 1054 | "ret": "void" 1055 | }, 1056 | { 1057 | "name": "SetIntField", 1058 | "args": [ 1059 | "JNIEnv*", 1060 | "jobject", 1061 | "jfieldID", 1062 | "jint" 1063 | ], 1064 | "ret": "void" 1065 | }, 1066 | { 1067 | "name": "SetLongField", 1068 | "args": [ 1069 | "JNIEnv*", 1070 | "jobject", 1071 | "jfieldID", 1072 | "jlong" 1073 | ], 1074 | "ret": "void" 1075 | }, 1076 | { 1077 | "name": "SetFloatField", 1078 | "args": [ 1079 | "JNIEnv*", 1080 | "jobject", 1081 | "jfieldID", 1082 | "jfloat" 1083 | ], 1084 | "ret": "void" 1085 | }, 1086 | { 1087 | "name": "SetDoubleField", 1088 | "args": [ 1089 | "JNIEnv*", 1090 | "jobject", 1091 | "jfieldID", 1092 | "jdouble" 1093 | ], 1094 | "ret": "void" 1095 | }, 1096 | { 1097 | "name": "GetStaticMethodID", 1098 | "args": [ 1099 | "JNIEnv*", 1100 | "jclass", 1101 | "char*", 1102 | "char*" 1103 | ], 1104 | "ret": "jmethodID" 1105 | }, 1106 | { 1107 | "name": "CallStaticObjectMethod", 1108 | "args": [ 1109 | "JNIEnv*", 1110 | "jclass", 1111 | "jmethodID", 1112 | "..." 1113 | ], 1114 | "ret": "jobject" 1115 | }, 1116 | { 1117 | "name": "CallStaticObjectMethodV", 1118 | "args": [ 1119 | "JNIEnv*", 1120 | "jclass", 1121 | "jmethodID", 1122 | "va_list" 1123 | ], 1124 | "ret": "jobject" 1125 | }, 1126 | { 1127 | "name": "CallStaticObjectMethodA", 1128 | "args": [ 1129 | "JNIEnv*", 1130 | "jclass", 1131 | "jmethodID", 1132 | "jvalue*" 1133 | ], 1134 | "ret": "jobject" 1135 | }, 1136 | { 1137 | "name": "CallStaticBooleanMethod", 1138 | "args": [ 1139 | "JNIEnv*", 1140 | "jclass", 1141 | "jmethodID", 1142 | "..." 1143 | ], 1144 | "ret": "jboolean" 1145 | }, 1146 | { 1147 | "name": "CallStaticBooleanMethodV", 1148 | "args": [ 1149 | "JNIEnv*", 1150 | "jclass", 1151 | "jmethodID", 1152 | "va_list" 1153 | ], 1154 | "ret": "jboolean" 1155 | }, 1156 | { 1157 | "name": "CallStaticBooleanMethodA", 1158 | "args": [ 1159 | "JNIEnv*", 1160 | "jclass", 1161 | "jmethodID", 1162 | "jvalue*" 1163 | ], 1164 | "ret": "jboolean" 1165 | }, 1166 | { 1167 | "name": "CallStaticByteMethod", 1168 | "args": [ 1169 | "JNIEnv*", 1170 | "jclass", 1171 | "jmethodID", 1172 | "..." 1173 | ], 1174 | "ret": "jbyte" 1175 | }, 1176 | { 1177 | "name": "CallStaticByteMethodV", 1178 | "args": [ 1179 | "JNIEnv*", 1180 | "jclass", 1181 | "jmethodID", 1182 | "va_list" 1183 | ], 1184 | "ret": "jbyte" 1185 | }, 1186 | { 1187 | "name": "CallStaticByteMethodA", 1188 | "args": [ 1189 | "JNIEnv*", 1190 | "jclass", 1191 | "jmethodID", 1192 | "jvalue*" 1193 | ], 1194 | "ret": "jbyte" 1195 | }, 1196 | { 1197 | "name": "CallStaticCharMethod", 1198 | "args": [ 1199 | "JNIEnv*", 1200 | "jclass", 1201 | "jmethodID", 1202 | "..." 1203 | ], 1204 | "ret": "jchar" 1205 | }, 1206 | { 1207 | "name": "CallStaticCharMethodV", 1208 | "args": [ 1209 | "JNIEnv*", 1210 | "jclass", 1211 | "jmethodID", 1212 | "va_list" 1213 | ], 1214 | "ret": "jchar" 1215 | }, 1216 | { 1217 | "name": "CallStaticCharMethodA", 1218 | "args": [ 1219 | "JNIEnv*", 1220 | "jclass", 1221 | "jmethodID", 1222 | "jvalue*" 1223 | ], 1224 | "ret": "jchar" 1225 | }, 1226 | { 1227 | "name": "CallStaticShortMethod", 1228 | "args": [ 1229 | "JNIEnv*", 1230 | "jclass", 1231 | "jmethodID", 1232 | "..." 1233 | ], 1234 | "ret": "jshort" 1235 | }, 1236 | { 1237 | "name": "CallStaticShortMethodV", 1238 | "args": [ 1239 | "JNIEnv*", 1240 | "jclass", 1241 | "jmethodID", 1242 | "va_list" 1243 | ], 1244 | "ret": "jshort" 1245 | }, 1246 | { 1247 | "name": "CallStaticShortMethodA", 1248 | "args": [ 1249 | "JNIEnv*", 1250 | "jclass", 1251 | "jmethodID", 1252 | "jvalue*" 1253 | ], 1254 | "ret": "jshort" 1255 | }, 1256 | { 1257 | "name": "CallStaticIntMethod", 1258 | "args": [ 1259 | "JNIEnv*", 1260 | "jclass", 1261 | "jmethodID", 1262 | "..." 1263 | ], 1264 | "ret": "jint" 1265 | }, 1266 | { 1267 | "name": "CallStaticIntMethodV", 1268 | "args": [ 1269 | "JNIEnv*", 1270 | "jclass", 1271 | "jmethodID", 1272 | "va_list" 1273 | ], 1274 | "ret": "jint" 1275 | }, 1276 | { 1277 | "name": "CallStaticIntMethodA", 1278 | "args": [ 1279 | "JNIEnv*", 1280 | "jclass", 1281 | "jmethodID", 1282 | "jvalue*" 1283 | ], 1284 | "ret": "jint" 1285 | }, 1286 | { 1287 | "name": "CallStaticLongMethod", 1288 | "args": [ 1289 | "JNIEnv*", 1290 | "jclass", 1291 | "jmethodID", 1292 | "..." 1293 | ], 1294 | "ret": "jlong" 1295 | }, 1296 | { 1297 | "name": "CallStaticLongMethodV", 1298 | "args": [ 1299 | "JNIEnv*", 1300 | "jclass", 1301 | "jmethodID", 1302 | "va_list" 1303 | ], 1304 | "ret": "jlong" 1305 | }, 1306 | { 1307 | "name": "CallStaticLongMethodA", 1308 | "args": [ 1309 | "JNIEnv*", 1310 | "jclass", 1311 | "jmethodID", 1312 | "jvalue*" 1313 | ], 1314 | "ret": "jlong" 1315 | }, 1316 | { 1317 | "name": "CallStaticFloatMethod", 1318 | "args": [ 1319 | "JNIEnv*", 1320 | "jclass", 1321 | "jmethodID", 1322 | "..." 1323 | ], 1324 | "ret": "jfloat" 1325 | }, 1326 | { 1327 | "name": "CallStaticFloatMethodV", 1328 | "args": [ 1329 | "JNIEnv*", 1330 | "jclass", 1331 | "jmethodID", 1332 | "va_list" 1333 | ], 1334 | "ret": "jfloat" 1335 | }, 1336 | { 1337 | "name": "CallStaticFloatMethodA", 1338 | "args": [ 1339 | "JNIEnv*", 1340 | "jclass", 1341 | "jmethodID", 1342 | "jvalue*" 1343 | ], 1344 | "ret": "jfloat" 1345 | }, 1346 | { 1347 | "name": "CallStaticDoubleMethod", 1348 | "args": [ 1349 | "JNIEnv*", 1350 | "jclass", 1351 | "jmethodID", 1352 | "..." 1353 | ], 1354 | "ret": "jdouble" 1355 | }, 1356 | { 1357 | "name": "CallStaticDoubleMethodV", 1358 | "args": [ 1359 | "JNIEnv*", 1360 | "jclass", 1361 | "jmethodID", 1362 | "va_list" 1363 | ], 1364 | "ret": "jdouble" 1365 | }, 1366 | { 1367 | "name": "CallStaticDoubleMethodA", 1368 | "args": [ 1369 | "JNIEnv*", 1370 | "jclass", 1371 | "jmethodID", 1372 | "jvalue*" 1373 | ], 1374 | "ret": "jdouble" 1375 | }, 1376 | { 1377 | "name": "CallStaticVoidMethod", 1378 | "args": [ 1379 | "JNIEnv*", 1380 | "jclass", 1381 | "jmethodID", 1382 | "..." 1383 | ], 1384 | "ret": "void" 1385 | }, 1386 | { 1387 | "name": "CallStaticVoidMethodV", 1388 | "args": [ 1389 | "JNIEnv*", 1390 | "jclass", 1391 | "jmethodID", 1392 | "va_list" 1393 | ], 1394 | "ret": "void" 1395 | }, 1396 | { 1397 | "name": "CallStaticVoidMethodA", 1398 | "args": [ 1399 | "JNIEnv*", 1400 | "jclass", 1401 | "jmethodID", 1402 | "jvalue*" 1403 | ], 1404 | "ret": "void" 1405 | }, 1406 | { 1407 | "name": "GetStaticFieldID", 1408 | "args": [ 1409 | "JNIEnv*", 1410 | "jclass", 1411 | "char*", 1412 | "char*" 1413 | ], 1414 | "ret": "jfieldID" 1415 | }, 1416 | { 1417 | "name": "GetStaticObjectField", 1418 | "args": [ 1419 | "JNIEnv*", 1420 | "jclass", 1421 | "jfieldID" 1422 | ], 1423 | "ret": "jobject" 1424 | }, 1425 | { 1426 | "name": "GetStaticBooleanField", 1427 | "args": [ 1428 | "JNIEnv*", 1429 | "jclass", 1430 | "jfieldID" 1431 | ], 1432 | "ret": "jboolean" 1433 | }, 1434 | { 1435 | "name": "GetStaticByteField", 1436 | "args": [ 1437 | "JNIEnv*", 1438 | "jclass", 1439 | "jfieldID" 1440 | ], 1441 | "ret": "jbyte" 1442 | }, 1443 | { 1444 | "name": "GetStaticCharField", 1445 | "args": [ 1446 | "JNIEnv*", 1447 | "jclass", 1448 | "jfieldID" 1449 | ], 1450 | "ret": "jchar" 1451 | }, 1452 | { 1453 | "name": "GetStaticShortField", 1454 | "args": [ 1455 | "JNIEnv*", 1456 | "jclass", 1457 | "jfieldID" 1458 | ], 1459 | "ret": "jshort" 1460 | }, 1461 | { 1462 | "name": "GetStaticIntField", 1463 | "args": [ 1464 | "JNIEnv*", 1465 | "jclass", 1466 | "jfieldID" 1467 | ], 1468 | "ret": "jint" 1469 | }, 1470 | { 1471 | "name": "GetStaticLongField", 1472 | "args": [ 1473 | "JNIEnv*", 1474 | "jclass", 1475 | "jfieldID" 1476 | ], 1477 | "ret": "jlong" 1478 | }, 1479 | { 1480 | "name": "GetStaticFloatField", 1481 | "args": [ 1482 | "JNIEnv*", 1483 | "jclass", 1484 | "jfieldID" 1485 | ], 1486 | "ret": "jfloat" 1487 | }, 1488 | { 1489 | "name": "GetStaticDoubleField", 1490 | "args": [ 1491 | "JNIEnv*", 1492 | "jclass", 1493 | "jfieldID" 1494 | ], 1495 | "ret": "jdouble" 1496 | }, 1497 | { 1498 | "name": "SetStaticObjectField", 1499 | "args": [ 1500 | "JNIEnv*", 1501 | "jclass", 1502 | "jfieldID", 1503 | "jobject" 1504 | ], 1505 | "ret": "void" 1506 | }, 1507 | { 1508 | "name": "SetStaticBooleanField", 1509 | "args": [ 1510 | "JNIEnv*", 1511 | "jclass", 1512 | "jfieldID", 1513 | "jboolean" 1514 | ], 1515 | "ret": "void" 1516 | }, 1517 | { 1518 | "name": "SetStaticByteField", 1519 | "args": [ 1520 | "JNIEnv*", 1521 | "jclass", 1522 | "jfieldID", 1523 | "jbyte" 1524 | ], 1525 | "ret": "void" 1526 | }, 1527 | { 1528 | "name": "SetStaticCharField", 1529 | "args": [ 1530 | "JNIEnv*", 1531 | "jclass", 1532 | "jfieldID", 1533 | "jchar" 1534 | ], 1535 | "ret": "void" 1536 | }, 1537 | { 1538 | "name": "SetStaticShortField", 1539 | "args": [ 1540 | "JNIEnv*", 1541 | "jclass", 1542 | "jfieldID", 1543 | "jshort" 1544 | ], 1545 | "ret": "void" 1546 | }, 1547 | { 1548 | "name": "SetStaticIntField", 1549 | "args": [ 1550 | "JNIEnv*", 1551 | "jclass", 1552 | "jfieldID", 1553 | "jint" 1554 | ], 1555 | "ret": "void" 1556 | }, 1557 | { 1558 | "name": "SetStaticLongField", 1559 | "args": [ 1560 | "JNIEnv*", 1561 | "jclass", 1562 | "jfieldID", 1563 | "jlong" 1564 | ], 1565 | "ret": "void" 1566 | }, 1567 | { 1568 | "name": "SetStaticFloatField", 1569 | "args": [ 1570 | "JNIEnv*", 1571 | "jclass", 1572 | "jfieldID", 1573 | "jfloat" 1574 | ], 1575 | "ret": "void" 1576 | }, 1577 | { 1578 | "name": "SetStaticDoubleField", 1579 | "args": [ 1580 | "JNIEnv*", 1581 | "jclass", 1582 | "jfieldID", 1583 | "jdouble" 1584 | ], 1585 | "ret": "void" 1586 | }, 1587 | { 1588 | "name": "NewString", 1589 | "args": [ 1590 | "JNIEnv*", 1591 | "jchar*", 1592 | "jsize" 1593 | ], 1594 | "ret": "jstring" 1595 | }, 1596 | { 1597 | "name": "GetStringLength", 1598 | "args": [ 1599 | "JNIEnv*", 1600 | "jstring" 1601 | ], 1602 | "ret": "jsize" 1603 | }, 1604 | { 1605 | "name": "GetStringChars", 1606 | "args": [ 1607 | "JNIEnv*", 1608 | "jstring", 1609 | "jboolean*" 1610 | ], 1611 | "ret": "jchar*" 1612 | }, 1613 | { 1614 | "name": "ReleaseStringChars", 1615 | "args": [ 1616 | "JNIEnv*", 1617 | "jstring", 1618 | "jchar*" 1619 | ], 1620 | "ret": "void" 1621 | }, 1622 | { 1623 | "name": "NewStringUTF", 1624 | "args": [ 1625 | "JNIEnv*", 1626 | "char*" 1627 | ], 1628 | "ret": "jstring" 1629 | }, 1630 | { 1631 | "name": "GetStringUTFLength", 1632 | "args": [ 1633 | "JNIEnv*", 1634 | "jstring" 1635 | ], 1636 | "ret": "jsize" 1637 | }, 1638 | { 1639 | "name": "GetStringUTFChars", 1640 | "args": [ 1641 | "JNIEnv*", 1642 | "jstring", 1643 | "jboolean*" 1644 | ], 1645 | "ret": "char*" 1646 | }, 1647 | { 1648 | "name": "ReleaseStringUTFChars", 1649 | "args": [ 1650 | "JNIEnv*", 1651 | "jstring", 1652 | "char*" 1653 | ], 1654 | "ret": "void" 1655 | }, 1656 | { 1657 | "name": "GetArrayLength", 1658 | "args": [ 1659 | "JNIEnv*", 1660 | "jarray" 1661 | ], 1662 | "ret": "jsize" 1663 | }, 1664 | { 1665 | "name": "NewObjectArray", 1666 | "args": [ 1667 | "JNIEnv*", 1668 | "jsize", 1669 | "jclass", 1670 | "jobject" 1671 | ], 1672 | "ret": "jobjectArray" 1673 | }, 1674 | { 1675 | "name": "GetObjectArrayElement", 1676 | "args": [ 1677 | "JNIEnv*", 1678 | "jobjectArray", 1679 | "jsize" 1680 | ], 1681 | "ret": "jobject" 1682 | }, 1683 | { 1684 | "name": "SetObjectArrayElement", 1685 | "args": [ 1686 | "JNIEnv*", 1687 | "jobjectArray", 1688 | "jsize", 1689 | "jobject" 1690 | ], 1691 | "ret": "void" 1692 | }, 1693 | { 1694 | "name": "NewBooleanArray", 1695 | "args": [ 1696 | "JNIEnv*", 1697 | "jsize" 1698 | ], 1699 | "ret": "jbooleanArray" 1700 | }, 1701 | { 1702 | "name": "NewByteArray", 1703 | "args": [ 1704 | "JNIEnv*", 1705 | "jsize" 1706 | ], 1707 | "ret": "jbyteArray" 1708 | }, 1709 | { 1710 | "name": "NewCharArray", 1711 | "args": [ 1712 | "JNIEnv*", 1713 | "jsize" 1714 | ], 1715 | "ret": "jcharArray" 1716 | }, 1717 | { 1718 | "name": "NewShortArray", 1719 | "args": [ 1720 | "JNIEnv*", 1721 | "jsize" 1722 | ], 1723 | "ret": "jshortArray" 1724 | }, 1725 | { 1726 | "name": "NewIntArray", 1727 | "args": [ 1728 | "JNIEnv*", 1729 | "jsize" 1730 | ], 1731 | "ret": "jintArray" 1732 | }, 1733 | { 1734 | "name": "NewLongArray", 1735 | "args": [ 1736 | "JNIEnv*", 1737 | "jsize" 1738 | ], 1739 | "ret": "jlongArray" 1740 | }, 1741 | { 1742 | "name": "NewFloatArray", 1743 | "args": [ 1744 | "JNIEnv*", 1745 | "jsize" 1746 | ], 1747 | "ret": "jfloatArray" 1748 | }, 1749 | { 1750 | "name": "NewDoubleArray", 1751 | "args": [ 1752 | "JNIEnv*", 1753 | "jsize" 1754 | ], 1755 | "ret": "jdoubleArray" 1756 | }, 1757 | { 1758 | "name": "GetBooleanArrayElements", 1759 | "args": [ 1760 | "JNIEnv*", 1761 | "jbooleanArray", 1762 | "jboolean*" 1763 | ], 1764 | "ret": "jboolean*" 1765 | }, 1766 | { 1767 | "name": "GetByteArrayElements", 1768 | "args": [ 1769 | "JNIEnv*", 1770 | "jbyteArray", 1771 | "jboolean*" 1772 | ], 1773 | "ret": "jbyte*" 1774 | }, 1775 | { 1776 | "name": "GetCharArrayElements", 1777 | "args": [ 1778 | "JNIEnv*", 1779 | "jcharArray", 1780 | "jboolean*" 1781 | ], 1782 | "ret": "jchar*" 1783 | }, 1784 | { 1785 | "name": "GetShortArrayElements", 1786 | "args": [ 1787 | "JNIEnv*", 1788 | "jshortArray", 1789 | "jboolean*" 1790 | ], 1791 | "ret": "jshort*" 1792 | }, 1793 | { 1794 | "name": "GetIntArrayElements", 1795 | "args": [ 1796 | "JNIEnv*", 1797 | "jintArray", 1798 | "jboolean*" 1799 | ], 1800 | "ret": "jint*" 1801 | }, 1802 | { 1803 | "name": "GetLongArrayElements", 1804 | "args": [ 1805 | "JNIEnv*", 1806 | "jlongArray", 1807 | "jboolean*" 1808 | ], 1809 | "ret": "jlong*" 1810 | }, 1811 | { 1812 | "name": "GetFloatArrayElements", 1813 | "args": [ 1814 | "JNIEnv*", 1815 | "jfloatArray", 1816 | "jboolean*" 1817 | ], 1818 | "ret": "jfloat*" 1819 | }, 1820 | { 1821 | "name": "GetDoubleArrayElements", 1822 | "args": [ 1823 | "JNIEnv*", 1824 | "jdoubleArray", 1825 | "jboolean*" 1826 | ], 1827 | "ret": "jdouble*" 1828 | }, 1829 | { 1830 | "name": "ReleaseBooleanArrayElements", 1831 | "args": [ 1832 | "JNIEnv*", 1833 | "jbooleanArray", 1834 | "jboolean*", 1835 | "jint" 1836 | ], 1837 | "ret": "void" 1838 | }, 1839 | { 1840 | "name": "ReleaseByteArrayElements", 1841 | "args": [ 1842 | "JNIEnv*", 1843 | "jbyteArray", 1844 | "jbyte*", 1845 | "jint" 1846 | ], 1847 | "ret": "void" 1848 | }, 1849 | { 1850 | "name": "ReleaseCharArrayElements", 1851 | "args": [ 1852 | "JNIEnv*", 1853 | "jcharArray", 1854 | "jchar*", 1855 | "jint" 1856 | ], 1857 | "ret": "void" 1858 | }, 1859 | { 1860 | "name": "ReleaseShortArrayElements", 1861 | "args": [ 1862 | "JNIEnv*", 1863 | "jshortArray", 1864 | "jshort*", 1865 | "jint" 1866 | ], 1867 | "ret": "void" 1868 | }, 1869 | { 1870 | "name": "ReleaseIntArrayElements", 1871 | "args": [ 1872 | "JNIEnv*", 1873 | "jintArray", 1874 | "jint*", 1875 | "jint" 1876 | ], 1877 | "ret": "void" 1878 | }, 1879 | { 1880 | "name": "ReleaseLongArrayElements", 1881 | "args": [ 1882 | "JNIEnv*", 1883 | "jlongArray", 1884 | "jlong*", 1885 | "jint" 1886 | ], 1887 | "ret": "void" 1888 | }, 1889 | { 1890 | "name": "ReleaseFloatArrayElements", 1891 | "args": [ 1892 | "JNIEnv*", 1893 | "jfloatArray", 1894 | "jfloat*", 1895 | "jint" 1896 | ], 1897 | "ret": "void" 1898 | }, 1899 | { 1900 | "name": "ReleaseDoubleArrayElements", 1901 | "args": [ 1902 | "JNIEnv*", 1903 | "jdoubleArray", 1904 | "jdouble*", 1905 | "jint" 1906 | ], 1907 | "ret": "void" 1908 | }, 1909 | { 1910 | "name": "GetBooleanArrayRegion", 1911 | "args": [ 1912 | "JNIEnv*", 1913 | "jbooleanArray", 1914 | "jsize", 1915 | "jsize", 1916 | "jboolean*" 1917 | ], 1918 | "ret": "void" 1919 | }, 1920 | { 1921 | "name": "GetByteArrayRegion", 1922 | "args": [ 1923 | "JNIEnv*", 1924 | "jbyteArray", 1925 | "jsize", 1926 | "jsize", 1927 | "jbyte*" 1928 | ], 1929 | "ret": "void" 1930 | }, 1931 | { 1932 | "name": "GetCharArrayRegion", 1933 | "args": [ 1934 | "JNIEnv*", 1935 | "jcharArray", 1936 | "jsize", 1937 | "jsize", 1938 | "jchar*" 1939 | ], 1940 | "ret": "void" 1941 | }, 1942 | { 1943 | "name": "GetShortArrayRegion", 1944 | "args": [ 1945 | "JNIEnv*", 1946 | "jshortArray", 1947 | "jsize", 1948 | "jsize", 1949 | "jshort*" 1950 | ], 1951 | "ret": "void" 1952 | }, 1953 | { 1954 | "name": "GetIntArrayRegion", 1955 | "args": [ 1956 | "JNIEnv*", 1957 | "jintArray", 1958 | "jsize", 1959 | "jsize", 1960 | "jint*" 1961 | ], 1962 | "ret": "void" 1963 | }, 1964 | { 1965 | "name": "GetLongArrayRegion", 1966 | "args": [ 1967 | "JNIEnv*", 1968 | "jlongArray", 1969 | "jsize", 1970 | "jsize", 1971 | "jlong*" 1972 | ], 1973 | "ret": "void" 1974 | }, 1975 | { 1976 | "name": "GetFloatArrayRegion", 1977 | "args": [ 1978 | "JNIEnv*", 1979 | "jfloatArray", 1980 | "jsize", 1981 | "jsize", 1982 | "jfloat*" 1983 | ], 1984 | "ret": "void" 1985 | }, 1986 | { 1987 | "name": "GetDoubleArrayRegion", 1988 | "args": [ 1989 | "JNIEnv*", 1990 | "jdoubleArray", 1991 | "jsize", 1992 | "jsize", 1993 | "jdouble*" 1994 | ], 1995 | "ret": "void" 1996 | }, 1997 | { 1998 | "name": "SetBooleanArrayRegion", 1999 | "args": [ 2000 | "JNIEnv*", 2001 | "jbooleanArray", 2002 | "jsize", 2003 | "jsize", 2004 | "jboolean*" 2005 | ], 2006 | "ret": "void" 2007 | }, 2008 | { 2009 | "name": "SetByteArrayRegion", 2010 | "args": [ 2011 | "JNIEnv*", 2012 | "jbyteArray", 2013 | "jsize", 2014 | "jsize", 2015 | "jbyte*" 2016 | ], 2017 | "ret": "void" 2018 | }, 2019 | { 2020 | "name": "SetCharArrayRegion", 2021 | "args": [ 2022 | "JNIEnv*", 2023 | "jcharArray", 2024 | "jsize", 2025 | "jsize", 2026 | "jchar*" 2027 | ], 2028 | "ret": "void" 2029 | }, 2030 | { 2031 | "name": "SetShortArrayRegion", 2032 | "args": [ 2033 | "JNIEnv*", 2034 | "jshortArray", 2035 | "jsize", 2036 | "jsize", 2037 | "jshort*" 2038 | ], 2039 | "ret": "void" 2040 | }, 2041 | { 2042 | "name": "SetIntArrayRegion", 2043 | "args": [ 2044 | "JNIEnv*", 2045 | "jintArray", 2046 | "jsize", 2047 | "jsize", 2048 | "jint*" 2049 | ], 2050 | "ret": "void" 2051 | }, 2052 | { 2053 | "name": "SetLongArrayRegion", 2054 | "args": [ 2055 | "JNIEnv*", 2056 | "jlongArray", 2057 | "jsize", 2058 | "jsize", 2059 | "jlong*" 2060 | ], 2061 | "ret": "void" 2062 | }, 2063 | { 2064 | "name": "SetFloatArrayRegion", 2065 | "args": [ 2066 | "JNIEnv*", 2067 | "jfloatArray", 2068 | "jsize", 2069 | "jsize", 2070 | "jfloat*" 2071 | ], 2072 | "ret": "void" 2073 | }, 2074 | { 2075 | "name": "SetDoubleArrayRegion", 2076 | "args": [ 2077 | "JNIEnv*", 2078 | "jdoubleArray", 2079 | "jsize", 2080 | "jsize", 2081 | "jdouble*" 2082 | ], 2083 | "ret": "void" 2084 | }, 2085 | { 2086 | "name": "RegisterNatives", 2087 | "args": [ 2088 | "JNIEnv*", 2089 | "jclass", 2090 | "JNINativeMethod*", 2091 | "jint" 2092 | ], 2093 | "ret": "jint" 2094 | }, 2095 | { 2096 | "name": "UnregisterNatives", 2097 | "args": [ 2098 | "JNIEnv*", 2099 | "jclass" 2100 | ], 2101 | "ret": "jint" 2102 | }, 2103 | { 2104 | "name": "MonitorEnter", 2105 | "args": [ 2106 | "JNIEnv*", 2107 | "jobject" 2108 | ], 2109 | "ret": "jint" 2110 | }, 2111 | { 2112 | "name": "MonitorExit", 2113 | "args": [ 2114 | "JNIEnv*", 2115 | "jobject" 2116 | ], 2117 | "ret": "jint" 2118 | }, 2119 | { 2120 | "name": "GetJavaVM", 2121 | "args": [ 2122 | "JNIEnv*", 2123 | "JavaVM**" 2124 | ], 2125 | "ret": "jint" 2126 | }, 2127 | { 2128 | "name": "GetStringRegion", 2129 | "args": [ 2130 | "JNIEnv*", 2131 | "jstring", 2132 | "jsize", 2133 | "jsize", 2134 | "jchar*" 2135 | ], 2136 | "ret": "void" 2137 | }, 2138 | { 2139 | "name": "GetStringUTFRegion", 2140 | "args": [ 2141 | "JNIEnv*", 2142 | "jstring", 2143 | "jsize", 2144 | "jsize", 2145 | "char*" 2146 | ], 2147 | "ret": "void" 2148 | }, 2149 | { 2150 | "name": "GetPrimitiveArrayCritical", 2151 | "args": [ 2152 | "JNIEnv*", 2153 | "jarray", 2154 | "jboolean*" 2155 | ], 2156 | "ret": "void" 2157 | }, 2158 | { 2159 | "name": "ReleasePrimitiveArrayCritical", 2160 | "args": [ 2161 | "JNIEnv*", 2162 | "jarray", 2163 | "void*", 2164 | "jint" 2165 | ], 2166 | "ret": "void" 2167 | }, 2168 | { 2169 | "name": "GetStringCritical", 2170 | "args": [ 2171 | "JNIEnv*", 2172 | "jstring", 2173 | "jboolean*" 2174 | ], 2175 | "ret": "jchar" 2176 | }, 2177 | { 2178 | "name": "ReleaseStringCritical", 2179 | "args": [ 2180 | "JNIEnv*", 2181 | "jstring", 2182 | "jchar*" 2183 | ], 2184 | "ret": "void" 2185 | }, 2186 | { 2187 | "name": "NewWeakGlobalRef", 2188 | "args": [ 2189 | "JNIEnv*", 2190 | "jobject" 2191 | ], 2192 | "ret": "jweak" 2193 | }, 2194 | { 2195 | "name": "DeleteWeakGlobalRef", 2196 | "args": [ 2197 | "JNIEnv*", 2198 | "jweak" 2199 | ], 2200 | "ret": "void" 2201 | }, 2202 | { 2203 | "name": "ExceptionCheck", 2204 | "args": [ 2205 | "JNIEnv*" 2206 | ], 2207 | "ret": "jboolean" 2208 | }, 2209 | { 2210 | "name": "NewDirectByteBuffer", 2211 | "args": [ 2212 | "JNIEnv*", 2213 | "void*", 2214 | "jlong" 2215 | ], 2216 | "ret": "jobject" 2217 | }, 2218 | { 2219 | "name": "GetDirectBufferAddress", 2220 | "args": [ 2221 | "JNIEnv*", 2222 | "jobject" 2223 | ], 2224 | "ret": "void" 2225 | }, 2226 | { 2227 | "name": "GetDirectBufferCapacity", 2228 | "args": [ 2229 | "JNIEnv*", 2230 | "jobject" 2231 | ], 2232 | "ret": "jlong" 2233 | }, 2234 | { 2235 | "name": "GetObjectRefType", 2236 | "args": [ 2237 | "JNIEnv*", 2238 | "jobject" 2239 | ], 2240 | "ret": "jobjectRefType" 2241 | } 2242 | ] 2243 | -------------------------------------------------------------------------------- /utils/android/jni/method_data.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: method_data.js 5 | * @time: 2020/12/22 8:51 下午 6 | * @desc: 7 | */ 8 | import {FCCommon} from "../../FCCommon"; 9 | import {Jni} from "../jnimgr"; 10 | 11 | class BacktraceJSONContainer { 12 | public readonly address: NativePointer; 13 | 14 | public readonly module: Module | null; 15 | 16 | // public readonly symbol: DebugSymbol | null; 17 | 18 | public constructor( 19 | address: NativePointer, 20 | module: Module | null, 21 | // symbol: DebugSymbol | null 22 | ) { 23 | this.address = address; 24 | this.module = module; 25 | // this.symbol = symbol; 26 | } 27 | } 28 | 29 | export class MethodData { 30 | private tag = 'MethodData'; 31 | private methodname: string; 32 | private args: InvocationArguments; 33 | private retval?: InvocationReturnValue; 34 | private methodDef: any; 35 | private jnival: { args: any[]; ret: any }; 36 | private backtrace: BacktraceJSONContainer[]; 37 | 38 | public constructor(ctx: CpuContext, methodname: string, methodDef: any, args: InvocationArguments, retval?: InvocationReturnValue) { 39 | this.methodname = methodname; 40 | this.methodDef = methodDef; 41 | this.args = args; 42 | this.jnival = {'args': [], 'ret': null}; 43 | // let bt = Thread.backtrace(ctx, Backtracer.ACCURATE); // Backtracer.FUZZY 44 | // this.backtrace = bt.map((addr: NativePointer): BacktraceJSONContainer => { 45 | // return new BacktraceJSONContainer( 46 | // addr, 47 | // Process.findModuleByAddress(addr), 48 | // DebugSymbol.fromAddress(addr) 49 | // ); 50 | // }); 51 | let addr = FCCommon.getLR(ctx); 52 | if (ptr(0) != addr) { 53 | this.backtrace = [new BacktraceJSONContainer(addr, Process.findModuleByAddress(addr))]; 54 | } else { 55 | this.backtrace = []; 56 | } 57 | 58 | let argTypes = this.methodDef.args as any[]; 59 | for (let i = 0; i < argTypes.length; i++) { 60 | let ptr = args[i]; 61 | let argType = argTypes[i]; 62 | let argval = MethodData.getFridaValue(argType, ptr); 63 | this.jnival.args.push({argType: argType, argVal: argval}); 64 | } 65 | 66 | if (null != retval) { 67 | this.setRetval(retval); 68 | } 69 | } 70 | 71 | public setRetval(retval: InvocationReturnValue) { 72 | this.retval = retval; 73 | let retType = this.methodDef.ret; 74 | let retVal = MethodData.getFridaValue(this.methodDef.ret, retval); 75 | this.jnival.ret = {retType: retType, retVal: retVal}; 76 | } 77 | 78 | public toString(): string { 79 | return JSON.stringify(this); 80 | } 81 | 82 | static getFridaValue(type: string, ptr: NativePointer) { 83 | if (null == ptr || 0 == ptr.toInt32()) { 84 | return ptr; 85 | } 86 | if (type.endsWith('*')) { 87 | if (type.startsWith('char')) { 88 | return ptr.readCString(); 89 | } else if (type.startsWith('jchar')) { 90 | let res = null; 91 | try { 92 | let tmp = ptr.readUtf16String(); 93 | if (tmp) { 94 | if (tmp[0].charCodeAt(0) < 0x80) { 95 | for (let i = 0; i < tmp.length; ++i) { 96 | if (tmp.charCodeAt(i) > 0x80) { 97 | tmp = tmp.substring(0, i); 98 | break; 99 | } 100 | } 101 | } 102 | if (tmp.length < 2) { 103 | tmp += "(hex:0x" + ptr.readU16().toString(16) + ")"; 104 | } 105 | } 106 | res = tmp; 107 | } catch (e) { 108 | } 109 | return res == null ? "" : res; 110 | } else { 111 | try { 112 | return ptr.readPointer(); 113 | } catch (e) { 114 | return ptr; 115 | } 116 | } 117 | } else { 118 | if ('jstring' === type) { 119 | return Java.vm.getEnv().stringFromJni(ptr); 120 | } else if ('jclass' === type) { 121 | return Java.vm.getEnv().getClassName(ptr); 122 | } else if ('jobject' === type) { 123 | return MethodData.printJObjectAsString(ptr); 124 | } else if ('jmethodID' === type) { 125 | let res = Jni.getMethodInfo(ptr); 126 | if (undefined != res) { 127 | return JSON.stringify(res); 128 | } 129 | else { 130 | return ptr; 131 | } 132 | } 133 | return ptr; 134 | } 135 | } 136 | 137 | // 传入一个 jobject 对象,尝试将其转换为 string 并打印 138 | static printJObjectAsString(obj: any): any { 139 | try { 140 | const javaObject = Java.cast(obj, Java.use("java.lang.Object")); 141 | // console.log('javaObject getClass: ' + javaObject.getClass().getName()); 142 | if (javaObject.getClass().getName() === "java.lang.String") { 143 | // console.log('javaString: ' + javaObject); 144 | return javaObject.toString(); 145 | } else { 146 | return `val: ${obj}, class: ${javaObject.getClass().getName()}`; 147 | } 148 | } catch (e) { 149 | // @ts-ignore 150 | console.error("Error converting jobject to string:", e.message); 151 | return obj; 152 | } 153 | } 154 | } -------------------------------------------------------------------------------- /utils/android/jnimgr.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: jnimgr.js 5 | * @time: 2020/6/18 5:14 PM 6 | * @desc: 7 | */ 8 | import {FCCommon} from "../FCCommon" 9 | import {DMLog} from "../dmlog"; 10 | import {MethodData} from "./jni/method_data"; 11 | // @ts-ignore 12 | import JNI_ENV_METHODS from "./jni/jni_env.json"; 13 | // struct JNINativeInterface : 14 | // https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129 15 | const jni_struct_array = [ 16 | "reserved0", 17 | "reserved1", 18 | "reserved2", 19 | "reserved3", 20 | "GetVersion", 21 | "DefineClass", 22 | "FindClass", 23 | "FromReflectedMethod", 24 | "FromReflectedField", 25 | "ToReflectedMethod", 26 | "GetSuperclass", 27 | "IsAssignableFrom", 28 | "ToReflectedField", 29 | "Throw", 30 | "ThrowNew", 31 | "ExceptionOccurred", 32 | "ExceptionDescribe", 33 | "ExceptionClear", 34 | "FatalError", 35 | "PushLocalFrame", 36 | "PopLocalFrame", 37 | "NewGlobalRef", 38 | "DeleteGlobalRef", 39 | "DeleteLocalRef", 40 | "IsSameObject", 41 | "NewLocalRef", 42 | "EnsureLocalCapacity", 43 | "AllocObject", 44 | "NewObject", 45 | "NewObjectV", 46 | "NewObjectA", 47 | "GetObjectClass", 48 | "IsInstanceOf", 49 | "GetMethodID", 50 | "CallObjectMethod", 51 | "CallObjectMethodV", 52 | "CallObjectMethodA", 53 | "CallBooleanMethod", 54 | "CallBooleanMethodV", 55 | "CallBooleanMethodA", 56 | "CallByteMethod", 57 | "CallByteMethodV", 58 | "CallByteMethodA", 59 | "CallCharMethod", 60 | "CallCharMethodV", 61 | "CallCharMethodA", 62 | "CallShortMethod", 63 | "CallShortMethodV", 64 | "CallShortMethodA", 65 | "CallIntMethod", 66 | "CallIntMethodV", 67 | "CallIntMethodA", 68 | "CallLongMethod", 69 | "CallLongMethodV", 70 | "CallLongMethodA", 71 | "CallFloatMethod", 72 | "CallFloatMethodV", 73 | "CallFloatMethodA", 74 | "CallDoubleMethod", 75 | "CallDoubleMethodV", 76 | "CallDoubleMethodA", 77 | "CallVoidMethod", 78 | "CallVoidMethodV", 79 | "CallVoidMethodA", 80 | "CallNonvirtualObjectMethod", 81 | "CallNonvirtualObjectMethodV", 82 | "CallNonvirtualObjectMethodA", 83 | "CallNonvirtualBooleanMethod", 84 | "CallNonvirtualBooleanMethodV", 85 | "CallNonvirtualBooleanMethodA", 86 | "CallNonvirtualByteMethod", 87 | "CallNonvirtualByteMethodV", 88 | "CallNonvirtualByteMethodA", 89 | "CallNonvirtualCharMethod", 90 | "CallNonvirtualCharMethodV", 91 | "CallNonvirtualCharMethodA", 92 | "CallNonvirtualShortMethod", 93 | "CallNonvirtualShortMethodV", 94 | "CallNonvirtualShortMethodA", 95 | "CallNonvirtualIntMethod", 96 | "CallNonvirtualIntMethodV", 97 | "CallNonvirtualIntMethodA", 98 | "CallNonvirtualLongMethod", 99 | "CallNonvirtualLongMethodV", 100 | "CallNonvirtualLongMethodA", 101 | "CallNonvirtualFloatMethod", 102 | "CallNonvirtualFloatMethodV", 103 | "CallNonvirtualFloatMethodA", 104 | "CallNonvirtualDoubleMethod", 105 | "CallNonvirtualDoubleMethodV", 106 | "CallNonvirtualDoubleMethodA", 107 | "CallNonvirtualVoidMethod", 108 | "CallNonvirtualVoidMethodV", 109 | "CallNonvirtualVoidMethodA", 110 | "GetFieldID", 111 | "GetObjectField", 112 | "GetBooleanField", 113 | "GetByteField", 114 | "GetCharField", 115 | "GetShortField", 116 | "GetIntField", 117 | "GetLongField", 118 | "GetFloatField", 119 | "GetDoubleField", 120 | "SetObjectField", 121 | "SetBooleanField", 122 | "SetByteField", 123 | "SetCharField", 124 | "SetShortField", 125 | "SetIntField", 126 | "SetLongField", 127 | "SetFloatField", 128 | "SetDoubleField", 129 | "GetStaticMethodID", 130 | "CallStaticObjectMethod", 131 | "CallStaticObjectMethodV", 132 | "CallStaticObjectMethodA", 133 | "CallStaticBooleanMethod", 134 | "CallStaticBooleanMethodV", 135 | "CallStaticBooleanMethodA", 136 | "CallStaticByteMethod", 137 | "CallStaticByteMethodV", 138 | "CallStaticByteMethodA", 139 | "CallStaticCharMethod", 140 | "CallStaticCharMethodV", 141 | "CallStaticCharMethodA", 142 | "CallStaticShortMethod", 143 | "CallStaticShortMethodV", 144 | "CallStaticShortMethodA", 145 | "CallStaticIntMethod", 146 | "CallStaticIntMethodV", 147 | "CallStaticIntMethodA", 148 | "CallStaticLongMethod", 149 | "CallStaticLongMethodV", 150 | "CallStaticLongMethodA", 151 | "CallStaticFloatMethod", 152 | "CallStaticFloatMethodV", 153 | "CallStaticFloatMethodA", 154 | "CallStaticDoubleMethod", 155 | "CallStaticDoubleMethodV", 156 | "CallStaticDoubleMethodA", 157 | "CallStaticVoidMethod", 158 | "CallStaticVoidMethodV", 159 | "CallStaticVoidMethodA", 160 | "GetStaticFieldID", 161 | "GetStaticObjectField", 162 | "GetStaticBooleanField", 163 | "GetStaticByteField", 164 | "GetStaticCharField", 165 | "GetStaticShortField", 166 | "GetStaticIntField", 167 | "GetStaticLongField", 168 | "GetStaticFloatField", 169 | "GetStaticDoubleField", 170 | "SetStaticObjectField", 171 | "SetStaticBooleanField", 172 | "SetStaticByteField", 173 | "SetStaticCharField", 174 | "SetStaticShortField", 175 | "SetStaticIntField", 176 | "SetStaticLongField", 177 | "SetStaticFloatField", 178 | "SetStaticDoubleField", 179 | "NewString", 180 | "GetStringLength", 181 | "GetStringChars", 182 | "ReleaseStringChars", 183 | "NewStringUTF", 184 | "GetStringUTFLength", 185 | "GetStringUTFChars", 186 | "ReleaseStringUTFChars", 187 | "GetArrayLength", 188 | "NewObjectArray", 189 | "GetObjectArrayElement", 190 | "SetObjectArrayElement", 191 | "NewBooleanArray", 192 | "NewByteArray", 193 | "NewCharArray", 194 | "NewShortArray", 195 | "NewIntArray", 196 | "NewLongArray", 197 | "NewFloatArray", 198 | "NewDoubleArray", 199 | "GetBooleanArrayElements", 200 | "GetByteArrayElements", 201 | "GetCharArrayElements", 202 | "GetShortArrayElements", 203 | "GetIntArrayElements", 204 | "GetLongArrayElements", 205 | "GetFloatArrayElements", 206 | "GetDoubleArrayElements", 207 | "ReleaseBooleanArrayElements", 208 | "ReleaseByteArrayElements", 209 | "ReleaseCharArrayElements", 210 | "ReleaseShortArrayElements", 211 | "ReleaseIntArrayElements", 212 | "ReleaseLongArrayElements", 213 | "ReleaseFloatArrayElements", 214 | "ReleaseDoubleArrayElements", 215 | "GetBooleanArrayRegion", 216 | "GetByteArrayRegion", 217 | "GetCharArrayRegion", 218 | "GetShortArrayRegion", 219 | "GetIntArrayRegion", 220 | "GetLongArrayRegion", 221 | "GetFloatArrayRegion", 222 | "GetDoubleArrayRegion", 223 | "SetBooleanArrayRegion", 224 | "SetByteArrayRegion", 225 | "SetCharArrayRegion", 226 | "SetShortArrayRegion", 227 | "SetIntArrayRegion", 228 | "SetLongArrayRegion", 229 | "SetFloatArrayRegion", 230 | "SetDoubleArrayRegion", 231 | "RegisterNatives", 232 | "UnregisterNatives", 233 | "MonitorEnter", 234 | "MonitorExit", 235 | "GetJavaVM", 236 | "GetStringRegion", 237 | "GetStringUTFRegion", 238 | "GetPrimitiveArrayCritical", 239 | "ReleasePrimitiveArrayCritical", 240 | "GetStringCritical", 241 | "ReleaseStringCritical", 242 | "NewWeakGlobalRef", 243 | "DeleteWeakGlobalRef", 244 | "ExceptionCheck", 245 | "NewDirectByteBuffer", 246 | "GetDirectBufferAddress", 247 | "GetDirectBufferCapacity", 248 | "GetObjectRefType" 249 | ]; 250 | 251 | export namespace Jni { 252 | // 定义保存函数名、签名和 jmethodID 的结构体 253 | type MethodInfo = { 254 | className: string, 255 | methodName: string, 256 | signature: string, 257 | isStatic: boolean, 258 | } 259 | 260 | // 保存函数名、签名和 jmethodID 的 Map 261 | const methodMap = new Map(); 262 | 263 | 264 | var have_record_method_info: Boolean = false; 265 | 266 | export function getJNIFunctionAdress(jnienv_addr: NativePointer, func_name: string) { 267 | let idx = jni_struct_array.indexOf(func_name); 268 | if (-1 == idx) { 269 | DMLog.e('getJNIFunctionAdress', `func name: ${func_name} not found!`); 270 | return ptr(0); 271 | } 272 | var offset = idx * Process.pointerSize; 273 | return jnienv_addr.add(offset).readPointer(); 274 | } 275 | 276 | export function getJNIAddr(name: string) { 277 | var env = Java.vm.getEnv(); 278 | var env_ptr = env.handle.readPointer(); 279 | const addr = Jni.getJNIFunctionAdress(env_ptr, name); 280 | // DMLog.d('Jni.getJNIAddr', 'addr: ' + addr); 281 | return addr; 282 | } 283 | 284 | export function hookJNI(name: string, callbacksOrProbe: InvocationListenerCallbacks | InstructionProbeCallback, 285 | data?: NativePointerValue) { 286 | const addr = Jni.getJNIAddr(name); 287 | console.log("Jni.getJNIAddr: " + name + ", addr: " + addr); 288 | return Interceptor.attach(addr, callbacksOrProbe); 289 | } 290 | 291 | /** 292 | * 分离仓库地址:https://github.com/deathmemory/fridaRegstNtv 293 | */ 294 | export function hook_registNatives() { 295 | const tag = 'fridaRegstNtv'; 296 | Jni.hookJNI("RegisterNatives", { 297 | onEnter: function (args) { 298 | var env = Java.vm.getEnv(); 299 | var p_size = Process.pointerSize; 300 | var methods = args[2]; 301 | var methodcount = args[3].toInt32(); 302 | // 获取类名 303 | var name = env.getClassName(args[1]); 304 | DMLog.i(tag, "==== class: " + name + " ===="); 305 | DMLog.i(tag, "==== methods: " + methods + " nMethods: " + methodcount + " ===="); 306 | /** 根据函数结构原型遍历动态注册信息 307 | typedef struct { 308 | const char* name; 309 | const char* signature; 310 | void* fnPtr; 311 | } JNINativeMethod; 312 | jint RegisterNatives(JNIEnv* env, jclass clazz, const JNINativeMethod* methods, jint nMethods) 313 | */ 314 | for (var i = 0; i < methodcount; i++) { 315 | var idx = i * p_size * 3; 316 | var fnPtr = methods.add(idx + p_size * 2).readPointer(); 317 | const module = Process.getModuleByAddress(fnPtr); 318 | if (module) { 319 | const modulename = module.name; 320 | const modulebase = module.base; 321 | var logstr = "name: " + methods.add(idx).readPointer().readCString() 322 | + ", signature: " + methods.add(idx + p_size).readPointer().readCString() 323 | + ", fnPtr: " + fnPtr 324 | + ", modulename: " + modulename + " -> base: " + modulebase; 325 | if (null != modulebase) { 326 | logstr += ", offset: " + fnPtr.sub(modulebase); 327 | } 328 | DMLog.i(tag, logstr); 329 | } 330 | else { 331 | DMLog.e(tag, 'module is null'); 332 | } 333 | } 334 | } 335 | }); 336 | } 337 | 338 | /** 339 | * trace 所有 Jni 方法 340 | * 可以配合 `python/android/traceLogCleaner.py` 脚本,格式化输出日志 341 | */ 342 | export function traceAllJNISimply() { 343 | // 遍历 Hook Jni 函数 344 | jni_struct_array.forEach(traceJNICore); 345 | } 346 | 347 | export function traceJNI(nameArray: string[]) { 348 | nameArray.forEach(function (name) { 349 | let idx = getJNIFunctionIndex(name); 350 | DMLog.i('traceJNI', 'name: ' + name + 'idx: ' + idx); 351 | if (-1 != idx) { 352 | traceJNICore(name, idx); 353 | } 354 | }); 355 | } 356 | 357 | export function traceJNICore(func_name: string, idx: number) { 358 | Jni.record_method_info(); 359 | if (!func_name.includes("reserved")) { 360 | Jni.hookJNI(func_name, { 361 | onEnter(args) { 362 | // 触发时将信息保存到对象中 363 | let md = new MethodData(this.context, func_name, JNI_ENV_METHODS[idx], args); 364 | this.md = md; 365 | }, 366 | onLeave(retval) { 367 | // 退出时将返回值追加到对象中 368 | this.md.setRetval(retval); 369 | // 发送日志 370 | send(JSON.stringify({tid: this.threadId, status: "jnitrace", data: this.md})); 371 | } 372 | }); 373 | } 374 | } 375 | 376 | export function getJNIFunctionIndex(funcName: string) { 377 | return JNI_ENV_METHODS.findIndex(method => method.name === funcName); 378 | } 379 | 380 | export function record_method_info() { 381 | if (have_record_method_info == false) { 382 | // hook GetMethodID 函数 383 | Jni.hookJNI("GetMethodID", { 384 | onEnter: function (args) { 385 | // const clsObj = Java.cast(args[1], Java.use('java.lang.Class')); 386 | this.methodName = args[2].readCString(); 387 | this.signature = args[3].readCString(); 388 | }, 389 | onLeave: function (retval) { 390 | // 保存函数名、签名和 jmethodID 到 Map 中 391 | methodMap.set(retval.toString(), { 392 | methodName: this.methodName, 393 | signature: this.signature, 394 | methodId: retval, 395 | isStatic: false 396 | }); 397 | } 398 | }); 399 | 400 | // hook GetStaticMethodID 函数 401 | Jni.hookJNI("GetStaticMethodID", { 402 | onEnter: function (args) { 403 | this.methodName = args[2].readCString(); 404 | this.signature = args[3].readCString(); 405 | }, 406 | onLeave: function (retval) { 407 | // 保存函数名、签名和 jmethodID 到 Map 中 408 | methodMap.set(retval.toString(), { 409 | methodName: this.methodName, 410 | signature: this.signature, 411 | methodId: retval, 412 | isStatic: true 413 | }); 414 | } 415 | }); 416 | 417 | have_record_method_info = true; 418 | } 419 | } 420 | 421 | // 获取函数名、签名和 jmethodID 的函数 422 | export function getMethodInfo(methodId: NativePointer) { 423 | return methodMap.get(methodId.toString()); 424 | } 425 | 426 | } -------------------------------------------------------------------------------- /utils/android/libs/gson-2.8.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deathmemory/FridaContainer/cd8abd8ca63951b5d912ef0906f9ef1fe15cbbd5/utils/android/libs/gson-2.8.6.jar -------------------------------------------------------------------------------- /utils/android/multi_unpinning.js: -------------------------------------------------------------------------------- 1 | /* Android ssl certificate pinning bypass script for various methods 2 | by Maurizio Siddu 3 | 4 | Run with: 5 | frida -U -f [APP_ID] -l frida_multiple_unpinning.js --no-pause 6 | */ 7 | 8 | function multi_unpinning() { 9 | Java.perform(function () { 10 | console.log(''); 11 | console.log('======'); 12 | console.log('[#] Android Bypass for various Certificate Pinning methods [#]'); 13 | console.log('======'); 14 | 15 | 16 | var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); 17 | var SSLContext = Java.use('javax.net.ssl.SSLContext'); 18 | 19 | 20 | // TrustManager (Android < 7) // 21 | //////////////////////////////// 22 | var TrustManager = Java.registerClass({ 23 | // Implement a custom TrustManager 24 | name: 'dev.asd.test.TrustManager', 25 | implements: [X509TrustManager], 26 | methods: { 27 | checkClientTrusted: function (chain, authType) {}, 28 | checkServerTrusted: function (chain, authType) {}, 29 | getAcceptedIssuers: function () {return []; } 30 | } 31 | }); 32 | // Prepare the TrustManager array to pass to SSLContext.init() 33 | var TrustManagers = [TrustManager.$new()]; 34 | // Get a handle on the init() on the SSLContext class 35 | var SSLContext_init = SSLContext.init.overload( 36 | '[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); 37 | try { 38 | // Override the init method, specifying the custom TrustManager 39 | SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) { 40 | console.log('[+] Bypassing Trustmanager (Android < 7) request'); 41 | SSLContext_init.call(this, keyManager, TrustManagers, secureRandom); 42 | }; 43 | } catch (err) { 44 | console.log('[-] TrustManager (Android < 7) pinner not found'); 45 | //console.log(err); 46 | } 47 | 48 | 49 | 50 | // OkHTTPv3 (quadruple bypass) // 51 | ///////////////////////////////// 52 | try { 53 | // Bypass OkHTTPv3 {1} 54 | var okhttp3_Activity_1 = Java.use('okhttp3.CertificatePinner'); 55 | okhttp3_Activity_1.check.overload('java.lang.String', 'java.util.List').implementation = function (a, b) { 56 | console.log('[+] Bypassing OkHTTPv3 {1}: ' + a); 57 | return; 58 | }; 59 | } catch (err) { 60 | console.log('[-] OkHTTPv3 {1} pinner not found'); 61 | //console.log(err); 62 | } 63 | try { 64 | // Bypass OkHTTPv3 {2} 65 | // This method of CertificatePinner.check could be found in some old Android app 66 | var okhttp3_Activity_2 = Java.use('okhttp3.CertificatePinner'); 67 | okhttp3_Activity_2.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function (a, b) { 68 | console.log('[+] Bypassing OkHTTPv3 {2}: ' + a); 69 | return; 70 | }; 71 | } catch (err) { 72 | console.log('[-] OkHTTPv3 {2} pinner not found'); 73 | //console.log(err); 74 | } 75 | try { 76 | // Bypass OkHTTPv3 {3} 77 | var okhttp3_Activity_3 = Java.use('okhttp3.CertificatePinner'); 78 | okhttp3_Activity_3.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function (a, b) { 79 | console.log('[+] Bypassing OkHTTPv3 {3}: ' + a); 80 | return; 81 | }; 82 | } catch(err) { 83 | console.log('[-] OkHTTPv3 {3} pinner not found'); 84 | //console.log(err); 85 | } 86 | try { 87 | // Bypass OkHTTPv3 {4} 88 | var okhttp3_Activity_4 = Java.use('okhttp3.CertificatePinner'); 89 | okhttp3_Activity_4['check$okhttp'].implementation = function (a, b) { 90 | console.log('[+] Bypassing OkHTTPv3 {4}: ' + a); 91 | }; 92 | } catch(err) { 93 | console.log('[-] OkHTTPv3 {4} pinner not found'); 94 | //console.log(err); 95 | } 96 | 97 | 98 | 99 | // Trustkit (triple bypass) // 100 | ////////////////////////////// 101 | try { 102 | // Bypass Trustkit {1} 103 | var trustkit_Activity_1 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier'); 104 | trustkit_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (a, b) { 105 | console.log('[+] Bypassing Trustkit {1}: ' + a); 106 | return true; 107 | }; 108 | } catch (err) { 109 | console.log('[-] Trustkit {1} pinner not found'); 110 | //console.log(err); 111 | } 112 | try { 113 | // Bypass Trustkit {2} 114 | var trustkit_Activity_2 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier'); 115 | trustkit_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (a, b) { 116 | console.log('[+] Bypassing Trustkit {2}: ' + a); 117 | return true; 118 | }; 119 | } catch (err) { 120 | console.log('[-] Trustkit {2} pinner not found'); 121 | //console.log(err); 122 | } 123 | try { 124 | // Bypass Trustkit {3} 125 | var trustkit_PinningTrustManager = Java.use('com.datatheorem.android.trustkit.pinning.PinningTrustManager'); 126 | trustkit_PinningTrustManager.checkServerTrusted.implementation = function () { 127 | console.log('[+] Bypassing Trustkit {3}'); 128 | }; 129 | } catch (err) { 130 | console.log('[-] Trustkit {3} pinner not found'); 131 | //console.log(err); 132 | } 133 | 134 | 135 | 136 | 137 | // TrustManagerImpl (Android > 7) // 138 | //////////////////////////////////// 139 | try { 140 | var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl'); 141 | TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) { 142 | console.log('[+] Bypassing TrustManagerImpl (Android > 7): ' + host); 143 | return untrustedChain; 144 | }; 145 | } catch (err) { 146 | console.log('[-] TrustManagerImpl (Android > 7) pinner not found'); 147 | //console.log(err); 148 | } 149 | 150 | 151 | 152 | // Appcelerator Titanium // 153 | /////////////////////////// 154 | try { 155 | var appcelerator_PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager'); 156 | appcelerator_PinningTrustManager.checkServerTrusted.implementation = function () { 157 | console.log('[+] Bypassing Appcelerator PinningTrustManager'); 158 | }; 159 | } catch (err) { 160 | console.log('[-] Appcelerator PinningTrustManager pinner not found'); 161 | //console.log(err); 162 | } 163 | 164 | 165 | 166 | // OpenSSLSocketImpl Conscrypt // 167 | ///////////////////////////////// 168 | try { 169 | var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl'); 170 | OpenSSLSocketImpl.verifyCertificateChain.implementation = function (certRefs, JavaObject, authMethod) { 171 | console.log('[+] Bypassing OpenSSLSocketImpl Conscrypt'); 172 | }; 173 | } catch (err) { 174 | console.log('[-] OpenSSLSocketImpl Conscrypt pinner not found'); 175 | //console.log(err); 176 | } 177 | 178 | 179 | 180 | // OpenSSLEngineSocketImpl Conscrypt // 181 | /////////////////////////////////////// 182 | try { 183 | var OpenSSLEngineSocketImpl_Activity = Java.use('com.android.org.conscrypt.OpenSSLEngineSocketImpl'); 184 | OpenSSLSocketImpl_Activity.verifyCertificateChain.overload('[Ljava.lang.Long;', 'java.lang.String').implementation = function (a, b) { 185 | console.log('[+] Bypassing OpenSSLEngineSocketImpl Conscrypt: ' + b); 186 | }; 187 | } catch (err) { 188 | console.log('[-] OpenSSLEngineSocketImpl Conscrypt pinner not found'); 189 | //console.log(err); 190 | } 191 | 192 | 193 | 194 | // OpenSSLSocketImpl Apache Harmony // 195 | ////////////////////////////////////// 196 | try { 197 | var OpenSSLSocketImpl_Harmony = Java.use('org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl'); 198 | OpenSSLSocketImpl_Harmony.verifyCertificateChain.implementation = function (asn1DerEncodedCertificateChain, authMethod) { 199 | console.log('[+] Bypassing OpenSSLSocketImpl Apache Harmony'); 200 | }; 201 | } catch (err) { 202 | console.log('[-] OpenSSLSocketImpl Apache Harmony pinner not found'); 203 | //console.log(err); 204 | } 205 | 206 | 207 | 208 | // PhoneGap sslCertificateChecker (https://github.com/EddyVerbruggen/SSLCertificateChecker-PhoneGap-Plugin) // 209 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 210 | try { 211 | var phonegap_Activity = Java.use('nl.xservices.plugins.sslCertificateChecker'); 212 | phonegap_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function (a, b, c) { 213 | console.log('[+] Bypassing PhoneGap sslCertificateChecker: ' + a); 214 | return true; 215 | }; 216 | } catch (err) { 217 | console.log('[-] PhoneGap sslCertificateChecker pinner not found'); 218 | //console.log(err); 219 | } 220 | 221 | 222 | 223 | // IBM MobileFirst pinTrustedCertificatePublicKey (double bypass) // 224 | //////////////////////////////////////////////////////////////////// 225 | try { 226 | // Bypass IBM MobileFirst {1} 227 | var WLClient_Activity_1 = Java.use('com.worklight.wlclient.api.WLClient'); 228 | WLClient_Activity_1.getInstance().pinTrustedCertificatePublicKey.overload('java.lang.String').implementation = function (cert) { 229 | console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {1}: ' + cert); 230 | return; 231 | }; 232 | } catch (err) { 233 | console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey {1} pinner not found'); 234 | //console.log(err); 235 | } 236 | try { 237 | // Bypass IBM MobileFirst {2} 238 | var WLClient_Activity_2 = Java.use('com.worklight.wlclient.api.WLClient'); 239 | WLClient_Activity_2.getInstance().pinTrustedCertificatePublicKey.overload('[Ljava.lang.String;').implementation = function (cert) { 240 | console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {2}: ' + cert); 241 | return; 242 | }; 243 | } catch (err) { 244 | console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey {2} pinner not found'); 245 | //console.log(err); 246 | } 247 | 248 | 249 | 250 | // IBM WorkLight (ancestor of MobileFirst) HostNameVerifierWithCertificatePinning (quadruple bypass) // 251 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 252 | try { 253 | // Bypass IBM WorkLight {1} 254 | var worklight_Activity_1 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning'); 255 | worklight_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSocket').implementation = function (a, b) { 256 | console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {1}: ' + a); 257 | return; 258 | }; 259 | } catch (err) { 260 | console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {1} pinner not found'); 261 | //console.log(err); 262 | } 263 | try { 264 | // Bypass IBM WorkLight {2} 265 | var worklight_Activity_2 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning'); 266 | worklight_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (a, b) { 267 | console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {2}: ' + a); 268 | return; 269 | }; 270 | } catch (err) { 271 | console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {2} pinner not found'); 272 | //console.log(err); 273 | } 274 | try { 275 | // Bypass IBM WorkLight {3} 276 | var worklight_Activity_3 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning'); 277 | worklight_Activity_3.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;').implementation = function (a, b) { 278 | console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {3}: ' + a); 279 | return; 280 | }; 281 | } catch (err) { 282 | console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {3} pinner not found'); 283 | //console.log(err); 284 | } 285 | try { 286 | // Bypass IBM WorkLight {4} 287 | var worklight_Activity_4 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning'); 288 | worklight_Activity_4.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (a, b) { 289 | console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {4}: ' + a); 290 | return true; 291 | }; 292 | } catch (err) { 293 | console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {4} pinner not found'); 294 | //console.log(err); 295 | } 296 | 297 | 298 | 299 | // Conscrypt CertPinManager // 300 | ////////////////////////////// 301 | try { 302 | var conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager'); 303 | conscrypt_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (a, b) { 304 | console.log('[+] Bypassing Conscrypt CertPinManager: ' + a); 305 | return true; 306 | }; 307 | } catch (err) { 308 | console.log('[-] Conscrypt CertPinManager pinner not found'); 309 | //console.log(err); 310 | } 311 | 312 | 313 | 314 | // CWAC-Netsecurity (unofficial back-port pinner for Android<4.2) CertPinManager // 315 | /////////////////////////////////////////////////////////////////////////////////// 316 | try { 317 | var cwac_CertPinManager_Activity = Java.use('com.commonsware.cwac.netsecurity.conscrypt.CertPinManager'); 318 | cwac_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (a, b) { 319 | console.log('[+] Bypassing CWAC-Netsecurity CertPinManager: ' + a); 320 | return true; 321 | }; 322 | } catch (err) { 323 | console.log('[-] CWAC-Netsecurity CertPinManager pinner not found'); 324 | //console.log(err); 325 | } 326 | 327 | 328 | 329 | // Worklight Androidgap WLCertificatePinningPlugin // 330 | ///////////////////////////////////////////////////// 331 | try { 332 | var androidgap_WLCertificatePinningPlugin_Activity = Java.use('com.worklight.androidgap.plugin.WLCertificatePinningPlugin'); 333 | androidgap_WLCertificatePinningPlugin_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function (a, b, c) { 334 | console.log('[+] Bypassing Worklight Androidgap WLCertificatePinningPlugin: ' + a); 335 | return true; 336 | }; 337 | } catch (err) { 338 | console.log('[-] Worklight Androidgap WLCertificatePinningPlugin pinner not found'); 339 | //console.log(err); 340 | } 341 | 342 | 343 | 344 | // Netty FingerprintTrustManagerFactory // 345 | ////////////////////////////////////////// 346 | try { 347 | var netty_FingerprintTrustManagerFactory = Java.use('io.netty.handler.ssl.util.FingerprintTrustManagerFactory'); 348 | //NOTE: sometimes this below implementation could be useful 349 | //var netty_FingerprintTrustManagerFactory = Java.use('org.jboss.netty.handler.ssl.util.FingerprintTrustManagerFactory'); 350 | netty_FingerprintTrustManagerFactory.checkTrusted.implementation = function (type, chain) { 351 | console.log('[+] Bypassing Netty FingerprintTrustManagerFactory'); 352 | }; 353 | } catch (err) { 354 | console.log('[-] Netty FingerprintTrustManagerFactory pinner not found'); 355 | //console.log(err); 356 | } 357 | 358 | 359 | try { 360 | let CertificatePinner = Java.use("okhttp3.CertificatePinner"); 361 | CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(str, list){ 362 | console.log('[+] bypass CertificatePinner {1}: ' + str); 363 | return; 364 | }; 365 | } 366 | catch (e) { 367 | console.log('[-] CertificatePinner {1} pinner not found'); 368 | } 369 | 370 | try { 371 | CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(str, certificateArr){ 372 | console.log('[+] bypass CertificatePinner {2}: ' + str); 373 | return; 374 | }; 375 | } 376 | catch (e) { 377 | console.log('[-] CertificatePinner {2} pinner not found'); 378 | } 379 | 380 | // Squareup CertificatePinner [OkHTTP -1 || expmthd.name.indexOf("OpenMemory") > -1) { 10 | console.log("unpack_common: " + JSON.stringify(expmthd)); 11 | Interceptor.attach(expmthd.address, { 12 | onEnter: function (args) { 13 | // console.log(`=== ${resMethod.name} entry`); 14 | if (Memory.readU32(args[1]) == DEX_MAGIC) { 15 | dexrec.push(args[1]); 16 | } 17 | } 18 | }); 19 | } 20 | }); 21 | 22 | if (Java.available) { 23 | Java.perform(function () { 24 | 25 | var dexBase64 = "ZGV4CjAzNQCjsh5+52qOBRMl1aMHk33QkLmfsSbOla5wDwAAcAAAAHhWNBIAAAAAAAAAAKAOAABpAAAAcAAAABwAAAAUAgAAGgAAAIQCAAABAAAAvAMAACUAAADEAwAAAQAAAOwEAABkCgAADAUAAAwFAAAPBQAAEgUAABcFAAAfBQAAIwUAADgFAABGBQAASQUAAE0FAABSBQAAVQUAAFkFAABeBQAAYwUAAHcFAACXBQAAtgUAAM8FAADiBQAA+AUAABEGAAAoBgAATAYAAG4GAACCBgAAlgYAALEGAADIBgAA4wYAAP4GAAAaBwAAMQcAAEgHAABzBwAAjAcAAKUHAADSBwAA6AcAAPoHAAD/BwAAAggAAAYIAAAKCAAADQgAABEIAAAlCAAAOggAAE8IAABsCAAAcQgAAHkIAACGCAAAlQgAAKAIAACnCAAAqggAALcIAADKCAAA0wgAANYIAADaCAAA4wgAAPEIAAD5CAAAAAkAAAsJAAAQCQAAGgkAACwJAABDCQAAVQkAAGkJAAB0CQAAfQkAAI0JAACgCQAArwkAAMAJAADJCQAAzAkAANQJAADeCQAA7AkAAPoJAAAFCgAADQoAABYKAAAcCgAAJgoAACwKAAA5CgAAQQoAAEcKAABQCgAAWgoAAGsKAABzCgAAggoAAIgKAACOCgAAlwoAAKAKAACqCgAAwAoAAAcAAAAOAAAADwAAABAAAAARAAAAEgAAABQAAAAVAAAAFgAAABcAAAAYAAAAGQAAABoAAAAbAAAAHAAAAB0AAAAeAAAAHwAAACIAAAAjAAAAJQAAACYAAAAoAAAAKwAAAC0AAAAuAAAALwAAADAAAAAHAAAAAAAAAAAAAAAIAAAAAAAAAMgKAAAJAAAAAAAAAAgLAAAKAAAABQAAAAAAAAALAAAABQAAAOgKAAAKAAAACgAAAAAAAAALAAAACgAAAMgKAAAMAAAACgAAANAKAAANAAAACgAAANgKAAANAAAACgAAAOAKAAAKAAAACwAAAAAAAAALAAAADAAAAOgKAAALAAAADwAAAOgKAAALAAAAEQAAAPAKAAAKAAAAEwAAAAAAAAAKAAAAFAAAAAAAAAAoAAAAFgAAAAAAAAApAAAAFgAAAPAKAAApAAAAFgAAAPgKAAAqAAAAFgAAAAALAAArAAAAFwAAAAAAAAAsAAAAFwAAAMgKAAAKAAAAGAAAAAAAAAALAAAAGQAAABALAAALAAAAGgAAAPAKAAAKAAAAGwAAAAAAAAACAAsAJwAAAAEAAgA3AAAAAgAQAAMAAAACAA0ARAAAAAIAGABFAAAAAgAIAEoAAAACABEAUwAAAAQADgA9AAAABQAMAEYAAAAFABkARwAAAAUACgBJAAAABQADAEwAAAAGAAQAVAAAAAgAEABfAAAACQAQAF8AAAAKABAAAwAAAAoAAwBDAAAACwAVAD8AAAAMABAAAwAAAAwACwAyAAAADAAKAGYAAAAOAAcAQgAAAA4AAQBIAAAADwAGAEIAAAAPABMAYQAAABAACgBJAAAAEAAWAEsAAAAQAAkAUAAAABEAEAADAAAAEQAVADEAAAARAA8AUQAAABEAAABiAAAAEQAXAGUAAAASABIAYwAAABMAFABNAAAAEwAFAFoAAAAUABQATgAAABQABQBZAAAAAgAAAAEAAAAKAAAAAAAAAAUAAAA8CwAAhA4AABYLAAABKAABKQADLS0+AAY8aW5pdD4AAj47ABNFbnVtZXJhdGVDbGFzcy5qYXZhAAxGUmlEQV9VTlBBQ0sAAUkAAklMAANJTEwAAUwAAkxMAANMTEkAA0xMTAASTGFuZHJvaWQvdXRpbC9Mb2c7AB5MY29tL3NtYXJ0ZG9uZS9FbnVtZXJhdGVDbGFzczsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABdMZGFsdmlrL3N5c3RlbS9EZXhGaWxlOwARTGphdmEvbGFuZy9DbGFzczsAFExqYXZhL2xhbmcvQ2xhc3M8Kj47ABdMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwAVTGphdmEvbGFuZy9FeGNlcHRpb247ACJMamF2YS9sYW5nL0lsbGVnYWxBY2Nlc3NFeGNlcHRpb247ACBMamF2YS9sYW5nL05vU3VjaEZpZWxkRXhjZXB0aW9uOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAZTGphdmEvbGFuZy9yZWZsZWN0L0FycmF5OwAZTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsAFUxqYXZhL3V0aWwvQXJyYXlMaXN0OwAVTGphdmEvdXRpbC9BcnJheUxpc3Q8AClMamF2YS91dGlsL0FycmF5TGlzdDxMamF2YS9sYW5nL1N0cmluZzs+OwAXTGphdmEvdXRpbC9Db2xsZWN0aW9uczsAF0xqYXZhL3V0aWwvRW51bWVyYXRpb247ACtMamF2YS91dGlsL0VudW1lcmF0aW9uPExqYXZhL2xhbmcvU3RyaW5nOz47ABRMamF2YS91dGlsL0l0ZXJhdG9yOwAQTGphdmEvdXRpbC9MaXN0OwADVEFHAAFWAAJWTAACVloAAVoAAlpMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL09iamVjdDsAE1tMamF2YS9sYW5nL1N0cmluZzsAG1tMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwADYWRkAAZhcHBlbmQAC2NsYXNzTG9hZGVyAA1jbGFzc05hbWVMaXN0AAljbGFzc2xpc3QABWNsYXp6AAFkAAtkZXhFbGVtZW50cwARZGV4RWxlbWVudHNMZW5ndGgAB2RleEZpbGUAAWUAAmUyAAdlbnRyaWVzAAxlbnVtZXJhdGlvbnMABmVxdWFscwAFZmllbGQACWZpZWxkTmFtZQADZ2V0AAhnZXRDbGFzcwAQZ2V0Q2xhc3NOYW1lTGlzdAAVZ2V0Q2xhc3NOYW1lTGlzdEFycmF5ABBnZXREZWNsYXJlZEZpZWxkABJnZXREZWNsYXJlZE1ldGhvZHMACWdldExlbmd0aAAHZ2V0TmFtZQAOZ2V0T2JqZWN0RmllbGQAEWdldFBhcmFtZXRlclR5cGVzAA1nZXRTdXBlcmNsYXNzAA9oYXNNb3JlRWxlbWVudHMAB2hhc05leHQAAWkABmludm9rZQAIaXRlcmF0b3IADGxvYWQgY2xhc3M6IAAMbG9hZEFsbENsYXNzAAlsb2FkQ2xhc3MABm1ldGhvZAAHbWV0aG9kcwAEbmFtZQAIbmFtZWxpc3QABG5leHQAC25leHRFbGVtZW50AAZvYmplY3QABG9ianMAB3Bhcm1sZW4ACHBhdGhMaXN0AA9wcmludFN0YWNrVHJhY2UABnJldHZhbAANc2V0QWNjZXNzaWJsZQAEc2l6ZQAEc29ydAAHc3VjY2VzcwAHdG9BcnJheQAIdG9TdHJpbmcAFHRyeSB0byBsb2FkIG1ldGhvZDogAAV2YWx1ZQAAAQAAAAoAAAACAAAACgAAAAIAAAAKAAsAAgAAAAoAGQABAAAACwAAAAEAAAAGAAAAAQAAABUAAAABAAAAFwAAAAIAAAALAAsAAQAAABkAARcGAgMBaBwGFwAXFBcBFyAXGRcEAAAAAAAAAAAAAQAAABkLAAAAAAAAAAAAAAEAAAAAAAAAAgAAADQLAAAOAA4AJAE0DlsEADUSIsMDATkLSwMCOgEdAwNQAS3/BAQ/FCVpoQUEQgUBBQIFAxwfPAA1ATQOSwQAWRIiaQMBYRs8ABMCXEIOSwMANwYBEBBLAwFBEEtdBQEeAwE9CTsFARkeAwE8CjxNBQEfAD0BNA5LBAA2EiL/AwJYDEsEAzcGFEsDBFccARoPaQMHVhFaAwheAS0DCV0aASYPSwJ7dwUHBQgFCUIFAgUDBQQgBQAbIAABAAEAAQAAAFQLAAAEAAAAcBAOAAAADgAHAAEAAgABAFgLAABBAAAAIgARAHAQGwAAABoBXgBxIAQAFgAMARoCOABxIAQAIQAMAXEQFQABAAoCEgM1IyUAcSAUADEADAQaBToAcSAEAFQADAQfBAQAbhAGAAQADARyECEABAAKBTgFDAByECIABAAMBR8FCwBuIBwAUAAo8dgDAwEo3CgCDQFxECAAAAARAAAABQAAADIAAQABAQc8AwABAAIAAACHCwAADgAAAHEQAgACAAwAbhAeAAAACgEjERoAbiAfABAAEQEFAAIAAgABAJgLAAAxAAAAbhAPAAMADABuEAkAAAAMARwCCgBuEAkAAgAMAm4gEAAhAAoBOQEdAG4gBwBAAAwBEhJuIBcAIQBuIBYAMQAMAhECDQFuEAwAAQAoCQ0BbhANAAEAbhAKAAAADAAo1hIBEQEAABQAAAAMAAEAAQIJJgghAAAOAAEAAwABAMILAAB7AAAAcRACAA0ADABuEB0AAAAMAXIQIwABAAoCOAJsAHIQJAABAAwCHwILAG4gCwAtAAwDbhAIAAMADAQaBQYAIgYMAHAQEQAGABoHUgBuIBIAdgBuEAkAAwAMB24gEgB2AG4QEwAGAAwGcSAAAGUAIUUSBjVWPwBGBwQGbhAZAAcADAghiCOJGQAaCgYAIgsMAHAQEQALABoMZwBuIBIAywBuEAkAAwAMDG4gEgDLABoMAgBuIBIAywBuEBgABwAMDG4gEgDLAG4QEwALAAwLcSAAALoAEgpuMBoApwkaCgYAGgtkAHEgAAC6ANgGBgEowiiRKAINAA4AAAAAAAAAdAABAAEBDXkBAAUAABoBgYAEiBgBCaAYAQnAGQEJ7BkBCfAaEQAAAAAAAAABAAAAAAAAAAEAAABpAAAAcAAAAAIAAAAcAAAAFAIAAAMAAAAaAAAAhAIAAAQAAAABAAAAvAMAAAUAAAAlAAAAxAMAAAYAAAABAAAA7AQAAAIgAABpAAAADAUAAAEQAAAKAAAAyAoAAAUgAAABAAAAFgsAAAQgAAABAAAAGQsAAAMQAAADAAAALAsAAAYgAAABAAAAPAsAAAMgAAAFAAAAVAsAAAEgAAAFAAAACAwAAAAgAAABAAAAhA4AAAAQAAABAAAAoA4AAA=="; 26 | var application = Java.use("android.app.Application"); 27 | var BaseDexClassLoader = Java.use("dalvik.system.BaseDexClassLoader"); 28 | var Base64 = Java.use("android.util.Base64"); 29 | var FileOutputStream = Java.use("java.io.FileOutputStream"); 30 | var DexClassLoader = Java.use("dalvik.system.DexClassLoader"); 31 | 32 | var reflectField = Java.use("java.lang.reflect.Field"); 33 | var reflectMethod = Java.use("java.lang.reflect.Method"); 34 | var reflectObject = Java.use("java.lang.Object"); 35 | var reflectClass = Java.use("java.lang.Class"); 36 | var reflectString = Java.use("java.lang.String"); 37 | var reflectClassloader = Java.use("java.lang.ClassLoader"); 38 | 39 | 40 | if (application != undefined) { 41 | application.attach.overload('android.content.Context').implementation = function (context) { 42 | var result = this.attach(context); 43 | var classloader = context.getClassLoader(); 44 | var filesDir = context.getFilesDir(); 45 | var codeCacheDir = context.getCodeCacheDir(); 46 | console.log("files dir: " + filesDir); 47 | console.log("code cache dir: " + codeCacheDir); 48 | if (classloader != undefined) { 49 | var casedloader = Java.cast(classloader, BaseDexClassLoader); 50 | var dexbytes = Base64.decode(dexBase64, 0); 51 | var dexpath = filesDir + "/emmm.dex"; 52 | var fout = FileOutputStream.$new(dexpath); 53 | fout.write(dexbytes, 0, dexbytes.length); 54 | fout.close(); 55 | console.log("write dex to " + dexpath); 56 | 57 | var dexstr = dexpath.toString(); 58 | var cachestr = codeCacheDir.toString(); 59 | 60 | var dyndex = DexClassLoader.$new(dexstr, cachestr, cachestr, classloader); 61 | console.log(dyndex.toString()); 62 | var EnumerateClass = dyndex.loadClass("com.smartdone.EnumerateClass"); 63 | var castedEnumerateClass = Java.cast(EnumerateClass, reflectClass); 64 | var methods = castedEnumerateClass.getDeclaredMethods(); 65 | // loadAllClass 66 | var loadAllClass = undefined; 67 | for (var i in methods) { 68 | console.log(methods[i].getName()); 69 | if (methods[i].getName() == "loadAllClass") { 70 | console.log("find loadAllClass"); 71 | loadAllClass = methods[i]; 72 | } 73 | } 74 | if (loadAllClass != undefined) { 75 | console.log("loadAllClass: " + loadAllClass.toString()); 76 | var args = Java.array('Ljava.lang.Object;', [classloader]); 77 | var classlist = loadAllClass.invoke(null, args); 78 | console.log("start dump dex "); 79 | for (var i in dexrec) { 80 | if (Memory.readU32(dexrec[i]) == DEX_MAGIC) { 81 | var dex_len = Memory.readU32(dexrec[i].add(0x20)); 82 | var dumppath = filesDir.toString() + "/" + dex_len.toString(0x10) + ".dex"; 83 | console.log(dumppath); 84 | var dumpdexfile = new File(dumppath, "wb"); 85 | dumpdexfile.write(Memory.readByteArray(dexrec[i], dex_len)); 86 | dumpdexfile.close(); 87 | console.log("write file to " + dumppath); 88 | } 89 | } 90 | console.log("End dump dex "); 91 | } 92 | } else { 93 | console.error("unable get classloader"); 94 | } 95 | return result; 96 | } 97 | } else { 98 | console.error("application is null"); 99 | } 100 | }); 101 | } 102 | } 103 | 104 | exports.unpack_common = unpack_common; 105 | -------------------------------------------------------------------------------- /utils/android/unpack/fridaUnpackSimply.js: -------------------------------------------------------------------------------- 1 | //代码在android os: 7.1.2上测试通过 2 | //32位的libart.so 3 | var opencommon = Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenCommonEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_PNS0_12VerifyResultE"); 4 | console.log("opencommon addr: "+opencommon); 5 | Interceptor.attach(opencommon, { 6 | onEnter: function (args) { 7 | 8 | //dex起始位置 9 | var begin = args[1] 10 | console.log(begin); 11 | //打印magic 12 | console.log("magic : " + Memory.readUtf8String(begin)) 13 | //dex fileSize 地址 14 | var address = parseInt(begin,16) + 0x20 15 | //dex 大小 16 | var dex_size = Memory.readInt(ptr(address)) 17 | 18 | console.log("dex_size :" + dex_size) 19 | //dump dex 到/data/data/pkg/目录下 20 | var file = new File("/sdcard/unpack/" + dex_size + ".dex", "wb") 21 | file.write(Memory.readByteArray(begin, dex_size)) 22 | file.flush() 23 | file.close() 24 | }, 25 | onLeave: function (retval) { 26 | if (retval.toInt32() > 0) { 27 | 28 | } 29 | } 30 | }); 31 | 32 | /* 33 | //64位的libart.so 34 | var openmemory = Module.findExportByName("libart.so","_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"); 35 | console.log("openmemory addr: "+openmemory); 36 | Interceptor.attach(openmemory, { 37 | onEnter: function (args) { 38 | 39 | //dex起始位置 40 | //64位这里获取的args[1]有bug,这里直接读取r0寄存器 41 | var begin = this.context.x0 42 | //console.log(this.context.x0); 43 | //打印magic 44 | console.log("magic : " + Memory.readUtf8String(begin)) 45 | //dex fileSize 地址 46 | var address = parseInt(begin,16) + 0x20 47 | //dex 大小 48 | var dex_size = Memory.readInt(ptr(address)) 49 | 50 | console.log("dex_size :" + dex_size) 51 | //dump dex 到/data/data/pkg/目录下 52 | var file = new File("/sdcard/unpack/" + dex_size + ".dex", "wb") 53 | file.write(Memory.readByteArray(begin, dex_size)) 54 | file.flush() 55 | file.close() 56 | }, 57 | onLeave: function (retval) { 58 | if (retval.toInt32() > 0) { 59 | 60 | } 61 | } 62 | }); 63 | */ 64 | 65 | -------------------------------------------------------------------------------- /utils/common/StringUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: StringUtils.js 5 | * @time: 2020/9/15 4:16 PM 6 | * @desc: 7 | */ 8 | 9 | export class StringUtils { 10 | 11 | static randomHexStr(count: number) { 12 | return this.random(count, "1234567890ABCDEF"); 13 | } 14 | 15 | static random(count: number, basestr: string) { 16 | var res = ""; 17 | for (var i = 0; i < count; i++) { 18 | res += basestr.charAt(Math.floor(Math.random() * basestr.length)); 19 | } 20 | return res; 21 | } 22 | 23 | static int2ip (ipInt: number, isHighEndian: boolean) { 24 | if (isHighEndian) { 25 | return ( (ipInt>>>24) +'.' + (ipInt>>16 & 255) +'.' + (ipInt>>8 & 255) +'.' + (ipInt & 255) ); 26 | } 27 | else { 28 | return ( (ipInt & 255) + '.' + (ipInt>>8 & 255) + '.' + (ipInt>>16 & 255) + '.' + (ipInt>>>24)); 29 | } 30 | } 31 | 32 | static ip2int(ip: string, isHighEndian: boolean) { 33 | if (isHighEndian) { 34 | return ip.split('.').reduce(function(ipInt, octet) { return (ipInt<<8) + parseInt(octet, 10)}, 0) >>> 0; 35 | } 36 | else { 37 | return ip.split('.').reduce(function(ipInt, octet, idx) { return (parseInt(octet, 10) << idx * 8) + ipInt}, 0) >>> 0; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /utils/dmlog.ts: -------------------------------------------------------------------------------- 1 | export class DMLog { 2 | static bDebug: boolean = true; 3 | 4 | static d(tag: string, str: string) { 5 | if (this.bDebug) { 6 | DMLog.log_(console.log, 'DEBUG', tag, str); 7 | } 8 | } 9 | 10 | static i(tag: string, str: string) { 11 | DMLog.log_(console.log, 'INFO', tag, str); 12 | } 13 | 14 | static w(tag: string, str: string) { 15 | DMLog.log_(console.warn, 'WARN', tag, str); 16 | } 17 | 18 | static e(tag: string, str: string) { 19 | DMLog.log_(console.error, 'ERROR', tag, str); 20 | } 21 | 22 | static log_(logfunc: (message?: any, ...optionalParams: any[]) => void, leval: string, tag: string, str: string) { 23 | let threadName = ""; 24 | if (Java.available) { 25 | Java.perform(() => { 26 | const Thread = Java.use('java.lang.Thread'); 27 | threadName = `[${Thread.currentThread().getName()}]`; 28 | }); 29 | } 30 | logfunc(`[${leval}][${new Date().toLocaleString('zh-CN')}][PID:${Process.id}]${threadName}[${Process.getCurrentThreadId()}][${tag}]: ${str}`); 31 | } 32 | 33 | static send(tag: string, content: string) { 34 | let tid = Process.getCurrentThreadId(); 35 | send(JSON.stringify({ 36 | tid: tid, 37 | status: 'msg', 38 | tag: tag, 39 | content: content 40 | })); 41 | } 42 | } -------------------------------------------------------------------------------- /utils/ios/AntiIOS.ts: -------------------------------------------------------------------------------- 1 | import {DMLog} from "../dmlog"; 2 | 3 | /** 4 | * @author: xingjun.xyf 5 | * @contact: deathmemory@163.com 6 | * @file: anti.js 7 | * @time: 2021/12/30 4:37 下午 8 | * @desc: 9 | */ 10 | 11 | export namespace AntiIOS { 12 | export function anti_ptrace() { 13 | var ptrace = Module.findExportByName(null, "ptrace"); 14 | if (null != ptrace) { 15 | DMLog.i('anti_ptrace', "ptrace addr: " + ptrace); 16 | Interceptor.replace(ptrace, new NativeCallback(function (p1: any, p2: any, p3: any, p4: any) { 17 | DMLog.i('anti_ptrace', 'entry'); 18 | return 1; 19 | }, 'long', ['int', "int", 'pointer', 'pointer'])); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /utils/repinning_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: xingjun.xyf 3 | * @contact: deathmemory@163.com 4 | * @file: repinning_test.js 5 | * @time: 2020/4/11 9:55 PM 6 | * @desc: 7 | */ 8 | 9 | setTimeout(function () { 10 | Java.perform(function () { 11 | var cf = null; 12 | console.log(""); 13 | console.log("[.] Cert Pinning Bypass/Re-Pinning"); 14 | 15 | var CertificateFactory = Java.use("java.security.cert.CertificateFactory"); 16 | var FileInputStream = Java.use("java.io.FileInputStream"); 17 | var BufferedInputStream = Java.use("java.io.BufferedInputStream"); 18 | var X509Certificate = Java.use("java.security.cert.X509Certificate"); 19 | var KeyStore = Java.use("java.security.KeyStore"); 20 | var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory"); 21 | var SSLContext = Java.use("javax.net.ssl.SSLContext"); 22 | 23 | // Load CAs from an InputStream 24 | console.log("[+] Loading our CA...") 25 | cf = CertificateFactory.getInstance("X.509"); 26 | 27 | try { 28 | var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt"); 29 | console.log("[i] fileInputStream: " + fileInputStream); 30 | } 31 | catch(err) { 32 | console.log("[o] " + err); 33 | } 34 | console.log("[i] BufferedInputStream: " + BufferedInputStream); 35 | 36 | var bufferedInputStream = BufferedInputStream.$new(fileInputStream); 37 | console.log("[i] ==========="); 38 | var ca = cf.generateCertificate(bufferedInputStream); 39 | bufferedInputStream.close(); 40 | 41 | var certInfo = Java.cast(ca, X509Certificate); 42 | console.log("[o] Our CA Info: " + certInfo.getSubjectDN()); 43 | 44 | // Create a KeyStore containing our trusted CAs 45 | console.log("[+] Creating a KeyStore for our CA..."); 46 | var keyStoreType = KeyStore.getDefaultType(); 47 | var keyStore = KeyStore.getInstance(keyStoreType); 48 | keyStore.load(null, null); 49 | keyStore.setCertificateEntry("ca", ca); 50 | 51 | // Create a TrustManager that trusts the CAs in our KeyStore 52 | console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore..."); 53 | var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 54 | var tmf = TrustManagerFactory.getInstance(tmfAlgorithm); 55 | tmf.init(keyStore); 56 | console.log("[+] Our TrustManager is ready..."); 57 | 58 | console.log("[+] Hijacking SSLContext methods now...") 59 | console.log("[-] Waiting for the app to invoke SSLContext.init()...") 60 | 61 | SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) { 62 | console.log("[o] App invoked javax.net.ssl.SSLContext.init..."); 63 | SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c); 64 | console.log("[+] SSLContext initialized with our custom TrustManager!"); 65 | } 66 | 67 | // var SSLContext = Java.use("javax.net.ssl.SSLContext"); 68 | // 69 | // // SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom") 70 | // // .implementation = function (keyManager, trustManager, sRandom) { 71 | // // console.log("[-] Waiting for the app to invoke SSLContext.init()..."); 72 | // // this.init(keyManager, trustManager, sRandom); 73 | // // console.log("[+] SSLContext initialized with our custom TrustManager!"); 74 | // // }; 75 | // SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) { 76 | // console.log("[o] App invoked javax.net.ssl.SSLContext.init..."); 77 | // SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c); 78 | // console.log("[+] SSLContext initialized with our custom TrustManager!"); 79 | // }; 80 | 81 | function showStacks() { 82 | console.log('showStacks', Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); // 打印堆栈 83 | } 84 | 85 | function hook_InMemoryDexClassLoader() { 86 | // dalvik.system.InMemoryDexClassLoader 87 | const InMemoryDexClassLoader = Java.use('dalvik.system.InMemoryDexClassLoader'); 88 | InMemoryDexClassLoader.$init.overload('java.nio.ByteBuffer', 'java.lang.ClassLoader') 89 | .implementation = function (buff, loader) { 90 | console.log('FaceBook.TAG' + ' hook_InMemoryDexClassLoader', 'entry'); 91 | this.$init(buff, loader); 92 | // showStacks(); 93 | var oldcl = Java.classFactory.loader; 94 | Java.classFactory.loader = this; 95 | var cls = Java.use('com.facebook.ads.redexgen.X.7J'); 96 | // var cls = this.loadClass('com.facebook.ads.redexgen.X.7J'); 97 | // var reflectClass = Java.use("java.lang.Class"); 98 | // var refcls = Java.cast(cls, reflectClass); 99 | // console.log('FaceBook.TAG' + ' hook_InMemoryDexClassLoader', 'name: ' + refcls.getName()); 100 | console.log('FaceBook.TAG' + ' hook_InMemoryDexClassLoader', 'cls: ' + cls); 101 | cls.A03.implementation = function (conn, set1, set2) { 102 | console.log('A03', 'entry'); 103 | console.log('A03', 'conn: ' + conn); 104 | console.log('A03', 'set1: ' + set1); 105 | console.log('A03', 'set2: ' + set2); 106 | // this.A03(conn, set1, set2); 107 | console.log('A03', 'Just returned'); 108 | }; 109 | Java.classFactory.loader = oldcl; 110 | // const _7J = loader0.loadClass('com.facebook.ads.redexgen.X.7J'); 111 | // console.log('FaceBook.TAG' + ' hook_InMemoryDexClassLoader', 'loader0: ' + loader0); 112 | // console.log('FaceBook.TAG' + ' hook_InMemoryDexClassLoader', 'find cls'); 113 | 114 | return undefined; 115 | } 116 | } 117 | 118 | function hook_DexClassLoader() { 119 | // dalvik.system.DexClassLoader; 120 | const DexClassLoader = Java.use('dalvik.system.DexClassLoader'); 121 | DexClassLoader.$init.implementation = function (p1, p2, p3, p4) { 122 | console.log('FaceBook.TAG' + ' hook_DexClassLoader', 'entry'); 123 | return this.$init(p1, p2, p3, p4); 124 | } 125 | } 126 | 127 | function hook_https() { 128 | const HttpsURLConnection = Java.use('javax.net.ssl.HttpsURLConnection'); 129 | HttpsURLConnection.getServerCertificates.implementation = function () { 130 | console.log('getServerCertificates', 'entry'); 131 | return this.getServerCertificates(); 132 | } 133 | } 134 | 135 | function hook_HttpURLConnection() { 136 | const HttpURLConnection = Java.use('java.net.HttpURLConnection'); 137 | HttpURLConnection.setConnectTimeout.implementation = function (num) { 138 | console.log('hook_HttpURLConnection', 'setConnectTimeout: ' + num); 139 | this.setConnectTimeout(num); 140 | } 141 | } 142 | 143 | hook_InMemoryDexClassLoader(); 144 | hook_DexClassLoader(); 145 | // hook_HttpURLConnection(); 146 | // hook_https(); 147 | }); 148 | }, 0); 149 | 150 | --------------------------------------------------------------------------------