├── .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 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
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 | 
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 | 
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 |
--------------------------------------------------------------------------------